def testIssue2671(self): self.Doc = App.newDocument("Issue2671") Box = self.Doc.addObject("Part::Box","Box") Mirroring = self.Doc.addObject("Part::Mirroring", 'Mirroring') Spreadsheet = self.Doc.addObject('Spreadsheet::Sheet', 'Spreadsheet') Mirroring.Source = Box Mirroring.Base = (8, 5, 25) Mirroring.Normal = (0.5, 0.2, 0.9) Spreadsheet.set('A1', '=Mirroring.Base.x') Spreadsheet.set('B1', '=Mirroring.Base.y') Spreadsheet.set('C1', '=Mirroring.Base.z') Spreadsheet.set('A2', '=Mirroring.Normal.x') Spreadsheet.set('B2', '=Mirroring.Normal.y') Spreadsheet.set('C2', '=Mirroring.Normal.z') self.Doc.recompute() self.assertEqual(Spreadsheet.A1, Units.Quantity('8 mm')) self.assertEqual(Spreadsheet.B1, Units.Quantity('5 mm')) self.assertEqual(Spreadsheet.C1, Units.Quantity('25 mm')) self.assertEqual(Spreadsheet.A2, Units.Quantity('0.5 mm')) self.assertEqual(Spreadsheet.B2, Units.Quantity('0.2 mm')) self.assertEqual(Spreadsheet.C2, Units.Quantity('0.9 mm')) App.closeDocument("Issue2671")
def get_region_data(self): # mesh regions if not self.mesh_obj.MeshRegionList: # print(' No mesh regions.') pass else: print(' Mesh regions, we need to get the elements.') # by the use of MeshRegion object and a BooleanSplitCompound # there could be problems with node numbers see # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467 # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520 part = self.part_obj if self.mesh_obj.MeshRegionList: # other part obj might not have a Proxy, thus an exception would be raised if part.Shape.ShapeType == "Compound" and hasattr( part, "Proxy"): if (part.Proxy.Type == "FeatureBooleanFragments" or part.Proxy.Type == "FeatureSlice" or part.Proxy.Type == "FeatureXOR"): error_message = ( ' The mesh to shape is a boolean split tools Compound ' 'and the mesh has mesh region list. ' 'Gmsh could return unexpected meshes in such circumstances. ' 'It is strongly recommended to extract the shape to mesh ' 'from the Compound and use this one.') FreeCAD.Console.PrintError(error_message + "\n") # TODO: no gui popup because FreeCAD will be in a endless output loop # as long as the pop up is on --> maybe find a better solution for # either of both --> thus the pop up is in task panel for mr_obj in self.mesh_obj.MeshRegionList: # print(mr_obj.Name) # print(mr_obj.CharacteristicLength) # print(Units.Quantity(mr_obj.CharacteristicLength).Value) if mr_obj.CharacteristicLength: if mr_obj.References: for sub in mr_obj.References: # print(sub[0]) # Part the elements belongs to # check if the shape of the mesh region is an element of the Part to mesh, # if not try to find the element in the shape to mesh search_ele_in_shape_to_mesh = False if not self.part_obj.Shape.isSame(sub[0].Shape): # print(" One element of the meshregion " + mr_obj.Name + " is not an element of the Part to mesh.") # print(" But we are going to try to find it in the Shape to mesh :-)") search_ele_in_shape_to_mesh = True for elems in sub[1]: # print(elems) # elems --> element if search_ele_in_shape_to_mesh: # we're going to try to find the element in the Shape to mesh and use the found element as elems # the method getElement(element) does not return Solid elements ele_shape = FemMeshTools.get_element( sub[0], elems) found_element = FemMeshTools.find_element_in_shape( self.part_obj.Shape, ele_shape) if found_element: elems = found_element else: FreeCAD.Console.PrintError( "One element of the meshregion {} could not be found " "in the Part to mesh. It will be ignored.\n" .format(mr_obj.Name)) # print(elems) # element if elems not in self.ele_length_map: self.ele_length_map[ elems] = Units.Quantity( mr_obj.CharacteristicLength).Value else: FreeCAD.Console.PrintError( "The element " + elems + " of the meshregion " + mr_obj.Name + " has been added to another mesh region.\n" ) else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the reference list is empty.\n" ) else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the CharacteristicLength is 0.0 mm.\n" ) for eleml in self.ele_length_map: ele_shape = FemMeshTools.get_element( self.part_obj, eleml ) # the method getElement(element) does not return Solid elements ele_vertexes = FemMeshTools.get_vertexes_by_element( self.part_obj.Shape, ele_shape) self.ele_node_map[eleml] = ele_vertexes print(' {}'.format(self.ele_length_map)) print(' {}'.format(self.ele_node_map))
def initUI(self): #self.setMinimumHeight(self.minHeight) self.setWindowTitle(translate("A2plus_constraintDialog",'Constraint properties')) #self.resize(300,600) self.mainLayout = QtGui.QGridLayout() # a VBoxLayout for the whole form #============================== lbl1 = QtGui.QLabel(self) lbl1.setText(self.constraintObject.Label) lbl1.setFrameStyle( QtGui.QFrame.Panel | QtGui.QFrame.Sunken ) self.mainLayout.addWidget(lbl1,self.lineNo,0,1,4) self.lineNo += 1 #============================== if hasattr(self.constraintObject,"directionConstraint"): lbl3 = QtGui.QLabel(self) lbl3.setText("Direction") lbl3.setFixedHeight(32) self.mainLayout.addWidget(lbl3,self.lineNo,0) self.directionCombo = QtGui.QComboBox(self) self.directionCombo.insertItem(0,"aligned") self.directionCombo.insertItem(1,"opposed") d = self.constraintObject.directionConstraint # not every constraint has a direction # # for compat with old A2plus assemblies if d == "none": self.directionCombo.insertItem(2,"none") # if d == "aligned": self.directionCombo.setCurrentIndex(0) elif d == "opposed": self.directionCombo.setCurrentIndex(1) elif d == "none": # will only occur with old A2plus assemblies self.directionCombo.setCurrentIndex(2) # self.directionCombo.setFixedHeight(32) self.directionCombo.currentIndexChanged[int].connect(self.flipDirection2) self.mainLayout.addWidget(self.directionCombo,self.lineNo,1) self.flipDirectionButton = QtGui.QPushButton(self) self.flipDirectionButton.setIcon(QtGui.QIcon(':/icons/a2p_FlipConstraint.svg')) self.flipDirectionButton.setText("Flip direction") self.flipDirectionButton.setFixedHeight(32) QtCore.QObject.connect(self.flipDirectionButton, QtCore.SIGNAL("clicked()"), self.flipDirection) self.mainLayout.addWidget(self.flipDirectionButton,self.lineNo,2) self.lineNo += 1 #============================== if hasattr(self.constraintObject,"offset"): offs = self.constraintObject.offset lbl4 = QtGui.QLabel(self) lbl4.setText("Offset") lbl4.setFixedHeight(32) self.mainLayout.addWidget(lbl4,self.lineNo,0) self.offsetEdit = QtGui.QDoubleSpinBox(self) # get the length unit as string self.offsetEdit.setSuffix(" " + str(FreeCAD.Units.Quantity(1, FreeCAD.Units.Length))[3:]) # the maximum is by default 99.99 and we can allow more self.offsetEdit.setMaximum(1e7) # allow up to 1 km # set minimum to negative of maximum self.offsetEdit.setMinimum(-1*self.offsetEdit.maximum()) # use the number of decimals defined by thew user in FC params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units") self.offsetEdit.setDecimals(params.GetInt('Decimals')) userPreferred = Units.Quantity(offs).getUserPreferred()[0] #offs is string with value and unit user_qty, user_unit = userPreferred.split(' ') user_qty = float(user_qty.replace(",",".")) #self.offsetEdit.setValue(offs.Value) #self.offsetEdit.setSuffix(" " + str(FreeCAD.Units.Quantity(1, FreeCAD.Units.Length))[3:]) self.offsetEdit.setSuffix(" " + user_unit) self.offsetEdit.setValue(user_qty) self.recentUnit = user_unit self.offsetEdit.setFixedHeight(32) QtCore.QObject.connect(self.offsetEdit, QtCore.SIGNAL("valueChanged(double)"), self.handleOffsetChanged) self.mainLayout.addWidget(self.offsetEdit,self.lineNo,1) self.offsetSetZeroButton = QtGui.QPushButton(self) self.offsetSetZeroButton.setText("Set Zero") self.offsetSetZeroButton.setFixedHeight(32) QtCore.QObject.connect(self.offsetSetZeroButton, QtCore.SIGNAL("clicked()"), self.setOffsetZero) self.mainLayout.addWidget(self.offsetSetZeroButton,self.lineNo,2) self.flipOffsetSignButton = QtGui.QPushButton(self) self.flipOffsetSignButton.setText("Flip sign") self.flipOffsetSignButton.setFixedHeight(32) QtCore.QObject.connect(self.flipOffsetSignButton, QtCore.SIGNAL("clicked()"), self.flipOffsetSign) self.mainLayout.addWidget(self.flipOffsetSignButton,self.lineNo,3) self.lineNo += 1 #============================== if hasattr(self.constraintObject,"angle"): angle = self.constraintObject.angle lbl5 = QtGui.QLabel(self) lbl5.setText("Angle") lbl5.setFixedHeight(32) self.mainLayout.addWidget(lbl5,self.lineNo,0) self.angleEdit = QtGui.QDoubleSpinBox(self) # get the angle unit as string self.angleEdit.setSuffix(" " + str(FreeCAD.Units.Quantity(1, FreeCAD.Units.Angle))[3:]) if self.constraintObject.Type == "axisPlaneAngle": self.angleEdit.setMaximum(90.0) self.angleEdit.setMinimum(0.0) # the solver treats negative values as positive else: self.angleEdit.setMaximum(180) self.angleEdit.setMinimum(0) # the solver treats negative values as positive # use the number of decimals defined by the user in FC params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units") self.angleEdit.setDecimals(params.GetInt('Decimals')) self.angleEdit.setValue(angle) self.angleEdit.setFixedHeight(32) self.angleEdit.setToolTip("Angle in the range 0 - 180 degrees") QtCore.QObject.connect(self.angleEdit, QtCore.SIGNAL("valueChanged(double)"), self.handleAngleChanged) self.mainLayout.addWidget(self.angleEdit,self.lineNo,1) self.roundAngleButton = QtGui.QPushButton(self) self.roundAngleButton.setText("Round") self.roundAngleButton.setFixedHeight(32) self.roundAngleButton.setToolTip("Round angle to multiples of 5") QtCore.QObject.connect(self.roundAngleButton, QtCore.SIGNAL("clicked()"), self.roundAngle) self.mainLayout.addWidget(self.roundAngleButton,self.lineNo,2) self.perpendicularAngleButton = QtGui.QPushButton(self) self.perpendicularAngleButton.setText("Perpendicular") self.perpendicularAngleButton.setFixedHeight(32) self.perpendicularAngleButton.setToolTip("Adds/deletes 90 degrees") QtCore.QObject.connect(self.perpendicularAngleButton, QtCore.SIGNAL("clicked()"), self.perpendicularAngle) self.mainLayout.addWidget(self.perpendicularAngleButton,self.lineNo,3) self.lineNo += 1 #============================== if hasattr(self.constraintObject,"lockRotation"): lbl6 = QtGui.QLabel(self) lbl6.setText("Lock Rotation") lbl6.setFixedHeight(32) self.mainLayout.addWidget(lbl6,self.lineNo,0) self.lockRotationCombo = QtGui.QComboBox(self) self.lockRotationCombo.insertItem(0,"False") self.lockRotationCombo.insertItem(1,"True") if self.constraintObject.lockRotation: # not every constraint has a direction self.lockRotationCombo.setCurrentIndex(1) else: self.lockRotationCombo.setCurrentIndex(0) self.lockRotationCombo.setFixedHeight(32) self.mainLayout.addWidget(self.lockRotationCombo,self.lineNo,1) self.flipLockRotationButton = QtGui.QPushButton(self) self.flipLockRotationButton.setIcon(QtGui.QIcon(':/icons/a2p_LockRotation.svg')) self.flipLockRotationButton.setText("Toggle") self.flipLockRotationButton.setFixedHeight(32) QtCore.QObject.connect(self.flipLockRotationButton, QtCore.SIGNAL("clicked()"), self.flipLockRotation) self.mainLayout.addWidget(self.flipLockRotationButton,self.lineNo,2) self.lineNo += 1 #============================== self.buttonPanel = QtGui.QWidget(self) self.buttonPanel.setFixedHeight(60) self.buttonPanel.setContentsMargins(4,4,4,4) self.buttonPanelLayout = QtGui.QHBoxLayout() self.deleteButton = QtGui.QPushButton(self.buttonPanel) self.deleteButton.setFixedHeight(32) self.deleteButton.setIcon(QtGui.QIcon(':/icons/a2p_DeleteConnections.svg')) #need new Icon self.deleteButton.setToolTip("Delete this constraint") self.deleteButton.setText("Delete this constraint") self.solveButton = QtGui.QPushButton(self.buttonPanel) self.solveButton.setFixedHeight(32) self.solveButton.setIcon(QtGui.QIcon(':/icons/a2p_Solver.svg')) self.solveButton.setToolTip("Solve constraints") self.solveButton.setText("Solve") self.acceptButton = QtGui.QPushButton(self.buttonPanel) self.acceptButton.setFixedHeight(32) self.acceptButton.setIcon(QtGui.QIcon(':/icons/a2p_CheckAssembly.svg')) #need new Icon self.acceptButton.setToolTip("Accept the settings") self.acceptButton.setText("Accept") #self.acceptButton.setDefault(True) self.buttonPanelLayout.addWidget(self.deleteButton) self.buttonPanelLayout.addWidget(self.solveButton) self.buttonPanelLayout.addWidget(self.acceptButton) self.buttonPanel.setLayout(self.buttonPanelLayout) self.mainLayout.addWidget(self.buttonPanel,self.lineNo,0,1,4) self.lineNo += 1 #============================== self.setLayout(self.mainLayout) #self.updateGeometry() #self.neededHight = 50+(self.lineNo+1)*40 #self.resize(self.neededHight,350) QtCore.QObject.connect(self.deleteButton, QtCore.SIGNAL("clicked()"), self.delete) QtCore.QObject.connect(self.solveButton, QtCore.SIGNAL("clicked()"), self.solve) QtCore.QObject.connect(self.acceptButton, QtCore.SIGNAL("clicked()"), self.accept)
def DVBalloonTest(): path = os.path.dirname(os.path.abspath(__file__)) print('TDBalloon path: ' + path) templateFileSpec = path + '/TestTemplate.svg' FreeCAD.newDocument("TDBalloon") FreeCAD.setActiveDocument("TDBalloon") FreeCAD.ActiveDocument = FreeCAD.getDocument("TDBalloon") #make source feature box = FreeCAD.ActiveDocument.addObject("Part::Box", "Box") sphere = FreeCAD.ActiveDocument.addObject("Part::Sphere", "Sphere") #make a page page = FreeCAD.ActiveDocument.addObject('TechDraw::DrawPage', 'Page') FreeCAD.ActiveDocument.addObject('TechDraw::DrawSVGTemplate', 'Template') FreeCAD.ActiveDocument.Template.Template = templateFileSpec FreeCAD.ActiveDocument.Page.Template = FreeCAD.ActiveDocument.Template page.Scale = 5.0 # page.ViewObject.show() # unit tests run in console mode #make Views view1 = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewPart', 'View') FreeCAD.ActiveDocument.View.Source = [FreeCAD.ActiveDocument.Box] rc = page.addView(view1) view1.X = Units.Quantity(30.0, Units.Length) view1.Y = Units.Quantity(150.0, Units.Length) view2 = FreeCAD.activeDocument().addObject('TechDraw::DrawViewPart', 'View001') FreeCAD.activeDocument().View001.Source = [FreeCAD.activeDocument().Sphere] rc = page.addView(view2) view2.X = Units.Quantity(220.0, Units.Length) view2.Y = Units.Quantity(150.0, Units.Length) FreeCAD.ActiveDocument.recompute() print("Place balloon") balloon1 = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewBalloon', 'Balloon1') balloon1.SourceView = view1 # balloon1.OriginIsSet=1 ##OriginIsSet property removed March 2020 balloon1.OriginX = view1.X + Units.Quantity(20.0, Units.Length) balloon1.OriginY = view1.Y + Units.Quantity(20.0, Units.Length) balloon1.Text = "1" balloon1.Y = balloon1.OriginX + Units.Quantity(20.0, Units.Length) balloon1.X = balloon1.OriginY + Units.Quantity(20.0, Units.Length) print("adding balloon1 to page") rc = page.addView(balloon1) balloon2 = FreeCAD.ActiveDocument.addObject('TechDraw::DrawViewBalloon', 'Balloon2') balloon2.SourceView = view2 # balloon2.OriginIsSet=1 balloon2.OriginX = view2.X + Units.Quantity(20.0, Units.Length) balloon2.OriginY = view2.Y + Units.Quantity(20.0, Units.Length) balloon2.Text = "2" balloon2.Y = balloon2.OriginX + Units.Quantity(20.0, Units.Length) balloon2.X = balloon2.OriginY + Units.Quantity(20.0, Units.Length) print("adding balloon2 to page") rc = page.addView(balloon2) FreeCAD.ActiveDocument.recompute() rc = False if ("Up-to-date" in balloon2.State) and ("Up-to-date" in balloon2.State): rc = True FreeCAD.closeDocument("TDBalloon") return rc
def getFromUi(value, unit, outputDim): quantity = Units.Quantity(str(value) + str(unit)) return convert(quantity, outputDim)
def testPrecedence(self): """ Precedence -- test precedence for relational operators and conditional operator. """ sheet = self.doc.addObject('Spreadsheet::Sheet', 'Spreadsheet') sheet.set('A1', '=1 < 2 ? 3 : 4') sheet.set('A2', '=1 + 2 < 3 + 4 ? 5 + 6 : 7 + 8') sheet.set( 'A3', '=1 + 2 * 1 < 3 + 4 ? 5 * 2 + 6 * 3 + 2 ^ 4 : 7 * 2 + 8 * 3 + 2 ^ 3' ) sheet.set('A4', '=123') sheet.set('A5', '=123 + 321') sheet.set('A6', '=123 * 2 + 321') sheet.set('A7', '=123 * 2 + 333 / 3') sheet.set('A8', '=123 * (2 + 321)') sheet.set('A9', '=3 ^ 4') sheet.set('A10', '=3 ^ 4 * 2') sheet.set('A11', '=3 ^ (4 * 2)') sheet.set('A12', '=3 ^ 4 + 4') sheet.set('A13', '=1 + 4 / 2 + 5') sheet.set('A14', '=(3 + 6) / (1 + 2)') sheet.set('A15', '=1 * 2 / 3 * 4') sheet.set('A16', '=(1 * 2) / (3 * 4)') # Test associativity sheet.set( 'A17', '=3 ^ 4 ^ 2' ) # exponentiation is left-associative; to follow excel, openoffice, matlab, octave sheet.set('A18', '=3 ^ (4 ^ 2)') # exponentiation is left-associative sheet.set('A19', '=(3 ^ 4) ^ 2') # exponentiation is left-associative sheet.set('A20', '=3 + 4 + 2') sheet.set('A21', '=3 + (4 + 2)') sheet.set('A22', '=(3 + 4) + 2') sheet.set('A23', '=3 - 4 - 2') sheet.set('A24', '=3 - (4 - 2)') sheet.set('A25', '=(3 - 4) - 2') sheet.set('A26', '=3 * 4 * 2') sheet.set('A27', '=3 * (4 * 2)') sheet.set('A28', '=(3 * 4) * 2') sheet.set('A29', '=3 / 4 / 2') sheet.set('A30', '=3 / (4 / 2)') sheet.set('A31', '=(3 / 4) / 2') sheet.set('A32', '=pi * 3') sheet.set('A33', '=A32 / 3') sheet.set('A34', '=1 < 2 ? <<A>> : <<B>>') sheet.set('A35', '=min(A32:A33)') sheet.set('A36', '=(1 < 2 ? 0 : 1) * 3') sheet.set('A37', '=8/(2^2*2)') sheet.set('A38', '=(2^2*2)/8') sheet.set('A39', '=2^(2*2)/8') sheet.set('A40', '=8/2^(2*2)') sheet.set('A41', '=-1') sheet.set('A42', '=-(1)') sheet.set('A43', '=-(1 + 1)') sheet.set('A44', '=-(1 - 1)') sheet.set('A45', '=-(-1 + 1)') sheet.set('A46', '=-(-1 + -1)') sheet.set('A47', '=+1') sheet.set('A48', '=+(1)') sheet.set('A49', '=+(1 + 1)') sheet.set('A50', '=+(1 - 1)') sheet.set('A51', '=+(-1 + 1)') sheet.set('A52', '=+(-1 + -1)') self.doc.addObject("Part::Cylinder", "Cylinder") self.doc.addObject("Part::Thickness", "Pipe") sheet.set('B1', '101') sheet.set('A53', '=-(-(B1-1)/2)') sheet.set('A54', '=-(Cylinder.Radius + Pipe.Value - 1"/2)') self.doc.recompute() self.assertEqual(sheet.getContents("A1"), "=1 < 2 ? 3 : 4") self.assertEqual(sheet.getContents("A2"), "=1 + 2 < 3 + 4 ? 5 + 6 : 7 + 8") self.assertEqual( sheet.getContents("A3"), "=1 + 2 * 1 < 3 + 4 ? 5 * 2 + 6 * 3 + 2 ^ 4 : 7 * 2 + 8 * 3 + 2 ^ 3" ) self.assertEqual(sheet.A1, 3) self.assertEqual(sheet.A2, 11) self.assertEqual(sheet.A3, 44) self.assertEqual(sheet.A4, 123) self.assertEqual(sheet.A5, 444) self.assertEqual(sheet.A6, 567) self.assertEqual(sheet.A7, 357) self.assertEqual(sheet.A8, 39729) self.assertEqual(sheet.A9, 81) self.assertEqual(sheet.A10, 162) self.assertEqual(sheet.A11, 6561) self.assertEqual(sheet.A12, 85) self.assertEqual(sheet.A13, 8) self.assertEqual(sheet.A14, 3) self.assertEqual(sheet.A15, 8.0 / 3) self.assertEqual(sheet.A16, 1.0 / 6) self.assertEqual(sheet.A17, 6561) self.assertEqual(sheet.A18, 43046721) self.assertEqual(sheet.A19, 6561) self.assertEqual(sheet.A20, 9) self.assertEqual(sheet.A21, 9) self.assertEqual(sheet.A22, 9) self.assertEqual(sheet.A23, -3) self.assertEqual(sheet.A24, 1) self.assertEqual(sheet.A25, -3) self.assertEqual(sheet.A26, 24) self.assertEqual(sheet.A27, 24) self.assertEqual(sheet.A28, 24) self.assertEqual(sheet.A29, 3.0 / 8) self.assertEqual(sheet.A30, 3.0 / 2) self.assertEqual(sheet.A31, 3.0 / 8) self.assertEqual(sheet.A37, 1) self.assertEqual(sheet.A38, 1) self.assertEqual(sheet.A39, 2) self.assertEqual(sheet.A40, 0.5) self.assertEqual(sheet.A41, -1) self.assertEqual(sheet.A42, -1) self.assertEqual(sheet.A43, -2) self.assertEqual(sheet.A44, 0) self.assertEqual(sheet.A45, 0) self.assertEqual(sheet.A46, 2) self.assertEqual(sheet.A47, 1) self.assertEqual(sheet.A48, 1) self.assertEqual(sheet.A49, 2) self.assertEqual(sheet.A50, 0) self.assertEqual(sheet.A51, 0) self.assertEqual(sheet.A52, -2) self.assertEqual(sheet.A53, 50) self.assertEqual(sheet.A54, Units.Quantity('9.7mm')) self.assertEqual(sheet.getContents('A1'), '=1 < 2 ? 3 : 4') self.assertEqual(sheet.getContents('A2'), '=1 + 2 < 3 + 4 ? 5 + 6 : 7 + 8') self.assertEqual( sheet.getContents('A3'), '=1 + 2 * 1 < 3 + 4 ? 5 * 2 + 6 * 3 + 2 ^ 4 : 7 * 2 + 8 * 3 + 2 ^ 3' ) self.assertEqual(sheet.getContents('A4'), '123') self.assertEqual(sheet.getContents('A5'), '=123 + 321') self.assertEqual(sheet.getContents('A6'), '=123 * 2 + 321') self.assertEqual(sheet.getContents('A7'), '=123 * 2 + 333 / 3') self.assertEqual(sheet.getContents('A8'), '=123 * (2 + 321)') self.assertEqual(sheet.getContents('A9'), '=3 ^ 4') self.assertEqual(sheet.getContents('A10'), '=3 ^ 4 * 2') self.assertEqual(sheet.getContents('A11'), '=3 ^ (4 * 2)') self.assertEqual(sheet.getContents('A12'), '=3 ^ 4 + 4') self.assertEqual(sheet.getContents('A13'), '=1 + 4 / 2 + 5') self.assertEqual(sheet.getContents('A14'), '=(3 + 6) / (1 + 2)') self.assertEqual(sheet.getContents('A15'), '=1 * 2 / 3 * 4') self.assertEqual(sheet.getContents('A16'), '=1 * 2 / (3 * 4)') self.assertEqual(sheet.getContents('A17'), '=3 ^ 4 ^ 2') self.assertEqual(sheet.getContents('A18'), '=3 ^ (4 ^ 2)') self.assertEqual(sheet.getContents('A19'), '=3 ^ 4 ^ 2') self.assertEqual(sheet.getContents('A20'), '=3 + 4 + 2') self.assertEqual(sheet.getContents('A21'), '=3 + 4 + 2') self.assertEqual(sheet.getContents('A22'), '=3 + 4 + 2') self.assertEqual(sheet.getContents('A23'), '=3 - 4 - 2') self.assertEqual(sheet.getContents('A24'), '=3 - (4 - 2)') self.assertEqual(sheet.getContents('A25'), '=3 - 4 - 2') self.assertEqual(sheet.getContents('A26'), '=3 * 4 * 2') self.assertEqual(sheet.getContents('A27'), '=3 * 4 * 2') self.assertEqual(sheet.getContents('A28'), '=3 * 4 * 2') self.assertEqual(sheet.getContents('A29'), '=3 / 4 / 2') self.assertEqual(sheet.getContents('A30'), '=3 / (4 / 2)') self.assertEqual(sheet.getContents('A31'), '=3 / 4 / 2') self.assertEqual(sheet.getContents('A32'), '=pi * 3') self.assertEqual(sheet.getContents('A33'), '=A32 / 3') self.assertEqual(sheet.getContents('A34'), '=1 < 2 ? <<A>> : <<B>>') self.assertEqual(sheet.getContents('A35'), '=min(A32:A33)') self.assertEqual(sheet.getContents('A36'), '=(1 < 2 ? 0 : 1) * 3') self.assertEqual(sheet.getContents('A37'), '=8 / (2 ^ 2 * 2)') self.assertEqual(sheet.getContents('A38'), '=2 ^ 2 * 2 / 8') self.assertEqual(sheet.getContents('A39'), '=2 ^ (2 * 2) / 8') self.assertEqual(sheet.getContents('A40'), '=8 / 2 ^ (2 * 2)') self.assertEqual(sheet.getContents('A41'), '=-1') self.assertEqual(sheet.getContents('A42'), '=-1') self.assertEqual(sheet.getContents('A43'), '=-(1 + 1)') self.assertEqual(sheet.getContents('A44'), '=-(1 - 1)') self.assertEqual(sheet.getContents('A45'), '=-(-1 + 1)') self.assertEqual(sheet.getContents('A46'), '=-(-1 + -1)') self.assertEqual(sheet.getContents('A47'), '=+1') self.assertEqual(sheet.getContents('A48'), '=+1') self.assertEqual(sheet.getContents('A49'), '=+(1 + 1)') self.assertEqual(sheet.getContents('A50'), '=+(1 - 1)') self.assertEqual(sheet.getContents('A51'), '=+(-1 + 1)') self.assertEqual(sheet.getContents('A52'), '=+(-1 + -1)')
def testAggregates(self): """ Test all aggregate functions """ sheet = self.doc.addObject('Spreadsheet::Sheet', 'Spreadsheet') sheet = self.doc.addObject('Spreadsheet::Sheet', 'Spreadsheet') sheet.set('B13', '4') sheet.set('B14', '5') sheet.set('B15', '6') sheet.set('C13', '4mm') sheet.set('C14', '5mm') sheet.set('C15', '6mm') sheet.set('C16', '6') sheet.set('A1', '=sum(1)') sheet.set('A2', '=sum(1;2)') sheet.set('A3', '=sum(1;2;3)') sheet.set('A4', '=sum(1;2;3;B13)') sheet.set('A5', '=sum(1;2;3;B13:B15)') sheet.set('B1', '=min(1)') sheet.set('B2', '=min(1;2)') sheet.set('B3', '=min(1;2;3)') sheet.set('B4', '=min(1;2;3;B13)') sheet.set('B5', '=min(1;2;3;B13:B15)') sheet.set('C1', '=max(1)') sheet.set('C2', '=max(1;2)') sheet.set('C3', '=max(1;2;3)') sheet.set('C4', '=max(1;2;3;B13)') sheet.set('C5', '=max(1;2;3;B13:B15)') sheet.set('D1', '=stddev(1)') sheet.set('D2', '=stddev(1;2)') sheet.set('D3', '=stddev(1;2;3)') sheet.set('D4', '=stddev(1;2;3;B13)') sheet.set('D5', '=stddev(1;2;3;B13:B15)') sheet.set('E1', '=count(1)') sheet.set('E2', '=count(1;2)') sheet.set('E3', '=count(1;2;3)') sheet.set('E4', '=count(1;2;3;B13)') sheet.set('E5', '=count(1;2;3;B13:B15)') sheet.set('F1', '=average(1)') sheet.set('F2', '=average(1;2)') sheet.set('F3', '=average(1;2;3)') sheet.set('F4', '=average(1;2;3;B13)') sheet.set('F5', '=average(1;2;3;B13:B15)') sheet.set('G1', '=average(C13:C15)') sheet.set('G2', '=min(C13:C15)') sheet.set('G3', '=max(C13:C15)') sheet.set('G4', '=count(C13:C15)') sheet.set('G5', '=stddev(C13:C15)') sheet.set('G6', '=sum(C13:C15)') sheet.set('H1', '=average(C13:C16)') sheet.set('H2', '=min(C13:C16)') sheet.set('H3', '=max(C13:C16)') sheet.set('H4', '=count(C13:C16)') sheet.set('H5', '=stddev(C13:C16)') sheet.set('H6', '=sum(C13:C16)') self.doc.recompute() self.assertEqual(sheet.A1, 1) self.assertEqual(sheet.A2, 3) self.assertEqual(sheet.A3, 6) self.assertEqual(sheet.A4, 10) self.assertEqual(sheet.A5, 21) self.assertEqual(sheet.B1, 1) self.assertEqual(sheet.B2, 1) self.assertEqual(sheet.B3, 1) self.assertEqual(sheet.B4, 1) self.assertEqual(sheet.B5, 1) self.assertEqual(sheet.C1, 1) self.assertEqual(sheet.C2, 2) self.assertEqual(sheet.C3, 3) self.assertEqual(sheet.C4, 4) self.assertEqual(sheet.C5, 6) self.assertEqual( sheet.D1, u'ERR: Invalid number of entries: at least two required.') self.assertEqual(sheet.D2, 0.7071067811865476) self.assertEqual(sheet.D3, 1.0) self.assertEqual(sheet.D4, 1.2909944487358056) self.assertEqual(sheet.D5, 1.8708286933869707) self.assertEqual(sheet.E1, 1) self.assertEqual(sheet.E2, 2) self.assertEqual(sheet.E3, 3) self.assertEqual(sheet.E4, 4) self.assertEqual(sheet.E5, 6) self.assertEqual(sheet.F1, 1) self.assertEqual(sheet.F2, (1.0 + 2.0) / 2.0) self.assertEqual(sheet.F3, (1.0 + 2 + 3) / 3) self.assertEqual(sheet.F4, (1.0 + 2 + 3 + 4) / 4) self.assertEqual(sheet.F5, (1.0 + 2 + 3 + 4 + 5 + 6) / 6) self.assertEqual(sheet.G1, Units.Quantity('5 mm')) self.assertEqual(sheet.G2, Units.Quantity('4 mm')) self.assertEqual(sheet.G3, Units.Quantity('6 mm')) self.assertEqual(sheet.G4, 3) self.assertEqual(sheet.G5, Units.Quantity('1 mm')) self.assertEqual(sheet.G6, Units.Quantity('15 mm')) self.assertEqual( sheet.H1, u'ERR: Quantity::operator +=(): Unit mismatch in plus operation') self.assertEqual( sheet.H2, u'ERR: Quantity::operator <(): quantities need to have same unit to compare' ) self.assertEqual( sheet.H3, u'ERR: Quantity::operator >(): quantities need to have same unit to compare' ) self.assertEqual(sheet.H4, 4) self.assertEqual( sheet.H5, u'ERR: Quantity::operator +(): Unit mismatch in minus operation') self.assertEqual( sheet.H6, u'ERR: Quantity::operator +=(): Unit mismatch in plus operation')
def __init__(self): self.name = "Orthogonal array" _log(_tr("Task panel:") + " {}".format(_tr(self.name))) # The .ui file must be loaded into an attribute # called `self.form` so that it is displayed in the task panel. ui_file = ":/ui/TaskPanel_OrthoArray.ui" self.form = Gui.PySideUic.loadUi(ui_file) icon_name = "Draft_Array" svg = ":/icons/" + icon_name pix = QtGui.QPixmap(svg) icon = QtGui.QIcon.fromTheme(icon_name, QtGui.QIcon(svg)) self.form.setWindowIcon(icon) self.form.setWindowTitle(_tr(self.name)) self.form.label_icon.setPixmap(pix.scaled(32, 32)) # ------------------------------------------------------------------- # Default values for the internal function, # and for the task panel interface start_x = U.Quantity(100.0, App.Units.Length) start_y = start_x start_z = start_x length_unit = start_x.getUserPreferred()[2] self.v_x = App.Vector(start_x.Value, 0, 0) self.v_y = App.Vector(0, start_y.Value, 0) self.v_z = App.Vector(0, 0, start_z.Value) self.form.input_X_x.setProperty('rawValue', self.v_x.x) self.form.input_X_x.setProperty('unit', length_unit) self.form.input_X_y.setProperty('rawValue', self.v_x.y) self.form.input_X_y.setProperty('unit', length_unit) self.form.input_X_z.setProperty('rawValue', self.v_x.z) self.form.input_X_z.setProperty('unit', length_unit) self.form.input_Y_x.setProperty('rawValue', self.v_y.x) self.form.input_Y_x.setProperty('unit', length_unit) self.form.input_Y_y.setProperty('rawValue', self.v_y.y) self.form.input_Y_y.setProperty('unit', length_unit) self.form.input_Y_z.setProperty('rawValue', self.v_y.z) self.form.input_Y_z.setProperty('unit', length_unit) self.form.input_Z_x.setProperty('rawValue', self.v_z.x) self.form.input_Z_x.setProperty('unit', length_unit) self.form.input_Z_y.setProperty('rawValue', self.v_z.y) self.form.input_Z_y.setProperty('unit', length_unit) self.form.input_Z_z.setProperty('rawValue', self.v_z.z) self.form.input_Z_z.setProperty('unit', length_unit) self.n_x = 2 self.n_y = 2 self.n_z = 1 self.form.spinbox_n_X.setValue(self.n_x) self.form.spinbox_n_Y.setValue(self.n_y) self.form.spinbox_n_Z.setValue(self.n_z) self.fuse = utils.get_param("Draft_array_fuse", False) self.use_link = utils.get_param("Draft_array_Link", True) self.form.checkbox_fuse.setChecked(self.fuse) self.form.checkbox_link.setChecked(self.use_link) # ------------------------------------------------------------------- # Some objects need to be selected before we can execute the function. self.selection = None # This is used to test the input of the internal function. # It should be changed to True before we can execute the function. self.valid_input = False self.set_widget_callbacks() self.tr_true = QT_TRANSLATE_NOOP("Draft", "True") self.tr_false = QT_TRANSLATE_NOOP("Draft", "False")
def parse(pathobj): global DRILL_RETRACT_MODE global MOTION_MODE global CURRENT_X global CURRENT_Y global CURRENT_Z out = '' lastcommand = None precision_string = '.' + str(PRECISION) + 'f' params = [ 'X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V', 'W', 'I', 'J', 'K', 'F', 'S', 'T', 'Q', 'R', 'L', 'P' ] if hasattr(pathobj, 'Group'): # We have a compound or project. if OUTPUT_COMMENTS: out += linenumber() + '(Compound: ' + pathobj.Label + ')\n' for p in pathobj.Group: out += parse(p) return out else: # Parsing simple path # groups might contain non-path things like stock. if not hasattr(pathobj, 'Path'): return out if OUTPUT_COMMENTS and OUTPUT_PATH: out += linenumber() + '(Path: ' + pathobj.Label + ')\n' for c in pathobj.Path.Commands: outlist = [] command = c.Name outlist.append(command) # Debug: # print('pathobj.Path.Commands:', c) # If modal is True, delete duplicate commands: if MODAL: if command == lastcommand: outlist.pop(0) # Add the remaining parameters in order: for param in params: if param in c.Parameters: if param == 'F': if command not in RAPID_MOVES: feedRate = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) if feedRate.getValueAs(UNIT_FEED_FORMAT) > 0.0: outlist.append(param + format( float(feedRate.getValueAs( UNIT_FEED_FORMAT)), precision_string)) elif param in ['T', 'H', 'D', 'S', 'P', 'L']: outlist.append(param + str(c.Parameters[param])) elif param in ['A', 'B', 'C']: outlist.append( param + format(c.Parameters[param], precision_string)) # [X, Y, Z, U, V, W, I, J, K, R, Q] else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outlist.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # Store the latest command: lastcommand = command # Capture the current position for subsequent calculations: if command in MOTION_COMMANDS: if 'X' in c.Parameters: CURRENT_X = Units.Quantity(c.Parameters['X'], FreeCAD.Units.Length) if 'Y' in c.Parameters: CURRENT_Y = Units.Quantity(c.Parameters['Y'], FreeCAD.Units.Length) if 'Z' in c.Parameters: CURRENT_Z = Units.Quantity(c.Parameters['Z'], FreeCAD.Units.Length) if command in ('G98', 'G99'): DRILL_RETRACT_MODE = command if TRANSLATE_DRILL_CYCLES: if command in ('G81', 'G82', 'G83'): out += drill_translate(outlist, command, c.Parameters) # Erase the line just translated: outlist = [] if SPINDLE_WAIT > 0: if command in ('M3', 'M03', 'M4', 'M04'): out += linenumber() + format_outlist(outlist) + '\n' # Marlin: P for milliseconds, S for seconds, change P to S out += linenumber() out += format_outlist(['G4', 'S%s' % SPINDLE_WAIT]) out += '\n' outlist = [] # Check for Tool Change: if command in ('M6', 'M06'): if OUTPUT_COMMENTS: out += linenumber() + '(Begin toolchange)\n' if OUTPUT_TOOL_CHANGE: for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line if not OUTPUT_TOOL_CHANGE and OUTPUT_COMMENTS: outlist[0] = '(' + outlist[0] outlist[-1] = outlist[-1] + ')' if not OUTPUT_TOOL_CHANGE and not OUTPUT_COMMENTS: outlist = [] if command == 'message': if OUTPUT_COMMENTS: outlist.pop(0) # remove the command else: out = [] if command in SUPPRESS_COMMANDS: outlist[0] = '(' + outlist[0] outlist[-1] = outlist[-1] + ')' # Remove embedded comments: if not OUTPUT_COMMENTS: tmplist = [] list_index = 0 while list_index < len(outlist): left_index = outlist[list_index].find('(') if left_index == -1: # Not a comment tmplist.append(outlist[list_index]) else: # This line contains a comment, and possibly more right_index = outlist[list_index].find(')') comment_area = outlist[list_index][ left_index:right_index + 1] line_minus_comment = outlist[list_index].replace( comment_area, '').strip() if line_minus_comment: # Line contained more than just a comment tmplist.append(line_minus_comment) list_index += 1 # Done removing comments outlist = tmplist # Prepend a line number and append a newline if len(outlist) >= 1: out += linenumber() + format_outlist(outlist) + '\n' return out
def check_prerequisites(self): from FreeCAD import Units message = "" # analysis if not self.analysis: message += "No active Analysis\n" if self.analysis_type not in self.known_analysis_types: message += "Unknown analysis type: {}\n".format(self.analysis_type) if not self.working_dir: message += "Working directory not set\n" import os if not (os.path.isdir(self.working_dir)): message += "Working directory \'{}\' doesn't exist.".format( self.working_dir) # solver if not self.solver: message += "No solver object defined in the analysis\n" else: if self.analysis_type == "frequency": if not hasattr(self.solver, "EigenmodeHighLimit"): message += "Frequency analysis: Solver has no EigenmodeHighLimit.\n" elif not hasattr(self.solver, "EigenmodeLowLimit"): message += "Frequency analysis: Solver has no EigenmodeLowLimit.\n" elif not hasattr(self.solver, "EigenmodesCount"): message += "Frequency analysis: Solver has no EigenmodesCount.\n" if hasattr(self.solver, "MaterialNonlinearity" ) and self.solver.MaterialNonlinearity == "nonlinear": if not self.materials_nonlinear: message += "Solver is set to nonlinear materials, but there is no nonlinear material in the analysis.\n" if self.solver.SolverType == 'FemSolverCalculix' and self.solver.GeometricalNonlinearity != "nonlinear": # nonlinear geometry --> should be set https://forum.freecadweb.org/viewtopic.php?f=18&t=23101&p=180489#p180489 message += "Solver CalculiX triggers nonlinear geometry for nonlinear material, thus it should to be set too.\n" # mesh if not self.mesh: message += "No mesh object defined in the analysis\n" if self.mesh: if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount > 0 and not self.shell_thicknesses: message += "FEM mesh has no volume elements, either define a shell thicknesses or provide a FEM mesh with volume elements.\n" if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount == 0 and self.mesh.FemMesh.EdgeCount > 0 and not self.beam_sections and not self.fluid_sections: message += "FEM mesh has no volume and no shell elements, either define a beam/fluid section or provide a FEM mesh with volume elements.\n" if self.mesh.FemMesh.VolumeCount == 0 and self.mesh.FemMesh.FaceCount == 0 and self.mesh.FemMesh.EdgeCount == 0: message += "FEM mesh has neither volume nor shell or edge elements. Provide a FEM mesh with elements!\n" # material linear and nonlinear if not self.materials_linear: message += "No material object defined in the analysis\n" has_no_references = False for m in self.materials_linear: if len(m['Object'].References) == 0: if has_no_references is True: message += "More than one material has an empty references list (Only one empty references list is allowed!).\n" has_no_references = True mat_ref_shty = '' for m in self.materials_linear: ref_shty = get_refshape_type(m['Object']) if not mat_ref_shty: mat_ref_shty = ref_shty if mat_ref_shty and ref_shty and ref_shty != mat_ref_shty: # mat_ref_shty could be empty in one material, only the not empty ones should have the same shape type message += 'Some material objects do not have the same reference shape type (all material objects must have the same reference shape type, at the moment).\n' for m in self.materials_linear: mat_map = m['Object'].Material mat_obj = m['Object'] if mat_obj.Category == 'Solid': if 'YoungsModulus' in mat_map: # print(Units.Quantity(mat_map['YoungsModulus']).Value) if not Units.Quantity(mat_map['YoungsModulus']).Value: message += "Value of YoungsModulus is set to 0.0.\n" else: message += "No YoungsModulus defined for at least one material.\n" if 'PoissonRatio' not in mat_map: message += "No PoissonRatio defined for at least one material.\n" # PoissonRatio is allowed to be 0.0 (in ccx), but it should be set anyway. if self.analysis_type == "frequency" or self.selfweight_constraints: if 'Density' not in mat_map: message += "No Density defined for at least one material.\n" if self.analysis_type == "thermomech": if 'ThermalConductivity' in mat_map: if not Units.Quantity( mat_map['ThermalConductivity']).Value: message += "Value of ThermalConductivity is set to 0.0.\n" else: message += "Thermomechanical analysis: No ThermalConductivity defined for at least one material.\n" if 'ThermalExpansionCoefficient' not in mat_map and mat_obj.Category == 'Solid': message += "Thermomechanical analysis: No ThermalExpansionCoefficient defined for at least one material.\n" # allowed to be 0.0 (in ccx) if 'SpecificHeat' not in mat_map: message += "Thermomechanical analysis: No SpecificHeat defined for at least one material.\n" # allowed to be 0.0 (in ccx) for m in self.materials_linear: has_nonlinear_material = False for nlm in self.materials_nonlinear: if nlm['Object'].LinearBaseMaterial == m['Object']: if has_nonlinear_material is False: has_nonlinear_material = True else: message += "At least two nonlinear materials use the same linear base material. Only one nonlinear material for each linear material allowed.\n" # which analysis needs which constraints # no check in the regard of loads existence (constraint force, pressure, self weight) is done because an analysis without loads at all is an valid analysis too if self.analysis_type == "static": if not (self.fixed_constraints or self.displacement_constraints): message += "Static analysis: Neither constraint fixed nor constraint displacement defined.\n" if self.analysis_type == "thermomech": if not self.initialtemperature_constraints: if not self.fluid_sections: message += "Thermomechanical analysis: No initial temperature defined.\n" if len(self.initialtemperature_constraints) > 1: message += "Thermomechanical analysis: Only one initial temperature is allowed.\n" # constraints # fixed if self.fixed_constraints: for c in self.fixed_constraints: if len(c['Object'].References) == 0: message += "At least one constraint fixed has an empty reference.\n" # displacement if self.displacement_constraints: for di in self.displacement_constraints: if len(di['Object'].References) == 0: message += "At least one constraint displacement has an empty reference.\n" # plane rotation if self.planerotation_constraints: for c in self.planerotation_constraints: if len(c['Object'].References) == 0: message += "At least one constraint plane rotation has an empty reference.\n" # contact if self.contact_constraints: for c in self.contact_constraints: if len(c['Object'].References) == 0: message += "At least one constraint contact has an empty reference.\n" # transform if self.transform_constraints: for c in self.transform_constraints: if len(c['Object'].References) == 0: message += "At least one constraint transform has an empty reference.\n" # pressure if self.pressure_constraints: for c in self.pressure_constraints: if len(c['Object'].References) == 0: message += "At least one constraint pressure has an empty reference.\n" # force if self.force_constraints: for c in self.force_constraints: if len(c['Object'].References) == 0: message += "At least one constraint force has an empty reference.\n" # temperature if self.temperature_constraints: for c in self.temperature_constraints: if len(c['Object'].References) == 0: message += "At least one constraint temperature has an empty reference.\n" # heat flux if self.heatflux_constraints: for c in self.heatflux_constraints: if len(c['Object'].References) == 0: message += "At least one constraint heat flux has an empty reference.\n" # beam section if self.beam_sections: if self.shell_thicknesses: # this needs to be checked only once either here or in shell_thicknesses message += "Beam Sections and shell thicknesses in one analysis is not supported at the moment.\n" if self.fluid_sections: # this needs to be checked only once either here or in shell_thicknesses message += "Beam Sections and Fluid Sections in one analysis is not supported at the moment.\n" has_no_references = False for b in self.beam_sections: if len(b['Object'].References) == 0: if has_no_references is True: message += "More than one beam section has an empty references list (Only one empty references list is allowed!).\n" has_no_references = True if self.mesh: if self.mesh.FemMesh.FaceCount > 0 or self.mesh.FemMesh.VolumeCount > 0: message += "Beam sections defined but FEM mesh has volume or shell elements.\n" if self.mesh.FemMesh.EdgeCount == 0: message += "Beam sections defined but FEM mesh has no edge elements.\n" # shell thickness if self.shell_thicknesses: has_no_references = False for s in self.shell_thicknesses: if len(s['Object'].References) == 0: if has_no_references is True: message += "More than one shell thickness has an empty references list (Only one empty references list is allowed!).\n" has_no_references = True if self.mesh: if self.mesh.FemMesh.VolumeCount > 0: message += "Shell thicknesses defined but FEM mesh has volume elements.\n" if self.mesh.FemMesh.FaceCount == 0: message += "Shell thicknesses defined but FEM mesh has no shell elements.\n" # fluid section if self.fluid_sections: if not self.selfweight_constraints: message += "A fluid network analysis requires self weight constraint to be applied" if self.analysis_type != "thermomech": message += "A fluid network analysis can only be done in a thermomech analysis" has_no_references = False for f in self.fluid_sections: if len(f['Object'].References) == 0: if has_no_references is True: message += "More than one fluid section has an empty references list (Only one empty references list is allowed!).\n" has_no_references = True if self.mesh: if self.mesh.FemMesh.FaceCount > 0 or self.mesh.FemMesh.VolumeCount > 0: message += "Fluid sections defined but FEM mesh has volume or shell elements.\n" if self.mesh.FemMesh.EdgeCount == 0: message += "Fluid sections defined but FEM mesh has no edge elements.\n" return message
def onChanged(self, vobj, prop): if prop in ["Text", "Decimals", "ShowUnit"]: if hasattr(self, "text1") and hasattr(self, "text2") and hasattr( vobj, "Text"): self.text1.string.deleteValues(0) self.text2.string.deleteValues(0) text1 = [] text2 = [] first = True for t in vobj.Text: if t: if hasattr(vobj.Object, "Area"): from FreeCAD import Units q = Units.Quantity(vobj.Object.Area, Units.Area).getUserPreferred() qt = vobj.Object.Area / q[1] if hasattr(vobj, "Decimals"): if vobj.Decimals == 0: qt = str(int(qt)) else: f = "%." + str(abs(vobj.Decimals)) + "f" qt = f % qt else: qt = str(qt) if hasattr(vobj, "ShowUnit"): if vobj.ShowUnit: qt = qt + q[2].replace( "^2", u"\xb2") # square symbol t = t.replace("$area", qt) t = t.replace("$label", vobj.Object.Label) if hasattr(vobj.Object, "Tag"): t = t.replace("$tag", vobj.Object.Tag) if hasattr(vobj.Object, "FinishFloor"): t = t.replace("$floor", vobj.Object.FinishFloor) if hasattr(vobj.Object, "FinishWalls"): t = t.replace("$walls", vobj.Object.FinishWalls) if hasattr(vobj.Object, "FinishCeiling"): t = t.replace("$ceiling", vobj.Object.FinishCeiling) if first: text1.append(t.encode("utf8")) else: text2.append(t.encode("utf8")) first = False if text1: self.text1.string.setValues(text1) if text2: self.text2.string.setValues(text2) elif prop == "FontName": if hasattr(self, "font") and hasattr(vobj, "FontName"): self.font.name = str(vobj.FontName) elif (prop == "FontSize"): if hasattr(self, "font") and hasattr(vobj, "FontSize"): self.font.size = vobj.FontSize.Value elif (prop == "FirstLine"): if hasattr(self, "header") and hasattr( vobj, "FontSize") and hasattr(vobj, "FirstLine"): scale = vobj.FirstLine.Value / vobj.FontSize.Value self.header.scaleFactor.setValue([scale, scale, scale]) elif prop == "TextColor": if hasattr(self, "color") and hasattr(vobj, "TextColor"): c = vobj.TextColor self.color.rgb.setValue(c[0], c[1], c[2]) elif prop == "TextPosition": if hasattr(self, "coords") and hasattr(self, "header") and hasattr( vobj, "TextPosition") and hasattr(vobj, "FirstLine"): pos = self.getTextPosition(vobj) self.coords.translation.setValue([pos.x, pos.y, pos.z]) up = vobj.FirstLine.Value * vobj.LineSpacing self.header.translation.setValue([0, up, 0]) elif prop == "LineSpacing": if hasattr(self, "text1") and hasattr(self, "text2") and hasattr( vobj, "LineSpacing"): self.text1.spacing = vobj.LineSpacing self.text2.spacing = vobj.LineSpacing self.onChanged(vobj, "TextPosition") elif prop == "TextAlign": if hasattr(self, "text1") and hasattr(self, "text2") and hasattr( vobj, "TextAlign"): from pivy import coin if vobj.TextAlign == "Center": self.text1.justification = coin.SoAsciiText.CENTER self.text2.justification = coin.SoAsciiText.CENTER elif vobj.TextAlign == "Right": self.text1.justification = coin.SoAsciiText.RIGHT self.text2.justification = coin.SoAsciiText.RIGHT else: self.text1.justification = coin.SoAsciiText.LEFT self.text2.justification = coin.SoAsciiText.LEFT
def parse(pathobj): global PRECISION global UNIT_FORMAT global UNIT_SPEED_FORMAT out = "" lastcommand = None precision_string = "." + str(PRECISION) + "f" # params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control the order of parameters params = ["X", "Y", "Z", "A", "B", "I", "J", "F", "S", "T", "Q", "R", "L"] if hasattr(pathobj, "Group"): # We have a compound or project. if OUTPUT_COMMENTS: out += linenumber() + "(compound: " + pathobj.Label + ")\n" for p in pathobj.Group: out += parse(p) return out else: # parsing simple path if not hasattr( pathobj, "Path"): # groups might contain non-path things like stock. return out if OUTPUT_COMMENTS: out += linenumber() + "(Path: " + pathobj.Label + ")\n" for c in pathobj.Path.Commands: outstring = [] command = c.Name outstring.append(command) # if modal: only print the command if it is not the same as the last one if MODAL is True: if command == lastcommand: outstring.pop(0) # Now add the remaining parameters in order for param in params: if param in c.Parameters: if param == "F": speed = Units.Quantity(c.Parameters["F"], FreeCAD.Units.Velocity) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: outstring.append(param + format( float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string, )) elif param == "S": outstring.append( param + format(c.Parameters[param], precision_string)) elif param == "T": outstring.append( param + format(c.Parameters["T"], precision_string)) else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outstring.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # store the latest command lastcommand = command # Check for Tool Change: if command == "M6": if OUTPUT_COMMENTS: out += linenumber() + "(begin toolchange)\n" for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line if command == "message": if OUTPUT_COMMENTS is False: out = [] else: outstring.pop(0) # remove the command # prepend a line number and append a newline if len(outstring) >= 1: if OUTPUT_LINE_NUMBERS: outstring.insert(0, (linenumber())) # append the line to the final output for w in outstring: out += w + COMMAND_SPACE out = out.strip() + "\n" return out
def writeMeshCase(self): """ Collect case settings, and finally build a runnable case. """ CfdTools.cfdMessage( "Populating mesh dictionaries in folder {}\n".format( self.meshCaseDir)) if self.mesh_obj.MeshUtility == "cfMesh": self.cf_settings['ClMax'] = self.clmax * self.scale if len(self.cf_settings['BoundaryLayers']) > 0: self.cf_settings['BoundaryLayerPresent'] = True else: self.cf_settings['BoundaryLayerPresent'] = False if len(self.cf_settings["InternalRegions"]) > 0: self.cf_settings['InternalRefinementRegionsPresent'] = True else: self.cf_settings['InternalRefinementRegionsPresent'] = False elif self.mesh_obj.MeshUtility == "snappyHexMesh": bound_box = self.part_obj.Shape.BoundBox bC = 5 # Number of background mesh buffer cells x_min = (bound_box.XMin - bC * self.clmax) * self.scale x_max = (bound_box.XMax + bC * self.clmax) * self.scale y_min = (bound_box.YMin - bC * self.clmax) * self.scale y_max = (bound_box.YMax + bC * self.clmax) * self.scale z_min = (bound_box.ZMin - bC * self.clmax) * self.scale z_max = (bound_box.ZMax + bC * self.clmax) * self.scale cells_x = int(math.ceil(bound_box.XLength / self.clmax) + 2 * bC) cells_y = int(math.ceil(bound_box.YLength / self.clmax) + 2 * bC) cells_z = int(math.ceil(bound_box.ZLength / self.clmax) + 2 * bC) snappy_settings = self.snappy_settings snappy_settings['BlockMesh'] = { "xMin": x_min, "xMax": x_max, "yMin": y_min, "yMax": y_max, "zMin": z_min, "zMax": z_max, "cellsX": cells_x, "cellsY": cells_y, "cellsZ": cells_z } inside_x = Units.Quantity( self.mesh_obj.PointInMesh.get('x')).Value * self.scale inside_y = Units.Quantity( self.mesh_obj.PointInMesh.get('y')).Value * self.scale inside_z = Units.Quantity( self.mesh_obj.PointInMesh.get('z')).Value * self.scale shape_face_names_list = [] for i in self.mesh_obj.ShapeFaceNames: shape_face_names_list.append(i) snappy_settings['ShapeFaceNames'] = tuple(shape_face_names_list) snappy_settings[ 'EdgeRefinementLevel'] = CfdTools.relLenToRefinementLevel( self.mesh_obj.EdgeRefinement) snappy_settings['PointInMesh'] = { "x": inside_x, "y": inside_y, "z": inside_z } snappy_settings[ 'CellsBetweenLevels'] = self.mesh_obj.CellsBetweenLevels if len(self.snappy_settings["InternalRegions"]) > 0: self.snappy_settings['InternalRefinementRegionsPresent'] = True else: self.snappy_settings[ 'InternalRefinementRegionsPresent'] = False elif self.mesh_obj.MeshUtility == "gmsh": if platform.system() == "Windows": exe = os.path.join(FreeCAD.getHomePath(), 'bin', 'gmsh.exe') else: exe = subprocess.check_output( ["which", "gmsh"], universal_newlines=True).rstrip('\n') self.gmsh_settings['Executable'] = CfdTools.translatePath(exe) self.gmsh_settings['ShapeFile'] = self.temp_file_shape self.gmsh_settings['HasLengthMap'] = False if self.ele_length_map: self.gmsh_settings['HasLengthMap'] = True self.gmsh_settings['LengthMap'] = self.ele_length_map self.gmsh_settings['NodeMap'] = {} for e in self.ele_length_map: ele_nodes = (''.join( (str(n + 1) + ', ') for n in self.ele_node_map[e])).rstrip(', ') self.gmsh_settings['NodeMap'][e] = ele_nodes self.gmsh_settings['ClMax'] = self.clmax self.gmsh_settings['ClMin'] = self.clmin sols = (''.join( (str(n + 1) + ', ') for n in range(len(self.mesh_obj.Part.Shape.Solids))) ).rstrip(', ') self.gmsh_settings['Solids'] = sols self.gmsh_settings['BoundaryFaceMap'] = {} # Write one boundary per face for i in range(len(self.mesh_obj.Part.Shape.Faces)): self.gmsh_settings['BoundaryFaceMap']['face' + str(i)] = i + 1 self.gmsh_settings['MeshFile'] = self.temp_file_mesh # Perform initialisation here rather than __init__ in case of path changes self.template_path = os.path.join(CfdTools.get_module_path(), "data", "defaultsMesh") mesh_region_present = False if self.mesh_obj.MeshUtility == "cfMesh" and len(self.cf_settings['MeshRegions']) > 0 or \ self.mesh_obj.MeshUtility == "snappyHexMesh" and len(self.snappy_settings['MeshRegions']) > 0: mesh_region_present = True self.settings = { 'Name': self.part_obj.Name, 'MeshPath': self.meshCaseDir, 'FoamRuntime': CfdTools.getFoamRuntime(), 'MeshUtility': self.mesh_obj.MeshUtility, 'MeshRegionPresent': mesh_region_present, 'CfSettings': self.cf_settings, 'SnappySettings': self.snappy_settings, 'GmshSettings': self.gmsh_settings, 'TwoDSettings': self.two_d_settings } if CfdTools.getFoamRuntime() != 'WindowsDocker': self.settings['TranslatedFoamPath'] = CfdTools.translatePath( CfdTools.getFoamDir()) if self.mesh_obj.NumberOfProcesses <= 1: self.mesh_obj.NumberOfProcesses = 1 self.settings['ParallelMesh'] = False else: self.settings['ParallelMesh'] = True self.settings['NumberOfProcesses'] = self.mesh_obj.NumberOfProcesses self.settings['NumberOfThreads'] = self.mesh_obj.NumberOfThreads TemplateBuilder.TemplateBuilder(self.meshCaseDir, self.template_path, self.settings) # Update Allmesh permission - will fail silently on Windows fname = os.path.join(self.meshCaseDir, "Allmesh") import stat s = os.stat(fname) os.chmod(fname, s.st_mode | stat.S_IEXEC) CfdTools.cfdMessage( "Successfully wrote meshCase to folder {}\n".format( self.meshCaseDir))
def processRefinements(self): """ Process mesh refinements """ mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj) if self.mesh_obj.MeshUtility == "gmsh": # mesh regions self.ele_length_map = {} # { 'ElementString' : element length } self.ele_node_map = {} # { 'ElementString' : [element nodes] } if not mr_objs: print(' No mesh refinements') else: print(' Mesh refinements found - getting elements') if self.part_obj.Shape.ShapeType == 'Compound': # see http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467 and http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520 err = "GMSH could return unexpected meshes for a boolean split tools Compound. It is strongly recommended to extract the shape to mesh from the Compound and use this one." FreeCAD.Console.PrintError(err + "\n") for mr_obj in mr_objs: if mr_obj.RelativeLength: if mr_obj.References: for sub in mr_obj.References: # Check if the shape of the mesh region is an element of the Part to mesh; # if not try to find the element in the shape to mesh search_ele_in_shape_to_mesh = False ref = FreeCAD.ActiveDocument.getObject(sub[0]) if not self.part_obj.Shape.isSame(ref.Shape): search_ele_in_shape_to_mesh = True elems = sub[1] if search_ele_in_shape_to_mesh: # Try to find the element in the Shape to mesh ele_shape = FemGeomTools.get_element( ref, elems ) # the method getElement(element) does not return Solid elements found_element = CfdTools.findElementInShape( self.part_obj.Shape, ele_shape) if found_element: elems = found_element else: FreeCAD.Console.PrintError( "One element of the meshregion " + mr_obj.Name + " could not be found in the Part to mesh. It will be ignored.\n" ) elems = None if elems: if elems not in self.ele_length_map: # self.ele_length_map[elems] = Units.Quantity(mr_obj.CharacteristicLength).Value mr_rellen = mr_obj.RelativeLength if mr_rellen > 1.0: mr_rellen = 1.0 FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " should not use a relative length greater than unity.\n" ) elif mr_rellen < 0.01: mr_rellen = 0.01 # Relative length should not be less than 1/100 of base length FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " should not use a relative length smaller than 0.01.\n" ) self.ele_length_map[ elems] = mr_rellen * self.clmax else: FreeCAD.Console.PrintError( "The element " + elems + " of the mesh refinement " + mr_obj.Name + " has been added to another mesh refinement.\n" ) else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the reference list is empty.\n" ) else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the CharacteristicLength is 0.0 mm.\n" ) for eleml in self.ele_length_map: ele_shape = FemGeomTools.get_element( self.part_obj, eleml ) # the method getElement(element) does not return Solid elements ele_vertexes = FemGeomTools.get_vertexes_by_element( self.part_obj.Shape, ele_shape) self.ele_node_map[eleml] = ele_vertexes else: cf_settings = self.cf_settings cf_settings['MeshRegions'] = {} cf_settings['BoundaryLayers'] = {} cf_settings['InternalRegions'] = {} snappy_settings = self.snappy_settings snappy_settings['MeshRegions'] = {} snappy_settings['InternalRegions'] = {} from collections import defaultdict ele_meshpatch_map = defaultdict(list) if not mr_objs: print(' No mesh refinement') else: print(' Mesh refinements - getting the elements') if "Boolean" in self.part_obj.Name: err = "Cartesian meshes should not be generated for boolean split compounds." FreeCAD.Console.PrintError(err + "\n") # Make list of list of all references for their corresponding mesh object bl_matched_faces = [] if self.mesh_obj.MeshUtility == 'cfMesh': CfdTools.cfdMessage("Matching refinement regions") region_face_list = [] for mr_id, mr_obj in enumerate(mr_objs): if mr_obj.NumberLayers > 1 and not mr_obj.Internal: refs = mr_obj.References for r in refs: obj = FreeCAD.ActiveDocument.getObject(r[0]) if not obj: raise RuntimeError( "Referenced object '{}' not found - object may " "have been deleted".format(r[0])) try: f = obj.Shape.getElement(r[1]) except Part.OCCError: raise RuntimeError( "Referenced face '{}:{}' not found - face may " "have been deleted".format(r[0], r[1])) region_face_list.append((f, mr_id)) # Make list of all faces in meshed shape with original index mesh_face_list = \ list(zip(self.mesh_obj.Part.Shape.Faces, range(len(self.mesh_obj.Part.Shape.Faces)))) # Match them up bl_matched_faces = CfdTools.matchFaces( region_face_list, mesh_face_list) for mr_id, mr_obj in enumerate(mr_objs): Internal = mr_obj.Internal if mr_obj.RelativeLength: # Store parameters per region mr_rellen = mr_obj.RelativeLength if mr_rellen > 1.0: mr_rellen = 1.0 FreeCAD.Console.PrintError( "The meshregion: {} should not use a relative length greater " "than unity.\n".format(mr_obj.Name)) elif mr_rellen < 0.001: mr_rellen = 0.001 # Relative length should not be less than 0.1% of base length FreeCAD.Console.PrintError( "The meshregion: {} should not use a relative length smaller " "than 0.001.\n".format(mr_obj.Name)) if not (self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle): fid = open( os.path.join(self.triSurfaceDir, mr_obj.Name + '.stl'), 'w') snappy_mesh_region_list = [] patch_list = [] for (si, sub) in enumerate(mr_obj.References): shape = FreeCAD.ActiveDocument.getObject( sub[0]).Shape elem = sub[1] if self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle: # Save baffle references or faces individually baffle = "{}{}{}".format( mr_obj.Name, sub[0], elem) fid = open( os.path.join(self.triSurfaceDir, baffle + ".stl"), 'w') snappy_mesh_region_list.append(baffle) if elem.startswith( 'Solid' ): # getElement doesn't work with solids for some reason elt = shape.Solids[int(elem.lstrip('Solid')) - 1] else: elt = shape.getElement(elem) if elt.ShapeType == 'Face' or elt.ShapeType == 'Solid': CfdTools.cfdMessage( "Triangulating part: {}:{} ...".format( FreeCAD.ActiveDocument.getObject( sub[0]).Label, sub[1])) facemesh = MeshPart.meshFromShape( elt, LinearDeflection=self.mesh_obj. STLLinearDeflection) CfdTools.cfdMessage(" writing to file\n") fid.write("solid {}{}{}\n".format( mr_obj.Name, sub[0], elem)) for face in facemesh.Facets: fid.write(" facet normal 0 0 0\n") fid.write(" outer loop\n") for i in range(3): p = [ i * self.scale for i in face.Points[i] ] fid.write( " vertex {} {} {}\n".format( p[0], p[1], p[2])) fid.write(" endloop\n") fid.write(" endfacet\n") fid.write("endsolid {}{}{}\n".format( mr_obj.Name, sub[0], elem)) if self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle: fid.close() if not (self.mesh_obj.MeshUtility == 'snappyHexMesh' and mr_obj.Baffle): fid.close() if self.mesh_obj.MeshUtility == 'cfMesh' and mr_obj.NumberLayers > 1 and not Internal: for mf in bl_matched_faces: if mr_id == mf[0]: sfN = self.mesh_obj.ShapeFaceNames[mf[1]] ele_meshpatch_map[mr_obj.Name].append(sfN) patch_list.append(sfN) # Limit expansion ratio to greater than 1.0 and less than 1.2 expratio = mr_obj.ExpansionRatio expratio = min(1.2, max(1.0, expratio)) cf_settings['BoundaryLayers'][ self.mesh_obj. ShapeFaceNames[mf[1]]] = { 'NumberLayers': mr_obj.NumberLayers, 'ExpansionRatio': expratio, 'FirstLayerHeight': self.scale * Units.Quantity( mr_obj.FirstLayerHeight).Value } if self.mesh_obj.MeshUtility == 'cfMesh': if not Internal: cf_settings['MeshRegions'][mr_obj.Name] = { 'RelativeLength': mr_rellen * self.clmax * self.scale, 'RefinementThickness': self.scale * Units.Quantity( mr_obj.RefinementThickness).Value, } else: cf_settings['InternalRegions'][mr_obj.Name] = { 'RelativeLength': mr_rellen * self.clmax * self.scale } elif self.mesh_obj.MeshUtility == 'snappyHexMesh': refinement_level = CfdTools.relLenToRefinementLevel( mr_obj.RelativeLength) if not Internal: if not mr_obj.Baffle: snappy_mesh_region_list.append(mr_obj.Name) edge_level = CfdTools.relLenToRefinementLevel( mr_obj.RegionEdgeRefinement) for rL in range(len(snappy_mesh_region_list)): mrName = mr_obj.Name + snappy_mesh_region_list[ rL] snappy_settings['MeshRegions'][mrName] = { 'RegionName': snappy_mesh_region_list[rL], 'RefinementLevel': refinement_level, 'EdgeRefinementLevel': edge_level, 'MaxRefinementLevel': max(refinement_level, edge_level), 'Baffle': mr_obj.Baffle } else: snappy_settings['InternalRegions'][ mr_obj.Name] = { 'RefinementLevel': refinement_level } else: FreeCAD.Console.PrintError( "The meshregion: " + mr_obj.Name + " is not used to create the mesh because the " "CharacteristicLength is 0.0 mm or the reference list is empty.\n" )
def getClmax(self): return Units.Quantity(self.clmax, Units.Length)
def get_boundary_layer_data(self): # mesh boundary layer # currently only one boundary layer setting object is allowed # but multiple boundary can be selected # Mesh.CharacteristicLengthMin, must be zero # or a value less than first inflation layer height if not self.mesh_obj.MeshBoundaryLayerList: # print(" No mesh boundary layer setting document object.") pass else: Console.PrintMessage(" Mesh boundary layers, we need to get the elements.\n") if self.part_obj.Shape.ShapeType == "Compound": # see http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467 and # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520 err = ( "Gmsh could return unexpected meshes for a boolean split tools Compound. " "It is strongly recommended to extract the shape to mesh " "from the Compound and use this one." ) Console.PrintError(err + "\n") for mr_obj in self.mesh_obj.MeshBoundaryLayerList: if mr_obj.MinimumThickness and Units.Quantity(mr_obj.MinimumThickness).Value > 0: if mr_obj.References: belem_list = [] for sub in mr_obj.References: # print(sub[0]) # Part the elements belongs to # check if the shape of the mesh boundary_layer is an # element of the Part to mesh # if not try to find the element in the shape to mesh search_ele_in_shape_to_mesh = False if not self.part_obj.Shape.isSame(sub[0].Shape): Console.PrintLog( " One element of the mesh boundary layer {} is " "not an element of the Part to mesh.\n" "But we are going to try to find it in " "the Shape to mesh :-)\n" .format(mr_obj.Name) ) search_ele_in_shape_to_mesh = True for elems in sub[1]: # print(elems) # elems --> element if search_ele_in_shape_to_mesh: # we try to find the element it in the Shape to mesh # and use the found element as elems # the method getElement(element) does not return Solid elements ele_shape = geomtools.get_element(sub[0], elems) found_element = geomtools.find_element_in_shape( self.part_obj.Shape, ele_shape ) if found_element: # also elems = found_element else: Console.PrintError( "One element of the mesh boundary layer {} could " "not be found in the Part to mesh. " "It will be ignored.\n" .format(mr_obj.Name) ) # print(elems) # element if elems not in self.bl_boundary_list: # fetch settings in DocumentObject # fan setting is not implemented belem_list.append(elems) self.bl_boundary_list.append(elems) else: Console.PrintError( "The element {} of the mesh boundary " "layer {} has been added " "to another mesh boundary layer.\n" .format(elems, mr_obj.Name) ) setting = {} setting["hwall_n"] = Units.Quantity(mr_obj.MinimumThickness).Value setting["ratio"] = mr_obj.GrowthRate setting["thickness"] = sum([ setting["hwall_n"] * setting["ratio"] ** i for i in range( mr_obj.NumberOfLayers ) ]) # setting["hwall_n"] * 5 # tangential cell dimension setting["hwall_t"] = setting["thickness"] # hfar: cell dimension outside boundary # should be set later if some character length is set if self.clmax > setting["thickness"] * 0.8 \ and self.clmax < setting["thickness"] * 1.6: setting["hfar"] = self.clmax else: # set a value for safety, it may works as background mesh cell size setting["hfar"] = setting["thickness"] # from face name -> face id is done in geo file write up # TODO: fan angle setup is not implemented yet if self.dimension == "2": setting["EdgesList"] = belem_list elif self.dimension == "3": setting["FacesList"] = belem_list else: Console.PrintError( "boundary layer is only supported for 2D and 3D mesh.\n" ) self.bl_setting_list.append(setting) else: Console.PrintError( "The mesh boundary layer: {} is not used to create " "the mesh because the reference list is empty.\n" .format(mr_obj.Name) ) else: Console.PrintError( "The mesh boundary layer: {} is not used to create " "the mesh because the min thickness is 0.0 mm.\n" .format(mr_obj.Name) ) Console.PrintMessage(" {}\n".format(self.bl_setting_list))
def __init__(self, gmsh_mesh_obj, analysis=None): # mesh obj self.mesh_obj = gmsh_mesh_obj # analysis if analysis: self.analysis = analysis else: self.analysis = None # part to mesh self.part_obj = self.mesh_obj.Part # clmax, CharacteristicLengthMax: float, 0.0 = 1e+22 self.clmax = Units.Quantity(self.mesh_obj.CharacteristicLengthMax).Value if self.clmax == 0.0: self.clmax = 1e+22 # clmin, CharacteristicLengthMin: float self.clmin = Units.Quantity(self.mesh_obj.CharacteristicLengthMin).Value # geotol, GeometryTolerance: float, 0.0 = 1e-08 self.geotol = self.mesh_obj.GeometryTolerance if self.geotol == 0.0: self.geotol = 1e-08 # order # known_element_orders = ["1st", "2nd"] self.order = self.mesh_obj.ElementOrder if self.order == "1st": self.order = "1" elif self.order == "2nd": self.order = "2" else: Console.PrintError("Error in order\n") # dimension self.dimension = self.mesh_obj.ElementDimension # Algorithm2D algo2D = self.mesh_obj.Algorithm2D if algo2D == "Automatic": self.algorithm2D = "2" elif algo2D == "MeshAdapt": self.algorithm2D = "1" elif algo2D == "Delaunay": self.algorithm2D = "5" elif algo2D == "Frontal": self.algorithm2D = "6" elif algo2D == "BAMG": self.algorithm2D = "7" elif algo2D == "DelQuad": self.algorithm2D = "8" else: self.algorithm2D = "2" # Algorithm3D algo3D = self.mesh_obj.Algorithm3D if algo3D == "Automatic": self.algorithm3D = "1" elif algo3D == "Delaunay": self.algorithm3D = "1" elif algo3D == "New Delaunay": self.algorithm3D = "2" elif algo3D == "Frontal": self.algorithm3D = "4" elif algo3D == "Frontal Delaunay": self.algorithm3D = "5" elif algo3D == "Frontal Hex": self.algorithm3D = "6" elif algo3D == "MMG3D": self.algorithm3D = "7" elif algo3D == "R-tree": self.algorithm3D = "9" else: self.algorithm3D = "1" # mesh groups if self.mesh_obj.GroupsOfNodes is True: self.group_nodes_export = True else: self.group_nodes_export = False self.group_elements = {} # mesh regions self.ele_length_map = {} # { "ElementString" : element length } self.ele_node_map = {} # { "ElementString" : [element nodes] } # mesh boundary layer self.bl_setting_list = [] # list of dict, each item map to MeshBoundaryLayer object self.bl_boundary_list = [] # to remove duplicated boundary edge or faces # other initializations self.temp_file_geometry = "" self.temp_file_mesh = "" self.temp_file_geo = "" self.mesh_name = "" self.gmsh_bin = "" self.error = False
def drill_translate(outlist, cmd, params): global DRILL_RETRACT_MODE global MOTION_MODE global CURRENT_X global CURRENT_Y global CURRENT_Z global UNITS global UNIT_FORMAT global UNIT_FEED_FORMAT class Drill: # Using a class is necessary for the nested functions. gcode = '' strFormat = '.' + str(PRECISION) + 'f' if OUTPUT_COMMENTS: # Comment the original command outlist[0] = '(' + outlist[0] outlist[-1] = outlist[-1] + ')' Drill.gcode += linenumber() + format_outlist(outlist) + '\n' # Cycle conversion only converts the cycles in the XY plane (G17). # --> ZX (G18) and YZ (G19) planes produce false gcode. drill_X = Units.Quantity(params['X'], FreeCAD.Units.Length) drill_Y = Units.Quantity(params['Y'], FreeCAD.Units.Length) drill_Z = Units.Quantity(params['Z'], FreeCAD.Units.Length) drill_R = Units.Quantity(params['R'], FreeCAD.Units.Length) drill_F = Units.Quantity(params['F'], FreeCAD.Units.Velocity) if cmd == 'G82': drill_DwellTime = params['P'] elif cmd == 'G83': drill_Step = Units.Quantity(params['Q'], FreeCAD.Units.Length) # R less than Z is error if drill_R < drill_Z: Drill.gcode += linenumber() + '(drill cycle error: R less than Z )\n' return Drill.gcode # Z height to retract to when drill cycle is done: if DRILL_RETRACT_MODE == 'G98' and CURRENT_Z > drill_R: RETRACT_Z = CURRENT_Z else: RETRACT_Z = drill_R # Z motion nested functions: def rapid_Z_to(new_Z): Drill.gcode += linenumber() + 'G0 Z' Drill.gcode += format(float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + '\n' def feed_Z_to(new_Z): Drill.gcode += linenumber() + 'G1 Z' Drill.gcode += format(float(new_Z.getValueAs(UNIT_FORMAT)), strFormat) + ' F' Drill.gcode += format(float(drill_F.getValueAs(UNIT_FEED_FORMAT)), '.2f') + '\n' # Make sure that Z is not below RETRACT_Z: if CURRENT_Z < RETRACT_Z: rapid_Z_to(RETRACT_Z) # Rapid to hole position XY: Drill.gcode += linenumber() + 'G0 X' Drill.gcode += format(float(drill_X.getValueAs(UNIT_FORMAT)), strFormat) + ' Y' Drill.gcode += format(float(drill_Y.getValueAs(UNIT_FORMAT)), strFormat) + '\n' # Rapid to R: rapid_Z_to(drill_R) # ************************************************************************* # * Drill cycles: * # * G80 Cancel the drill cycle * # * G81 Drill full depth in one pass * # * G82 Drill full depth in one pass, and pause at the bottom * # * G83 Drill in pecks, raising the drill to R height after each peck * # * In preparation for a rapid to the next hole position: * # * G98 After the hole has been drilled, retract to the initial Z value * # * G99 After the hole has been drilled, retract to R height * # * Select G99 only if safe to move from hole to hole at the R height * # ************************************************************************* if cmd in ('G81', 'G82'): feed_Z_to(drill_Z) # Drill hole in one step if cmd == 'G82': # Dwell time delay at the bottom of the hole Drill.gcode += linenumber() + 'G4 S' + str(drill_DwellTime) + '\n' # Marlin uses P for milliseconds, S for seconds, change P to S elif cmd == 'G83': # Peck drill cycle: chip_Space = drill_Step * 0.5 next_Stop_Z = drill_R - drill_Step while next_Stop_Z >= drill_Z: feed_Z_to(next_Stop_Z) # Drill one peck of depth # Set next depth, next_Stop_Z is still at the current hole depth if (next_Stop_Z - drill_Step) >= drill_Z: # Rapid up to clear chips: rapid_Z_to(drill_R) # Rapid down to just above last peck depth: rapid_Z_to(next_Stop_Z + chip_Space) # Update next_Stop_Z to next depth: next_Stop_Z -= drill_Step elif next_Stop_Z == drill_Z: break # Done else: # More to drill, but less than drill_Step # Rapid up to clear chips: rapid_Z_to(drill_R) # Rapid down to just above last peck depth: rapid_Z_to(next_Stop_Z + chip_Space) # Dril remainder of the hole depth: feed_Z_to(drill_Z) break # Done rapid_Z_to(RETRACT_Z) # Done, retract the drill return Drill.gcode
def testFunctions(self): """ Test all built-in simple functions """ doc = FreeCAD.newDocument() sheet = self.doc.addObject('Spreadsheet::Sheet', 'Spreadsheet') sheet.set('A1', '=cos(60)') # Cos sheet.set('B1', '=cos(60deg)') sheet.set('C1', '=cos(pi / 2 * 1rad)') sheet.set('A2', '=sin(30)') # Sin sheet.set('B2', '=sin(30deg)') sheet.set('C2', '=sin(pi / 6 * 1rad)') sheet.set('A3', '=tan(45)') # Tan sheet.set('B3', '=tan(45deg)') sheet.set('C3', '=tan(pi / 4 * 1rad)') sheet.set('A4', '=abs(3)') # Abs sheet.set('B4', '=abs(-3)') sheet.set('C4', '=abs(-3mm)') sheet.set('A5', '=exp(3)') # Exp sheet.set('B5', '=exp(-3)') sheet.set('C5', '=exp(-3mm)') sheet.set('A6', '=log(3)') # Log sheet.set('B6', '=log(-3)') sheet.set('C6', '=log(-3mm)') sheet.set('A7', '=log10(10)') # Log10 sheet.set('B7', '=log10(-3)') sheet.set('C7', '=log10(-3mm)') sheet.set('A8', '=round(3.4)') # Round sheet.set('B8', '=round(3.6)') sheet.set('C8', '=round(-3.4)') sheet.set('D8', '=round(-3.6)') sheet.set('E8', '=round(3.4mm)') sheet.set('F8', '=round(3.6mm)') sheet.set('G8', '=round(-3.4mm)') sheet.set('H8', '=round(-3.6mm)') sheet.set('A9', '=trunc(3.4)') # Trunc sheet.set('B9', '=trunc(3.6)') sheet.set('C9', '=trunc(-3.4)') sheet.set('D9', '=trunc(-3.6)') sheet.set('E9', '=trunc(3.4mm)') sheet.set('F9', '=trunc(3.6mm)') sheet.set('G9', '=trunc(-3.4mm)') sheet.set('H9', '=trunc(-3.6mm)') sheet.set('A10', '=ceil(3.4)') # Ceil sheet.set('B10', '=ceil(3.6)') sheet.set('C10', '=ceil(-3.4)') sheet.set('D10', '=ceil(-3.6)') sheet.set('E10', '=ceil(3.4mm)') sheet.set('F10', '=ceil(3.6mm)') sheet.set('G10', '=ceil(-3.4mm)') sheet.set('H10', '=ceil(-3.6mm)') sheet.set('A11', '=floor(3.4)') # Floor sheet.set('B11', '=floor(3.6)') sheet.set('C11', '=floor(-3.4)') sheet.set('D11', '=floor(-3.6)') sheet.set('E11', '=floor(3.4mm)') sheet.set('F11', '=floor(3.6mm)') sheet.set('G11', '=floor(-3.4mm)') sheet.set('H11', '=floor(-3.6mm)') sheet.set('A12', '=asin(0.5)') # Asin sheet.set('B12', '=asin(0.5mm)') sheet.set('A13', '=acos(0.5)') # Acos sheet.set('B13', '=acos(0.5mm)') sheet.set('A14', '=atan(sqrt(3))') # Atan sheet.set('B14', '=atan(0.5mm)') sheet.set('A15', '=sinh(0.5)') # Sinh sheet.set('B15', '=sinh(0.5mm)') sheet.set('A16', '=cosh(0.5)') # Cosh sheet.set('B16', '=cosh(0.5mm)') sheet.set('A17', '=tanh(0.5)') # Tanh sheet.set('B17', '=tanh(0.5mm)') sheet.set('A18', '=sqrt(4)') # Sqrt sheet.set('B18', '=sqrt(4mm^2)') sheet.set('A19', '=mod(7; 4)') # Mod sheet.set('B19', '=mod(-7; 4)') sheet.set('C19', '=mod(7mm; 4)') sheet.set('D19', '=mod(7mm; 4mm)') sheet.set('A20', '=atan2(3; 3)') # Atan2 sheet.set('B20', '=atan2(-3; 3)') sheet.set('C20', '=atan2(3mm; 3)') sheet.set('D20', '=atan2(3mm; 3mm)') sheet.set('A21', '=pow(7; 4)') # Pow sheet.set('B21', '=pow(-7; 4)') sheet.set('C21', '=pow(7mm; 4)') sheet.set('D21', '=pow(7mm; 4mm)') sheet.set('A23', '=hypot(3; 4)') # Hypot sheet.set('B23', '=hypot(-3; 4)') sheet.set('C23', '=hypot(3mm; 4)') sheet.set('D23', '=hypot(3mm; 4mm)') sheet.set('A24', '=hypot(3; 4; 5)') # Hypot sheet.set('B24', '=hypot(-3; 4; 5)') sheet.set('C24', '=hypot(3mm; 4; 5)') sheet.set('D24', '=hypot(3mm; 4mm; 5mm)') sheet.set('A26', '=cath(5; 3)') # Cath sheet.set('B26', '=cath(-5; 3)') sheet.set('C26', '=cath(5mm; 3)') sheet.set('D26', '=cath(5mm; 3mm)') l = math.sqrt(5 * 5 + 4 * 4 + 3 * 3) sheet.set('A27', '=cath(%0.15f; 5; 4)' % l) # Cath sheet.set('B27', '=cath(%0.15f; -5; 4)' % l) sheet.set('C27', '=cath(%0.15f mm; 5mm; 4)' % l) sheet.set('D27', '=cath(%0.15f mm; 5mm; 4mm)' % l) self.doc.recompute() self.assertMostlyEqual(sheet.A1, 0.5) # Cos self.assertMostlyEqual(sheet.B1, 0.5) self.assertMostlyEqual(sheet.C1, 0) self.assertMostlyEqual(sheet.A2, 0.5) # Sin self.assertMostlyEqual(sheet.B2, 0.5) self.assertMostlyEqual(sheet.C2, 0.5) self.assertMostlyEqual(sheet.A3, 1) # Tan self.assertMostlyEqual(sheet.B3, 1) self.assertMostlyEqual(sheet.C3, 1) self.assertMostlyEqual(sheet.A4, 3) # Abs self.assertMostlyEqual(sheet.B4, 3) self.assertMostlyEqual(sheet.C4, Units.Quantity('3 mm')) self.assertMostlyEqual(sheet.A5, math.exp(3)) # Exp self.assertMostlyEqual(sheet.B5, math.exp(-3)) self.assertEqual(sheet.C5, u'ERR: Unit must be empty.') self.assertMostlyEqual(sheet.A6, math.log(3)) # Log self.assertTrue(math.isnan(sheet.B6)) self.assertEqual(sheet.C6, u'ERR: Unit must be empty.') self.assertMostlyEqual(sheet.A7, math.log10(10)) # Log10 self.assertTrue(math.isnan(sheet.B7)) self.assertEqual(sheet.C7, u'ERR: Unit must be empty.') self.assertMostlyEqual(sheet.A8, 3) # Round self.assertMostlyEqual(sheet.B8, 4) self.assertMostlyEqual(sheet.C8, -3) self.assertMostlyEqual(sheet.D8, -4) self.assertEqual(sheet.E8, Units.Quantity('3 mm')) self.assertEqual(sheet.F8, Units.Quantity('4 mm')) self.assertEqual(sheet.G8, Units.Quantity('-3 mm')) self.assertEqual(sheet.H8, Units.Quantity('-4 mm')) self.assertMostlyEqual(sheet.A9, 3) # Trunc self.assertMostlyEqual(sheet.B9, 3) self.assertMostlyEqual(sheet.C9, -3) self.assertMostlyEqual(sheet.D9, -3) self.assertEqual(sheet.E9, Units.Quantity('3 mm')) self.assertEqual(sheet.F9, Units.Quantity('3 mm')) self.assertEqual(sheet.G9, Units.Quantity('-3 mm')) self.assertEqual(sheet.H9, Units.Quantity('-3 mm')) self.assertMostlyEqual(sheet.A10, 4) # Ceil self.assertMostlyEqual(sheet.B10, 4) self.assertMostlyEqual(sheet.C10, -3) self.assertMostlyEqual(sheet.D10, -3) self.assertMostlyEqual(sheet.E10, Units.Quantity('4 mm')) self.assertMostlyEqual(sheet.F10, Units.Quantity('4 mm')) self.assertMostlyEqual(sheet.G10, Units.Quantity('-3 mm')) self.assertMostlyEqual(sheet.H10, Units.Quantity('-3 mm')) self.assertMostlyEqual(sheet.A11, 3) # Floor self.assertMostlyEqual(sheet.B11, 3) self.assertMostlyEqual(sheet.C11, -4) self.assertMostlyEqual(sheet.D11, -4) self.assertMostlyEqual(sheet.E11, Units.Quantity('3 mm')) self.assertMostlyEqual(sheet.F11, Units.Quantity('3 mm')) self.assertMostlyEqual(sheet.G11, Units.Quantity('-4 mm')) self.assertMostlyEqual(sheet.H11, Units.Quantity('-4 mm')) self.assertMostlyEqual(sheet.A12, Units.Quantity('30 deg')) # Asin self.assertEqual(sheet.B12, u'ERR: Unit must be empty.') self.assertMostlyEqual(sheet.A13, Units.Quantity('60 deg')) # Acos self.assertEqual(sheet.B13, u'ERR: Unit must be empty.') self.assertMostlyEqual(sheet.A14, Units.Quantity('60 deg')) # Atan self.assertEqual(sheet.B14, u'ERR: Unit must be empty.') self.assertMostlyEqual(sheet.A15, math.sinh(0.5)) # Sinh self.assertEqual(sheet.B15, u'ERR: Unit must be empty.') self.assertMostlyEqual(sheet.A16, math.cosh(0.5)) # Cosh self.assertEqual(sheet.B16, u'ERR: Unit must be empty.') self.assertMostlyEqual(sheet.A17, math.tanh(0.5)) # Tanh self.assertEqual(sheet.B17, u'ERR: Unit must be empty.') self.assertMostlyEqual(sheet.A18, 2) # Sqrt self.assertMostlyEqual(sheet.B18, Units.Quantity('2 mm')) self.assertMostlyEqual(sheet.A19, 3) # Mod self.assertMostlyEqual(sheet.B19, -3) self.assertMostlyEqual(sheet.C19, Units.Quantity('3 mm')) self.assertEqual(sheet.D19, 3) self.assertMostlyEqual(sheet.A20, Units.Quantity('45 deg')) # Atan2 self.assertMostlyEqual(sheet.B20, Units.Quantity('-45 deg')) self.assertEqual(sheet.C20, u'ERR: Units must be equal') self.assertMostlyEqual(sheet.D20, Units.Quantity('45 deg')) self.assertMostlyEqual(sheet.A21, 2401) # Pow self.assertMostlyEqual(sheet.B21, 2401) self.assertMostlyEqual(sheet.C21, Units.Quantity('2401mm^4')) self.assertEqual(sheet.D21, u'ERR: Exponent is not allowed to have a unit.') self.assertMostlyEqual(sheet.A23, 5) # Hypot self.assertMostlyEqual(sheet.B23, 5) self.assertEqual(sheet.C23, u'ERR: Units must be equal') self.assertMostlyEqual(sheet.D23, Units.Quantity('5mm')) l = math.sqrt(3 * 3 + 4 * 4 + 5 * 5) self.assertMostlyEqual(sheet.A24, l) # Hypot self.assertMostlyEqual(sheet.B24, l) self.assertEqual(sheet.C24, u'ERR: Units must be equal') self.assertMostlyEqual(sheet.D24, Units.Quantity("7.07106781186548 mm")) self.assertMostlyEqual(sheet.A26, 4) # Cath self.assertMostlyEqual(sheet.B26, 4) self.assertEqual(sheet.C26, u'ERR: Units must be equal') self.assertMostlyEqual(sheet.D26, Units.Quantity('4mm')) l = math.sqrt(5 * 5 + 4 * 4 + 3 * 3) l = math.sqrt(l * l - 5 * 5 - 4 * 4) self.assertMostlyEqual(sheet.A27, l) # Cath self.assertMostlyEqual(sheet.B27, l) self.assertEqual(sheet.C27, u'ERR: Units must be equal') self.assertMostlyEqual(sheet.D27, Units.Quantity("3 mm")) FreeCAD.closeDocument(doc.Name)
def parse(pathobj): global PRECISION global MODAL global OUTPUT_DOUBLES global UNIT_FORMAT global UNIT_SPEED_FORMAT global POWER_ON_DELAY global PRE_FEED global POST_FEED out = "" lastcommand = None precision_string = '.' + str(PRECISION) + 'f' currLocation = {} # keep track for no doubles RAPID_MOVES = ["G0", "G00"] FEED_MOVES = ["G1", "G01", "G2", "G02", "G3", "G03"] # the order of parameters params = [ 'X', 'Y', 'Z', 'A', 'B', 'C', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L', 'H', 'D', 'P' ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters if hasattr(pathobj, "Group"): # We have a compound or project. for p in pathobj.Group: out += parse(p) return out else: # parsing simple path # groups might contain non-path things like stock. if not hasattr(pathobj, "Path"): return out for c in pathobj.Path.Commands: outstring = [] command = c.Name controlstring = "" if command in FEED_MOVES and lastcommand in RAPID_MOVES: controlstring = PRE_FEED.format(POWER_ON_DELAY) elif command in RAPID_MOVES and lastcommand in FEED_MOVES: controlstring = POST_FEED if len(controlstring) > 0: out += controlstring outstring.append(command) # if modal: suppress the command if it is the same as the last one if MODAL is True: if command == lastcommand: outstring.pop(0) if c.Name[0] == '(' and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: if param == 'F' and ( currLocation[param] != c.Parameters[param] or OUTPUT_DOUBLES): if c.Name not in RAPID_MOVES: # linuxcnc doesn't use rapid speeds speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: outstring.append(param + format( float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) else: continue elif param == 'T': outstring.append(param + str(int(c.Parameters['T']))) elif param == 'H': outstring.append(param + str(int(c.Parameters['H']))) elif param == 'D': outstring.append(param + str(int(c.Parameters['D']))) elif param == 'S': outstring.append(param + str(int(c.Parameters['S']))) else: if (not OUTPUT_DOUBLES) and ( param in currLocation) and (currLocation[param] == c.Parameters[param]): continue else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outstring.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: if command == 'M6': continue # if OUTPUT_COMMENTS: # out += linenumber() + "(begin toolchange)\n" for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line if command == "message": if OUTPUT_COMMENTS is False: out = [] else: outstring.pop(0) # remove the command # prepend a line number and append a newline if len(outstring) >= 1: if OUTPUT_LINE_NUMBERS: outstring.insert(0, (linenumber())) # append the line to the final output for w in outstring: out += w + COMMAND_SPACE out = out.strip() + "\n" return out
def testUnits(self): """ Units -- test unit calculations. """ sheet = self.doc.addObject('Spreadsheet::Sheet', 'Spreadsheet') sheet.set('A1', '=2mm + 3mm') sheet.set('A2', '=2mm - 3mm') sheet.set('A3', '=2mm * 3mm') sheet.set('A4', '=4mm / 2mm') sheet.set('A5', '=(4mm)^2') sheet.set('A6', '=5(mm^2)') sheet.set('A7', '=5mm^2') # ^2 operates on whole number sheet.set('A8', '=5') sheet.set('A9', '=5*1/K') # Currently fails sheet.set('A10', '=5 K^-1') # Currently fails sheet.set('A11', '=9.8 m/s^2') # Currently fails sheet.setDisplayUnit('A8', '1/K') self.doc.recompute() self.assertEqual(sheet.A1, Units.Quantity('5mm')) self.assertEqual(sheet.A2, Units.Quantity('-1 mm')) self.assertEqual(sheet.A3, Units.Quantity('6 mm^2')) self.assertEqual(sheet.A4, Units.Quantity('2')) self.assertEqual(sheet.A5, Units.Quantity('16 mm^2')) self.assertEqual(sheet.A6, Units.Quantity('5 mm^2')) self.assertEqual(sheet.A7, Units.Quantity('5 mm^2')) self.assertEqual(sheet.A8, Units.Quantity('5')) self.assertEqual(sheet.A9, Units.Quantity('5 K^-1')) self.assertEqual(sheet.A10, Units.Quantity('5 K^-1')) self.assertEqual(sheet.A11, Units.Quantity('9.8 m/s^2'))
def drill_translate(outstring, cmd, params): global DRILL_RETRACT_MODE global MOTION_MODE global CURRENT_X global CURRENT_Y global CURRENT_Z global UNITS global UNIT_FORMAT global UNIT_SPEED_FORMAT strFormat = '.' + str(PRECISION) + 'f' trBuff = "" if OUTPUT_COMMENTS: # Comment the original command outstring[0] = "(" + outstring[0] outstring[-1] = outstring[-1] + ")" trBuff += linenumber() + format_outstring(outstring) + "\n" # cycle conversion # currently only cycles in XY are provided (G17) # other plains ZX (G18) and YZ (G19) are not dealt with : Z drilling only. drill_X = Units.Quantity(params['X'], FreeCAD.Units.Length) drill_Y = Units.Quantity(params['Y'], FreeCAD.Units.Length) drill_Z = Units.Quantity(params['Z'], FreeCAD.Units.Length) RETRACT_Z = Units.Quantity(params['R'], FreeCAD.Units.Length) # R less than Z is error if RETRACT_Z < drill_Z : trBuff += linenumber() + "(drill cycle error: R less than Z )\n" return trBuff if MOTION_MODE == 'G91': # G91 relative movements drill_X += CURRENT_X drill_Y += CURRENT_Y drill_Z += CURRENT_Z RETRACT_Z += CURRENT_Z if DRILL_RETRACT_MODE == 'G98' and CURRENT_Z >= RETRACT_Z: RETRACT_Z = CURRENT_Z # get the other parameters drill_feedrate = Units.Quantity(params['F'], FreeCAD.Units.Velocity) if cmd == 'G83': drill_Step = Units.Quantity(params['Q'], FreeCAD.Units.Length) a_bit = drill_Step * 0.05 # NIST 3.5.16.4 G83 Cycle: "current hole bottom, backed off a bit." elif cmd == 'G82': drill_DwellTime = params['P'] # wrap this block to ensure machine MOTION_MODE is restored in case of error try: if MOTION_MODE == 'G91': trBuff += linenumber() + "G90\n" # force absolute coordinates during cycles strG0_RETRACT_Z = 'G0 Z' + format(float(RETRACT_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n" strF_Feedrate = ' F' + format(float(drill_feedrate.getValueAs(UNIT_SPEED_FORMAT)), '.2f') + "\n" print (strF_Feedrate) # preliminary mouvement(s) if CURRENT_Z < RETRACT_Z: trBuff += linenumber() + strG0_RETRACT_Z trBuff += linenumber() + 'G0 X' + format(float(drill_X.getValueAs(UNIT_FORMAT)), strFormat) + ' Y' + format(float(drill_Y.getValueAs(UNIT_FORMAT)), strFormat) + "\n" if CURRENT_Z > RETRACT_Z: # NIST GCODE 3.5.16.1 Preliminary and In-Between Motion says G0 to RETRACT_Z. Here use G1 since retract height may be below surface ! trBuff += linenumber() + 'G1 Z' + format(float(RETRACT_Z.getValueAs(UNIT_FORMAT)), strFormat) + strF_Feedrate last_Stop_Z = RETRACT_Z # drill moves if cmd in ('G81', 'G82'): trBuff += linenumber() + 'G1 Z' + format(float(drill_Z.getValueAs(UNIT_FORMAT)), strFormat) + strF_Feedrate # pause where applicable if cmd == 'G82': trBuff += linenumber() + 'G4 P' + str(drill_DwellTime) + "\n" trBuff += linenumber() + strG0_RETRACT_Z else: # 'G83' if params['Q'] != 0 : while 1: if last_Stop_Z != RETRACT_Z : clearance_depth = last_Stop_Z + a_bit # rapid move to just short of last drilling depth trBuff += linenumber() + 'G0 Z' + format(float(clearance_depth.getValueAs(UNIT_FORMAT)) , strFormat) + "\n" next_Stop_Z = last_Stop_Z - drill_Step if next_Stop_Z > drill_Z: trBuff += linenumber() + 'G1 Z' + format(float(next_Stop_Z.getValueAs(UNIT_FORMAT)), strFormat) + strF_Feedrate trBuff += linenumber() + strG0_RETRACT_Z last_Stop_Z = next_Stop_Z else: trBuff += linenumber() + 'G1 Z' + format(float(drill_Z.getValueAs(UNIT_FORMAT)), strFormat) + strF_Feedrate trBuff += linenumber() + strG0_RETRACT_Z break except Exception as e: pass if MOTION_MODE == 'G91': trBuff += linenumber() + 'G91' # Restore if changed return trBuff
def parse(pathobj): out = "" lastcommand = None global SPINDLE_SPEED # params = ['X','Y','Z','A','B','I','J','K','F','S'] #This list control # the order of parameters # linuxcnc doesn't want K properties on XY plane Arcs need work. params = ['X', 'Y', 'Z', 'A', 'B', 'I', 'J', 'F', 'S', 'T', 'Q', 'R', 'L'] if hasattr(pathobj, "Group"): # We have a compound or project. # if OUTPUT_COMMENTS: # out += linenumber() + "(compound: " + pathobj.Label + ")\n" for p in pathobj.Group: out += parse(p) return out else: # parsing simple path # groups might contain non-path things like stock. if not hasattr(pathobj, "Path"): return out # if OUTPUT_COMMENTS: # out += linenumber() + "(" + pathobj.Label + ")\n" for c in pathobj.Path.Commands: outstring = [] command = c.Name outstring.append(command) # if modal: only print the command if it is not the same as the # last one if MODAL is True: if command == lastcommand: outstring.pop(0) # Now add the remaining parameters in order for param in params: if param in c.Parameters: if param == 'F': if c.Name not in ["G0", "G00"]: #linuxcnc doesn't use rapid speeds speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) outstring.append( param + format(float(speed.getValueAs(UNIT_FORMAT)), '.2f') ) elif param == 'T': outstring.append(param + str(c.Parameters['T'])) elif param == 'S': outstring.append(param + str(c.Parameters['S'])) SPINDLE_SPEED = c.Parameters['S'] else: outstring.append( param + format(c.Parameters[param], '.4f')) if command in ['G1', 'G01', 'G2', 'G02', 'G3', 'G03']: outstring.append('S' + str(SPINDLE_SPEED)) # store the latest command lastcommand = command # Check for Tool Change: if command == 'M6': # if OUTPUT_COMMENTS: # out += linenumber() + "(begin toolchange)\n" for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line if command == "message": if OUTPUT_COMMENTS is False: out = [] else: outstring.pop(0) # remove the command # prepend a line number and append a newline if len(outstring) >= 1: if OUTPUT_LINE_NUMBERS: outstring.insert(0, (linenumber())) # append the line to the final output for w in outstring: out += w + COMMAND_SPACE out = out.strip() + "\n" return out
def __init__(self, gmsh_mesh_obj, analysis=None): self.mesh_obj = gmsh_mesh_obj if analysis: self.analysis = analysis else: self.analysis = None # part to mesh self.part_obj = self.mesh_obj.Part # clmax, CharacteristicLengthMax: float, 0.0 = 1e+22 self.clmax = Units.Quantity( self.mesh_obj.CharacteristicLengthMax).Value if self.clmax == 0.0: self.clmax = 1e+22 # clmin, CharacteristicLengthMin: float self.clmin = Units.Quantity( self.mesh_obj.CharacteristicLengthMin).Value # geotol, GeometryTolerance: float, 0.0 = 1e-08 self.geotol = self.mesh_obj.GeometryTolerance if self.geotol == 0.0: self.geotol = 1e-08 # order # known_element_orders = ['1st', '2nd'] self.order = self.mesh_obj.ElementOrder if self.order == '1st': self.order = '1' elif self.order == '2nd': self.order = '2' else: print('Error in order') # dimension self.dimension = self.mesh_obj.ElementDimension # Algorithm2D algo2D = self.mesh_obj.Algorithm2D if algo2D == 'Automatic': self.algorithm2D = '2' elif algo2D == 'MeshAdapt': self.algorithm2D = '1' elif algo2D == 'Delaunay': self.algorithm2D = '5' elif algo2D == 'Frontal': self.algorithm2D = '6' elif algo2D == 'BAMG': self.algorithm2D = '7' elif algo2D == 'DelQuad': self.algorithm2D = '8' else: self.algorithm2D = '2' # Algorithm3D algo3D = self.mesh_obj.Algorithm3D if algo3D == 'Automatic': self.algorithm3D = '1' elif algo3D == 'Delaunay': self.algorithm3D = '1' elif algo3D == 'New Delaunay': self.algorithm3D = '2' elif algo3D == 'Frontal': self.algorithm3D = '4' elif algo3D == 'Frontal Delaunay': self.algorithm3D = '5' elif algo3D == 'Frontal Hex': self.algorithm3D = '6' elif algo3D == 'MMG3D': self.algorithm3D = '7' elif algo3D == 'R-tree': self.algorithm3D = '9' else: self.algorithm3D = '1' # mesh groups if self.mesh_obj.GroupsOfNodes is True: self.group_nodes_export = True else: self.group_nodes_export = False self.group_elements = {} # mesh regions self.ele_length_map = {} # { 'ElementString' : element length } self.ele_node_map = {} # { 'ElementString' : [element nodes] } # mesh boundary layer self.bl_setting_list = [ ] # list of dict, each item map to MeshBoundaryLayer object self.bl_boundary_list = [ ] # to remove duplicated boundary edge or faces # other initializations self.temp_file_geometry = '' self.temp_file_mesh = '' self.temp_file_geo = '' self.mesh_name = '' self.gmsh_bin = '' self.error = False
def convert(quantityStr, unit): quantity = Units.Quantity(quantityStr) for key, setting in UNITS.items(): unit = unit.replace(key, setting) return float(quantity.getValueAs(unit))
def parse(pathobj): global DRILL_RETRACT_MODE global MOTION_MODE global CURRENT_X global CURRENT_Y global CURRENT_Z out = "" lastcommand = None precision_string = '.' + str(PRECISION) + 'f' params = [ 'X', 'Y', 'Z', 'A', 'B', 'C', 'U', 'V', 'W', 'I', 'J', 'K', 'F', 'S', 'T', 'Q', 'R', 'L', 'P' ] if hasattr(pathobj, "Group"): # We have a compound or project. if OUTPUT_COMMENTS: out += linenumber() + "(compound: " + pathobj.Label + ")\n" for p in pathobj.Group: out += parse(p) return out else: # parsing simple path if not hasattr( pathobj, "Path"): # groups might contain non-path things like stock. return out if OUTPUT_COMMENTS: out += linenumber() + "(Path: " + pathobj.Label + ")\n" for c in pathobj.Path.Commands: outstring = [] command = c.Name outstring.append(command) # if modal: only print the command if it is not the same as the last one if MODAL: if command == lastcommand: outstring.pop(0) # Now add the remaining parameters in order for param in params: if param in c.Parameters: if param == 'F': if command not in RAPID_MOVES: speed = Units.Quantity(c.Parameters['F'], FreeCAD.Units.Velocity) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: outstring.append(param + format( float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string)) elif param in ['T', 'H', 'D', 'S', 'P', 'L']: outstring.append(param + str(c.Parameters[param])) elif param in ['A', 'B', 'C']: outstring.append( param + format(c.Parameters[param], precision_string)) else: # [X, Y, Z, U, V, W, I, J, K, R, Q] (Conversion eventuelle mm/inches) pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) outstring.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # store the latest command lastcommand = command # Memorise la position courante pour calcul des mouvements relatis et du plan de retrait if command in MOTION_COMMANDS: if 'X' in c.Parameters: CURRENT_X = Units.Quantity(c.Parameters['X'], FreeCAD.Units.Length) if 'Y' in c.Parameters: CURRENT_Y = Units.Quantity(c.Parameters['Y'], FreeCAD.Units.Length) if 'Z' in c.Parameters: CURRENT_Z = Units.Quantity(c.Parameters['Z'], FreeCAD.Units.Length) if command in ('G98', 'G99'): DRILL_RETRACT_MODE = command if command in ('G90', 'G91'): MOTION_MODE = command if TRANSLATE_DRILL_CYCLES: if command in ('G81', 'G82', 'G83'): out += drill_translate(outstring, command, c.Parameters) # Efface la ligne que l'on vient de translater del (outstring[:]) outstring = [] # Check for Tool Change: if command in ('M6', 'M06'): if OUTPUT_COMMENTS: out += linenumber() + "(begin toolchange)\n" if not OUTPUT_TOOL_CHANGE: outstring[0] = "(" + outstring[0] outstring[-1] = outstring[-1] + ")" else: for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line if command == "message": if OUTPUT_COMMENTS is False: out = [] else: outstring.pop(0) # remove the command if command in SUPPRESS_COMMANDS: outstring[0] = "(" + outstring[0] outstring[-1] = outstring[-1] + ")" # prepend a line number and append a newline if len(outstring) >= 1: out += linenumber() + format_outstring(outstring) + "\n" return out
def parse(pathobj): out = "" lastcommand = None precision_string = "." + str(PRECISION) + "f" currLocation = {} # keep track for no doubles # The params list control the order of parameters params = [ "X", "Y", "Z", "A", "B", "C", "I", "J", "K", "R", "F", "S", "T", "H", "L", "Q", ] firstmove = Path.Command("G0", {"X": -1, "Y": -1, "Z": -1, "F": 0.0}) currLocation.update(firstmove.Parameters) # set First location Parameters if hasattr(pathobj, "Group"): # We have a compound or project. # if OUTPUT_COMMENTS: # out += linenumber() + "(compound: " + pathobj.Label + ")\n" for p in pathobj.Group: out += parse(p) return out else: # parsing simple path # groups might contain non-path things like stock. if not hasattr(pathobj, "Path"): return out # if OUTPUT_COMMENTS: # out += linenumber() + "(" + pathobj.Label + ")\n" for c in pathobj.Path.Commands: commandlist = [ ] # list of elements in the command, code and params. command = c.Name.strip() # command M or G code or comment string commandlist.append(command) # if modal: only print the command if it is not the same as the last one if MODAL is True: if command == lastcommand: commandlist.pop(0) if c.Name[0] == "(" and not OUTPUT_COMMENTS: # command is a comment continue # Now add the remaining parameters in order for param in params: if param in c.Parameters: if param == "F" and ( currLocation[param] != c.Parameters[param] or REPEAT_ARGUMENTS): if c.Name not in ["G0", "G00"]: # No F in G0 speed = Units.Quantity(c.Parameters["F"], FreeCAD.Units.Velocity) if speed.getValueAs(UNIT_SPEED_FORMAT) > 0.0: commandlist.append(param + format( float(speed.getValueAs(UNIT_SPEED_FORMAT)), precision_string, )) else: continue elif param == "T": commandlist.append(param + str(int(c.Parameters["T"]))) elif param == "H": commandlist.append(param + str(int(c.Parameters["H"]))) elif param == "D": commandlist.append(param + str(int(c.Parameters["D"]))) elif param == "S": commandlist.append(param + str(int(c.Parameters["S"]))) else: if ((not REPEAT_ARGUMENTS) and (param in currLocation) and (currLocation[param] == c.Parameters[param])): continue else: pos = Units.Quantity(c.Parameters[param], FreeCAD.Units.Length) commandlist.append( param + format(float(pos.getValueAs(UNIT_FORMAT)), precision_string)) # store the latest command lastcommand = command currLocation.update(c.Parameters) # Check for Tool Change: if command == "M6": for line in TOOL_CHANGE.splitlines(True): out += linenumber() + line # add height offset if USE_TLO: tool_height = "\nG43 H" + str(int(c.Parameters["T"])) commandlist.append(tool_height) if command == "message": if OUTPUT_COMMENTS is False: out = [] else: commandlist.pop(0) # remove the command # prepend a line number and append a newline if len(commandlist) >= 1: if OUTPUT_LINE_NUMBERS: commandlist.insert(0, (linenumber())) # append the line to the final output for w in commandlist: out += w.strip() + COMMAND_SPACE if trace_gcode: print("parse : >>{}".format(out)) out = out.strip() + "\n" return out
def drill_translate(outstring, cmd, params): global DRILL_RETRACT_MODE global MOTION_MODE global CURRENT_X global CURRENT_Y global CURRENT_Z global UNITS global UNIT_FORMAT global UNIT_SPEED_FORMAT strFormat = '.' + str(PRECISION) + 'f' trBuff = "" if OUTPUT_COMMENTS: # Comment the original command outstring[0] = "(" + outstring[0] outstring[-1] = outstring[-1] + ")" trBuff += linenumber() + format_outstring(outstring) + "\n" # Conversion du cycle # Pour l'instant, on gere uniquement les cycles dans le plan XY (G17) # les autres plans ZX (G18) et YZ (G19) ne sont pas traites : Calculs sur Z uniquement. if MOTION_MODE == 'G90': # Deplacements en coordonnees absolues drill_X = Units.Quantity(params['X'], FreeCAD.Units.Length) drill_Y = Units.Quantity(params['Y'], FreeCAD.Units.Length) drill_Z = Units.Quantity(params['Z'], FreeCAD.Units.Length) RETRACT_Z = Units.Quantity(params['R'], FreeCAD.Units.Length) else: # G91 Deplacements relatifs drill_X = CURRENT_X + Units.Quantity(params['X'], FreeCAD.Units.Length) drill_Y = CURRENT_Y + Units.Quantity(params['Y'], FreeCAD.Units.Length) drill_Z = CURRENT_Z + Units.Quantity(params['Z'], FreeCAD.Units.Length) RETRACT_Z = CURRENT_Z + Units.Quantity(params['R'], FreeCAD.Units.Length) if DRILL_RETRACT_MODE == 'G98' and CURRENT_Z >= RETRACT_Z: RETRACT_Z = CURRENT_Z # Recupere les valeurs des autres parametres drill_Speed = Units.Quantity(params['F'], FreeCAD.Units.Velocity) if cmd == 'G83': drill_Step = Units.Quantity(params['Q'], FreeCAD.Units.Length) elif cmd == 'G82': drill_DwellTime = params['P'] if MOTION_MODE == 'G91': trBuff += linenumber( ) + "G90" + "\n" # Force des deplacements en coordonnees absolues pendant les cycles # Mouvement(s) preliminaire(s)) if CURRENT_Z < RETRACT_Z: trBuff += linenumber() + 'G0 Z' + format( float(RETRACT_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n" trBuff += linenumber() + 'G0 X' + format( float(drill_X.getValueAs(UNIT_FORMAT)), strFormat) + ' Y' + format( float(drill_Y.getValueAs(UNIT_FORMAT)), strFormat) + "\n" if CURRENT_Z > RETRACT_Z: trBuff += linenumber() + 'G0 Z' + format( float(CURRENT_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n" # Mouvement de percage if cmd in ('G81', 'G82'): trBuff += linenumber() + 'G1 Z' + format( float(drill_Z.getValueAs(UNIT_FORMAT)), strFormat) + ' F' + format( float(drill_Speed.getValueAs(UNIT_SPEED_FORMAT)), '.2f') + "\n" # Temporisation eventuelle if cmd == 'G82': trBuff += linenumber() + 'G4 P' + str(drill_DwellTime) + "\n" # Sortie de percage trBuff += linenumber() + 'G0 Z' + format( float(RETRACT_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n" else: # 'G83' next_Stop_Z = RETRACT_Z - drill_Step while 1: if next_Stop_Z > drill_Z: trBuff += linenumber() + 'G1 Z' + format( float(next_Stop_Z.getValueAs(UNIT_FORMAT)), strFormat) + ' F' + format( float(drill_Speed.getValueAs(UNIT_SPEED_FORMAT)), '.2f') + "\n" trBuff += linenumber() + 'G0 Z' + format( float(RETRACT_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n" next_Stop_Z -= drill_Step else: trBuff += linenumber() + 'G1 Z' + format( float(drill_Z.getValueAs(UNIT_FORMAT)), strFormat) + ' F' + format( float(drill_Speed.getValueAs(UNIT_SPEED_FORMAT)), '.2f') + "\n" trBuff += linenumber() + 'G0 Z' + format( float(RETRACT_Z.getValueAs(UNIT_FORMAT)), strFormat) + "\n" break if MOTION_MODE == 'G91': trBuff += linenumber( ) + 'G91' # Restore le mode de deplacement relatif return trBuff
def compute(lc, fs_ref=True, doc=App.ActiveDocument): objs = {'fs':None, 'ship':None, 'tanks':[], 'COG': None, 'B': None} points, ship, weights, tanks = GZ.gz( lc, [Units.parseQuantity("0 deg")], True) if points == []: return None, 0, 0, 0, objs gz, draft, trim = points[0] group_objs = [] # Create a free surface L = ship.Length B = ship.Breadth name = __make_name('SinkAndTrim_FS', doc) shape = Part.makePlane(2.0 * L, 2.0 * B, App.Vector(-L, -B, 0)) if not fs_ref: shape = __place_shape(shape, -draft, -trim) Part.show(shape, name) doc.recompute() if Gui: fs = Gui.getDocument(doc.Name).getObject(name) fs.LineColor = (0.0, 0.0, 0.5) fs.ShapeColor = (0.0, 0.7, 1.0) group_objs.append(doc.getObject(name)) objs['fs'] = group_objs[-1] # Copy and place the ship name = __make_name('SinkAndTrim_{}'.format(ship.Name), doc) shape = ship.Shape.copy() if fs_ref: shape = __place_shape(ship.Shape.copy(), draft, trim) Part.show(shape, name) doc.recompute() if Gui: plot_ship = Gui.getDocument(doc.Name).getObject(name) plot_ship.Transparency = 50 group_objs.append(doc.getObject(name)) doc.getObject(name).Label = 'SinkAndTrim_' + ship.Label objs['ship'] = group_objs[-1] # Copy and place the tanks tank_shapes = [] # Untransformed for tank, dens, level in tanks: name = __make_name('SinkAndTrim_{}'.format(tank.Name), doc) vol = tank.Proxy.getVolume(tank, level) shape = tank.Proxy.getFluidShape(tank, vol, trim=trim) tank_shapes.append(shape) if shape is None: continue if fs_ref: shape = __place_shape(shape.copy(), draft, trim) Part.show(shape, name) doc.recompute() group_objs.append(doc.getObject(name)) doc.getObject(name).Label = 'SinkAndTrim_' + tank.Label objs['tanks'].append(group_objs[-1]) # Place the bouyancy center disp, B, _ = Hydrostatics.displacement(ship, draft, trim=trim) disp *= GZ.G name = __make_name('SinkAndTrim_B', doc) shape = Part.Vertex(B) if fs_ref: shape = __place_shape(shape, draft, trim) Part.show(shape, name) doc.recompute() if Gui: b = Gui.getDocument(doc.Name).getObject(name) b.PointSize = 10.00 group_objs.append(doc.getObject(name)) objs['B'] = group_objs[-1] # Place the COG COG, W = GZ.weights_cog(weights) mom_x = Units.Quantity(COG.x, Units.Length) * W mom_y = Units.Quantity(COG.y, Units.Length) * W mom_z = Units.Quantity(COG.z, Units.Length) * W for i, tank_data in enumerate(tanks): dens = tank_data[1] shape = tank_shapes[i] if shape is None: continue tank_cog, tank_vol = __vol_cog(shape) tank_weight = Units.Quantity(tank_vol, Units.Volume) * dens * GZ.G mom_x += Units.Quantity(tank_cog.x, Units.Length) * tank_weight mom_y += Units.Quantity(tank_cog.y, Units.Length) * tank_weight mom_z += Units.Quantity(tank_cog.z, Units.Length) * tank_weight W += tank_weight COG = Vector(mom_x / W, mom_y / W, mom_z / W) name = __make_name('SinkAndTrim_COG', doc) shape = Part.Vertex(COG) if fs_ref: shape = __place_shape(shape, draft, trim) Part.show(shape, name) doc.recompute() if Gui: cog = Gui.getDocument(doc.Name).getObject(name) cog.PointSize = 10.00 group_objs.append(doc.getObject(name)) objs['COG'] = group_objs[-1] # Create a group where the results will be placed name = __make_name('SinkAndTrim_results', doc) group = doc.addObject('App::DocumentObjectGroup', name) for obj in group_objs: group.addObject(obj) doc.recompute() return group, draft, trim, disp / GZ.G, objs
def get_region_data(self): # mesh regions if not self.mesh_obj.MeshRegionList: # print(" No mesh regions.") pass else: Console.PrintMessage(' Mesh regions, we need to get the elements.\n') # by the use of MeshRegion object and a BooleanSplitCompound # there could be problems with node numbers see # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&start=40#p149467 # http://forum.freecadweb.org/viewtopic.php?f=18&t=18780&p=149520#p149520 part = self.part_obj if ( self.mesh_obj.MeshRegionList and part.Shape.ShapeType == "Compound" and ( femutils.is_of_type(part, "FeatureBooleanFragments") or femutils.is_of_type(part, "FeatureSlice") or femutils.is_of_type(part, "FeatureXOR") ) ): error_message = ( " The mesh to shape is a boolean split tools Compound " "and the mesh has mesh region list. " "Gmsh could return unexpected meshes in such circumstances. " "It is strongly recommended to extract the shape to mesh " "from the Compound and use this one." ) Console.PrintError(error_message + "\n") # TODO: no gui popup because FreeCAD will be in a endless output loop # as long as the pop up is on --> maybe find a better solution for # either of both --> thus the pop up is in task panel for mr_obj in self.mesh_obj.MeshRegionList: # print(mr_obj.Name) # print(mr_obj.CharacteristicLength) # print(Units.Quantity(mr_obj.CharacteristicLength).Value) if mr_obj.CharacteristicLength: if mr_obj.References: for sub in mr_obj.References: # print(sub[0]) # Part the elements belongs to # check if the shape of the mesh region # is an element of the Part to mesh # if not try to find the element in the shape to mesh search_ele_in_shape_to_mesh = False if not self.part_obj.Shape.isSame(sub[0].Shape): Console.PrintLog( " One element of the meshregion {} is " "not an element of the Part to mesh.\n" "But we are going to try to find it in " "the Shape to mesh :-)\n" .format(mr_obj.Name) ) search_ele_in_shape_to_mesh = True for elems in sub[1]: # print(elems) # elems --> element if search_ele_in_shape_to_mesh: # we're going to try to find the element in the # Shape to mesh and use the found element as elems # the method getElement(element) # does not return Solid elements ele_shape = geomtools.get_element(sub[0], elems) found_element = geomtools.find_element_in_shape( self.part_obj.Shape, ele_shape ) if found_element: elems = found_element else: Console.PrintError( "One element of the meshregion {} could not be found " "in the Part to mesh. It will be ignored.\n" .format(mr_obj.Name) ) # print(elems) # element if elems not in self.ele_length_map: self.ele_length_map[elems] = Units.Quantity( mr_obj.CharacteristicLength ).Value else: Console.PrintError( "The element {} of the meshregion {} has " "been added to another mesh region.\n" .format(elems, mr_obj.Name) ) else: Console.PrintError( "The meshregion: {} is not used to create the mesh " "because the reference list is empty.\n" .format(mr_obj.Name) ) else: Console.PrintError( "The meshregion: {} is not used to create the " "mesh because the CharacteristicLength is 0.0 mm.\n" .format(mr_obj.Name) ) for eleml in self.ele_length_map: # the method getElement(element) does not return Solid elements ele_shape = geomtools.get_element(self.part_obj, eleml) ele_vertexes = geomtools.get_vertexes_by_element(self.part_obj.Shape, ele_shape) self.ele_node_map[eleml] = ele_vertexes Console.PrintMessage(" {}\n".format(self.ele_length_map)) Console.PrintMessage(" {}\n".format(self.ele_node_map))