def clicked(self, button): if button == QtGui.QDialogButtonBox.Ignore: # ask for confirmation before resetting everything msgName = Asm4.nameLabel(self.selectedLink) # see whether the ExpressionEngine field is filled if self.selectedLink.ExpressionEngine: # if yes, then ask for confirmation confirmed = Asm4.confirmBox( 'This command will release all attachments on ' + msgName + ' and set it to manual positioning in its current location.' ) # if not, then it's useless to bother the user else: confirmed = True if confirmed: # unset the ExpressionEngine for the Placement self.selectedLink.setExpression('Placement', None) # reset the assembly properties Asm4.makeAsmProperties(self.selectedLink, reset=True) # finish FCC.PrintWarning("Part is now manually placed\n") self.finish() else: FCC.PrintWarning("Part untouched\n") self.finish()
def _get_translation_offset(reference_dimensions: List[float], rotation: Rotation, origin_translation_offset: Vector): reference_box = Part.makeBox(*reference_dimensions) reference_box.rotate(Vector(0, 0, 0), rotation.Axis, degrees(rotation.Angle)) first_vertex = reference_box.Vertexes[0] first_vertex_edges = find_edges_connected_to_vertex( first_vertex, reference_box.Edges) num_edges = len(first_vertex_edges) if num_edges != 3: message = 'Found {} edges'.format(num_edges) message += ' connected to cube vertex instead of 3.\n' Console.PrintWarning(message) x = y = z = 1.0 for e in first_vertex_edges: if is_edge_parallel_to_x_axis(e): x = e.Length elif is_edge_parallel_to_y_axis(e): y = e.Length elif is_edge_parallel_to_z_axis(e): z = e.Length else: def round_vec(v): return Vector(round(v.x), round(v.y), round(v.z)) Console.PrintWarning( 'Edge: {} not parallel to x, y, or z axes\n'.format( [round_vec(v.Point) for v in e.Vertexes])) return Vector(x * origin_translation_offset.x, y * origin_translation_offset.y, z * origin_translation_offset.z)
def log_warning_if_odd_number_of_z_axes(num_z_axes, num_heated_bed_rods): if num_z_axes % 2 == 1: Console.PrintWarning( 'An odd number of {} Z axes has been detected when determining number of heated bed rods.\n' .format(num_z_axes)) Console.PrintWarning( 'Rounding down to {} Z axes and {} heated bed rods.\n'.format( num_z_axes - 1, num_heated_bed_rods))
def get_face_side(self, frame, face): # Exclude cylindrical surfaces and holes if not isinstance(face.Surface, Part.Plane): Console.PrintWarning('Face is not planar.\n') return None lower_side, upper_side = self._get_sides() if self._is_between_lower_bounds(face, frame): return lower_side elif self._is_between_upper_bounds(face, frame): return upper_side else: warning_message = 'Face is not between upper or lower bounds.\n' Console.PrintWarning(warning_message) return None
def Activated(self): #Handle single selected App::Link selectedLink = None parentAssembly = None selection = Asm4.getSelectedLink() if not selection: # This shouldn't happen FCC.PrintWarning( "This is not an error message you are supposed to see, something went wrong\n" ) return else: parent = selection.getParentGeoFeatureGroup() # only handle if the parent is at the root of the document: if parent and parent.TypeId == 'App::Part' and parent.getParentGeoFeatureGroup( ) is None: # only accept Asm4 Models as assemblies # this is self-imposed, works also without an Asm4 Model if parent.Name == 'Model': # launch the UI in the task panel Gui.Control.showDialog(placeLinkUI()) else: Asm4.warningBox( 'Please select a link in the assembly Model') else: Asm4.warningBox('Please select a link in the assembly Model.') return
def accept(self): if self.onApply(): if self.selectedDatum: self.selectedDatum.ViewObject.ShowLabel = False self.finish() else: FCC.PrintWarning("Problem in selections\n")
def onApply(self): # get the name of the part to attach to: # it's either the top level part name ('Model') # or the provided link's name. if self.parentList.currentText() == 'Parent Assembly': a_Link = 'Parent Assembly' a_Part = None elif self.parentList.currentIndex() > 1: parent = self.parentTable[ self.parentList.currentIndex() ] a_Link = parent.Name a_Part = parent.LinkedObject.Document.Name else: a_Link = None a_Part = None # the attachment LCS's name in the parent # check that something is selected in the QlistWidget if self.attLCSlist.selectedItems(): a_LCS = self.attLCStable[ self.attLCSlist.currentRow() ].Name else: a_LCS = None # check that all of them have something in if a_Link and a_LCS : # <<LinkName>>.Placement.multiply( <<LinkName>>.<<LCS.>>.Placement ) # expr = '<<'+ a_Part +'>>.Placement.multiply( <<'+ a_Part +'>>.<<'+ a_LCS +'.>>.Placement )' Asm4.placeObjectToLCS(self.selectedFastener, a_Link, a_Part, a_LCS) # highlight the selected fastener in its new position Gui.Selection.clearSelection() Gui.Selection.addSelection( self.activeDoc.Name, 'Model', self.selectedFastener.Name +'.') else: FCC.PrintWarning("Problem in selections\n") return
def accept(self): if self.Apply(): # highlight in the 3D window the object we placed self.finish() else: FCC.PrintWarning("Problem in selections\n") return
def find_selected_point() -> Optional[Tuple[Vector, str]]: main_window = Gui.getMainWindow() # MDI (Multiple Document Interface) mdi_area = main_window.findChild(QtGui.QMdiArea) active_sub_window = mdi_area.activeSubWindow() if active_sub_window.widget().metaObject().className( ) == 'SpreadsheetGui::SheetView': sheet = active_sub_window.widget() table = sheet.findChild(QtGui.QTableView) indexes = table.selectedIndexes() if len(indexes) > 0: Console.PrintMessage( f'{len(indexes)} indexes selected, picking 1st.\n') first = indexes[0] row = str(first.row() + 1) column = map_number_to_column(first.column() + 1) cell_address = column + row selection = Gui.Selection.getSelection() if len(selection) > 0: Console.PrintMessage( f'{len(selection)} objects selected, picking 1st.\n') selected_sheet = selection[0] if selected_sheet.TypeId == 'Spreadsheet::Sheet': obj = selected_sheet.get(cell_address) label = selected_sheet.getAlias(cell_address) if isinstance(obj, Placement): return obj.Base, label elif isinstance(obj, Vector): return obj, label else: Console.PrintWarning( f'Selected cell {cell_address} does not contain vector.\n' ) else: Console.PrintWarning(f'Selected object must be sheet.\n') else: Console.PrintWarning('No selected objects.\n') else: Console.PrintWarning('No selected cell in spreasheet.\n') else: Console.PrintWarning('No active spreadsheet.\n')
def get_furl_transforms(root_document: Document) -> List[Transform]: root_document_path = Path(root_document.FileName) documents_path = root_document_path.parent tail_document_path = documents_path.joinpath('Tail', 'Tail.FCStd') tail_document = App.openDocument(str(tail_document_path)) tail = find_object_by_label(tail_document, 'Tail') if len(tail.InList) == 0: Console.PrintWarning(f'{tail.Label} has no parents.\n') return None if len(tail.InList) > 1: Console.PrintWarning( f'{tail.Label} has more than 1 parent. Choosing 1st.\n') tail_parent = tail.InList[0] parent_placement = calculate_global_placement(tail_parent) hinge_outer = find_object_by_label(tail_document, 'Hinge_Outer') return [ placement_to_dict('parent', parent_placement), placement_to_dict('tail', tail.Placement), placement_to_dict('hinge', hinge_outer.Placement) ]
def export(objectslist, fileString, group_values_dict_nogui=None): """ Called when freecad exports a file. group_dict_no_gui: dictionary with group_numbers as keys and tuples of (marked_value (default=1), default_value (default=0)) """ if len(objectslist) != 1: Console.PrintError("This exporter can only export one object.\n") return obj = objectslist[0] if not obj.isDerivedFrom("Fem::FemMeshObject"): Console.PrintError("No FEM mesh object selected.\n") return if fileString != "": fileName, fileExtension = os.path.splitext(fileString) if fileExtension.lower() == ".xml": Console.PrintWarning( "XML is not designed to save higher order elements.\n") Console.PrintWarning("Reducing order for second order mesh.\n") Console.PrintWarning("Tri6 -> Tri3, Tet10 -> Tet4, etc.\n") writeFenicsXML.write_fenics_mesh_xml(obj, fileString) elif fileExtension.lower() == ".xdmf": mesh_groups = importToolsFem.get_FemMeshObjectMeshGroups(obj) if mesh_groups != (): # if there are groups found, make task panel available if GuiUp if FreeCAD.GuiUp == 1: panel = WriteXDMFTaskPanel(obj, fileString) FreeCADGui.Control.showDialog(panel) else: # create default dict if groupdict_nogui is not None if group_values_dict_nogui is None: group_values_dict_nogui = dict([(g, (1, 0)) for g in mesh_groups]) writeFenicsXDMF.write_fenics_mesh_xdmf( obj, fileString, group_values_dict=group_values_dict_nogui) else: writeFenicsXDMF.write_fenics_mesh_xdmf(obj, fileString)
def get_bound_box(obj: object) -> Optional[BoundBox]: bound_box = BoundBox() if (hasattr(obj, 'Shape') and obj.Shape and obj.Shape.BoundBox.isValid()): bound_box.add(obj.Shape.BoundBox) else: Console.PrintWarning( f'{obj.Label} has no Shape, calculating manual bounding box.') bound_box.add(Draft.get_bbox(obj)) if not bound_box.isValid(): Console.PrintError( f'{obj.Label} does not have a a valid bounding box.') return None return bound_box
def makeVarLink(self, obj): # create a new, empty, hidden, temporary document tmpDocName = 'varTmpDoc_' i = 1 while i < 100 and tmpDocName + str(i) in App.listDocuments(): i += 1 if i < 100: tmpDocName = 'varTmpDoc_' + str(i) tmpDoc = App.newDocument(tmpDocName, hidden=True, temp=True) # deep-copy the source object and link it back obj.LinkedObject = tmpDoc.copyObject(obj.SourceObject, True) else: FCC.PrintWarning( '100 temporary variant documents are already in use, not creating a new one.\n' ) return
def calculate_global_placement(child: object, placements: Placement = []) -> Placement: placements.append(child.Placement) in_list = child.InList num_in = len(in_list) if len(in_list) == 0: global_placement = Placement() placements.reverse() # Reverse list in order of parent to child. for placement in placements: global_placement *= placement return global_placement if num_in > 1: Console.PrintWarning( f'{child.Label} has more than 1 parent. Choosing 1st.\n') parent = in_list[0] return calculate_global_placement(parent, placements)
def onApply(self): # get the name of the part to attach to: # it's either the top level part name ('Model') # or the provided link's name. if self.parentList.currentText() == 'Parent Assembly': a_Link = 'Parent Assembly' a_Part = None elif self.parentList.currentIndex() > 1: parent = self.parentTable[ self.parentList.currentIndex() ] a_Link = parent.Name a_Part = parent.LinkedObject.Document.Name else: a_Link = None a_Part = None # the attachment LCS's name in the parent # check that something is selected in the QlistWidget if self.attLCSlist.selectedItems(): a_LCS = self.attLCStable[ self.attLCSlist.currentRow() ].Name else: a_LCS = None # check that all of them have something in if a_Link and a_LCS : # <<LinkName>>.Placement.multiply( <<LinkName>>.<<LCS.>>.Placement ) # expr = '<<'+ a_Part +'>>.Placement.multiply( <<'+ a_Part +'>>.<<'+ a_LCS +'.>>.Placement )' expr = self.makeExpressionFastener( a_Link, a_Part, a_LCS ) # indicate the this fastener has been placed with the Assembly4 workbench if not hasattr(self.selectedFastener,'AssemblyType'): Asm4.makeAsmProperties(self.selectedFastener) self.selectedFastener.AssemblyType = 'Asm4EE' # the fastener is attached by its Origin, no extra LCS self.selectedFastener.AttachedBy = 'Origin' # store the part where we're attached to in the constraints object self.selectedFastener.AttachedTo = a_Link+'#'+a_LCS # load the built expression into the Expression field of the constraint self.selectedFastener.setExpression( 'Placement', expr ) # recompute the object to apply the placement: self.selectedFastener.recompute() self.parentAssembly.recompute() self.activeDoc.recompute() # highlight the selected fastener in its new position Gui.Selection.clearSelection() Gui.Selection.addSelection( self.activeDoc.Name, 'Model', self.selectedFastener.Name +'.') else: FCC.PrintWarning("Problem in selections\n") return
def onApply(self): # get the name of the part to attach to: # it's either the top level part name ('Model') # or the provided link's name. if self.parentList.currentText() == 'Parent Assembly': a_Link = 'Parent Assembly' a_Part = None elif self.parentList.currentIndex() > 1: parent = self.parentTable[self.parentList.currentIndex()] a_Link = parent.Name a_Part = parent.LinkedObject.Document.Name else: a_Link = None a_Part = None # the attachment LCS's name in the parent # check that something is selected in the QlistWidget if self.attLCSlist.selectedItems(): a_LCS = self.attLCStable[self.attLCSlist.currentRow()].Name else: a_LCS = None # check that all of them have something in if a_Link and a_LCS: # add Asm4 properties if necessary Asm4.makeAsmProperties(self.selectedFastener, reset=True) # hide "offset" and "invert" properties to avoid confusion as they are not used in Asm4 if hasattr(self.selectedFastener, 'offset'): self.selectedFastener.setPropertyStatus('offset', 'Hidden') if hasattr(self.selectedFastener, 'invert'): self.selectedFastener.setPropertyStatus('invert', 'Hidden') # <<LinkName>>.Placement.multiply( <<LinkName>>.<<LCS.>>.Placement ) # expr = '<<'+ a_Part +'>>.Placement.multiply( <<'+ a_Part +'>>.<<'+ a_LCS +'.>>.Placement )' Asm4.placeObjectToLCS(self.selectedFastener, a_Link, a_Part, a_LCS) # highlight the selected fastener in its new position Gui.Selection.clearSelection() Gui.Selection.addSelection(self.activeDoc.Name, self.rootAssembly.Name, self.selectedFastener.Name + '.') else: FCC.PrintWarning("Problem in selections\n") return
def get_group_data(self): # TODO: solids, faces, edges and vertexes don't seem to work together in one group, # some output message or make them work together # mesh group objects if not self.mesh_obj.MeshGroupList: # print(" No mesh group objects.") pass else: Console.PrintMessage( " Mesh group objects, we need to get the elements.\n") for mg in self.mesh_obj.MeshGroupList: new_group_elements = meshtools.get_mesh_group_elements( mg, self.part_obj) for ge in new_group_elements: if ge not in self.group_elements: self.group_elements[ge] = new_group_elements[ge] else: Console.PrintError( " A group with this name exists already.\n") # group meshing for analysis analysis_group_meshing = FreeCAD.ParamGet( "User parameter:BaseApp/Preferences/Mod/Fem/General").GetBool( "AnalysisGroupMeshing", False) if self.analysis and analysis_group_meshing: Console.PrintWarning( " Group meshing for analysis is set to true in FEM General Preferences. " "Are you really sure about this? You could run into trouble!\n" ) self.group_nodes_export = True new_group_elements = meshtools.get_analysis_group_elements( self.analysis, self.part_obj) for ge in new_group_elements: if ge not in self.group_elements: self.group_elements[ge] = new_group_elements[ge] else: Console.PrintError( " A group with this name exists already.\n") else: Console.PrintMessage(" No Group meshing for analysis.\n") if self.group_elements: Console.PrintMessage(" {}\n".format(self.group_elements))
def Activated(self, name=translate("draft", "Fillet")): DraftTools.Creator.Activated(self, name) if not self.doc: FCC.PrintWarning(translate("draft", "No active document") + "\n") return if self.ui: self.rad = 100 self.chamfer = False self.delete = False label = translate("draft", "Fillet radius") tooltip = translate("draft", "Radius of fillet") # Call the Task panel for a radius # The graphical widgets are defined in DraftGui self.ui.taskUi(title=name, icon="Draft_Fillet") self.ui.radiusUi() self.ui.sourceCmd = self self.ui.labelRadius.setText(label) self.ui.radiusValue.setToolTip(tooltip) self.ui.setRadiusValue(self.rad, "Length") self.ui.check_delete = self.ui._checkbox("isdelete", self.ui.layout, checked=self.delete) self.ui.check_delete.setText( translate("draft", "Delete original objects")) self.ui.check_delete.show() self.ui.check_chamfer = self.ui._checkbox("ischamfer", self.ui.layout, checked=self.chamfer) self.ui.check_chamfer.setText(translate("draft", "Create chamfer")) self.ui.check_chamfer.show() QtCore.QObject.connect(self.ui.check_delete, QtCore.SIGNAL("stateChanged(int)"), self.set_delete) QtCore.QObject.connect(self.ui.check_chamfer, QtCore.SIGNAL("stateChanged(int)"), self.set_chamfer) self.linetrack = trackers.lineTracker(dotted=True) self.arctrack = trackers.arcTracker() # self.call = self.view.addEventCallback("SoEvent", self.action) FCC.PrintMessage(translate("draft", "Enter radius") + "\n")
def typecheck(args_and_types, name="?"): """Check that the arguments are instances of certain types. Parameters ---------- args_and_types : list A list of tuples. The first element of a tuple is tested as being an instance of the second element. :: args_and_types = [(a, Type), (b, Type2), ...] Then :: isinstance(a, Type) isinstance(b, Type2) A `Type` can also be a tuple of many types, in which case the check is done for any of them. :: args_and_types = [(a, (Type3, int, float)), ...] isinstance(a, (Type3, int, float)) name : str, optional Defaults to `'?'`. The name of the check. Raises ------- TypeError If the first element in the tuple is not an instance of the second element. """ for v, t in args_and_types: if not isinstance(v, t): _msg = ("typecheck[" + str(name) + "]: " + str(v) + " is not " + str(t) + "\n") FCC.PrintWarning(_msg) raise TypeError("fcvec." + str(name))
def _print_warning_message(num_matches, subject, object_type): message_template = '{} {} matching type "{}" found in selection.' message_template += ' Returning first match.' message = message_template.format(num_matches, subject, object_type) Console.PrintWarning(message)
def importAvs(filename, analysis=None, result_name_prefix=""): import ObjectsFem from feminout import importToolsFem TUNE = False if TUNE: import time cur = time.time() if analysis: doc = analysis.Document else: doc = FreeCAD.ActiveDocument m = read_avs_result(filename) result_mesh_object = None res_obj = None if TUNE: new = time.time() Console.PrintMessage("dtime 1:" + '%8.3f' % (new - cur) + "\n") cur = new if len(m["Nodes"]) > 0: mesh = importToolsFem.make_femmesh(m) if TUNE: new = time.time() Console.PrintMessage("dtime 2:" + '%8.3f' % (new - cur) + "\n") cur = new result_mesh_object = ObjectsFem.makeMeshResult(doc, "ResultMesh") result_mesh_object.FemMesh = mesh res_mesh_is_compacted = False nodenumbers_for_compacted_mesh = [] number_of_increments = len(m["Results"]) Console.PrintLog("Increments: " + str(number_of_increments) + "\n") if TUNE: new = time.time() Console.PrintMessage("dtime 3:" + '%8.3f' % (new - cur) + "\n") cur = new if len(m["Results"]) > 0: for result_set in m["Results"]: if "number" in result_set: eigenmode_number = result_set["number"] else: eigenmode_number = 0 step_time = result_set["time"] step_time = round(step_time, 2) if eigenmode_number > 0: results_name = ("{}Mode{}_Results".format( result_name_prefix, eigenmode_number)) elif number_of_increments > 1: results_name = ("{}Time{}_Results".format( result_name_prefix, step_time)) else: results_name = ("{}Results".format(result_name_prefix)) if TUNE: new = time.time() Console.PrintMessage("dtime 4:" + '%8.3f' % (new - cur) + "\n") cur = new res_obj = ObjectsFem.makeResultMechanical(doc, results_name) res_obj.Mesh = result_mesh_object if TUNE: new = time.time() Console.PrintMessage("dtime 5:" + '%8.3f' % (new - cur) + "\n") cur = new res_obj = importToolsFem.fill_femresult_mechanical( res_obj, result_set) if analysis: # need to be here, becasause later on, the analysis objs are needed # see fill of principal stresses analysis.addObject(res_obj) if TUNE: new = time.time() Console.PrintMessage("dtime 6:" + '%8.3f' % (new - cur) + "\n") cur = new # more result object calculations from femresult import resulttools from femtools import femutils if not res_obj.MassFlowRate: if res_mesh_is_compacted is False: # first result set, compact FemMesh and NodeNumbers res_obj = resulttools.compact_result(res_obj) res_mesh_is_compacted = True nodenumbers_for_compacted_mesh = res_obj.NodeNumbers else: # all other result sets, do not compact FemMesh, only set NodeNumbers res_obj.NodeNumbers = nodenumbers_for_compacted_mesh if TUNE: new = time.time() Console.PrintMessage("dtime 7:" + '%8.3f' % (new - cur) + "\n") cur = new # fill DisplacementLengths res_obj = resulttools.add_disp_apps(res_obj) if TUNE: new = time.time() Console.PrintMessage("dtime 8:" + '%8.3f' % (new - cur) + "\n") cur = new # fill vonMises mstress = [] for nid in res_obj.NodeNumbers: mstress.append(result_set["mises"][nid]) res_obj.vonMises = mstress if TUNE: new = time.time() Console.PrintMessage("dtime 9:" + '%8.3f' % (new - cur) + "\n") cur = new # fill principal stress prinstress1 = [] prinstress2 = [] prinstress3 = [] for nid in res_obj.NodeNumbers: pstr = result_set["pstress"][nid] prinstress1.append(pstr[0]) prinstress2.append(pstr[1]) prinstress3.append(pstr[2]) res_obj.PrincipalMax = prinstress1 res_obj.PrincipalMed = prinstress2 res_obj.PrincipalMin = prinstress3 # fill Stats res_obj = resulttools.fill_femresult_stats(res_obj) if TUNE: new = time.time() Console.PrintMessage("dtime10:" + '%8.3f' % (new - cur) + "\n") cur = new else: error_message = ( "Nodes, but no results found in avs file. " "It means there only is a mesh but no results in avs file. " "Usually this happens for: \n" "- analysis type 'NOANALYSIS'\n" "- if FrontISTR returned no results " "(happens on nonpositive jacobian determinant in at least one element)\n" "- just no avs results where requestet in input file " "(neither 'node file' nor 'el file' in output section')\n") Console.PrintWarning(error_message) # create a result obj, even if we have no results but a result mesh in avs file # see error message above for more information if not res_obj: if result_name_prefix: results_name = ("{}_Results".format(result_name_prefix)) else: results_name = ("Results".format(result_name_prefix)) res_obj = ObjectsFem.makeResultMechanical(doc, results_name) res_obj.Mesh = result_mesh_object # TODO, node numbers in result obj could be set if analysis: analysis.addObject(res_obj) if FreeCAD.GuiUp: if analysis: import FemGui FemGui.setActiveAnalysis(analysis) doc.recompute() if TUNE: new = time.time() Console.PrintMessage("dtime11:" + '%8.3f' % (new - cur) + "\n") cur = new else: Console.PrintError( "Problem on avs file import. No nodes found in avs file.\n") # None will be returned # or would it be better to raise an exception if there are not even nodes in avs file? return res_obj
placements: Placement = []) -> Placement: placements.append(child.Placement) in_list = child.InList num_in = len(in_list) if num_in == 0: global_placement = Placement() placements.reverse() # Reverse list in order of parent to child. for placement in placements: global_placement *= placement return global_placement if num_in > 1: Console.PrintWarning( f'{child.Label} has more than 1 parent. Choosing 1st.\n') parent = in_list[0] Console.PrintMessage(f'{parent.Label} ({parent.TypeId})\n') return calculate_global_placement(parent, placements) selection = Selection.getSelection() if len(selection) == 0: Console.PrintWarning(f'Must select at least 1 object.') else: if len(selection) > 1: Console.PrintWarning(f'Selected more than 1 object. Choosing 1st.') selected = selection[0] global_placement = calculate_global_placement(selected) Console.PrintMessage(f'{selected.Label} Global Placement\n') Console.PrintMessage(global_placement)
def read_mesh_block(mesh_block): """ Reading mesh block from XML file. The mesh block only contains cells and vertices. """ dim = int(mesh_block.get("dim")) cell_type = mesh_block.get("celltype") vertex_size = 0 Console.PrintLog("Mesh dimension: %d\n" % (dim, )) Console.PrintLog("Mesh cell type: %s\n" % (cell_type, )) # every cell type contains a dict with key=dimension and value=number cells_parts_dim = { "point": { 0: 1 }, "interval": { 0: 2, 1: 1 }, "triangle": { 0: 3, 1: 3, 2: 1 }, "tetrahedron": { 0: 4, 1: 6, 2: 4, 3: 1 }, "quadrilateral": { 0: 4, 1: 4, 2: 1 }, "hexahedron": { 0: 8, 1: 12, 2: 6, 3: 1 } } find_vertices = mesh_block.find("vertices") find_cells = mesh_block.find("cells") nodes_dict = {} cell_dict = {} if find_vertices is None: Console.PrintWarning("No vertices found!\n") else: vertex_size = int(find_vertices.attrib.get("size")) Console.PrintLog("Reading %d vertices\n" % (vertex_size, )) for vertex in find_vertices: ind = int(vertex.get("index")) if vertex.tag.lower() == "vertex": [node_x, node_y, node_z] = [ float(vertex.get(coord, 0.)) for coord in ["x", "y", "z"] ] nodes_dict[ind + 1] = FreeCAD.Vector( node_x, node_y, node_z) # increase node index by one, since fenics starts at 0, FreeCAD at 1 # print("%d %f %f %f" % (ind, node_x, node_y, node_z)) else: Console.PrintWarning("found strange vertex tag: %s\n" % (vertex.tag, )) if find_cells is None: Console.PrintWarning("No cells found!\n") else: Console.PrintLog("Reading %d cells\n" % (int(find_cells.attrib.get("size")), )) for cell in find_cells: ind = int(cell.get("index")) if cell.tag.lower() != cell_type.lower(): Console.PrintWarning( "Strange mismatch between cell type {} and cell tag {}\n" .format(cell_type, cell.tag.lower())) num_vertices = cells_parts_dim[cell_type][0] vtupel = tuple([ int(cell.get("v" + str(vnum))) + 1 for vnum in range(num_vertices) ]) # generate "v0", "v1", ... from dimension lookup table # increase numbers by one to match FC numbering convention cell_dict[ind + 1] = vtupel # valtupel = tuple([ind] + list(vtupel)) # print(("%d " + ("%d "*len(vtupel))) % valtupel) return (nodes_dict, cell_dict, cell_type, dim)
def read_fenics_mesh_xml(xmlfilename): """ Returns element dictionary to be evaluated by make_femmesh later """ Fenics_to_FreeCAD_dict = { "triangle": "tria3", "tetrahedron": "tetra4", "hexahedron": "hexa8", "interval": "seg2", "quadrilateral": "quad4", } def read_mesh_block(mesh_block): """ Reading mesh block from XML file. The mesh block only contains cells and vertices. """ dim = int(mesh_block.get("dim")) cell_type = mesh_block.get("celltype") vertex_size = 0 Console.PrintLog("Mesh dimension: %d\n" % (dim, )) Console.PrintLog("Mesh cell type: %s\n" % (cell_type, )) # every cell type contains a dict with key=dimension and value=number cells_parts_dim = { "point": { 0: 1 }, "interval": { 0: 2, 1: 1 }, "triangle": { 0: 3, 1: 3, 2: 1 }, "tetrahedron": { 0: 4, 1: 6, 2: 4, 3: 1 }, "quadrilateral": { 0: 4, 1: 4, 2: 1 }, "hexahedron": { 0: 8, 1: 12, 2: 6, 3: 1 } } find_vertices = mesh_block.find("vertices") find_cells = mesh_block.find("cells") nodes_dict = {} cell_dict = {} if find_vertices is None: Console.PrintWarning("No vertices found!\n") else: vertex_size = int(find_vertices.attrib.get("size")) Console.PrintLog("Reading %d vertices\n" % (vertex_size, )) for vertex in find_vertices: ind = int(vertex.get("index")) if vertex.tag.lower() == "vertex": [node_x, node_y, node_z] = [ float(vertex.get(coord, 0.)) for coord in ["x", "y", "z"] ] nodes_dict[ind + 1] = FreeCAD.Vector( node_x, node_y, node_z) # increase node index by one, since fenics starts at 0, FreeCAD at 1 # print("%d %f %f %f" % (ind, node_x, node_y, node_z)) else: Console.PrintWarning("found strange vertex tag: %s\n" % (vertex.tag, )) if find_cells is None: Console.PrintWarning("No cells found!\n") else: Console.PrintLog("Reading %d cells\n" % (int(find_cells.attrib.get("size")), )) for cell in find_cells: ind = int(cell.get("index")) if cell.tag.lower() != cell_type.lower(): Console.PrintWarning( "Strange mismatch between cell type {} and cell tag {}\n" .format(cell_type, cell.tag.lower())) num_vertices = cells_parts_dim[cell_type][0] vtupel = tuple([ int(cell.get("v" + str(vnum))) + 1 for vnum in range(num_vertices) ]) # generate "v0", "v1", ... from dimension lookup table # increase numbers by one to match FC numbering convention cell_dict[ind + 1] = vtupel # valtupel = tuple([ind] + list(vtupel)) # print(("%d " + ("%d "*len(vtupel))) % valtupel) return (nodes_dict, cell_dict, cell_type, dim) def generate_lower_dimensional_structures(nodes, cell_dict, cell_type, dim): def correct_volume_det(element_dict): """ Checks whether the cell elements all have the same volume (<0?) sign (is necessary to avoid negative Jacobian errors). Works only with tet4 and tri3 elements at the moment """ if dim == 3: for (ind, tet) in list(element_dict["tetra4"].items()): v0 = nodes[tet[0]] v1 = nodes[tet[1]] v2 = nodes[tet[2]] v3 = nodes[tet[3]] a = v1 - v0 b = v2 - v0 c = v3 - v0 if a.dot(b.cross(c)) > 0: element_dict["tetra4"][ind] = (tet[1], tet[0], tet[2], tet[3]) if dim == 2: nz = FreeCAD.Vector(0., 0., 1.) for (ind, tria) in list(element_dict["tria3"].items()): v0 = nodes[tria[0]] v1 = nodes[tria[1]] v2 = nodes[tria[2]] a = v1 - v0 b = v2 - v0 if nz.dot(a.cross(b)) < 0: element_dict["tria3"][ind] = (tria[1], tria[0], tria[2]) element_dict = {} element_counter = {} # TODO: remove upper level lookup for (key, val) in list(Fenics_to_FreeCAD_dict.items()): element_dict[val] = {} element_counter[ key] = 0 # count every distinct element and sub element type def addtupletodict(di, tpl, counter): sortedtpl = tuple(sorted(tpl)) if di.get(sortedtpl) is None: di[sortedtpl] = counter counter += 1 return counter def invertdict(dic): invdic = {} for (key, it) in list(dic.items()): invdic[it] = key return invdic num_vert_dict = { "interval": 2, "triangle": 3, "tetrahedron": 4, "hexahedron": 8, "quadrilateral": 4 } lower_dims_dict = { "interval": [], "triangle": ["interval"], "tetrahedron": ["triangle", "interval"], "hexahedron": ["quadrilateral", "interval"], "quadrilateral": ["interval"] } # generate cell list from file # read vertex list from cells # generate lower dimensional objects in mesh from cell for (cell_index, cell) in list(cell_dict.items()): cell_lower_dims = lower_dims_dict[cell_type] element_counter[cell_type] += 1 element_dict[Fenics_to_FreeCAD_dict[cell_type]][ cell] = element_counter[cell_type] for ld in cell_lower_dims: for vertextuple in itertools.combinations( cell, num_vert_dict[ld]): element_counter[ld] = addtupletodict( element_dict[Fenics_to_FreeCAD_dict[ld]], vertextuple, element_counter[ld]) length_counter = len(nodes) # maintain distinct counting values # print("nodes") # print("len & len counter", length_counter) for (key, val_dict) in list(element_dict.items()): # to ensure distinct indices for FreeCAD # print("key: ", key) for (vkey, it) in list(val_dict.items()): val_dict[ vkey] = it + length_counter # maintain distinct element numbers len_val_dict = len(val_dict) if len_val_dict > 0: length_counter += len_val_dict + 1 # only if preceding list is not empty # print("len: ", len_val_dict) # print("lencounter: ", length_counter) # inverse of the dict (dict[key] = val -> dict[val] = key) element_dict[key] = invertdict(val_dict) correct_volume_det(element_dict) # corrects negative determinants return element_dict # returns complete element dictionary nodes = {} element_dict = {} # TODO: remove two times initialization for val in list(Fenics_to_FreeCAD_dict.values()): element_dict[val] = {} tree = ET.parse(xmlfilename) root = tree.getroot() if root.tag.lower() != "dolfin": Console.PrintWarning("Strange root tag, should be dolfin!\n") find_mesh = root.find("mesh") if find_mesh is not None: # these are consistency checks of the XML structure Console.PrintMessage("Mesh found\n") (nodes, cells_dict, cell_type, dim) = read_mesh_block(find_mesh) element_dict = generate_lower_dimensional_structures( nodes, cells_dict, cell_type, dim) Console.PrintMessage("Show min max element dict") for (elm, numbers) in list(element_dict.items()): lst = sorted(list(numbers.items()), key=lambda x: x[0]) if lst != []: Console.PrintWarning(elm, " min: ", lst[0], " max: ", lst[-1], "\n") else: Console.PrintError("No mesh found") if root.find("data") is not None: Console.PrintLog("Internal mesh data found\n") return { "Nodes": nodes, "Seg2Elem": element_dict["seg2"], "Seg3Elem": {}, "Tria3Elem": element_dict["tria3"], "Tria6Elem": {}, "Quad4Elem": element_dict["quad4"], "Quad8Elem": {}, "Tetra4Elem": element_dict["tetra4"], "Tetra10Elem": {}, "Hexa8Elem": {}, "Hexa20Elem": {}, "Penta6Elem": {}, "Penta15Elem": {} }
def __init__(self): # remove selectionFilter self.selectionFilterStatus = selectionFilter.observerStatus() selectionFilter.observerDisable() # get the current active document to avoid errors if user changes tab self.activeDoc = App.ActiveDocument # we have checked before that all this is correct selection = Asm4.getSelectedLink() if selection is None: selection = Asm4.getSelectedVarLink() self.selectedObj = selection #self.rootAssembly = self.selectedObj.getParentGeoFeatureGroup() self.rootAssembly = Asm4.getAssembly() # has been checked before, this is for security only if Asm4.isAsm4EE(self.selectedObj): # get the old values self.old_AO = self.selectedObj.AttachmentOffset self.old_linkLCS = self.selectedObj.AttachedBy[1:] else: # this shouldn't happen FCC.PrintWarning("WARNING : unsupported Assembly/Solver/Part combination, you shouldn't be seeing this\n") Asm4.makeAsmProperties(self.selectedObj) self.old_AO = [] self.old_linkLCS = '' # define the GUI # draw the GUI, objects are defined later down # self.UI = QtGui.QWidget() # self.form = self.UI self.form = QtGui.QWidget() iconFile = os.path.join( Asm4.iconPath , 'Place_Link.svg') self.form.setWindowIcon(QtGui.QIcon( iconFile )) self.form.setWindowTitle('Place linked Part') self.drawUI(self.form) #save original AttachmentOffset of linked part self.old_LinkAttachmentOffset = self.selectedObj.AttachmentOffset self.old_LinkRotation = self.selectedObj.AttachmentOffset.Rotation self.old_LinkPosition = self.selectedObj.AttachmentOffset.Base # default values correspond to original AttachmentOffset of linked part self.Xtranslation = self.old_LinkPosition[0] self.Ytranslation = self.old_LinkPosition[1] self.Ztranslation = self.old_LinkPosition[2] self.XrotationAngle = self.old_LinkRotation.toEuler()[0] self.YrotationAngle = self.old_LinkRotation.toEuler()[1] self.ZrotationAngle = self.old_LinkRotation.toEuler()[2] # save previous view properties self.old_OverrideMaterial = self.selectedObj.ViewObject.OverrideMaterial self.old_DrawStyle = self.selectedObj.ViewObject.DrawStyle self.old_LineWidth = self.selectedObj.ViewObject.LineWidth self.old_DiffuseColor = self.selectedObj.ViewObject.ShapeMaterial.DiffuseColor self.old_Transparency = self.selectedObj.ViewObject.ShapeMaterial.Transparency # set new view properties self.selectedObj.ViewObject.OverrideMaterial = True self.selectedObj.ViewObject.DrawStyle = DrawStyle self.selectedObj.ViewObject.LineWidth = LineWidth self.selectedObj.ViewObject.ShapeMaterial.DiffuseColor = DiffuseColor self.selectedObj.ViewObject.ShapeMaterial.Transparency = Transparency # get the old values self.old_EE = '' old_Parent = '' old_ParentPart = '' old_attLCS = '' constrName = '' linkedDoc = '' old_linkLCS = '' # get and store the current expression engine: self.old_EE = Asm4.placementEE(self.selectedObj.ExpressionEngine) # decode the old ExpressionEngine # if the decode is unsuccessful, old_Expression is set to False and the other things are set to 'None' (self.old_Parent, separator, self.old_parentLCS) = self.selectedObj.AttachedTo.partition('#') ( old_Parent, old_attLCS, old_linkLCS ) = self.splitExpressionLink( self.old_EE, self.old_Parent ) # sometimes, the object is in << >> which is an error by FreeCAD, # because that's reserved for labels, but we still look for it if len(old_attLCS)>4 and old_attLCS[:2]=='<<' and old_attLCS[-2:]=='>>': old_attLCS = old_attLCS[2:-2] if len(old_linkLCS)>4 and old_linkLCS[:2]=='<<' and old_linkLCS[-2:]=='>>': old_linkLCS = old_linkLCS[2:-2] # initialize the UI with the current data self.attLCStable = [] self.initUI() # now self.parentList and self.parentTable are available # find all the linked parts in the assembly for obj in self.activeDoc.findObjects("App::Link"): if self.rootAssembly.getObject(obj.Name) is not None and hasattr(obj.LinkedObject,'isDerivedFrom'): linkedObj = obj.LinkedObject if linkedObj.isDerivedFrom('App::Part') or linkedObj.isDerivedFrom('PartDesign::Body'): # ... except if it's the selected link itself if obj != self.selectedObj: self.parentTable.append( obj ) # add to the drop-down combo box with the assembly tree's parts objIcon = linkedObj.ViewObject.Icon objText = Asm4.labelName(obj) self.parentList.addItem( objIcon, objText, obj) # find all the LCS in the selected link self.partLCStable = Asm4.getPartLCS( self.selectedObj.LinkedObject ) # build the list self.partLCSlist.clear() for lcs in self.partLCStable: newItem = QtGui.QListWidgetItem() newItem.setText(Asm4.labelName(lcs)) newItem.setIcon( lcs.ViewObject.Icon ) self.partLCSlist.addItem(newItem) # find the old LCS in the list of LCS of the linked part... # MatchExactly, MatchContains, MatchEndsWith ... # find with Name ... lcs_found = self.partLCSlist.findItems( old_linkLCS, QtCore.Qt.MatchExactly ) # ... or with (Name) if not lcs_found: lcs_found = self.partLCSlist.findItems( '('+old_linkLCS+')', QtCore.Qt.MatchEndsWith ) if lcs_found: # ... and select it self.partLCSlist.setCurrentItem( lcs_found[0] ) # find the oldPart in the part list... if old_Parent == 'Parent Assembly': parent_found = True parent_index = 1 else: parent_found = False parent_index = 1 for item in self.parentTable[1:]: if item.Name == old_Parent: parent_found = True break else: parent_index = parent_index +1 if not parent_found: parent_index = 0 self.parentList.setCurrentIndex( parent_index ) # this should have triggered self.getPartLCS() to fill the LCS list # find the old attachment Datum in the list of the Datums in the linked part... lcs_found = self.attLCSlist.findItems( old_attLCS, QtCore.Qt.MatchExactly ) if not lcs_found: lcs_found = self.attLCSlist.findItems( '('+old_attLCS+')', QtCore.Qt.MatchEndsWith ) if lcs_found: # ... and select it self.attLCSlist.setCurrentItem( lcs_found[0] ) # selection observer to detect selection of LCS in the 3D window and tree Gui.Selection.addObserver(self, 0)
def Apply( self ): # get the instance to attach to: # it's either the top level assembly or a sister App::Link if self.parentList.currentText() == 'Parent Assembly': a_Link = 'Parent Assembly' a_Part = None elif self.parentList.currentIndex() > 1: parent = self.parentTable[ self.parentList.currentIndex() ] a_Link = parent.Name a_Part = parent.LinkedObject.Document.Name else: a_Link = None a_Part = None # the attachment LCS's name in the parent # check that something is selected in the QlistWidget if self.attLCSlist.selectedItems(): a_LCS = self.attLCStable[ self.attLCSlist.currentRow() ].Name else: a_LCS = None # the linked App::Part's name l_Part = self.selectedObj.LinkedObject.Document.Name # the LCS's name in the linked part to be used for its attachment # check that something is selected in the QlistWidget if self.partLCSlist.selectedItems(): #l_LCS = self.partLCSlist.selectedItems()[0].text() l_LCS = self.partLCStable[ self.partLCSlist.currentRow() ].Name else: l_LCS = None # check that all of them have something in # constrName has been checked at the beginning if a_Link and a_LCS and l_Part and l_LCS : # add the Asm4 properties if it's a pure App::Link Asm4.makeAsmProperties(self.selectedObj) # self.selectedObj.AssemblyType = 'Part::Link' self.selectedObj.AttachedBy = '#'+l_LCS self.selectedObj.AttachedTo = a_Link+'#'+a_LCS self.selectedObj.SolverId = 'Placement::ExpressionEngine' # build the expression for the ExpressionEngine # this is where all the magic is, see: # # https://forum.freecadweb.org/viewtopic.php?p=278124#p278124 # # as of FreeCAD v0.19 the syntax is different: # https://forum.freecadweb.org/viewtopic.php?f=17&t=38974&p=337784#p337784 # # expr = ParentLink.Placement * ParentPart#LCS.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1' # expr = LCS_in_the_assembly.Placement * constr_LinkName.AttachmentOffset * LinkedPart#LCS.Placement ^ -1' expr = Asm4.makeExpressionPart( a_Link, a_Part, a_LCS, l_Part, l_LCS ) # load the expression into the link's Expression Engine self.selectedObj.setExpression('Placement', expr ) # recompute the object to apply the placement: self.selectedObj.recompute() self.rootAssembly.recompute(True) return True else: FCC.PrintWarning("Problem in selections\n") return False
def _is_between_lower_bounds(self, face, frame): Console.PrintWarning('Bottom side bounds checking not supported.\n') pass
def __init__(self): self.base = QtGui.QWidget() self.form = self.base iconFile = os.path.join(Asm4.iconPath, 'Place_Link.svg') self.form.setWindowIcon(QtGui.QIcon(iconFile)) self.form.setWindowTitle('Place linked Part') # check that we have selected two LCS or an App::Link object self.selectedLink = [] self.selectedLCSA = None self.selectedLinkB = None self.selectedLCSB = None selection = Asm4.getSelectedLink() #selectedLCSPair = Asm4.getLinkAndDatum2() self.Xtranslation = 0.00 self.Ytranslation = 0.00 self.Ztranslation = 0.00 self.XrotationAngle = 0.00 self.YrotationAngle = 0.00 self.ZrotationAngle = 0.00 # draw the GUI, objects are defined later down self.drawUI() global taskUI taskUI = self #Handle single selected App::Link if not selection: # This shouldn't happen FCC.PrintWarning( "This is not an error message you are supposed to see, something went wrong\n" ) Gui.Control.closeDialog() else: self.selectedLink = selection Asm4.makeAsmProperties(self.selectedLink) #save original AttachmentOffset of linked part self.old_LinkAttachmentOffset = self.selectedLink.AttachmentOffset self.old_LinkRotation = self.selectedLink.AttachmentOffset.Rotation self.old_LinkPosition = self.selectedLink.AttachmentOffset.Base # default values correspond to original AttachmentOffset of linked part self.Xtranslation = self.old_LinkPosition[0] self.Ytranslation = self.old_LinkPosition[1] self.Ztranslation = self.old_LinkPosition[2] self.XrotationAngle = self.old_LinkRotation.toEuler()[0] self.YrotationAngle = self.old_LinkRotation.toEuler()[1] self.ZrotationAngle = self.old_LinkRotation.toEuler()[2] # save previous view properties self.old_OverrideMaterial = self.selectedLink.ViewObject.OverrideMaterial self.old_DrawStyle = self.selectedLink.ViewObject.DrawStyle self.old_LineWidth = self.selectedLink.ViewObject.LineWidth self.old_DiffuseColor = self.selectedLink.ViewObject.ShapeMaterial.DiffuseColor self.old_Transparency = self.selectedLink.ViewObject.ShapeMaterial.Transparency # set new view properties self.selectedLink.ViewObject.OverrideMaterial = True self.selectedLink.ViewObject.DrawStyle = DrawStyle self.selectedLink.ViewObject.LineWidth = LineWidth self.selectedLink.ViewObject.ShapeMaterial.DiffuseColor = DiffuseColor self.selectedLink.ViewObject.ShapeMaterial.Transparency = Transparency # get the current active document to avoid errors if user changes tab self.activeDoc = App.activeDocument() # the parent (top-level) assembly is the App::Part called Model (hard-coded) self.parentAssembly = self.activeDoc.Model # check that the link is an Asm4 link: self.isAsm4EE = False if hasattr(self.selectedLink, 'AssemblyType'): if self.selectedLink.AssemblyType == 'Asm4EE' or self.selectedLink.AssemblyType == '': self.isAsm4EE = True else: Asm4.warningBox( "This Link's assembly type doesn't correspond to this WorkBench" ) return # initialize the UI with the current data self.attLCStable = [] self.initUI() # now self.parentList and self.parentTable are available # find all the linked parts in the assembly for objName in self.parentAssembly.getSubObjects(): # remove the trailing . obj = self.activeDoc.getObject(objName[0:-1]) if obj.TypeId == 'App::Link' and hasattr(obj.LinkedObject, 'isDerivedFrom'): if obj.LinkedObject.isDerivedFrom( 'App::Part') or obj.LinkedObject.isDerivedFrom( 'PartDesign::Body'): # ... except if it's the selected link itself if obj != self.selectedLink: self.parentTable.append(obj) # add to the drop-down combo box with the assembly tree's parts objIcon = obj.LinkedObject.ViewObject.Icon objText = Asm4.nameLabel(obj) self.parentList.addItem(objIcon, objText, obj) # find all the LCS in the selected link self.partLCStable = Asm4.getPartLCS(self.selectedLink.LinkedObject) # build the list self.partLCSlist.clear() for lcs in self.partLCStable: newItem = QtGui.QListWidgetItem() newItem.setText(Asm4.nameLabel(lcs)) newItem.setIcon(lcs.ViewObject.Icon) self.partLCSlist.addItem(newItem) # get the old values if self.isAsm4EE: self.old_AO = self.selectedLink.AttachmentOffset self.old_linkLCS = self.selectedLink.AttachedBy[1:] (self.old_Parent, separator, self.old_parentLCS) = self.selectedLink.AttachedTo.partition('#') else: self.old_AO = [] self.old_Parent = '' self.old_EE = '' # get and store the current expression engine: self.old_EE = Asm4.placementEE(self.selectedLink.ExpressionEngine) # decode the old ExpressionEngine old_Parent = '' old_ParentPart = '' old_attLCS = '' constrName = '' linkedDoc = '' old_linkLCS = '' # if the decode is unsuccessful, old_Expression is set to False and the other things are set to 'None' (old_Parent, old_attLCS, old_linkLCS) = Asm4.splitExpressionLink(self.old_EE, self.old_Parent) # find the old LCS in the list of LCS of the linked part... # MatchExactly, MatchContains, MatchEndsWith ... lcs_found = self.partLCSlist.findItems(old_linkLCS, QtCore.Qt.MatchExactly) if not lcs_found: lcs_found = self.partLCSlist.findItems(old_linkLCS + ' (', QtCore.Qt.MatchStartsWith) if lcs_found: # ... and select it self.partLCSlist.setCurrentItem(lcs_found[0]) # find the oldPart in the part list... if old_Parent == 'Parent Assembly': parent_found = True parent_index = 1 else: parent_found = False parent_index = 1 for item in self.parentTable[1:]: if item.Name == old_Parent: parent_found = True break else: parent_index = parent_index + 1 if not parent_found: parent_index = 0 self.parentList.setCurrentIndex(parent_index) # this should have triggered self.getPartLCS() to fill the LCS list # find the old attachment Datum in the list of the Datums in the linked part... lcs_found = self.attLCSlist.findItems(old_attLCS, QtCore.Qt.MatchExactly) if not lcs_found: lcs_found = self.attLCSlist.findItems(old_attLCS + ' (', QtCore.Qt.MatchStartsWith) if lcs_found: # ... and select it self.attLCSlist.setCurrentItem(lcs_found[0])
def get_lengths(arc, mat): """ Get needed parameters for arc calculation from the user-defined arc or the calculated vector matrix """ #[0,1] = Radius; [2, 3] = Tangent, [4] = Middle, [5] = Chord lengths = mat.diagonal().A[0] params = [ arc.get('Radius'), arc.get('Tangent'), arc.get('Middle'), arc.get('Chord'), arc.get('Delta') ] for _i in range(0, 2): #get two consecutive elements, saving only if they're valid _s = [_v for _v in lengths[_i * 2:(_i + 1) * 2] if _v] #skip the rest if not defined, we'll use the user values if not any(_s): continue #duplicate the only calculated length if len(_s) == 1: _s.append(_s[0]) #if both were calculated and they aren't the same, quit if all(_s) and not support.within_tolerance(_s[0], _s[1]): _attribs = ['radius', 'Start-Center-End'] if _i == 1: _attribs = ['tangent', 'Start-PI-End'] Console.PrintWarning(""" \nArc {0} length and {1} distance mismatch by {2:f} mm. Using calculated value of {3:f} mm """\ .format(_attribs[0], _attribs[1], abs(_s[1] - _s[0]), _s[0])) if _s[0]: if not support.within_tolerance(_s[0], params[_i]): params[_i] = _s[0] #test middle and chord. #If no user-defined value or out-of-tolerance, use calculated for _i in range(4, 6): if lengths[_i]: if not support.within_tolerance(lengths[_i], params[_i - 2]): params[_i - 2] = lengths[_i] return { 'Radius': params[0], 'Tangent': params[1], 'Middle': params[2], 'Chord': params[3] }
def importFrd(filename, analysis=None, result_name_prefix=""): import ObjectsFem from . import importToolsFem if analysis: doc = analysis.Document else: doc = FreeCAD.ActiveDocument m = read_frd_result(filename) result_mesh_object = None res_obj = None if len(m["Nodes"]) > 0: mesh = importToolsFem.make_femmesh(m) result_mesh_object = ObjectsFem.makeMeshResult(doc, "ResultMesh") result_mesh_object.FemMesh = mesh res_mesh_is_compacted = False nodenumbers_for_compacted_mesh = [] number_of_increments = len(m["Results"]) Console.PrintLog("Increments: " + str(number_of_increments) + "\n") if len(m["Results"]) > 0: for result_set in m["Results"]: if "number" in result_set: eigenmode_number = result_set["number"] else: eigenmode_number = 0 step_time = result_set["time"] step_time = round(step_time, 2) if eigenmode_number > 0: results_name = ("{}Mode{}_Results".format( result_name_prefix, eigenmode_number)) elif number_of_increments > 1: results_name = ("{}Time{}_Results".format( result_name_prefix, step_time)) else: results_name = ("{}Results".format(result_name_prefix)) res_obj = ObjectsFem.makeResultMechanical(doc, results_name) res_obj.Mesh = result_mesh_object res_obj = importToolsFem.fill_femresult_mechanical( res_obj, result_set) if analysis: # need to be here, becasause later on, the analysis objs are needed # see fill of principal stresses analysis.addObject(res_obj) # more result object calculations from femresult import resulttools from femtools import femutils if not res_obj.MassFlowRate: # information 1: # only compact result if not Flow 1D results # compact result object, workaround for bug 2873 # https://www.freecadweb.org/tracker/view.php?id=2873 # information 2: # if the result data has multiple result sets there will be multiple result objs # they all will use one mesh obj # on the first res obj fill: the mesh obj will be compacted, thus # it does not need to be compacted on further result sets # but NodeNumbers need to be compacted for every result set (res object fill) # example frd file: https://forum.freecadweb.org/viewtopic.php?t=32649#p274291 if res_mesh_is_compacted is False: # first result set, compact FemMesh and NodeNumbers res_obj = resulttools.compact_result(res_obj) res_mesh_is_compacted = True nodenumbers_for_compacted_mesh = res_obj.NodeNumbers else: # all other result sets, do not compact FemMesh, only set NodeNumbers res_obj.NodeNumbers = nodenumbers_for_compacted_mesh # fill DisplacementLengths res_obj = resulttools.add_disp_apps(res_obj) # fill vonMises res_obj = resulttools.add_von_mises(res_obj) # fill principal stress # if material reinforced object use add additional values to the res_obj if res_obj.getParentGroup(): has_reinforced_mat = False for obj in res_obj.getParentGroup().Group: if femutils.is_of_type(obj, "Fem::MaterialReinforced"): has_reinforced_mat = True Console.PrintLog( "Reinfoced material object detected, " "reinforced principal stresses and standard principal " " stresses will be added.\n") resulttools.add_principal_stress_reinforced( res_obj) break if has_reinforced_mat is False: Console.PrintLog( "No einfoced material object detected, " "standard principal stresses will be added.\n") # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear res_obj = resulttools.add_principal_stress_std(res_obj) else: Console.PrintLog( "No Analysis detected, standard principal stresses will be added.\n" ) # if a pure frd file was opened no analysis and thus no parent group # fill PrincipalMax, PrincipalMed, PrincipalMin, MaxShear res_obj = resulttools.add_principal_stress_std(res_obj) # fill Stats res_obj = resulttools.fill_femresult_stats(res_obj) else: error_message = ( "Nodes, but no results found in frd file. " "It means there only is a mesh but no results in frd file. " "Usually this happens for: \n" "- analysis type 'NOANALYSIS'\n" "- if CalculiX returned no results " "(happens on nonpositive jacobian determinant in at least one element)\n" "- just no frd results where requestet in input file " "(neither 'node file' nor 'el file' in output section')\n") Console.PrintWarning(error_message) # create a result obj, even if we have no results but a result mesh in frd file # see error message above for more information if not res_obj: if result_name_prefix: results_name = ("{}_Results".format(result_name_prefix)) else: results_name = ("Results".format(result_name_prefix)) res_obj = ObjectsFem.makeResultMechanical(doc, results_name) res_obj.Mesh = result_mesh_object # TODO, node numbers in result obj could be set if analysis: analysis.addObject(res_obj) if FreeCAD.GuiUp: if analysis: import FemGui FemGui.setActiveAnalysis(analysis) doc.recompute() else: Console.PrintError( "Problem on frd file import. No nodes found in frd file.\n") # None will be returned # or would it be better to raise an exception if there are not even nodes in frd file? return res_obj