diff --git a/src/Mod/Arch/importIFC.py b/src/Mod/Arch/importIFC.py index 23c5f52986..c416a5c648 100644 --- a/src/Mod/Arch/importIFC.py +++ b/src/Mod/Arch/importIFC.py @@ -45,11 +45,11 @@ import importIFCHelper # # This module provides tools to import IFC files. -DEBUG = False # Set to True to see debug messages. Otherwise, totally silent -ZOOMOUT = True # Set to False to not zoom extents after import +DEBUG = False # Set to True to see debug messages. Otherwise, totally silent +ZOOMOUT = True # Set to False to not zoom extents after import if open.__module__ in ['__builtin__','io']: - pyopen = open # because we'll redefine open below + pyopen = open # because we'll redefine open below # ************************************************************************************************ @@ -232,10 +232,10 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): # IfcOpenShell multiplies the precision value of the file by 100 # So we raise the precision by 100 too to compensate... - #ctxs = ifcfile.by_type("IfcGeometricRepresentationContext") - #for ctx in ctxs: - # if not ctx.is_a("IfcGeometricRepresentationSubContext"): - # ctx.Precision = ctx.Precision/100 + # ctxs = ifcfile.by_type("IfcGeometricRepresentationContext") + # for ctx in ctxs: + # if not ctx.is_a("IfcGeometricRepresentationSubContext"): + # ctx.Precision = ctx.Precision/100 # set default ifcopenshell options to work in brep mode from ifcopenshell import geom @@ -258,12 +258,12 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): materials = ifcfile.by_type("IfcMaterial") products, annotations = importIFCHelper.buildRelProductsAnnotations(ifcfile, preferences['ROOT_ELEMENT']) # empty relation tables - objects = {} # { id:object, ... } - shapes = {} # { id:shaoe } only used for merge mode - structshapes = {} # { id:shaoe } only used for merge mode - sharedobjects = {} # { representationmapid:object } - parametrics = [] # a list of imported objects whose parametric relationships need processing after all objects have been created - profiles = {} # to store reused extrusion profiles {ifcid:fcobj,...} + objects = {} # { id:object, ... } + shapes = {} # { id:shaoe } only used for merge mode + structshapes = {} # { id:shaoe } only used for merge mode + sharedobjects = {} # { representationmapid:object } + parametrics = [] # a list of imported objects whose parametric relationships need processing after all objects have been created + profiles = {} # to store reused extrusion profiles {ifcid:fcobj,...} # filled relation tables # TODO for the following tables might be better use inverse attributes, done for properties # see https://forum.freecadweb.org/viewtopic.php?f=39&t=37892 @@ -272,7 +272,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): groups = importIFCHelper.buildRelGroups(ifcfile) subtractions = importIFCHelper.buildRelSubtractions(ifcfile) mattable = importIFCHelper.buildRelMattable(ifcfile) - colors = importIFCHelper.buildRelColors(ifcfile, prodrepr) + colors = importIFCHelper.buildRelProductColors(ifcfile, prodrepr) if preferences['DEBUG']: print("done.") # only import a list of IDs and their children, if defined @@ -356,10 +356,10 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): if preferences['MERGE_MODE_STRUCT'] == 3 and not archobj: if preferences['DEBUG']: print(" skipped.") continue - if pid in skip: # user given id skip list + if pid in skip: # user given id skip list if preferences['DEBUG']: print(" skipped.") continue - if ptype in preferences['SKIP']: # preferences-set type skip list + if ptype in preferences['SKIP']: # preferences-set type skip list if preferences['DEBUG']: print(" skipped.") continue @@ -380,7 +380,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): clone = sharedobjects[originalid] else: sharedobjects[originalid] = None - store = originalid # flag this object to be stored later + store = originalid # flag this object to be stored later # set additional setting for structural entities if hasattr(settings,"INCLUDE_CURVES"): @@ -392,7 +392,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): cr = ifcopenshell.geom.create_shape(settings,product) brep = cr.geometry.brep_data except: - pass # IfcOpenShell will yield an error if a given product has no shape, but we don't care, we're brave enough + pass # IfcOpenShell will yield an error if a given product has no shape, but we don't care, we're brave enough # from now on we have a brep string if brep: @@ -401,7 +401,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): # create a Part shape shape = Part.Shape() shape.importBrepFromString(brep,False) - shape.scale(1000.0) # IfcOpenShell always outputs in meters, we convert to mm, the freecad internal unit + shape.scale(1000.0) # IfcOpenShell always outputs in meters, we convert to mm, the freecad internal unit if shape.isNull(): if preferences['DEBUG']: print("null shape ",end="") @@ -428,7 +428,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): # we are not using Arch objects # additional tweaks to set when not using Arch objects - if ptype == "IfcSpace": # do not add spaces to compounds + if ptype == "IfcSpace": # do not add spaces to compounds if preferences['DEBUG']: print("skipping space ",pid,end="") elif structobj: structshapes[pid] = shape @@ -451,7 +451,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): sortmethod = "z" else: sortmethod = "area" - ex = Arch.getExtrusionData(shape,sortmethod) # is this an extrusion? + ex = Arch.getExtrusionData(shape,sortmethod) # is this an extrusion? if ex: # check for extrusion profile @@ -623,9 +623,9 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): for attribute in ArchIFCSchema.IfcProducts[product.is_a()]["attributes"]: if attribute["name"] == "Name": continue - #print("attribute:",attribute["name"]) + # print("attribute:",attribute["name"]) if hasattr(product, attribute["name"]) and getattr(product, attribute["name"]) and hasattr(obj,attribute["name"]): - #print("Setting attribute",attribute["name"],"to",getattr(product, attribute["name"])) + # print("Setting attribute",attribute["name"],"to",getattr(product, attribute["name"])) setattr(obj, attribute["name"], getattr(product, attribute["name"])) # TODO: ArchIFCSchema.IfcProducts uses the IFC version from the FreeCAD prefs. # This might not coincide with the file being opened, hence some attributes are not properly read. @@ -726,7 +726,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): if preferences['DEBUG']: print("property NominalValue",l.NominalValue.is_a(),type(l.NominalValue.is_a())) print("property NominalValue.wrappedValue",l.NominalValue.wrappedValue,type(l.NominalValue.wrappedValue)) - #print("l.NominalValue.Unit",l.NominalValue.Unit,type(l.NominalValue.Unit)) + # print("l.NominalValue.Unit",l.NominalValue.Unit,type(l.NominalValue.Unit)) ifc_spreadsheet.set(str('C'+str(n)), l.NominalValue.is_a()) if l.NominalValue.is_a() in ['IfcLabel','IfcText','IfcIdentifier','IfcDescriptiveMeasure']: if six.PY2: @@ -756,13 +756,13 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): for p in psets[c]: l = ifcfile[p] if l.is_a("IfcPropertySingleValue"): - a[l.Name.encode("utf8")] = str(l.NominalValue) # no py3 support here + a[l.Name.encode("utf8")] = str(l.NominalValue) # no py3 support here obj.IfcData = a # color if FreeCAD.GuiUp and (pid in colors) and hasattr(obj.ViewObject,"ShapeColor"): - #if preferences['DEBUG']: print(" setting color: ",int(colors[pid][0]*255),"/",int(colors[pid][1]*255),"/",int(colors[pid][2]*255)) + # if preferences['DEBUG']: print(" setting color: ",int(colors[pid][0]*255),"/",int(colors[pid][1]*255),"/",int(colors[pid][2]*255)) obj.ViewObject.ShapeColor = colors[pid] # if preferences['DEBUG'] is on, recompute after each shape @@ -818,7 +818,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): if preferences['DEBUG']: print("Joining Structural shapes...",end="") - for host,children in groups.items(): # Structural + for host,children in groups.items(): # Structural if ifcfile[host].is_a("IfcStructuralAnalysisModel"): compound = [] for c in children: @@ -831,7 +831,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) obj.Label = name obj.Shape = Part.makeCompound(compound) - if structshapes: # remaining Structural shapes + if structshapes: # remaining Structural shapes obj = FreeCAD.ActiveDocument.addObject("Part::Feature","UnclaimedStruct") obj.Shape = Part.makeCompound(structshapes.values()) @@ -902,7 +902,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): if preferences['DEBUG']: print("Joining Arch shapes...",end="") - for host,children in additions.items(): # Arch + for host,children in additions.items(): # Arch if ifcfile[host].is_a("IfcBuildingStorey"): compound = [] for c in children: @@ -920,7 +920,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) obj.Label = name obj.Shape = Part.makeCompound(compound) - if shapes: # remaining Arch shapes + if shapes: # remaining Arch shapes obj = FreeCAD.ActiveDocument.addObject("Part::Feature","UnclaimedArch") obj.Shape = Part.makeCompound(shapes.values()) @@ -951,7 +951,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): cobs = [] for child in children: if child in objects.keys() \ - and child not in swallowed: # don't add objects already in groups + and child not in swallowed: # don't add objects already in groups cobs.append(objects[child]) if not cobs: continue @@ -995,9 +995,9 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): if preferences['DEBUG']: print(count,"/",len(annotations),"object #"+str(aid),":",annotation.is_a(),end="") if aid in skip: - continue # user given id skip list + continue # user given id skip list if annotation.is_a() in preferences['SKIP']: - continue # preferences-set type skip list + continue # preferences-set type skip list if annotation.is_a("IfcGrid"): axes = [] uvwaxes = () @@ -1010,7 +1010,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): for axis in uvwaxes: if axis.AxisCurve: sh = importIFCHelper.get2DShape(axis.AxisCurve,ifcscale) - if sh and (len(sh[0].Vertexes) == 2): # currently only straight axes are supported + if sh and (len(sh[0].Vertexes) == 2): # currently only straight axes are supported sh = sh[0] l = sh.Length pl = FreeCAD.Placement() @@ -1055,7 +1055,7 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): anno = FreeCAD.ActiveDocument.addObject("Part::Feature",name) anno.Shape = sh p = importIFCHelper.getPlacement(annotation.ObjectPlacement,ifcscale) - if p: # and annotation.is_a("IfcAnnotation"): + if p: # and annotation.is_a("IfcAnnotation"): anno.Placement = p else: if preferences['DEBUG']: print(" no shape") @@ -1075,8 +1075,8 @@ def insert(filename,docname,skip=[],only=[],root=None,preferences=None): # Materials if preferences['DEBUG'] and materials: print("Creating materials...",end="") - #print("mattable:",mattable) - #print("materials:",materials) + # print("mattable:",mattable) + # print("materials:",materials) fcmats = {} for material in materials: # get and set material name diff --git a/src/Mod/Arch/importIFCHelper.py b/src/Mod/Arch/importIFCHelper.py index 3e0dd5d02c..d29dec1f16 100644 --- a/src/Mod/Arch/importIFCHelper.py +++ b/src/Mod/Arch/importIFCHelper.py @@ -166,7 +166,7 @@ def buildRelProductsAnnotations(ifcfile, root_element): def buildRelProductRepresentation(ifcfile): """build the product/representations relation table""" - prodrepr = {} # product/representations table + prodrepr = {} # product/representations table for p in ifcfile.by_type("IfcProduct"): if hasattr(p,"Representation"): @@ -188,7 +188,7 @@ def buildRelProductRepresentation(ifcfile): def buildRelAdditions(ifcfile): """build the additions relation table""" - additions = {} # { host:[child,...], ... } + additions = {} # { host:[child,...], ... } for r in ifcfile.by_type("IfcRelContainedInSpatialStructure"): additions.setdefault(r.RelatingStructure.id(),[]).extend([e.id() for e in r.RelatedElements]) @@ -201,7 +201,7 @@ def buildRelAdditions(ifcfile): def buildRelGroups(ifcfile): """build the groups relation table""" - groups = {} # { host:[child,...], ... } # used in structural IFC + groups = {} # { host:[child,...], ... } # used in structural IFC for r in ifcfile.by_type("IfcRelAssignsToGroup"): groups.setdefault(r.RelatingGroup.id(),[]).extend([e.id() for e in r.RelatedObjects]) @@ -212,7 +212,7 @@ def buildRelGroups(ifcfile): def buildRelSubtractions(ifcfile): """build the subtractions relation table""" - subtractions = [] # [ [opening,host], ... ] + subtractions = [] # [ [opening,host], ... ] for r in ifcfile.by_type("IfcRelVoidsElement"): subtractions.append([r.RelatedOpeningElement.id(), r.RelatingBuildingElement.id()]) @@ -223,7 +223,7 @@ def buildRelSubtractions(ifcfile): def buildRelMattable(ifcfile): """build the mattable relation table""" - mattable = {} # { objid:matid } + mattable = {} # { objid:matid } for r in ifcfile.by_type("IfcRelAssociatesMaterial"): for o in r.RelatedObjects: @@ -239,10 +239,17 @@ def buildRelMattable(ifcfile): return mattable +# ************************************************************************************************ +# color relation tables +# products can have a color and materials can have a color and products can have a material +# colors for material assigned to a product and product color can be different + + def buildRelColors(ifcfile, prodrepr): """build the colors relation table and""" - colors = {} # { id:(r,g,b) } + # returns all IfcStyledItem colors, material and product colors + colors = {} # { id:(r,g,b) } style_material_id = {} # { style_entity_id: material_id) } # get style_color_rgb table @@ -269,12 +276,13 @@ def buildRelColors(ifcfile, prodrepr): for p in prodrepr.keys(): if r.Item.id() in prodrepr[p]: style_material_id[r.id()] = p - # print(p) + # print(p) # print(ifcfile[p]) # product ''' # a much faster version for Nova style_material_id with product_ids + # no material colors, Nova ifc files often do not have materials at all for p in prodrepr.keys(): - # print("\n") + # print("\n") # print(ifcfile[p]) # IfcProduct # print(ifcfile[p].Representation) # IfcProductDefinitionShape # print(ifcfile[p].Representation.Representations[0]) # IfcShapeRepresentation @@ -300,13 +308,120 @@ def buildRelColors(ifcfile, prodrepr): return colors -def getRelProperties(ifcfile): - """Builds and returns a dictionary of {object:[properties]} from an IFC file""" +def buildRelProductColors(ifcfile, prodrepr): - # this method no longer used by this importer module + # gets the colors for the products + colors = {} # { id:(r,g,b) } + + for p in prodrepr.keys(): + + # print(p) + + # representation item, see docu IfcRepresentationItem + # all kind of geometric or topological representation items + # IfcExtrudedAreaSolid, IfcMappedItem, IfcFacetedBrep, IfcBooleanResult, etc + representation_item = ifcfile[p].Representation.Representations[0].Items[0] + # print(representation_item) + + # get the geometric representations which have a presentation style + # all representation items have the inverse attribute StyledByItem for this + # there will be gemetric representations which do not have a presentation style + # the StyledByItem will be empty than + if representation_item.StyledByItem: + + # it has to be a IfcStyledItem, no check needed + styled_item = representation_item.StyledByItem[0] + + # write into colors table if a IfcStyledItem exists for this product + # write None if something goes wrong or if the ifc file has errors and thus no valid color is returned + colors[p] = getColorFromStyledItem(styled_item) + + return colors + + +def buildRelMaterialColors(ifcfile, prodrepr): + # not implemented + pass + + +def getColorFromStyledItem(styled_item): + + # styled_item should be a IfcStyledItem + if not styled_item.is_a("IfcStyledItem"): + print("Not a IfcStyledItem passed.") + return None + + rgb_color = None + + # print(styled_item) + # The IfcStyledItem holds presentation style information for products, + # either explicitly for an IfcGeometricRepresentationItem being part of + # an IfcShapeRepresentation assigned to a product, or by assigning presentation + # information to IfcMaterial being assigned as other representation for a product. + + # In current IFC release (IFC2x3) only one presentation style assignment shall be assigned. + + if len(styled_item.Styles) != 1: + if len(styled_item.Styles) == 0: + pass + # ca 100x in 210_King_Merged.ifc + # empty styles, #4952778=IfcStyledItem(#4952779,(),$) + # this is an error in the ifc file IMHO + # print(ifcfile[p]) + # print(styled_item) + # print(styled_item.Styles) + else: + pass + # never seen an ifc with more than one Styles in IfcStyledItem + else: + # get the IfcPresentationStyleAssignment, there should only be one, see above + assign_style = styled_item.Styles[0] + # print(assign_style) # IfcPresentationStyleAssignment + + # IfcPresentationStyleAssignment can hold various kinde and count of styles + # see IfcPresentationStyleSelect + if assign_style.Styles[0].is_a("IfcSurfaceStyle"): + # Schependomlaan and Nova and others + # print(assign_style.Styles[0].Styles[0]) # IfcSurfaceStyleRendering + rgb_color = assign_style.Styles[0].Styles[0].SurfaceColour # IfcColourRgb + # print(rgb_color) + elif assign_style.Styles[0].is_a("IfcCurveStyle"): + if ( + len(assign_style.Styles) == 2 + and assign_style.Styles[1].is_a("IfcSurfaceStyle") + ): + # Allplan, new IFC export started in 2017 + # print(assign_style.Styles[0].CurveColour) # IfcDraughtingPreDefinedColour + # on index 1 ist das was wir brauchen !!! + rgb_color = assign_style.Styles[1].Styles[0].SurfaceColour + # print(rgb_color) + else: + # 2x Annotations in 210_King_Merged.ifc + # print(ifcfile[p]) + # print(assign_style.Styles[0]) + # print(assign_style.Styles[0].CurveColour) + rgb_color = assign_style.Styles[0].CurveColour + + if rgb_color is not None: + col = rgb_color.Red, rgb_color.Green, rgb_color.Blue + # print(col) + else: + col = None + + return col + + +# ************************************************************************************************ +# property related methods +def buildRelProperties(ifcfile): + """ + Builds and returns a dictionary of {object:[properties]} from an IFC file + """ + + # this method no longer used by the importer module # but this relation table might be useful anyway for other purposes - properties = {} # { objid : { psetid : [propertyid, ... ], ... }, ... } + properties = {} # { objid : { psetid : [propertyid, ... ], ... }, ... } for r in ifcfile.by_type("IfcRelDefinesByProperties"): for obj in r.RelatedObjects: if not obj.id() in properties: @@ -336,6 +451,37 @@ def getIfcPropertySets(ifcfile, pid): return psets +def getIfcProperties(ifcfile, pid, psets, d): + """builds valid property values for FreeCAD""" + + for pset in psets.keys(): + # print("reading pset: ",pset) + psetname = ifcfile[pset].Name + if six.PY2: + psetname = psetname.encode("utf8") + for prop in psets[pset]: + e = ifcfile[prop] + pname = e.Name + if six.PY2: + pname = pname.encode("utf8") + if e.is_a("IfcPropertySingleValue"): + if e.NominalValue: + ptype = e.NominalValue.is_a() + if ptype in ['IfcLabel','IfcText','IfcIdentifier','IfcDescriptiveMeasure']: + pvalue = e.NominalValue.wrappedValue + if six.PY2: + pvalue = pvalue.encode("utf8") + else: + pvalue = str(e.NominalValue.wrappedValue) + if hasattr(e.NominalValue,'Unit'): + if e.NominalValue.Unit: + pvalue += e.NominalValue.Unit + d[pname+";;"+psetname] = ptype+";;"+pvalue + # print("adding property: ",pname,ptype,pvalue," pset ",psetname) + return d + + +# ************************************************************************************************ def getScaling(ifcfile): """returns a scaling factor from file units to mm""" @@ -362,7 +508,7 @@ def getScaling(ifcfile): for u in ua.Units: if u.UnitType == "LENGTHUNIT": if u.is_a("IfcConversionBasedUnit"): - f = getUnit(u.ConversionFactor.UnitComponent) + f = getUnit(u.ConversionFactor.UnitComponent) return f * u.ConversionFactor.ValueComponent.wrappedValue elif u.is_a("IfcSIUnit") or u.is_a("IfcUnit"): return getUnit(u) @@ -402,8 +548,8 @@ def getPlacement(entity,scaling=1000): if loc: pl.move(loc) elif entity.is_a("IfcLocalPlacement"): - pl = getPlacement(entity.PlacementRelTo,1) # original placement - relpl = getPlacement(entity.RelativePlacement,1) # relative transf + pl = getPlacement(entity.PlacementRelTo,1) # original placement + relpl = getPlacement(entity.RelativePlacement,1) # relative transf if pl and relpl: pl = pl.multiply(relpl) elif relpl: @@ -425,7 +571,7 @@ def getVector(entity,scaling=1000): v = None if entity.is_a("IfcDirection"): if len(entity.DirectionRatios) == 3: - v= FreeCAD.Vector(tuple(entity.DirectionRatios)) + v = FreeCAD.Vector(tuple(entity.DirectionRatios)) else: v = FreeCAD.Vector(tuple(entity.DirectionRatios+[0])) elif entity.is_a("IfcCartesianPoint"): @@ -433,8 +579,8 @@ def getVector(entity,scaling=1000): v = FreeCAD.Vector(tuple(entity.Coordinates)) else: v = FreeCAD.Vector(tuple(entity.Coordinates+[0])) - #if v: - # v.multiply(scaling) + # if v: + # v.multiply(scaling) return v @@ -531,43 +677,13 @@ def get2DShape(representation,scaling=1000): if rot.Angle: pla.Rotation = rot for r in preresult: - #r.Placement = pla + # r.Placement = pla result.append(r) else: result = preresult elif item.is_a("IfcTextLiteral"): t = Draft.makeText([item.Literal],point=getPlacement(item.Placement,scaling).Base) - return t # dirty hack... Object creation should not be done here + return t # dirty hack... Object creation should not be done here elif representation.is_a() in ["IfcPolyline","IfcCircle","IfcTrimmedCurve"]: result = getCurveSet(representation) return result - - -def getIfcProperties(ifcfile, pid, psets, d): - """builds valid property values for FreeCAD""" - - for pset in psets.keys(): - #print("reading pset: ",pset) - psetname = ifcfile[pset].Name - if six.PY2: - psetname = psetname.encode("utf8") - for prop in psets[pset]: - e = ifcfile[prop] - pname = e.Name - if six.PY2: - pname = pname.encode("utf8") - if e.is_a("IfcPropertySingleValue"): - if e.NominalValue: - ptype = e.NominalValue.is_a() - if ptype in ['IfcLabel','IfcText','IfcIdentifier','IfcDescriptiveMeasure']: - pvalue = e.NominalValue.wrappedValue - if six.PY2: - pvalue = pvalue.encode("utf8") - else: - pvalue = str(e.NominalValue.wrappedValue) - if hasattr(e.NominalValue,'Unit'): - if e.NominalValue.Unit: - pvalue += e.NominalValue.Unit - d[pname+";;"+psetname] = ptype+";;"+pvalue - #print("adding property: ",pname,ptype,pvalue," pset ",psetname) - return d diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 45dd4942bc..ac54097671 100644 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -872,21 +872,28 @@ def sortEdgesOld(lEdges, aVertex=None): return [] -def invert(edge): - '''invert(edge): returns an inverted copy of this edge''' - if len(edge.Vertexes) == 1: - return edge - if geomType(edge) == "Line": - return Part.LineSegment(edge.Vertexes[-1].Point,edge.Vertexes[0].Point).toShape() - elif geomType(edge) == "Circle": - mp = findMidpoint(edge) - return Part.Arc(edge.Vertexes[-1].Point,mp,edge.Vertexes[0].Point).toShape() - elif geomType(edge) in ["BSplineCurve","BezierCurve"]: - if isLine(edge.Curve): - return Part.LineSegment(edge.Vertexes[-1].Point,edge.Vertexes[0].Point).toShape() - print("DraftGeomUtils.invert: unable to invert ",edge.Curve) - return edge - +def invert(shape): + '''invert(edge): returns an inverted copy of this edge or wire''' + if shape.ShapeType == "Wire": + edges = [invert(edge) for edge in shape.OrderedEdges] + edges.reverse() + return Part.Wire(edges) + elif shape.ShapeType == "Edge": + if len(shape.Vertexes) == 1: + return shape + if geomType(shape) == "Line": + return Part.LineSegment(shape.Vertexes[-1].Point,shape.Vertexes[0].Point).toShape() + elif geomType(shape) == "Circle": + mp = findMidpoint(shape) + return Part.Arc(shape.Vertexes[-1].Point,mp,shape.Vertexes[0].Point).toShape() + elif geomType(shape) in ["BSplineCurve","BezierCurve"]: + if isLine(shape.Curve): + return Part.LineSegment(shape.Vertexes[-1].Point,shape.Vertexes[0].Point).toShape() + print("DraftGeomUtils.invert: unable to invert",shape.Curve) + return shape + else: + print("DraftGeomUtils.invert: unable to handle",shape.ShapeType) + return shape def flattenWire(wire): '''flattenWire(wire): forces a wire to get completely flat diff --git a/src/Mod/Draft/getSVG.py b/src/Mod/Draft/getSVG.py index 6166d54ad1..faea430ec7 100644 --- a/src/Mod/Draft/getSVG.py +++ b/src/Mod/Draft/getSVG.py @@ -1,7 +1,6 @@ import six import FreeCAD, math, os, DraftVecUtils, WorkingPlane -import Part, DraftGeomUtils from FreeCAD import Vector from Draft import getType, getrgb, svgpatterns, gui @@ -68,24 +67,34 @@ def getPattern(pat): return '' -def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direction=None,linestyle=None,color=None,linespacing=None,techdraw=False,rotation=0,fillSpaces=False): +def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direction=None,linestyle=None,color=None,linespacing=None,techdraw=False,rotation=0,fillSpaces=False,override=True): '''getSVG(object,[scale], [linewidth],[fontsize],[fillstyle],[direction],[linestyle],[color],[linespacing]): returns a string containing a SVG representation of the given object, with the given linewidth and fontsize (used if the given object contains any text). You can also supply an arbitrary projection vector. the scale parameter allows to scale linewidths down, so they are resolution-independant.''' + import Part, DraftGeomUtils + # if this is a group, gather all the svg views of its children if hasattr(obj,"isDerivedFrom"): - if obj.isDerivedFrom("App::DocumentObjectGroup"): + if obj.isDerivedFrom("App::DocumentObjectGroup") or getType(obj) == "Layer": svg = "" for child in obj.Group: - svg += getSVG(child,scale,linewidth,fontsize,fillstyle,direction,linestyle,color,linespacing,techdraw) + svg += getSVG(child,scale,linewidth,fontsize,fillstyle,direction,linestyle,color,linespacing,techdraw,rotation,fillSpaces,override) return svg pathdata = [] svg = "" linewidth = float(linewidth)/scale + if not override: + if hasattr(obj,"ViewObject"): + if hasattr(obj.ViewObject,"LineWidth"): + if hasattr(obj.ViewObject.LineWidth,"Value"): + lw = obj.ViewObject.LineWidth.Value + else: + lw = obj.ViewObject.LineWidth + linewidth = lw*linewidth fontsize = (float(fontsize)/scale)/2 if linespacing: linespacing = float(linespacing)/scale @@ -102,7 +111,7 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct elif isinstance(direction,WorkingPlane.plane): plane = direction stroke = "#000000" - if color: + if color and override: if "#" in color: stroke = color else: @@ -111,10 +120,18 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct if hasattr(obj,"ViewObject"): if hasattr(obj.ViewObject,"LineColor"): stroke = getrgb(obj.ViewObject.LineColor) - + elif hasattr(obj.ViewObject,"TextColor"): + stroke = getrgb(obj.ViewObject.TextColor) + lstyle = "none" + if override: + lstyle = getLineStyle(linestyle, scale) + else: + if hasattr(obj,"ViewObject"): + if hasattr(obj.ViewObject,"DrawStyle"): + lstyle = getLineStyle(obj.ViewObject.DrawStyle, scale) def getPath(edges=[],wires=[],pathname=None): - import Part,DraftGeomUtils + svg = "\n' return svg - def getText(color,fontsize,fontname,angle,base,text,linespacing=0.5,align="center",flip=True): + def getText(tcolor,fontsize,fontname,angle,base,text,linespacing=0.5,align="center",flip=True): if isinstance(angle,FreeCAD.Rotation): if not plane: angle = angle.Angle @@ -390,13 +413,13 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct if techdraw: svg = "" for i in range(len(text)): - t = text[i] + t = text[i].replace("&","&").replace("<","<").replace(">",">") if six.PY2 and not isinstance(t, six.text_type): t = t.decode("utf8") # possible workaround if UTF8 is unsupported # import unicodedata # t = u"".join([c for c in unicodedata.normalize("NFKD",t) if not unicodedata.combining(c)]).encode("utf8") - svg += '\n' else: - svg = '",">") except: - svg += text[0].decode("utf8") + svg += text[0].decode("utf8").replace("&","&").replace("<","<").replace(">",">") else: for i in range(len(text)): if i == 0: @@ -436,9 +459,9 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct else: svg += '' try: - svg += text[i] + svg += text[i].replace("&","&").replace("<","<").replace(">",">") except: - svg += text[i].decode("utf8") + svg += text[i].decode("utf8").replace("&","&").replace("<","<").replace(">",">") svg += '\n' svg += '\n' return svg @@ -454,7 +477,6 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct fill = "#888888" else: fill = 'url(#'+fillstyle+')' - lstyle = getLineStyle(linestyle, scale) svg += getPath(obj.Edges,pathname="") @@ -552,7 +574,6 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct # drawing arc fill= "none" - lstyle = getLineStyle(linestyle, scale) if obj.ViewObject.DisplayMode == "2D": svg += getPath([prx.circle]) else: @@ -666,7 +687,7 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct print ("export of axes to SVG is only available in GUI mode") else: vobj = obj.ViewObject - lorig = getLineStyle(linestyle, scale) + lorig = lstyle fill = 'none' rad = vobj.BubbleSize.Value/2 n = 0 @@ -703,10 +724,10 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct svg += '' + obj.ViewObject.Proxy.bubbletexts[n].string.getValues()[0] + '\n' svg += '\n' n += 1 + lstyle = lorig elif getType(obj) == "Pipe": fill = stroke - lstyle = getLineStyle(linestyle, scale) if obj.Base and obj.Diameter: svg += getPath(obj.Base.Shape.Edges) for f in obj.Shape.Faces: @@ -716,7 +737,6 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct elif getType(obj) == "Rebar": fill = "none" - lstyle = getLineStyle(linestyle, scale) if obj.Proxy: if not hasattr(obj.Proxy,"wires"): obj.Proxy.execute(obj) @@ -743,7 +763,6 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct fill_opacity = 1 - (obj.ViewObject.Transparency / 100.0) else: fill = "#888888" - lstyle = getLineStyle(linestyle, scale) svg += getPath(wires=[obj.Proxy.face.OuterWire]) c = getrgb(obj.ViewObject.TextColor) n = obj.ViewObject.FontName @@ -790,12 +809,14 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct fill = "#888888" else: fill = 'none' - lstyle = getLineStyle(linestyle, scale) if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i,f in enumerate(obj.Shape.Faces): + # place outer wire first + wires = [f.OuterWire] + wires.extend([w for w in f.Wires if w.hashCode() != f.OuterWire.hashCode()]) svg += getPath(wires=f.Wires,pathname='%s_f%04d' % \ (obj.Name,i)) wiredEdges.extend(f.Edges) @@ -827,5 +848,5 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct # techdraw expects bottom-to-top coordinates if techdraw: - svg = ''+svg+'' + svg = '\n '+svg+'\n' return svg diff --git a/src/Mod/Draft/importDXF.py b/src/Mod/Draft/importDXF.py index 1f87c136c6..d55ff2f149 100644 --- a/src/Mod/Draft/importDXF.py +++ b/src/Mod/Draft/importDXF.py @@ -271,6 +271,13 @@ def getACI(ob,text=False): if not gui: return 0 else: + # detect if we need to set "BYLAYER" + for parent in ob.InList: + if Draft.getType(parent) == "Layer": + if ob in parent.Group: + if hasattr(parent,"ViewObject") and hasattr(parent.ViewObject,"OverrideChildren"): + if parent.ViewObject.OverrideChildren: + return 256 # BYLAYER if text: col=ob.ViewObject.TextColor else: diff --git a/src/Mod/Fem/femmesh/gmshtools.py b/src/Mod/Fem/femmesh/gmshtools.py index 9e75f8a9b6..96487bf4fc 100644 --- a/src/Mod/Fem/femmesh/gmshtools.py +++ b/src/Mod/Fem/femmesh/gmshtools.py @@ -62,55 +62,55 @@ class GmshTools(): self.geotol = 1e-08 # order - # known_element_orders = ['1st', '2nd'] + # 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' + if self.order == "1st": + self.order = "1" + elif self.order == "2nd": + self.order = "2" else: - print('Error in order') + 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' + 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' + 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' + 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' + self.algorithm3D = "1" # mesh groups if self.mesh_obj.GroupsOfNodes is True: @@ -120,30 +120,30 @@ class GmshTools(): self.group_elements = {} # mesh regions - self.ele_length_map = {} # { 'ElementString' : element length } - self.ele_node_map = {} # { 'ElementString' : [element nodes] } + 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.temp_file_geometry = "" + self.temp_file_mesh = "" + self.temp_file_geo = "" + self.mesh_name = "" + self.gmsh_bin = "" self.error = False def create_mesh(self): print("\nWe are going to start Gmsh FEM mesh run!") print( - ' Part to mesh: Name --> {}, Label --> {}, ShapeType --> {}' + " Part to mesh: Name --> {}, Label --> {}, ShapeType --> {}" .format(self.part_obj.Name, self.part_obj.Label, self.part_obj.Shape.ShapeType) ) - print(' CharacteristicLengthMax: ' + str(self.clmax)) - print(' CharacteristicLengthMin: ' + str(self.clmin)) - print(' ElementOrder: ' + self.order) + print(" CharacteristicLengthMax: " + str(self.clmax)) + print(" CharacteristicLengthMin: " + str(self.clmin)) + print(" ElementOrder: " + self.order) self.get_dimension() self.get_tmp_file_paths() self.get_gmsh_command() @@ -158,45 +158,45 @@ class GmshTools(): def get_dimension(self): # Dimension - # known_element_dimensions = ['From Shape', '1D', '2D', '3D'] + # known_element_dimensions = ["From Shape", "1D", "2D", "3D"] # if not given, Gmsh uses the highest available. # A use case for not "From Shape" would be a surface (2D) mesh of a solid - if self.dimension == 'From Shape': + if self.dimension == "From Shape": shty = self.part_obj.Shape.ShapeType - if shty == 'Solid' or shty == 'CompSolid': - # print('Found: ' + shty) - self.dimension = '3' - elif shty == 'Face' or shty == 'Shell': - # print('Found: ' + shty) - self.dimension = '2' - elif shty == 'Edge' or shty == 'Wire': - # print('Found: ' + shty) - self.dimension = '1' - elif shty == 'Vertex': - # print('Found: ' + shty) + if shty == "Solid" or shty == "CompSolid": + # print("Found: " + shty) + self.dimension = "3" + elif shty == "Face" or shty == "Shell": + # print("Found: " + shty) + self.dimension = "2" + elif shty == "Edge" or shty == "Wire": + # print("Found: " + shty) + self.dimension = "1" + elif shty == "Vertex": + # print("Found: " + shty) FreeCAD.Console.PrintError("You can not mesh a Vertex.\n") - self.dimension = '0' - elif shty == 'Compound': - # print(' Found a ' + shty) + self.dimension = "0" + elif shty == "Compound": + # print(" Found a " + shty) FreeCAD.Console.PrintLog( " Found a Compound. Since it could contain" "any kind of shape dimension 3 is used.\n" ) - self.dimension = '3' # dimension 3 works for 2D and 1d shapes as well + self.dimension = "3" # dimension 3 works for 2D and 1d shapes as well else: - self.dimension = '0' + self.dimension = "0" FreeCAD.Console.PrintError( - 'Could not retrieve Dimension from shape type. Please choose dimension.' + "Could not retrieve Dimension from shape type. Please choose dimension." ) - elif self.dimension == '3D': - self.dimension = '3' - elif self.dimension == '2D': - self.dimension = '2' - elif self.dimension == '1D': - self.dimension = '1' + elif self.dimension == "3D": + self.dimension = "3" + elif self.dimension == "2D": + self.dimension = "2" + elif self.dimension == "1D": + self.dimension = "1" else: - print('Error in dimension') - print(' ElementDimension: ' + self.dimension) + print("Error in dimension") + print(" ElementDimension: " + self.dimension) def get_tmp_file_paths(self): if system() == "Linux": @@ -207,15 +207,15 @@ class GmshTools(): path_sep = "/" tmpdir = tempfile.gettempdir() # geometry file - self.temp_file_geometry = tmpdir + path_sep + self.part_obj.Name + '_Geometry.brep' - print(' ' + self.temp_file_geometry) + self.temp_file_geometry = tmpdir + path_sep + self.part_obj.Name + "_Geometry.brep" + print(" " + self.temp_file_geometry) # mesh file - self.mesh_name = self.part_obj.Name + '_Mesh_TmpGmsh' - self.temp_file_mesh = tmpdir + path_sep + self.mesh_name + '.unv' - print(' ' + self.temp_file_mesh) + self.mesh_name = self.part_obj.Name + "_Mesh_TmpGmsh" + self.temp_file_mesh = tmpdir + path_sep + self.mesh_name + ".unv" + print(" " + self.temp_file_mesh) # Gmsh input file - self.temp_file_geo = tmpdir + path_sep + 'shape2mesh.geo' - print(' ' + self.temp_file_geo) + self.temp_file_geo = tmpdir + path_sep + "shape2mesh.geo" + print(" " + self.temp_file_geo) def get_gmsh_command(self): gmsh_std_location = FreeCAD.ParamGet( @@ -229,12 +229,12 @@ class GmshTools(): ).SetString("gmshBinaryPath", gmsh_path) self.gmsh_bin = gmsh_path elif system() == "Linux": - p1 = subprocess.Popen(['which', 'gmsh'], stdout=subprocess.PIPE) + p1 = subprocess.Popen(["which", "gmsh"], stdout=subprocess.PIPE) if p1.wait() == 0: output = p1.stdout.read() if sys.version_info.major >= 3: - output = output.decode('utf-8') - gmsh_path = output.split('\n')[0] + output = output.decode("utf-8") + gmsh_path = output.split("\n")[0] elif p1.wait() == 1: error_message = ( "Gmsh binary gmsh not found in standard system binary path. " @@ -263,7 +263,7 @@ class GmshTools(): self.gmsh_bin = FreeCAD.getHomePath() + "bin/gmsh.exe" else: self.gmsh_bin = "gmsh" - print(' ' + self.gmsh_bin) + print(" " + self.gmsh_bin) def get_group_data(self): # TODO: solids, faces, edges and vertexes don't seem to work together in one group, @@ -271,10 +271,10 @@ class GmshTools(): # mesh group objects if not self.mesh_obj.MeshGroupList: - # print(' No mesh group objects.') + # print(" No mesh group objects.") pass else: - print(' Mesh group objects, we need to get the elements.') + print(" Mesh group objects, we need to get the elements.") 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: @@ -288,7 +288,7 @@ class GmshTools(): "User parameter:BaseApp/Preferences/Mod/Fem/General" ).GetBool("AnalysisGroupMeshing", False) if self.analysis and analysis_group_meshing: - print(' Group meshing for analysis.') + print(" Group meshing for analysis.") self.group_nodes_export = True new_group_elements = meshtools.get_analysis_group_elements( self.analysis, @@ -300,18 +300,18 @@ class GmshTools(): else: FreeCAD.Console.PrintError(" A group with this name exists already.\n") else: - print(' No Group meshing for analysis.') + print(" No Group meshing for analysis.") if self.group_elements: - print(' {}'.format(self.group_elements)) + print(" {}".format(self.group_elements)) def get_region_data(self): # mesh regions if not self.mesh_obj.MeshRegionList: - # print(' No mesh regions.') + # print(" No mesh regions.") pass else: - print(' Mesh regions, we need to get the elements.') + 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 @@ -324,11 +324,11 @@ class GmshTools(): 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.' + " 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 @@ -387,14 +387,14 @@ class GmshTools(): ) else: FreeCAD.Console.PrintError( - 'The meshregion: {} is not used to create the mesh ' - 'because the reference list is empty.\n' + "The meshregion: {} is not used to create the mesh " + "because the reference list is empty.\n" .format(mr_obj.Name) ) else: FreeCAD.Console.PrintError( - 'The meshregion: {} is not used to create the ' - 'mesh because the CharacteristicLength is 0.0 mm.\n' + "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: @@ -402,8 +402,8 @@ class GmshTools(): ele_shape = meshtools.get_element(self.part_obj, eleml) ele_vertexes = meshtools.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)) + print(" {}".format(self.ele_length_map)) + print(" {}".format(self.ele_node_map)) def get_boundary_layer_data(self): # mesh boundary layer @@ -412,11 +412,11 @@ class GmshTools(): # 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.') + # print(" No mesh boundary layer setting document object.") pass else: - print(' Mesh boundary layers, we need to get the elements.') - if self.part_obj.Shape.ShapeType == 'Compound': + print(" Mesh boundary layers, we need to get the 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 = ( @@ -478,30 +478,30 @@ class GmshTools(): .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( + 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'] + # 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 + 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'] + 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 + if self.dimension == "2": + setting["EdgesList"] = belem_list + elif self.dimension == "3": + setting["FacesList"] = belem_list else: FreeCAD.Console.PrintError( "boundary layer is only supported for 2D and 3D mesh" @@ -509,38 +509,38 @@ class GmshTools(): self.bl_setting_list.append(setting) else: FreeCAD.Console.PrintError( - 'The mesh boundary layer: {} is not used to create ' - 'the mesh because the reference list is empty.\n' + "The mesh boundary layer: {} is not used to create " + "the mesh because the reference list is empty.\n" .format(mr_obj.Name) ) else: FreeCAD.Console.PrintError( - 'The mesh boundary layer: {} is not used to create ' - 'the mesh because the min thickness is 0.0 mm.\n' + "The mesh boundary layer: {} is not used to create " + "the mesh because the min thickness is 0.0 mm.\n" .format(mr_obj.Name) ) - print(' {}'.format(self.bl_setting_list)) + print(" {}".format(self.bl_setting_list)) def write_boundary_layer(self, geo): # currently single body is supported if len(self.bl_setting_list): geo.write("// boundary layer setting\n") - print(' Start to write boundary layer setup') + print(" Start to write boundary layer setup") field_number = 1 for item in self.bl_setting_list: prefix = "Field[" + str(field_number) + "]" geo.write(prefix + " = BoundaryLayer;\n") for k in item: v = item[k] - if k in set(['EdgesList', 'FacesList']): + if k in set(["EdgesList", "FacesList"]): # the element name of FreeCAD which starts - # with 1 (example: 'Face1'), same as Gmsh + # with 1 (example: "Face1"), same as Gmsh # el_id = int(el[4:]) # FIXME: strip `face` or `edge` prefix - ele_nodes = (''.join((str(el[4:]) + ', ') for el in v)).rstrip(', ') - line = prefix + '.' + str(k) + ' = {' + ele_nodes + ' };\n' + ele_nodes = ("".join((str(el[4:]) + ", ") for el in v)).rstrip(", ") + line = prefix + "." + str(k) + " = {" + ele_nodes + " };\n" geo.write(line) else: - line = prefix + '.' + str(k) + ' = ' + str(v) + ';\n' + line = prefix + "." + str(k) + " = " + str(v) + ";\n" geo.write(line) print(line) geo.write("BoundaryLayer Field = " + str(field_number) + ";\n") @@ -548,9 +548,9 @@ class GmshTools(): field_number += 1 geo.write("\n") geo.flush() - print(' finished in boundary layer setup') + print(" finished in boundary layer setup") else: - # print(' no boundary layer setup is found for this mesh') + # print(" no boundary layer setup is found for this mesh") geo.write("// no boundary layer settings for this mesh\n") def write_part_file(self): @@ -561,40 +561,40 @@ class GmshTools(): geo.write("// geo file for meshing with Gmsh meshing software created by FreeCAD\n") geo.write("\n") geo.write("// open brep geometry\n") - geo.write('Merge "' + self.temp_file_geometry + '";\n') + geo.write("Merge "" + self.temp_file_geometry + "";\n") geo.write("\n") if self.group_elements: - # print(' We are going to have to find elements to make mesh groups for.') + # print(" We are going to have to find elements to make mesh groups for.") geo.write("// group data\n") # we use the element name of FreeCAD which starts - # with 1 (example: 'Face1'), same as Gmsh + # with 1 (example: "Face1"), same as Gmsh # for unit test we need them to have a fixed order for group in sorted(self.group_elements.keys()): gdata = self.group_elements[group] # print(gdata) # geo.write("// " + group + "\n") - ele_nr = '' - if gdata[0].startswith('Solid'): - physical_type = 'Volume' + ele_nr = "" + if gdata[0].startswith("Solid"): + physical_type = "Volume" for ele in gdata: - ele_nr += (ele.lstrip('Solid') + ', ') - elif gdata[0].startswith('Face'): - physical_type = 'Surface' + ele_nr += (ele.lstrip("Solid") + ", ") + elif gdata[0].startswith("Face"): + physical_type = "Surface" for ele in gdata: - ele_nr += (ele.lstrip('Face') + ', ') - elif gdata[0].startswith('Edge'): - physical_type = 'Line' + ele_nr += (ele.lstrip("Face") + ", ") + elif gdata[0].startswith("Edge"): + physical_type = "Line" for ele in gdata: - ele_nr += (ele.lstrip('Edge') + ', ') - elif gdata[0].startswith('Vertex'): - physical_type = 'Point' + ele_nr += (ele.lstrip("Edge") + ", ") + elif gdata[0].startswith("Vertex"): + physical_type = "Point" for ele in gdata: - ele_nr += (ele.lstrip('Vertex') + ', ') + ele_nr += (ele.lstrip("Vertex") + ", ") if ele_nr: - ele_nr = ele_nr.rstrip(', ') + ele_nr = ele_nr.rstrip(", ") # print(ele_nr) geo.write( - 'Physical ' + physical_type + '("' + group + '") = {' + ele_nr + '};\n' + "Physical " + physical_type + "("" + group + "") = {" + ele_nr + "};\n" ) geo.write("\n") geo.write("// Characteristic Length\n") @@ -604,8 +604,8 @@ class GmshTools(): geo.write("// Characteristic Length according CharacteristicLengthMap\n") for e in self.ele_length_map: ele_nodes = ( - ''.join((str(n + 1) + ', ') for n in self.ele_node_map[e]) - ).rstrip(', ') + "".join((str(n + 1) + ", ") for n in self.ele_node_map[e]) + ).rstrip(", ") geo.write("// " + e + "\n") elestr1 = "{" elestr2 = "}" @@ -633,23 +633,23 @@ class GmshTools(): else: geo.write("Mesh.CharacteristicLengthMin = " + str(self.clmin) + ";\n") geo.write("\n") - if hasattr(self.mesh_obj, 'RecombineAll') and self.mesh_obj.RecombineAll is True: + if hasattr(self.mesh_obj, "RecombineAll") and self.mesh_obj.RecombineAll is True: geo.write("// other mesh options\n") geo.write("Mesh.RecombineAll = 1;\n") geo.write("\n") geo.write("// optimize the mesh\n") # Gmsh tetra optimizer - if hasattr(self.mesh_obj, 'OptimizeStd') and self.mesh_obj.OptimizeStd is True: + if hasattr(self.mesh_obj, "OptimizeStd") and self.mesh_obj.OptimizeStd is True: geo.write("Mesh.Optimize = 1;\n") else: geo.write("Mesh.Optimize = 0;\n") # Netgen optimizer in Gmsh - if hasattr(self.mesh_obj, 'OptimizeNetgen') and self.mesh_obj.OptimizeNetgen is True: + if hasattr(self.mesh_obj, "OptimizeNetgen") and self.mesh_obj.OptimizeNetgen is True: geo.write("Mesh.OptimizeNetgen = 1;\n") else: geo.write("Mesh.OptimizeNetgen = 0;\n") # higher order mesh optimizing - if hasattr(self.mesh_obj, 'HighOrderOptimize') and self.mesh_obj.HighOrderOptimize is True: + if hasattr(self.mesh_obj, "HighOrderOptimize") and self.mesh_obj.HighOrderOptimize is True: geo.write( "Mesh.HighOrderOptimize = 1; // for more HighOrderOptimize " "parameter check http://gmsh.info/doc/texinfo/gmsh.html\n" @@ -673,7 +673,7 @@ class GmshTools(): "5=Delaunay, 6=Frontal, 7=BAMG, 8=DelQuad)\n" ) if len(self.bl_setting_list) and self.dimension == 3: - geo.write("Mesh.Algorithm = " + 'DelQuad' + ";\n") # Frontal/DelQuad are tested + geo.write("Mesh.Algorithm = " + "DelQuad" + ";\n") # Frontal/DelQuad are tested else: geo.write("Mesh.Algorithm = " + self.algorithm2D + ";\n") geo.write( @@ -686,7 +686,7 @@ class GmshTools(): geo.write("// meshing\n") # remove duplicate vertices # see https://forum.freecadweb.org/viewtopic.php?f=18&t=21571&start=20#p179443 - if hasattr(self.mesh_obj, 'CoherenceMesh') and self.mesh_obj.CoherenceMesh is True: + if hasattr(self.mesh_obj, "CoherenceMesh") and self.mesh_obj.CoherenceMesh is True: geo.write( "Geometry.Tolerance = {}; // set geometrical " "tolerance (also used for merging nodes)\n" @@ -727,7 +727,7 @@ class GmshTools(): geo.close() def run_gmsh_with_geo(self): - comandlist = [self.gmsh_bin, '-', self.temp_file_geo] + comandlist = [self.gmsh_bin, "-", self.temp_file_geo] # print(comandlist) try: p = subprocess.Popen( @@ -738,14 +738,14 @@ class GmshTools(): ) output, error = p.communicate() if sys.version_info.major >= 3: - # output = output.decode('utf-8') - error = error.decode('utf-8') + # output = output.decode("utf-8") + error = error.decode("utf-8") # stdout is still cut at some point # but the warnings are in stderr and thus printed :-) # print(output) # print(error) except: - error = 'Error executing: {}\n'.format(" ".join(comandlist)) + error = "Error executing: {}\n".format(" ".join(comandlist)) FreeCAD.Console.PrintError(error) self.error = True return error @@ -754,25 +754,25 @@ class GmshTools(): if not self.error: fem_mesh = Fem.read(self.temp_file_mesh) self.mesh_obj.FemMesh = fem_mesh - FreeCAD.Console.PrintMessage(' The Part should have a pretty new FEM mesh!\n') + FreeCAD.Console.PrintMessage(" The Part should have a pretty new FEM mesh!\n") else: - FreeCAD.Console.PrintError('No mesh was created.\n') + FreeCAD.Console.PrintError("No mesh was created.\n") del self.temp_file_geometry del self.temp_file_mesh ## @} -''' +""" # simple example how to use the class GmshTools import Part, ObjectsFem doc = App.ActiveDocument -box_obj = doc.addObject('Part::Box', 'Box') +box_obj = doc.addObject("Part::Box", "Box") doc.recompute() -femmesh_obj = ObjectsFem.makeMeshGmsh(doc, box_obj.Name + '_Mesh') +femmesh_obj = ObjectsFem.makeMeshGmsh(doc, box_obj.Name + "_Mesh") femmesh_obj.Part = box_obj doc.recompute() box_obj.ViewObject.Visibility = False @@ -783,9 +783,9 @@ error = gmsh_mesh.create_mesh() print(error) doc.recompute() -''' +""" -''' +""" TODO class GmshTools should be splittet in two classes one class should only collect the mesh parameter from mesh object and his childs @@ -794,4 +794,4 @@ writes the input file runs gmsh reads back the unv and returns a FemMesh gmsh binary will be collected in the second class with this we could mesh without document objects create a shape and run meshinging class, get the FemMesh :-) -''' +""" diff --git a/src/Mod/Fem/femmesh/meshtools.py b/src/Mod/Fem/femmesh/meshtools.py index 7cc82168cf..aae871210d 100644 --- a/src/Mod/Fem/femmesh/meshtools.py +++ b/src/Mod/Fem/femmesh/meshtools.py @@ -37,8 +37,8 @@ def get_femnodes_by_femobj_with_references( ): node_set = [] if femmesh.GroupCount: - node_set = get_femmesh_groupdata_sets_by_name(femmesh, femobj, 'Node') - # FreeCAD.Console.PrintMessage('node_set_group: {}\n'.format(node_set)) + node_set = get_femmesh_groupdata_sets_by_name(femmesh, femobj, "Node") + # FreeCAD.Console.PrintMessage("node_set_group: {}\n".format(node_set)) if node_set: FreeCAD.Console.PrintMessage( " Finite element mesh nodes where retrieved " @@ -49,8 +49,8 @@ def get_femnodes_by_femobj_with_references( " Finite element mesh nodes will be retrieved " "by searching the appropriate nodes in the finite element mesh.\n" ) - node_set = get_femnodes_by_references(femmesh, femobj['Object'].References) - # FreeCAD.Console.PrintMessage('node_set_nogroup: {}\n'.format(node_set)) + node_set = get_femnodes_by_references(femmesh, femobj["Object"].References) + # FreeCAD.Console.PrintMessage("node_set_nogroup: {}\n".format(node_set)) return node_set @@ -62,8 +62,8 @@ def get_femelements_by_references( references, femnodes_ele_table=None ): - '''get the femelements for a list of references - ''' + """get the femelements for a list of references + """ references_femelements = [] for ref in references: # femnodes for the current ref @@ -91,8 +91,8 @@ def get_femnodes_by_references( femmesh, references ): - '''get the femnodes for a list of references - ''' + """get the femnodes for a list of references + """ references_femnodes = [] for ref in references: references_femnodes += get_femnodes_by_refshape(femmesh, ref) @@ -112,25 +112,25 @@ def get_femnodes_by_refshape( # the following method getElement(element) does not return Solid elements r = get_element(ref[0], refelement) FreeCAD.Console.PrintMessage( - ' ' - 'ReferenceShape ... Type: {0}, ' - 'Object name: {1}, ' - 'Object label: {2}, ' - 'Element name: {3}\n' + " " + "ReferenceShape ... Type: {0}, " + "Object name: {1}, " + "Object label: {2}, " + "Element name: {3}\n" .format(r.ShapeType, ref[0].Name, ref[0].Label, refelement) ) - if r.ShapeType == 'Vertex': + if r.ShapeType == "Vertex": nodes += femmesh.getNodesByVertex(r) - elif r.ShapeType == 'Edge': + elif r.ShapeType == "Edge": nodes += femmesh.getNodesByEdge(r) - elif r.ShapeType == 'Face': + elif r.ShapeType == "Face": nodes += femmesh.getNodesByFace(r) - elif r.ShapeType == 'Solid': + elif r.ShapeType == "Solid": nodes += femmesh.getNodesBySolid(r) else: FreeCAD.Console.PrintMessage( - ' ' - 'No Vertice, Edge, Face or Solid as reference shapes!\n' + " " + "No Vertice, Edge, Face or Solid as reference shapes!\n" ) return nodes @@ -151,7 +151,7 @@ def get_femelement_table( for i in femmesh.Edges: femelement_table[i] = femmesh.getElementNodes(i) else: - FreeCAD.Console.PrintError('Neither solid nor face nor edge femmesh!\n') + FreeCAD.Console.PrintError("Neither solid nor face nor edge femmesh!\n") return femelement_table @@ -199,7 +199,7 @@ def get_femnodes_ele_table( femnodes_mesh, femelement_table ): - '''the femnodes_ele_table contains for each node its membership in elements + """the femnodes_ele_table contains for each node its membership in elements {nodeID : [[eleID, NodePosition], [], ...], nodeID : [[], [], ...], ...} stored information is: element number, the number of nodes per element @@ -213,22 +213,22 @@ def get_femnodes_ele_table( volume or face or edgemesh the femnodes_ele_table only has either volume or face or edge elements see get_femelement_table() - ''' + """ femnodes_ele_table = {} # node_dict in ulrichs class for n in femnodes_mesh: # initialize it with sorted node keys and empty lists femnodes_ele_table[n] = [] for ele in femelement_table: ele_list = femelement_table[ele] - # FreeCAD.Console.PrintMessage('{}\n'.format(ele_list)) + # FreeCAD.Console.PrintMessage("{}\n".format(ele_list)) pos = int(1) for ele_node in ele_list: femnodes_ele_table[ele_node].append([ele, pos]) pos = pos << 1 FreeCAD.Console.PrintLog( - 'len femnodes_ele_table: {}\n' + "len femnodes_ele_table: {}\n" .format(len(femnodes_ele_table)) ) - FreeCAD.Console.PrintLog('femnodes_ele_table: {}\n'.format(femnodes_ele_table)) + FreeCAD.Console.PrintLog("femnodes_ele_table: {}\n".format(femnodes_ele_table)) return femnodes_ele_table @@ -236,8 +236,8 @@ def get_femnodes_ele_table( def get_copy_of_empty_femelement_table( femelement_table ): - '''{eleID : 0, eleID : 0, ...} - ''' + """{eleID : 0, eleID : 0, ...} + """ empty_femelement_table = {} for ele in femelement_table: # initialize it with sorted element keys and empty int empty_femelement_table[ele] = 0 @@ -250,9 +250,9 @@ def get_bit_pattern_dict( femnodes_ele_table, node_set ): - '''Now we are looking for nodes inside of the Faces = filling the bit_pattern_dict + """Now we are looking for nodes inside of the Faces = filling the bit_pattern_dict {eleID : [lenEleNodes, binary_position]} - see forum post for a very good explanation of what's really happening + see forum post for a very good explanation of what"s really happening https://forum.freecadweb.org/viewtopic.php?f=18&t=17318&start=50#p141108 The bit_pattern_dict holds later an integer (bit array) for each element, which gives us the information we are searching for: @@ -260,10 +260,10 @@ def get_bit_pattern_dict( or has this element a face we are searching for? The number in the ele_dict is organized as a bit array. The corresponding bit is set, if the node of the node_set is contained in the element. - ''' - FreeCAD.Console.PrintLog('len femnodes_ele_table: ' + str(len(femnodes_ele_table)) + '\n') - FreeCAD.Console.PrintLog('len node_set: ' + str(len(node_set)) + '\n') - FreeCAD.Console.PrintLog('node_set: {}\n'.format(node_set)) + """ + FreeCAD.Console.PrintLog("len femnodes_ele_table: " + str(len(femnodes_ele_table)) + "\n") + FreeCAD.Console.PrintLog("len node_set: " + str(len(node_set)) + "\n") + FreeCAD.Console.PrintLog("node_set: {}\n".format(node_set)) bit_pattern_dict = get_copy_of_empty_femelement_table(femelement_table) # # initializing the bit_pattern_dict for ele in femelement_table: @@ -272,8 +272,8 @@ def get_bit_pattern_dict( for node in node_set: for nList in femnodes_ele_table[node]: bit_pattern_dict[nList[0]][1] += nList[1] - FreeCAD.Console.PrintLog('len bit_pattern_dict: ' + str(len(bit_pattern_dict)) + '\n') - # FreeCAD.Console.PrintMessage('bit_pattern_dict: {}\n'.format(bit_pattern_dict)) + FreeCAD.Console.PrintLog("len bit_pattern_dict: " + str(len(bit_pattern_dict)) + "\n") + # FreeCAD.Console.PrintMessage("bit_pattern_dict: {}\n".format(bit_pattern_dict)) return bit_pattern_dict @@ -281,8 +281,8 @@ def get_bit_pattern_dict( def get_ccxelement_faces_from_binary_search( bit_pattern_dict ): - '''get the CalculiX element face numbers - ''' + """get the CalculiX element face numbers + """ tet10_mask = { 119: 1, 411: 2, @@ -332,8 +332,8 @@ def get_ccxelement_faces_from_binary_search( for key in mask_dict: if (key & bit_pattern_dict[ele][1]) == key: faces.append([ele, mask_dict[key]]) - FreeCAD.Console.PrintLog('found Faces: {}\n'.format(len(faces))) - # FreeCAD.Console.PrintMessage('faces: {}\n'.format(faces)) + FreeCAD.Console.PrintLog("found Faces: {}\n".format(len(faces))) + # FreeCAD.Console.PrintMessage("faces: {}\n".format(faces)) return faces @@ -343,12 +343,12 @@ def get_femelements_by_femnodes_bin( femnodes_ele_table, node_list ): - '''for every femelement of femelement_table + """for every femelement of femelement_table if all nodes of the femelement are in node_list, the femelement is added to the list which is returned blind fast binary search, but works for volumes only - ''' - FreeCAD.Console.PrintMessage('binary search: get_femelements_by_femnodes_bin\n') + """ + FreeCAD.Console.PrintMessage("binary search: get_femelements_by_femnodes_bin\n") vol_masks = { 4: 15, 6: 63, @@ -358,7 +358,7 @@ def get_femelements_by_femnodes_bin( 20: 1048575} # Now we are looking for nodes inside of the Volumes = filling the bit_pattern_dict FreeCAD.Console.PrintMessage( - 'len femnodes_ele_table: {}\n' + "len femnodes_ele_table: {}\n" .format(len(femnodes_ele_table)) ) bit_pattern_dict = get_bit_pattern_dict(femelement_table, femnodes_ele_table, node_list) @@ -366,12 +366,12 @@ def get_femelements_by_femnodes_bin( ele_list = [] # The ele_list contains the result of the search. for ele in bit_pattern_dict: FreeCAD.Console.PrintLog( - 'bit_pattern_dict[ele][0]: {}\n'.format(bit_pattern_dict[ele][0]) + "bit_pattern_dict[ele][0]: {}\n".format(bit_pattern_dict[ele][0]) ) if bit_pattern_dict[ele][1] == vol_masks[bit_pattern_dict[ele][0]]: ele_list.append(ele) - FreeCAD.Console.PrintMessage('found Volumes: {}\n'.format(len(ele_list))) - # FreeCAD.Console.PrintMessage(' volumes: {}\n'.format(ele_list)) + FreeCAD.Console.PrintMessage("found Volumes: {}\n".format(len(ele_list))) + # FreeCAD.Console.PrintMessage(" volumes: {}\n".format(ele_list)) return ele_list @@ -380,12 +380,12 @@ def get_femelements_by_femnodes_std( femelement_table, node_list ): - '''for every femelement of femelement_table + """for every femelement of femelement_table if all nodes of the femelement are in node_list, the femelement is added to the list which is returned e: elementlist - nodes: nodelist ''' - FreeCAD.Console.PrintMessage('std search: get_femelements_by_femnodes_std\n') + nodes: nodelist """ + FreeCAD.Console.PrintMessage("std search: get_femelements_by_femnodes_std\n") e = [] # elementlist for elementID in sorted(femelement_table): nodecount = 0 @@ -402,7 +402,7 @@ def get_femvolumeelements_by_femfacenodes( femelement_table, node_list ): - '''assume femelement_table only has volume elements + """assume femelement_table only has volume elements for every femvolumeelement of femelement_table for tetra4 and tetra10 the C++ methods could be used --> test again to be sure if hexa8 volume element @@ -414,7 +414,7 @@ def get_femvolumeelements_by_femfacenodes( if penta15 volume element --> if exact 6 or 8 element nodes are in node_list --> add femelement e: elementlist - nodes: nodelist ''' + nodes: nodelist """ e = [] # elementlist for elementID in sorted(femelement_table): nodecount = 0 @@ -457,11 +457,11 @@ def get_femvolumeelements_by_femfacenodes( e.append(elementID) else: FreeCAD.Console.PrintError( - 'Error in get_femvolumeelements_by_femfacenodes(): ' - 'unknown volume element: {}\n' + "Error in get_femvolumeelements_by_femfacenodes(): " + "unknown volume element: {}\n" .format(el_nd_ct) ) - # FreeCAD.Console.PrintMessage('{}\n'.format(sorted(e))) + # FreeCAD.Console.PrintMessage("{}\n".format(sorted(e))) return e @@ -478,14 +478,14 @@ def get_femelement_sets( referenced_femelements = [] has_remaining_femelements = None for fem_object_i, fem_object in enumerate(fem_objects): - obj = fem_object['Object'] + obj = fem_object["Object"] FreeCAD.Console.PrintMessage( "Constraint: {} --> We're going to search " "in the mesh for the element ID's.\n" .format(obj.Name) ) # unique short identifier - fem_object['ShortName'] = get_elset_short_name(obj, fem_object_i) + fem_object["ShortName"] = get_elset_short_name(obj, fem_object_i) if obj.References: ref_shape_femelements = [] ref_shape_femelements = get_femelements_by_references( @@ -495,7 +495,7 @@ def get_femelement_sets( ) referenced_femelements += ref_shape_femelements count_femelements += len(ref_shape_femelements) - fem_object['FEMElements'] = ref_shape_femelements + fem_object["FEMElements"] = ref_shape_femelements else: has_remaining_femelements = obj.Name # get remaining femelements for the fem_objects @@ -506,15 +506,15 @@ def get_femelement_sets( remaining_femelements.append(elemid) count_femelements += len(remaining_femelements) for fem_object in fem_objects: - obj = fem_object['Object'] + obj = fem_object["Object"] if obj.Name == has_remaining_femelements: - fem_object['FEMElements'] = sorted(remaining_femelements) + fem_object["FEMElements"] = sorted(remaining_femelements) # check if all worked out well if femelements_count_ok(len(femelement_table), count_femelements): return True else: FreeCAD.Console.PrintError( - 'Error in get_femelement_sets -- > femelements_count_ok() failed!\n' + "Error in get_femelement_sets -- > femelements_count_ok() failed!\n" ) return False @@ -526,17 +526,17 @@ def get_femelement_direction1D_set( beamrotation_objects, theshape=None ): - ''' + """ get for each geometry edge direction, the normal and the element ids and # write all into the beamrotation_objects means no return value, we're going to write into the beamrotation_objects dictionary FEMRotations1D is a list of dictionaries for every beamdirection of all edges - beamrot_obj['FEMRotations1D'] = [{ - 'ids' : [theids], - 'direction' : direction, - 'normal' : normal + beamrot_obj["FEMRotations1D"] = [{ + "ids" : [theids], + "direction" : direction, + "normal" : normal }, ... ] - ''' + """ if len(beamrotation_objects) == 0: # no beamrotation document object, all beams use standard rotation of 0 degree (angle) # we need theshape (the shape which was meshed) @@ -545,9 +545,9 @@ def get_femelement_direction1D_set( # add normals for each direction rotation_angle = 0 for rot in rotations_ids: - rot['normal'] = get_beam_normal(rot['direction'], rotation_angle) - # key 'Object' will be empty - beamrotation_objects.append({'FEMRotations1D': rotations_ids, 'ShortName': 'Rstd'}) + rot["normal"] = get_beam_normal(rot["direction"], rotation_angle) + # key "Object" will be empty + beamrotation_objects.append({"FEMRotations1D": rotations_ids, "ShortName": "Rstd"}) elif len(beamrotation_objects) == 1: # one beamrotation document object with no references # all beams use rotation from this object @@ -555,11 +555,11 @@ def get_femelement_direction1D_set( # since ccx needs to split them in sets anyway we need to take care of this too rotations_ids = get_femelement_directions_theshape(femmesh, femelement_table, theshape) # add normals for each direction - rotation_angle = beamrotation_objects[0]['Object'].Rotation + rotation_angle = beamrotation_objects[0]["Object"].Rotation for rot in rotations_ids: - rot['normal'] = get_beam_normal(rot['direction'], rotation_angle) - beamrotation_objects[0]['FEMRotations1D'] = rotations_ids - beamrotation_objects[0]['ShortName'] = 'R0' + rot["normal"] = get_beam_normal(rot["direction"], rotation_angle) + beamrotation_objects[0]["FEMRotations1D"] = rotations_ids + beamrotation_objects[0]["ShortName"] = "R0" elif len(beamrotation_objects) > 1: # multiple beam rotation document objects # rotations defined by reference shapes, TODO implement this @@ -573,9 +573,9 @@ def get_femelement_direction1D_set( # pre check, only one beam rotation with empty ref shapes is allowed # we need theshape for multiple rotations too # because of the corner cases mentioned above - FreeCAD.Console.PrintError('Multiple Rotations not yet supported!\n') + FreeCAD.Console.PrintError("Multiple Rotations not yet supported!\n") for rot_object in beamrotation_objects: # debug output - FreeCAD.Console.PrintMessage('{}\n'.format(rot_object['FEMRotations1D'])) + FreeCAD.Console.PrintMessage("{}\n".format(rot_object["FEMRotations1D"])) # ************************************************************************************************ @@ -585,15 +585,15 @@ def get_femelement_directions_theshape(femmesh, femelement_table, theshape): # add directions and all ids for each direction for e in theshape.Shape.Edges: the_edge = {} - the_edge['direction'] = e.Vertexes[1].Point - e.Vertexes[0].Point + the_edge["direction"] = e.Vertexes[1].Point - e.Vertexes[0].Point edge_femnodes = femmesh.getNodesByEdge(e) # femnodes for the current edge # femelements for this edge - the_edge['ids'] = get_femelements_by_femnodes_std(femelement_table, edge_femnodes) + the_edge["ids"] = get_femelements_by_femnodes_std(femelement_table, edge_femnodes) for rot in rotations_ids: # tolerance will be managed by FreeCAD # see https://forum.freecadweb.org/viewtopic.php?f=22&t=14179 - if rot['direction'] == the_edge['direction']: - rot['ids'] += the_edge['ids'] + if rot["direction"] == the_edge["direction"]: + rot["ids"] += the_edge["ids"] break else: rotations_ids.append(the_edge) @@ -651,8 +651,8 @@ def get_beam_normal(beam_direction, defined_angle): dot_nt = vector_a[0] * normal_n[0] + vector_a[1] * normal_n[1] + vector_a[2] * normal_n[2] dot = vector_a[0] * normal_n[0] + vector_a[1] * normal_n[1] + vector_a[2] * normal_n[2] - FreeCAD.Console.PrintLog('{}\n'.format(dot)) - FreeCAD.Console.PrintLog('{}\n'.format(normal_n)) + FreeCAD.Console.PrintLog("{}\n".format(dot)) + FreeCAD.Console.PrintLog("{}\n".format(normal_n)) # dummy usage of the axis dot to get flake8 quiet del dot_x, dot_y, dot_z, dot, dot_nt @@ -671,15 +671,15 @@ def get_femmesh_groupdata_sets_by_name( # no check is done in this regard !!! # we just check for the group name and the group data type # what happens if a reference shape was changed - # but the mesh and the mesh groups were not created new !?! - obj = fem_object['Object'] + # but the mesh and the mesh groups where not created new !?! + obj = fem_object["Object"] if femmesh.GroupCount: for g in femmesh.Groups: grp_name = femmesh.getGroupName(g) if grp_name.startswith(obj.Name + "_"): if femmesh.getGroupElementType(g) == group_data_type: FreeCAD.Console.PrintMessage( - ' found mesh group for the IDs: {}, Type: {}\n' + " found mesh group for the IDs: {}, Type: {}\n" .format(grp_name, group_data_type) ) return femmesh.getGroupElements(g) # == ref_shape_femelements @@ -695,24 +695,24 @@ def get_femelement_sets_from_group_data( count_femelements = 0 sum_group_elements = [] for fem_object_i, fem_object in enumerate(fem_objects): - obj = fem_object['Object'] + obj = fem_object["Object"] FreeCAD.Console.PrintMessage( "Constraint: {} --> We have mesh groups. " "We will search for appropriate group data.\n" .format(obj.Name) ) # unique short identifier - fem_object['ShortName'] = get_elset_short_name(obj, fem_object_i) + fem_object["ShortName"] = get_elset_short_name(obj, fem_object_i) # see comments over there ! - group_elements = get_femmesh_groupdata_sets_by_name(femmesh, fem_object, 'Volume') + group_elements = get_femmesh_groupdata_sets_by_name(femmesh, fem_object, "Volume") sum_group_elements += group_elements count_femelements += len(group_elements) - fem_object['FEMElements'] = group_elements + fem_object["FEMElements"] = group_elements # check if all worked out well if not femelements_count_ok(femmesh.VolumeCount, count_femelements): FreeCAD.Console.PrintError( - 'Error in get_femelement_sets_from_group_data -- > ' - 'femelements_count_ok() failed!\n' + "Error in get_femelement_sets_from_group_data -- > " + "femelements_count_ok() failed!\n" ) return False else: @@ -724,20 +724,20 @@ def get_elset_short_name( obj, i ): - if hasattr(obj, "Proxy") and obj.Proxy.Type == 'Fem::Material': - return 'M' + str(i) - elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'Fem::FemElementGeometry1D': - return 'B' + str(i) - elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'Fem::FemElementRotation1D': - return 'R' + str(i) - elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'Fem::FemElementFluid1D': - return 'F' + str(i) - elif hasattr(obj, "Proxy") and obj.Proxy.Type == 'Fem::FemElementGeometry2D': - return 'S' + str(i) + if hasattr(obj, "Proxy") and obj.Proxy.Type == "Fem::Material": + return "M" + str(i) + elif hasattr(obj, "Proxy") and obj.Proxy.Type == "Fem::FemElementGeometry1D": + return "B" + str(i) + elif hasattr(obj, "Proxy") and obj.Proxy.Type == "Fem::FemElementRotation1D": + return "R" + str(i) + elif hasattr(obj, "Proxy") and obj.Proxy.Type == "Fem::FemElementFluid1D": + return "F" + str(i) + elif hasattr(obj, "Proxy") and obj.Proxy.Type == "Fem::FemElementGeometry2D": + return "S" + str(i) else: FreeCAD.Console.PrintError( - 'Error in creating short elset name ' - 'for obj: {} --> Proxy.Type: {}\n' + "Error in creating short elset name " + "for obj: {} --> Proxy.Type: {}\n" .format(obj.Name, obj.Proxy.Type) ) @@ -751,9 +751,9 @@ def get_force_obj_vertex_nodeload_table( ): # force_obj_node_load_table: # [ - # ('refshape_name.elemname', node_load_table), + # ("refshape_name.elemname", node_load_table), # ..., - # ('refshape_name.elemname', node_load_table) + # ("refshape_name.elemname", node_load_table) # ] force_obj_node_load_table = [] node_load = frc_obj.Force / len(frc_obj.References) @@ -762,15 +762,15 @@ def get_force_obj_vertex_nodeload_table( for elem in elem_tup: ref_node = o.Shape.getElement(elem) FreeCAD.Console.PrintMessage( - ' ' - 'ReferenceShape ... Type: {0}, ' - 'Object name: {1}, ' - 'Object label: {2}, ' - 'Element name: {3}\n' + " " + "ReferenceShape ... Type: {0}, " + "Object name: {1}, " + "Object label: {2}, " + "Element name: {3}\n" .format(ref_node.ShapeType, o.Name, o.Label, elem) ) node = femmesh.getNodesByVertex(ref_node) - elem_info_string = 'node load on shape: ' + o.Name + ':' + elem + elem_info_string = "node load on shape: " + o.Name + ":" + elem force_obj_node_load_table.append( (elem_info_string, {node[0]: node_load / node_count}) ) @@ -789,9 +789,9 @@ def get_force_obj_edge_nodeload_table( ): # force_obj_node_load_table: # [ - # ('refshape_name.elemname', node_load_table), + # ("refshape_name.elemname", node_load_table), # ..., - # ('refshape_name.elemname', node_load_table) + # ("refshape_name.elemname", node_load_table) # ] force_obj_node_load_table = [] sum_ref_edge_length = 0 @@ -801,11 +801,11 @@ def get_force_obj_edge_nodeload_table( for elem in elem_tup: ref_edge = o.Shape.getElement(elem) FreeCAD.Console.PrintMessage( - ' ' - 'ReferenceShape ... Type: {0}, ' - 'Object name: {1}, ' - 'Object label: {2}, ' - 'Element name: {3}\n' + " " + "ReferenceShape ... Type: {0}, " + "Object name: {1}, " + "Object label: {2}, " + "Element name: {3}\n" .format(ref_edge.ShapeType, o.Name, o.Label, elem) ) sum_ref_edge_length += ref_edge.Length @@ -840,14 +840,14 @@ def get_force_obj_edge_nodeload_table( ratio_refedge_lengths = sum_node_lengths / ref_edge.Length if ratio_refedge_lengths < 0.99 or ratio_refedge_lengths > 1.01: FreeCAD.Console.PrintError( - 'Error on: ' + frc_obj.Name + ' --> ' + o.Name + '.' + elem + '\n' + "Error on: " + frc_obj.Name + " --> " + o.Name + "." + elem + "\n" ) - FreeCAD.Console.PrintMessage(' sum_node_lengths: {}\n'.format(sum_node_lengths)) - FreeCAD.Console.PrintMessage(' refedge_length: {}\n'.format(ref_edge.Length)) + FreeCAD.Console.PrintMessage(" sum_node_lengths: {}\n".format(sum_node_lengths)) + FreeCAD.Console.PrintMessage(" refedge_length: {}\n".format(ref_edge.Length)) bad_refedge = ref_edge sum_ref_edge_node_length += sum_node_lengths - elem_info_string = 'node loads on shape: ' + o.Name + ':' + elem + elem_info_string = "node loads on shape: " + o.Name + ":" + elem force_obj_node_load_table.append((elem_info_string, node_load_table)) for ref_shape in force_obj_node_load_table: @@ -857,74 +857,74 @@ def get_force_obj_edge_nodeload_table( ratio = sum_node_load / frc_obj.Force if ratio < 0.99 or ratio > 1.01: FreeCAD.Console.PrintMessage( - 'Deviation sum_node_load to frc_obj.Force is more than 1% : {}\n' + "Deviation sum_node_load to frc_obj.Force is more than 1% : {}\n" .format(ratio) ) FreeCAD.Console.PrintMessage( - ' sum_ref_edge_node_length: {}\n' + " sum_ref_edge_node_length: {}\n" .format(sum_ref_edge_node_length) ) FreeCAD.Console.PrintMessage( - ' sum_ref_edge_length: {}\n' + " sum_ref_edge_length: {}\n" .format(sum_ref_edge_length) ) FreeCAD.Console.PrintMessage( - ' sum_node_load: {}\n' + " sum_node_load: {}\n" .format(sum_node_load) ) FreeCAD.Console.PrintMessage( - ' frc_obj.Force: {}\n' + " frc_obj.Force: {}\n" .format(frc_obj.Force) ) FreeCAD.Console.PrintMessage( - ' the reason could be simply a circle length --> ' - 'see method get_ref_edge_node_lengths\n' + " the reason could be simply a circle length --> " + "see method get_ref_edge_node_lengths\n" ) FreeCAD.Console.PrintMessage( - ' the reason could also be a problem in ' - 'retrieving the ref_edge_node_length\n' + " the reason could also be a problem in " + "retrieving the ref_edge_node_length\n" ) # try debugging of the last bad refedge - FreeCAD.Console.PrintMessage('DEBUGGING\n') - FreeCAD.Console.PrintMessage('\n'.format(bad_refedge)) + FreeCAD.Console.PrintMessage("DEBUGGING\n") + FreeCAD.Console.PrintMessage("\n".format(bad_refedge)) - FreeCAD.Console.PrintMessage('bad_refedge_nodes\n') + FreeCAD.Console.PrintMessage("bad_refedge_nodes\n") bad_refedge_nodes = femmesh.getNodesByEdge(bad_refedge) - FreeCAD.Console.PrintMessage('{}\n'.format(len(bad_refedge_nodes))) - FreeCAD.Console.PrintMessage('{}\n'.format(bad_refedge_nodes)) + FreeCAD.Console.PrintMessage("{}\n".format(len(bad_refedge_nodes))) + FreeCAD.Console.PrintMessage("{}\n".format(bad_refedge_nodes)) # import FreeCADGui # FreeCADGui.ActiveDocument.Compound_Mesh.HighlightedNodes = bad_refedge_nodes - FreeCAD.Console.PrintMessage('bad_edge_table\n') + FreeCAD.Console.PrintMessage("bad_edge_table\n") # bad_edge_table: # { meshedgeID : ( nodeID, ... , nodeID ) } bad_edge_table = get_ref_edgenodes_table(femmesh, femelement_table, bad_refedge) - FreeCAD.Console.PrintMessage('{}\n'.format(len(bad_edge_table))) + FreeCAD.Console.PrintMessage("{}\n".format(len(bad_edge_table))) bad_edge_table_nodes = [] for elem in bad_edge_table: - FreeCAD.Console.PrintMessage(elem, ' --> \n'.format(bad_edge_table[elem])) + FreeCAD.Console.PrintMessage(elem, " --> \n".format(bad_edge_table[elem])) for node in bad_edge_table[elem]: if node not in bad_edge_table_nodes: bad_edge_table_nodes.append(node) - FreeCAD.Console.PrintMessage('sorted(bad_edge_table_nodes)\n') + FreeCAD.Console.PrintMessage("sorted(bad_edge_table_nodes)\n") # should be == bad_refedge_nodes - FreeCAD.Console.PrintMessage('{}\n'.format(sorted(bad_edge_table_nodes))) + FreeCAD.Console.PrintMessage("{}\n".format(sorted(bad_edge_table_nodes))) # import FreeCADGui # FreeCADGui.ActiveDocument.Compound_Mesh.HighlightedNodes = bad_edge_table_nodes # bad_node_length_table: # [ (nodeID, length), ... , (nodeID, length) ] # some nodes will have more than one entry - FreeCAD.Console.PrintMessage('good_edge_table\n') + FreeCAD.Console.PrintMessage("good_edge_table\n") good_edge_table = delete_duplicate_mesh_elements(bad_edge_table) for elem in good_edge_table: - FreeCAD.Console.PrintMessage('{} --> {}\n'.format(elem, bad_edge_table[elem])) + FreeCAD.Console.PrintMessage("{} --> {}\n".format(elem, bad_edge_table[elem])) - FreeCAD.Console.PrintMessage('bad_node_length_table\n') + FreeCAD.Console.PrintMessage("bad_node_length_table\n") bad_node_length_table = get_ref_edgenodes_lengths(femnodes_mesh, bad_edge_table) for n, l in bad_node_length_table: - FreeCAD.Console.PrintMessage('{} --> {}\n'.format(n, l)) + FreeCAD.Console.PrintMessage("{} --> {}\n".format(n, l)) return force_obj_node_load_table @@ -1012,7 +1012,7 @@ def get_ref_edgenodes_lengths( return [] node_length_table = [] mesh_edge_length = 0 - # FreeCAD.Console.PrintMessage('{}\n'.format(len(edge_table))) + # FreeCAD.Console.PrintMessage("{}\n".format(len(edge_table))) for me in edge_table: femmesh_edgetype = len(edge_table[me]) if femmesh_edgetype == 2: # 2 node femmesh edge @@ -1023,7 +1023,7 @@ def get_ref_edgenodes_lengths( P2 = femnodes_mesh[edge_table[me][1]] edge_vec = P2 - P1 mesh_edge_length = edge_vec.Length - # FreeCAD.Console.PrintMessage('{}\n'.format(mesh_edge_length)) + # FreeCAD.Console.PrintMessage("{}\n".format(mesh_edge_length)) end_node_length = mesh_edge_length / 2.0 node_length_table.append((edge_table[me][0], end_node_length)) node_length_table.append((edge_table[me][1], end_node_length)) @@ -1039,7 +1039,7 @@ def get_ref_edgenodes_lengths( edge_vec1 = P3 - P1 edge_vec2 = P2 - P3 mesh_edge_length = edge_vec1.Length + edge_vec2.Length - # FreeCAD.Console.PrintMessage('{} --> {}\n'.format(me, mesh_edge_length)) + # FreeCAD.Console.PrintMessage("{} --> {}\n".format(me, mesh_edge_length)) end_node_length = mesh_edge_length / 6.0 middle_node_length = mesh_edge_length * 2.0 / 3.0 node_length_table.append((edge_table[me][0], end_node_length)) @@ -1061,9 +1061,9 @@ def get_force_obj_face_nodeload_table( ): # force_obj_node_load_table: # [ - # ('refshape_name.elemname',node_load_table), + # ("refshape_name.elemname",node_load_table), # ..., - # ('refshape_name.elemname',node_load_table) + # ("refshape_name.elemname",node_load_table) # ] force_obj_node_load_table = [] sum_ref_face_area = 0 @@ -1073,11 +1073,11 @@ def get_force_obj_face_nodeload_table( for elem in elem_tup: ref_face = o.Shape.getElement(elem) FreeCAD.Console.PrintMessage( - ' ' - 'ReferenceShape ... Type: {0}, ' - 'Object name: {1}, ' - 'Object label: {2}, ' - 'Element name: {3}\n' + " " + "ReferenceShape ... Type: {0}, " + "Object name: {1}, " + "Object label: {2}, " + "Element name: {3}\n" .format(ref_face.ShapeType, o.Name, o.Label, elem) ) sum_ref_face_area += ref_face.Area @@ -1112,13 +1112,13 @@ def get_force_obj_face_nodeload_table( ratio_refface_areas = sum_node_areas / ref_face.Area if ratio_refface_areas < 0.99 or ratio_refface_areas > 1.01: FreeCAD.Console.PrintError( - 'Error on: ' + frc_obj.Name + ' --> ' + o.Name + '.' + elem + '\n' + "Error on: " + frc_obj.Name + " --> " + o.Name + "." + elem + "\n" ) - FreeCAD.Console.PrintMessage(' sum_node_areas: {}\n'.format(sum_node_areas)) - FreeCAD.Console.PrintMessage(' ref_face_area: {}\n'.format(ref_face.Area)) + FreeCAD.Console.PrintMessage(" sum_node_areas: {}\n".format(sum_node_areas)) + FreeCAD.Console.PrintMessage(" ref_face_area: {}\n".format(ref_face.Area)) sum_ref_face_node_area += sum_node_areas - elem_info_string = 'node loads on shape: ' + o.Name + ':' + elem + elem_info_string = "node loads on shape: " + o.Name + ":" + elem force_obj_node_load_table.append((elem_info_string, node_load_table)) for ref_shape in force_obj_node_load_table: @@ -1128,32 +1128,32 @@ def get_force_obj_face_nodeload_table( ratio = sum_node_load / frc_obj.Force if ratio < 0.99 or ratio > 1.01: FreeCAD.Console.PrintMessage( - 'Deviation sum_node_load to frc_obj.Force is more than 1% : {}\n' + "Deviation sum_node_load to frc_obj.Force is more than 1% : {}\n" .format(ratio) ) FreeCAD.Console.PrintMessage( - ' sum_ref_face_node_area: {}\n' + " sum_ref_face_node_area: {}\n" .format(sum_ref_face_node_area) ) FreeCAD.Console.PrintMessage( - ' sum_ref_face_area: {}\n' + " sum_ref_face_area: {}\n" .format(sum_ref_face_area) ) FreeCAD.Console.PrintMessage( - ' sum_node_load: {}\n' + " sum_node_load: {}\n" .format(sum_node_load) ) FreeCAD.Console.PrintMessage( - ' frc_obj.Force: {}\n' + " frc_obj.Force: {}\n" .format(frc_obj.Force) ) FreeCAD.Console.PrintMessage( - ' the reason could be simply a circle area --> ' - 'see method get_ref_face_node_areas\n' + " the reason could be simply a circle area --> " + "see method get_ref_face_node_areas\n" ) FreeCAD.Console.PrintMessage( - ' the reason could also be a problem in ' - 'retrieving the ref_face_node_area\n' + " the reason could also be a problem in " + "retrieving the ref_face_node_area\n" ) return force_obj_node_load_table @@ -1169,9 +1169,9 @@ def get_ref_facenodes_table( if is_solid_femmesh(femmesh): if has_no_face_data(femmesh): FreeCAD.Console.PrintMessage( - ' No face data in finite volume element mesh. ' - 'FreeCAD uses getccxVolumesByFace() ' - 'to retrieve the volume elements of the ref_face.\n' + " No face data in finite volume element mesh. " + "FreeCAD uses getccxVolumesByFace() " + "to retrieve the volume elements of the ref_face.\n" ) # there is no face data # if we retrieve the nodes ourself we will have a problem: @@ -1186,8 +1186,8 @@ def get_ref_facenodes_table( ref_face_volume_elements = femmesh.getccxVolumesByFace(ref_face) if ref_face_volume_elements: # mesh with tetras FreeCAD.Console.PrintLog( - ' Use of getccxVolumesByFace() has ' - 'returned volume elements of the ref_face.\n' + " Use of getccxVolumesByFace() has " + "returned volume elements of the ref_face.\n" ) for ve in ref_face_volume_elements: veID = ve[0] @@ -1199,9 +1199,9 @@ def get_ref_facenodes_table( face_table[veID] = ve_ref_face_nodes else: # mesh with hexa or penta FreeCAD.Console.PrintLog( - ' The use of getccxVolumesByFace() has NOT returned ' - 'volume elements of the ref_face. ' - 'FreeCAD tries to use get_femvolumeelements_by_femfacenodes().\n' + " The use of getccxVolumesByFace() has NOT returned " + "volume elements of the ref_face. " + "FreeCAD tries to use get_femvolumeelements_by_femfacenodes().\n" ) # list of integer [mv] ref_face_volume_elements = get_femvolumeelements_by_femfacenodes( @@ -1226,7 +1226,7 @@ def get_ref_facenodes_table( ref_face_elements = get_femelements_by_femnodes_std(femelement_table, ref_face_nodes) for mf in ref_face_elements: face_table[mf] = femelement_table[mf] - # FreeCAD.Console.PrintMessage('{}\n'.format(face_table)) + # FreeCAD.Console.PrintMessage("{}\n".format(face_table)) return face_table @@ -1397,13 +1397,13 @@ def build_mesh_faces_of_volume_elements( face_nodenumber_table[veID] = [] for n in face_table[veID]: index = femelement_table[veID].index(n) - # FreeCAD.Console.PrintMessage('{}\n'.format(index)) + # FreeCAD.Console.PrintMessage("{}\n".format(index)) # local node number = index + 1 face_nodenumber_table[veID].append(index + 1) - FreeCAD.Console.PrintLog('VolElement: {}\n'.format(veID)) - FreeCAD.Console.PrintLog(' --> {}\n'.format(femelement_table[veID])) - FreeCAD.Console.PrintLog(' --> {}\n'.format(face_table[veID])) - FreeCAD.Console.PrintLog(' --> {}\n'.format(face_nodenumber_table[veID])) + FreeCAD.Console.PrintLog("VolElement: {}\n".format(veID)) + FreeCAD.Console.PrintLog(" --> {}\n".format(femelement_table[veID])) + FreeCAD.Console.PrintLog(" --> {}\n".format(face_table[veID])) + FreeCAD.Console.PrintLog(" --> {}\n".format(face_nodenumber_table[veID])) for veID in face_nodenumber_table: vol_node_ct = len(femelement_table[veID]) face_node_indexs = sorted(face_nodenumber_table[veID]) @@ -1539,7 +1539,7 @@ def build_mesh_faces_of_volume_elements( # index = node number - 1i -= 1 face_nodes.append(femelement_table[veID][i]) face_table[veID] = face_nodes # reset the entry in face_table - # FreeCAD.Console.PrintMessage(' --> {}\n'.format(face_table[veID])) + # FreeCAD.Console.PrintMessage(" --> {}\n".format(face_table[veID])) return face_table @@ -1551,7 +1551,7 @@ def get_ref_shape_node_sum_geom_table( # sum of length or area for each node of the ref_shape node_sum_geom_table = {} for n, A in node_geom_table: - # FreeCAD.Console.PrintMessage('{} --> {}\n'.format(n, A)) + # FreeCAD.Console.PrintMessage("{} --> {}\n".format(n, A)) if n in node_sum_geom_table: node_sum_geom_table[n] = node_sum_geom_table[n] + A else: @@ -1571,7 +1571,7 @@ def get_pressure_obj_faces( # get the nodes # sorted and duplicates removed prs_face_node_set = get_femnodes_by_femobj_with_references(femmesh, femobj) - # FreeCAD.Console.PrintMessage('prs_face_node_set: {}\n'.format(prs_face_node_set)) + # FreeCAD.Console.PrintMessage("prs_face_node_set: {}\n".format(prs_face_node_set)) # fill the bit_pattern_dict and search for the faces bit_pattern_dict = get_bit_pattern_dict( femelement_table, @@ -1584,8 +1584,8 @@ def get_pressure_obj_faces( # normally we should call get_femelements_by_references and # the group check should be integrated there if femmesh.GroupCount: - meshfaces = get_femmesh_groupdata_sets_by_name(femmesh, femobj, 'Face') - # FreeCAD.Console.PrintMessage('{}\n'.format(meshfaces)) + meshfaces = get_femmesh_groupdata_sets_by_name(femmesh, femobj, "Face") + # FreeCAD.Console.PrintMessage("{}\n".format(meshfaces)) if not meshfaces: FreeCAD.Console.PrintError( "Error: Something went wrong in getting the group element faces.\n" @@ -1615,12 +1615,12 @@ def get_pressure_obj_faces_depreciated( femobj ): pressure_faces = [] - for o, elem_tup in femobj['Object'].References: + for o, elem_tup in femobj["Object"].References: for elem in elem_tup: ref_shape = o.Shape.getElement(elem) - elem_info_string = 'face load on shape: ' + o.Name + ':' + elem - FreeCAD.Console.PrintMessage('{}\n'.format(elem_info_string)) - if ref_shape.ShapeType == 'Face': + elem_info_string = "face load on shape: " + o.Name + ":" + elem + FreeCAD.Console.PrintMessage("{}\n".format(elem_info_string)) + if ref_shape.ShapeType == "Face": pressure_faces.append( (elem_info_string, femmesh.getccxVolumesByFace(ref_shape)) ) @@ -1633,17 +1633,17 @@ def get_mesh_group_elements( mesh_group_obj, aPart ): - '''the Reference shapes of the mesh_group_object are searched in the Shape of aPart. + """the Reference shapes of the mesh_group_object are searched in the Shape of aPart. If found in shape they are added to a dict - {MeshGroupIdentifier : ['ShapeType of the Elements'], [ElementID, ElementID, ...], ...} - ''' + {MeshGroupIdentifier : ["ShapeType of the Elements"], [ElementID, ElementID, ...], ...} + """ group_elements = {} # { name : [element, element, ... , element]} if mesh_group_obj.References: grp_ele = get_reference_group_elements(mesh_group_obj, aPart) group_elements[grp_ele[0]] = grp_ele[1] else: FreeCAD.Console.PrintError( - ' Empty reference in mesh group object: {} {}\n' + " Empty reference in mesh group object: {} {}\n" .format(mesh_group_obj.Name, mesh_group_obj.Label) ) return group_elements @@ -1654,10 +1654,10 @@ def get_analysis_group_elements( aAnalysis, aPart ): - ''' all Reference shapes of all Analysis member are searched in the Shape of aPart. + """ all Reference shapes of all Analysis member are searched in the Shape of aPart. If found in shape they are added to a dict - {ConstraintName : ['ShapeType of the Elements'], [ElementID, ElementID, ...], ...} - ''' + {ConstraintName : ["ShapeType of the Elements"], [ElementID, ElementID, ...], ...} + """ group_elements = {} # { name : [element, element, ... , element]} empty_references = [] for m in aAnalysis.Group: @@ -1668,7 +1668,7 @@ def get_analysis_group_elements( grp_ele = get_reference_group_elements(m, aPart) group_elements[grp_ele[0]] = grp_ele[1] else: - FreeCAD.Console.PrintMessage(' Empty reference: ' + m.Name + '\n') + FreeCAD.Console.PrintMessage(" Empty reference: " + m.Name + "\n") empty_references.append(m) if empty_references: if len(empty_references) == 1: @@ -1679,29 +1679,29 @@ def get_analysis_group_elements( ) else: FreeCAD.Console.PrintError( - 'Problem: more than one object with empty references.\n' + "Problem: more than one object with empty references.\n" ) FreeCAD.Console.PrintMessage( - 'We are going to try to get the empty material references anyway.\n' + "We are going to try to get the empty material references anyway.\n" ) # FemElementGeometry2D, ElementGeometry1D and # FemElementFluid1D could have empty references, # but on solid meshes only materials should have empty references for er in empty_references: - FreeCAD.Console.PrintMessage(er.Name + '\n') + FreeCAD.Console.PrintMessage(er.Name + "\n") group_elements = get_anlysis_empty_references_group_elements( group_elements, aAnalysis, aPart.Shape ) # check if all groups have at least one element, - # it doesn't mean ALL reference shapes for a group have been found + # it doesn"t mean ALL reference shapes for a group have been found for g in group_elements: - # FreeCAD.Console.PrintMessage('{}\n'.format(group_elements[g])) + # FreeCAD.Console.PrintMessage("{}\n".format(group_elements[g])) if len(group_elements[g]) == 0: FreeCAD.Console.PrintError( - 'Error: The shapes for the mesh group for the reference ' - 'shapes of analysis member: {} could not be found!\n' + "Error: The shapes for the mesh group for the reference " + "shapes of analysis member: {} could not be found!\n" .format(g) ) return group_elements @@ -1712,14 +1712,14 @@ def get_reference_group_elements( obj, aPart ): - ''' obj is an FEM object which has reference shapes like the group object + """ obj is an FEM object which has reference shapes like the group object the material, most of the constraints aPart is geometry feature normally CompSolid the method searches all reference shapes of obj inside aPart even if the reference shapes are a totally different geometry feature. - a tuple is returned ('Name or Label of the FEMobject', ['Element1', 'Element2', ...]) + a tuple is returned ("Name or Label of the FEMobject", ["Element1", "Element2", ...]) The names in the list are the Elements of the geometry aPart - whereas 'Solid1' == aPart.Shape.Solids[0] + whereas "Solid1" == aPart.Shape.Solids[0] !!! It is strongly recommended to use as reference shapes the Solids of a CompSolid and not the Solids the CompSolid is made of !!! see https://forum.freecadweb.org/viewtopic.php?f=18&t=12212&p=175777#p175777 @@ -1727,7 +1727,7 @@ def get_reference_group_elements( Occt might change the Solids a CompSolid is made of during creation of the CompSolid by adding Edges and vertices Thus the Elements do not have the same geometry anymore - ''' + """ aShape = aPart.Shape if hasattr(obj, "UseLabel") and obj.UseLabel: # TODO: check the character of the Label @@ -1740,8 +1740,8 @@ def get_reference_group_elements( for r in obj.References: parent = r[0] childs = r[1] - # FreeCAD.Console.PrintMessage('{}\n'.format(parent)) - # FreeCAD.Console.PrintMessage('{}\n'.format(childs)) + # FreeCAD.Console.PrintMessage("{}\n".format(parent)) + # FreeCAD.Console.PrintMessage("{}\n".format(childs)) for child in childs: # the method getElement(element) does not return Solid elements ref_shape = get_element(parent, child) @@ -1749,42 +1749,42 @@ def get_reference_group_elements( stype = ref_shape.ShapeType elif stype != ref_shape.ShapeType: FreeCAD.Console.PrintError( - 'Error, two refshapes in References with different ShapeTypes.\n' + "Error, two refshapes in References with different ShapeTypes.\n" ) - FreeCAD.Console.PrintLog('\n'.format(ref_shape)) + FreeCAD.Console.PrintLog("\n".format(ref_shape)) found_element = find_element_in_shape(aShape, ref_shape) if found_element is not None: elements.append(found_element) else: FreeCAD.Console.PrintError( - 'Problem: For the geometry of the ' - 'following shape was no Shape found: {}\n' + "Problem: For the geometry of the " + "following shape was no Shape found: {}\n" .format(ref_shape) ) - FreeCAD.Console.PrintMessage(' ' + obj.Name + '\n') - FreeCAD.Console.PrintMessage(' ' + str(obj.References) + '\n') - FreeCAD.Console.PrintMessage(' ' + r[0].Name + '\n') + FreeCAD.Console.PrintMessage(" " + obj.Name + "\n") + FreeCAD.Console.PrintMessage(" " + str(obj.References) + "\n") + FreeCAD.Console.PrintMessage(" " + r[0].Name + "\n") if parent.Name != aPart.Name: FreeCAD.Console.PrintError( - 'The reference Shape is not a child ' - 'nor it is the shape the mesh is made of. : {}\n' + "The reference Shape is not a child " + "nor it is the shape the mesh is made of. : {}\n" .format(ref_shape) ) FreeCAD.Console.PrintMessage( - '{}--> Name of the Feature we where searching in.\n' + "{}--> Name of the Feature we where searching in.\n" .format(aPart.Name) ) FreeCAD.Console.PrintMessage( - '{} --> Name of the parent Feature of reference Shape ' - '(Use the same as in the line before and you ' - 'will have less trouble :-) !!!!!!).\n' + "{} --> Name of the parent Feature of reference Shape " + "(Use the same as in the line before and you " + "will have less trouble :-) !!!!!!).\n" .format(parent.Name) ) # import Part # Part.show(aShape) # Part.show(ref_shape) else: - FreeCAD.Console.PrintError('This should not happen, please debug!\n') + FreeCAD.Console.PrintError("This should not happen, please debug!\n") # in this case we would not have needed to use the # is_same_geometry() inside find_element_in_shape() # AFAIK we could have used the Part methods isPartner() or even isSame() @@ -1798,16 +1798,16 @@ def get_anlysis_empty_references_group_elements( aAnalysis, aShape ): - '''get the elementIDs if the Reference shape is empty + """get the elementIDs if the Reference shape is empty see get_analysis_group_elements() for more information on solid meshes only material objects could have an empty reference without there being something wrong! face meshes could have empty ShellThickness and edge meshes could have empty BeamSection/FluidSection - ''' - # FreeCAD.Console.PrintMessage('{}\n'.format(group_elements)) + """ + # FreeCAD.Console.PrintMessage("{}\n".format(group_elements)) material_ref_shapes = [] - material_shape_type = '' + material_shape_type = "" missed_material_refshapes = [] empty_reference_material = None for m in aAnalysis.Group: @@ -1817,52 +1817,52 @@ def get_anlysis_empty_references_group_elements( empty_reference_material = m.Name else: FreeCAD.Console.PrintError( - 'Problem in get_anlysis_empty_references_group_elements, ' - 'we seem to have two or more materials with empty references\n' + "Problem in get_anlysis_empty_references_group_elements, " + "we seem to have two or more materials with empty references\n" ) return {} elif hasattr(m, "References") and m.References: # ShapeType of the group elements, strip the number of the first group element # http://stackoverflow.com/questions/12851791/removing-numbers-from-string - group_shape_type = ''.join(i for i in group_elements[m.Name][0] if not i.isdigit()) + group_shape_type = "".join(i for i in group_elements[m.Name][0] if not i.isdigit()) if not material_shape_type: material_shape_type = group_shape_type elif material_shape_type != group_shape_type: FreeCAD.Console.PrintError( - 'Problem, material shape type does not match ' - 'get_anlysis_empty_references_group_elements\n' + "Problem, material shape type does not match " + "get_anlysis_empty_references_group_elements\n" ) for ele in group_elements[m.Name]: material_ref_shapes.append(ele) - if material_shape_type == 'Solid': - # FreeCAD.Console.PrintMessage('{}\n'.format(len(aShape.Solids))) + if material_shape_type == "Solid": + # FreeCAD.Console.PrintMessage("{}\n".format(len(aShape.Solids))) for i in range(len(aShape.Solids)): - ele = 'Solid' + str(i + 1) + ele = "Solid" + str(i + 1) if ele not in material_ref_shapes: missed_material_refshapes.append(ele) - elif material_shape_type == 'Face': - # FreeCAD.Console.PrintMessage('{}\n'.format(len(aShape.Faces))) + elif material_shape_type == "Face": + # FreeCAD.Console.PrintMessage("{}\n".format(len(aShape.Faces))) for i in range(len(aShape.Faces)): - ele = 'Face' + str(i + 1) + ele = "Face" + str(i + 1) if ele not in material_ref_shapes: missed_material_refshapes.append(ele) - elif material_shape_type == 'Edge': - # FreeCAD.Console.PrintMessage('{}\n'.format(len(aShape.Edges))) + elif material_shape_type == "Edge": + # FreeCAD.Console.PrintMessage("{}\n".format(len(aShape.Edges))) for i in range(len(aShape.Edges)): - ele = 'Edge' + str(i + 1) + ele = "Edge" + str(i + 1) if ele not in material_ref_shapes: missed_material_refshapes.append(ele) else: FreeCAD.Console.PrintMessage( - ' One material with no reference shapes. No need to make a group for materials.\n' + " One material with no reference shapes. No need to make a group for materials.\n" ) # make no changes group_elements return group_elements - # FreeCAD.Console.PrintMessage('{}\n'.format(sorted(material_ref_shapes))) - # FreeCAD.Console.PrintMessage('{}\n'.format(sorted(missed_material_refshapes))) + # FreeCAD.Console.PrintMessage("{}\n".format(sorted(material_ref_shapes))) + # FreeCAD.Console.PrintMessage("{}\n".format(sorted(missed_material_refshapes))) # FreeCAD.Console.PrintMessage(group_elements) group_elements[empty_reference_material] = sorted(missed_material_refshapes) - # FreeCAD.Console.PrintMessage('{}\n'.format(group_elements)) + # FreeCAD.Console.PrintMessage("{}\n".format(group_elements)) return group_elements @@ -1873,52 +1873,52 @@ def find_element_in_shape( ): # import Part ele_st = anElement.ShapeType - if ele_st == 'Solid' or ele_st == 'CompSolid': + if ele_st == "Solid" or ele_st == "CompSolid": for index, solid in enumerate(aShape.Solids): - # FreeCAD.Console.PrintMessage('{}\n'.format(is_same_geometry(solid, anElement))) + # FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(solid, anElement))) if is_same_geometry(solid, anElement): - # FreeCAD.Console.PrintMessage('{}\n'.format(index)) + # FreeCAD.Console.PrintMessage("{}\n".format(index)) # Part.show(aShape.Solids[index]) ele = ele_st + str(index + 1) return ele FreeCAD.Console.PrintError( - 'Solid ' + str(anElement) + ' not found in: ' + str(aShape) + '\n' + "Solid " + str(anElement) + " not found in: " + str(aShape) + "\n" ) - if ele_st == 'Solid' and aShape.ShapeType == 'Solid': + if ele_st == "Solid" and aShape.ShapeType == "Solid": message_part = ( - 'We have been searching for a Solid in a Solid and we have not found it. ' - 'In most cases this should be searching for a Solid inside a CompSolid. ' - 'Check the ShapeType of your Part to mesh.' + "We have been searching for a Solid in a Solid and we have not found it. " + "In most cases this should be searching for a Solid inside a CompSolid. " + "Check the ShapeType of your Part to mesh." ) - FreeCAD.Console.PrintMessage(message_part + '\n') + FreeCAD.Console.PrintMessage(message_part + "\n") # Part.show(anElement) # Part.show(aShape) - elif ele_st == 'Face' or ele_st == 'Shell': + elif ele_st == "Face" or ele_st == "Shell": for index, face in enumerate(aShape.Faces): - # FreeCAD.Console.PrintMessage('{}\n'.format(is_same_geometry(face, anElement))) + # FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(face, anElement))) if is_same_geometry(face, anElement): - # FreeCAD.Console.PrintMessage('{}\n'.format(index)) + # FreeCAD.Console.PrintMessage("{}\n".format(index)) # Part.show(aShape.Faces[index]) ele = ele_st + str(index + 1) return ele - elif ele_st == 'Edge' or ele_st == 'Wire': + elif ele_st == "Edge" or ele_st == "Wire": for index, edge in enumerate(aShape.Edges): - # FreeCAD.Console.PrintMessage('{}\n'.format(is_same_geometry(edge, anElement))) + # FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(edge, anElement))) if is_same_geometry(edge, anElement): - # FreeCAD.Console.PrintMessage(index, '\n') + # FreeCAD.Console.PrintMessage(index, "\n") # Part.show(aShape.Edges[index]) ele = ele_st + str(index + 1) return ele - elif ele_st == 'Vertex': + elif ele_st == "Vertex": for index, vertex in enumerate(aShape.Vertexes): - # FreeCAD.Console.PrintMessage('{}\n'.format(is_same_geometry(vertex, anElement))) + # FreeCAD.Console.PrintMessage("{}\n".format(is_same_geometry(vertex, anElement))) if is_same_geometry(vertex, anElement): - # FreeCAD.Console.PrintMessage('{}\n'.format(index)) + # FreeCAD.Console.PrintMessage("{}\n".format(index)) # Part.show(aShape.Vertexes[index]) ele = ele_st + str(index + 1) return ele - elif ele_st == 'Compound': - FreeCAD.Console.PrintError('Compound is not supported.\n') + elif ele_st == "Compound": + FreeCAD.Console.PrintError("Compound is not supported.\n") # ************************************************************************************************ @@ -1930,44 +1930,44 @@ def get_vertexes_by_element( # import Part ele_vertexes = [] ele_st = anElement.ShapeType - if ele_st == 'Solid' or ele_st == 'CompSolid': + if ele_st == "Solid" or ele_st == "CompSolid": for index, solid in enumerate(aShape.Solids): if is_same_geometry(solid, anElement): for vele in aShape.Solids[index].Vertexes: for i, v in enumerate(aShape.Vertexes): if vele.isSame(v): # use isSame, because orientation could be different ele_vertexes.append(i) - # FreeCAD.Console.PrintMessage(' ' + str(sorted(ele_vertexes)), '\n') + # FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)), "\n") return ele_vertexes FreeCAD.Console.PrintError( - 'Error, Solid ' + str(anElement) + ' not found in: ' + str(aShape) + '\n' + "Error, Solid " + str(anElement) + " not found in: " + str(aShape) + "\n" ) - elif ele_st == 'Face' or ele_st == 'Shell': + elif ele_st == "Face" or ele_st == "Shell": for index, face in enumerate(aShape.Faces): if is_same_geometry(face, anElement): for vele in aShape.Faces[index].Vertexes: for i, v in enumerate(aShape.Vertexes): if vele.isSame(v): # use isSame, because orientation could be different ele_vertexes.append(i) - # FreeCAD.Console.PrintMessage(' ' + str(sorted(ele_vertexes)) + '\n') + # FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)) + "\n") return ele_vertexes - elif ele_st == 'Edge' or ele_st == 'Wire': + elif ele_st == "Edge" or ele_st == "Wire": for index, edge in enumerate(aShape.Edges): if is_same_geometry(edge, anElement): for vele in aShape.Edges[index].Vertexes: for i, v in enumerate(aShape.Vertexes): if vele.isSame(v): # use isSame, because orientation could be different ele_vertexes.append(i) - # FreeCAD.Console.PrintMessage(' ' + str(sorted(ele_vertexes)) + '\n') + # FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)) + "\n") return ele_vertexes - elif ele_st == 'Vertex': + elif ele_st == "Vertex": for index, vertex in enumerate(aShape.Vertexes): if is_same_geometry(vertex, anElement): ele_vertexes.append(index) - # FreeCAD.Console.PrintMessage(' ' + str(sorted(ele_vertexes)) + '\n') + # FreeCAD.Console.PrintMessage(" " + str(sorted(ele_vertexes)) + "\n") return ele_vertexes - elif ele_st == 'Compound': - FreeCAD.Console.PrintError('Compound is not supported.\n') + elif ele_st == "Compound": + FreeCAD.Console.PrintError("Compound is not supported.\n") # ************************************************************************************************ @@ -1979,8 +1979,8 @@ def is_same_geometry( # it is a hack, but I do not know any better ! # check of Volume and Area before starting with the vertices could be added # BoundBox is possible too, but is BB calculations robust?! - # FreeCAD.Console.PrintMessage('{}\n'.format(shape1)) - # FreeCAD.Console.PrintMessage('{}\n'.format(shape2)) + # FreeCAD.Console.PrintMessage("{}\n".format(shape1)) + # FreeCAD.Console.PrintMessage("{}\n".format(shape2)) same_Vertexes = 0 if len(shape1.Vertexes) == len(shape2.Vertexes) and len(shape1.Vertexes) > 1: # compare CenterOfMass @@ -1993,7 +1993,7 @@ def is_same_geometry( if vs1.X == vs2.X and vs1.Y == vs2.Y and vs1.Z == vs2.Z: same_Vertexes += 1 continue - # FreeCAD.Console.PrintMessage('{}\n'.(same_Vertexes)) + # FreeCAD.Console.PrintMessage("{}\n".(same_Vertexes)) if same_Vertexes == len(shape1.Vertexes): return True else: @@ -2014,11 +2014,11 @@ def get_element( part, element ): - if element.startswith('Solid'): - index = int(element.lstrip('Solid')) - 1 + if element.startswith("Solid"): + index = int(element.lstrip("Solid")) - 1 if index >= len(part.Shape.Solids): FreeCAD.Console.PrintError( - 'Index out of range. This Solid does not exist in the Shape!\n' + "Index out of range. This Solid does not exist in the Shape!\n" ) return None else: @@ -2033,18 +2033,18 @@ def femelements_count_ok( count_femelements ): FreeCAD.Console.PrintMessage( - 'Count finite elements as sum of constraints: {}\n' + "Count finite elements as sum of constraints: {}\n" .format(count_femelements) ) FreeCAD.Console.PrintMessage( - 'Count finite elements of the finite element mesh: {}\n' + "Count finite elements of the finite element mesh: {}\n" .format(len_femelement_table) ) if count_femelements == len_femelement_table: return True else: FreeCAD.Console.PrintMessage( - 'ERROR: femelement_table != count_femelements\n' + "ERROR: femelement_table != count_femelements\n" ) return False @@ -2144,8 +2144,8 @@ def get_three_non_colinear_nodes( # Code to obtain three non-colinear nodes on the PlaneRotation support face # nodes_coords --> [(nodenumber, x, y, z), (nodenumber, x, y, z), ...] if not nodes_coords: - FreeCAD.Console.PrintMessage('{}\n'.format(len(nodes_coords))) - FreeCAD.Console.PrintMessage('Error: No nodes in nodes_coords\n') + FreeCAD.Console.PrintMessage("{}\n".format(len(nodes_coords))) + FreeCAD.Console.PrintMessage("Error: No nodes in nodes_coords\n") return [] dum_max = [1, 2, 3, 4, 5, 6, 7, 8, 0] for i in range(len(nodes_coords)): @@ -2180,7 +2180,7 @@ def get_three_non_colinear_nodes( node_dis = [node_3, tot] node_1 = int(dum_max[0]) node_2 = int(dum_max[4]) - FreeCAD.Console.PrintMessage('{}\n'.format([node_1, node_2, node_3])) + FreeCAD.Console.PrintMessage("{}\n".format([node_1, node_2, node_3])) return [node_1, node_2, node_3] @@ -2217,9 +2217,9 @@ def get_rectangular_coords( b_y = B[1] * cos(z_rot) - B[0] * sin(z_rot) A = [a_x, a_y, a_z] B = [b_x, b_y, b_z] - A_coords = str(round(A[0], 4)) + ',' + str(round(A[1], 4)) + ',' + str(round(A[2], 4)) - B_coords = str(round(B[0], 4)) + ',' + str(round(B[1], 4)) + ',' + str(round(B[2], 4)) - coords = A_coords + ',' + B_coords + A_coords = str(round(A[0], 4)) + "," + str(round(A[1], 4)) + "," + str(round(A[2], 4)) + B_coords = str(round(B[0], 4)) + "," + str(round(B[1], 4)) + "," + str(round(B[2], 4)) + coords = A_coords + "," + B_coords return coords @@ -2237,9 +2237,9 @@ def get_cylindrical_coords( Bz = base[2] - 10 * vec[2] A = [Ax, Ay, Az] B = [Bx, By, Bz] - A_coords = str(A[0]) + ',' + str(A[1]) + ',' + str(A[2]) - B_coords = str(B[0]) + ',' + str(B[1]) + ',' + str(B[2]) - coords = A_coords + ',' + B_coords + A_coords = str(A[0]) + "," + str(A[1]) + "," + str(A[2]) + B_coords = str(B[0]) + "," + str(B[1]) + "," + str(B[2]) + coords = A_coords + "," + B_coords return coords @@ -2248,7 +2248,7 @@ def write_D_network_element_to_inputfile( fileName ): # replace B32 elements with D elements for fluid section - f = open(fileName, 'r+') + f = open(fileName, "r+") lines = f.readlines() f.seek(0) for line in lines: @@ -2267,7 +2267,7 @@ def use_correct_fluidinout_ele_def( fileName, fluid_inout_nodes_file ): - f = open(fileName, 'r') + f = open(fileName, "r") cnt = 0 line = f.readline() @@ -2280,7 +2280,7 @@ def use_correct_fluidinout_ele_def( # obtain element line numbers for inlet and outlet while (len(line) > 1): - ind = line.find(',') + ind = line.find(",") elem = line[0:ind] for i in range(len(FluidInletoutlet_ele)): if (elem == FluidInletoutlet_ele[i][0]): @@ -2290,30 +2290,30 @@ def use_correct_fluidinout_ele_def( f.close() # re-define elements for INLET and OUTLET - f = open(fileName, 'r+') + f = open(fileName, "r+") lines = f.readlines() f.seek(0) cnt = 0 elem_counter = 0 FreeCAD.Console.PrintMessage( - '1DFlow inout nodes file: {}\n' + "1DFlow inout nodes file: {}\n" .format(fluid_inout_nodes_file) ) inout_nodes_file = open(fluid_inout_nodes_file, "w") for line in lines: - new_line = '' + new_line = "" for i in range(len(FluidInletoutlet_ele)): if (cnt == FluidInletoutlet_ele[i][2]): elem_counter = elem_counter + 1 - a = line.split(',') + a = line.split(",") for j in range(len(a)): if elem_counter == 1: if j == 1: - new_line = new_line + ' 0,' + new_line = new_line + " 0," node1 = int(a[j + 2]) node2 = int(a[j + 1]) node3 = int(a[j]) - inout_nodes_file.write('{},{},{},{}\n'.format( + inout_nodes_file.write("{},{},{},{}\n".format( node1, node2, node3, @@ -2322,22 +2322,22 @@ def use_correct_fluidinout_ele_def( elif j == 3: new_line = new_line + a[j] else: - new_line = new_line + a[j] + ',' + new_line = new_line + a[j] + "," else: if j == 3: - new_line = new_line + ' 0\n' + new_line = new_line + " 0\n" node1 = int(a[j - 2]) node2 = int(a[j - 1]) node3 = int(a[j]) - inout_nodes_file.write('{},{},{},{}\n'.format( + inout_nodes_file.write("{},{},{},{}\n".format( node1, node2, node3, FluidInletoutlet_ele[i][1] )) else: - new_line = new_line + a[j] + ',' - if new_line == '': + new_line = new_line + a[j] + "," + if new_line == "": f.write(line) else: f.write(new_line) @@ -2351,10 +2351,10 @@ def use_correct_fluidinout_ele_def( def compact_mesh( old_femmesh ): - ''' + """ removes all gaps in node and element ids, start ids with 1 returns a tuple (FemMesh, node_assignment_map, element_assignment_map) - ''' + """ node_map = {} # {old_node_id: new_node_id, ...} elem_map = {} # {old_elem_id: new_elem_id, ...} old_nodes = old_femmesh.Nodes diff --git a/src/Mod/Path/App/AppPath.cpp b/src/Mod/Path/App/AppPath.cpp index 026968c503..2e93cd169b 100644 --- a/src/Mod/Path/App/AppPath.cpp +++ b/src/Mod/Path/App/AppPath.cpp @@ -34,6 +34,7 @@ #include "CommandPy.h" #include "Path.h" #include "PathPy.h" +#include "Tool.h" #include "Tooltable.h" #include "ToolPy.h" #include "TooltablePy.h" diff --git a/src/Mod/Path/App/CMakeLists.txt b/src/Mod/Path/App/CMakeLists.txt index 2ef5835eac..32fc5b19ae 100644 --- a/src/Mod/Path/App/CMakeLists.txt +++ b/src/Mod/Path/App/CMakeLists.txt @@ -43,6 +43,7 @@ SET(Python_SRCS PathPy.xml PathPyImp.cpp ToolPy.xml + ToolPyImp.cpp TooltablePy.xml TooltablePyImp.cpp FeaturePathCompoundPy.xml @@ -65,6 +66,8 @@ SET(Path_SRCS Command.h Path.cpp Path.h + Tool.cpp + Tool.h Tooltable.cpp Tooltable.h PropertyPath.cpp diff --git a/src/Mod/Path/App/PropertyTool.h b/src/Mod/Path/App/PropertyTool.h index 85e1756fe9..706e4fae6b 100644 --- a/src/Mod/Path/App/PropertyTool.h +++ b/src/Mod/Path/App/PropertyTool.h @@ -24,7 +24,7 @@ #ifndef PROPERTYTOOL_H #define PROPERTYTOOL_H -#include "Tooltable.h" +#include "Tool.h" #include namespace Path diff --git a/src/Mod/Path/App/Tool.cpp b/src/Mod/Path/App/Tool.cpp new file mode 100644 index 0000000000..288626911e --- /dev/null +++ b/src/Mod/Path/App/Tool.cpp @@ -0,0 +1,258 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#ifndef _PreComp_ + +#endif +#include +#include +#include +#include "Tool.h" + +using namespace Base; +using namespace Path; + +TYPESYSTEM_SOURCE(Path::Tool , Base::Persistence); + +// Constructors & destructors + +Tool::Tool(const char* name, + ToolType type, + ToolMaterial /*material*/, + double diameter, + double lengthoffset, + double flatradius, + double cornerradius, + double cuttingedgeangle, + double cuttingedgeheight) +:Name(name),Type(type),Material(MATUNDEFINED),Diameter(diameter),LengthOffset(lengthoffset), +FlatRadius(flatradius),CornerRadius(cornerradius),CuttingEdgeAngle(cuttingedgeangle), +CuttingEdgeHeight(cuttingedgeheight) +{ +} + +Tool::Tool() +{ + Type = UNDEFINED; + Material = MATUNDEFINED; + Diameter = 0; + LengthOffset = 0; + FlatRadius = 0; + CornerRadius = 0; + CuttingEdgeAngle = 180; + CuttingEdgeHeight = 0; +} + +Tool::~Tool() +{ +} + +// Reimplemented from base class +unsigned int Tool::getMemSize (void) const +{ + return 0; +} + +void Tool::Save (Writer &writer) const +{ + writer.Stream() << writer.ind() << "" << std::endl; +} + +void Tool::Restore(XMLReader &reader) +{ + reader.readElement("Tool"); + Name = reader.getAttribute("name"); + Diameter = reader.hasAttribute("diameter") ? (double) reader.getAttributeAsFloat("diameter") : 0.0; + LengthOffset = reader.hasAttribute("length") ? (double) reader.getAttributeAsFloat("length") : 0.0; + FlatRadius = reader.hasAttribute("flat") ? (double) reader.getAttributeAsFloat("flat") : 0.0; + CornerRadius = reader.hasAttribute("corner") ? (double) reader.getAttributeAsFloat("corner") : 0.0; + CuttingEdgeAngle = reader.hasAttribute("angle") ? (double) reader.getAttributeAsFloat("angle") : 180.0; + CuttingEdgeHeight = reader.hasAttribute("height") ? (double) reader.getAttributeAsFloat("height") : 0.0; + std::string type = reader.hasAttribute("type") ? reader.getAttribute("type") : ""; + std::string mat = reader.hasAttribute("mat") ? reader.getAttribute("mat") : ""; + + Type = getToolType(type); + Material = getToolMaterial(mat); + + +} + +const std::vector Tool::ToolTypes(void) +{ + std::vector toolTypes(13); + toolTypes[0] ="EndMill"; + toolTypes[1] ="Drill"; + toolTypes[2] ="CenterDrill"; + toolTypes[3] ="CounterSink"; + toolTypes[4] ="CounterBore"; + toolTypes[5] ="FlyCutter"; + toolTypes[6] ="Reamer"; + toolTypes[7] ="Tap"; + toolTypes[8] ="SlotCutter"; + toolTypes[9] ="BallEndMill"; + toolTypes[10] ="ChamferMill"; + toolTypes[11] ="CornerRound"; + toolTypes[12] ="Engraver"; + return toolTypes; + +} + +const std::vector Tool::ToolMaterials(void) +{ + std::vector toolMat(7); + toolMat[0] ="Carbide"; + toolMat[1] ="HighSpeedSteel"; + toolMat[2] ="HighCarbonToolSteel"; + toolMat[3] ="CastAlloy"; + toolMat[4] ="Ceramics"; + toolMat[5] ="Diamond"; + toolMat[6] ="Sialon"; + return toolMat; + +} + +Tool::ToolType Tool::getToolType(std::string type) +{ + Tool::ToolType Type; + if(type=="EndMill") + Type = Tool::ENDMILL; + else if(type=="Drill") + Type = Tool::DRILL; + else if(type=="CenterDrill") + Type = Tool::CENTERDRILL; + else if(type=="CounterSink") + Type = Tool::COUNTERSINK; + else if(type=="CounterBore") + Type = Tool::COUNTERBORE; + else if(type=="FlyCutter") + Type = Tool::FLYCUTTER; + else if(type=="Reamer") + Type = Tool::REAMER; + else if(type=="Tap") + Type = Tool::TAP; + else if(type=="SlotCutter") + Type = Tool::SLOTCUTTER; + else if(type=="BallEndMill") + Type = Tool::BALLENDMILL; + else if(type=="ChamferMill") + Type = Tool::CHAMFERMILL; + else if(type=="CornerRound") + Type = Tool::CORNERROUND; + else if(type=="Engraver") + Type = Tool::ENGRAVER; + else + Type = Tool::UNDEFINED; + + return Type; +} + +Tool::ToolMaterial Tool::getToolMaterial(std::string mat) +{ + Tool::ToolMaterial Material; + if(mat=="Carbide") + Material = Tool::CARBIDE; + else if(mat=="HighSpeedSteel") + Material = Tool::HIGHSPEEDSTEEL; + else if(mat=="HighCarbonToolSteel") + Material = Tool::HIGHCARBONTOOLSTEEL; + else if(mat=="CastAlloy") + Material = Tool::CASTALLOY; + else if(mat=="Ceramics") + Material = Tool::CERAMICS; + else if(mat=="Diamond") + Material = Tool::DIAMOND; + else if(mat=="Sialon") + Material = Tool::SIALON; + else + Material = Tool::MATUNDEFINED; + + return Material; +} + +const char* Tool::TypeName(Tool::ToolType typ) { + switch (typ) { + case Tool::DRILL: + return "Drill"; + case Tool::CENTERDRILL: + return "CenterDrill"; + case Tool::COUNTERSINK: + return "CounterSink"; + case Tool::COUNTERBORE: + return "CounterBore"; + case Tool::FLYCUTTER: + return "FlyCutter"; + case Tool::REAMER: + return "Reamer"; + case Tool::TAP: + return "Tap"; + case Tool::ENDMILL: + return "EndMill"; + case Tool::SLOTCUTTER: + return "SlotCutter"; + case Tool::BALLENDMILL: + return "BallEndMill"; + case Tool::CHAMFERMILL: + return "ChamferMill"; + case Tool::CORNERROUND: + return "CornerRound"; + case Tool::ENGRAVER: + return "Engraver"; + case Tool::UNDEFINED: + return "Undefined"; + } + return "Undefined"; +} + +const char* Tool::MaterialName(Tool::ToolMaterial mat) +{ + switch (mat) { + case Tool::HIGHSPEEDSTEEL: + return "HighSpeedSteel"; + case Tool::CARBIDE: + return "Carbide"; + case Tool::HIGHCARBONTOOLSTEEL: + return "HighCarbonToolSteel"; + case Tool::CASTALLOY: + return "CastAlloy"; + case Tool::CERAMICS: + return "Ceramics"; + case Tool::DIAMOND: + return "Diamond"; + case Tool::SIALON: + return "Sialon"; + case Tool::MATUNDEFINED: + return "Undefined"; + } + return "Undefined"; +} \ No newline at end of file diff --git a/src/Mod/Path/App/Tool.h b/src/Mod/Path/App/Tool.h new file mode 100644 index 0000000000..c8a5690fc3 --- /dev/null +++ b/src/Mod/Path/App/Tool.h @@ -0,0 +1,104 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef PATH_TOOL_H +#define PATH_TOOL_H + +#include +#include +#include +#include + +namespace Path +{ + + /** The representation of a single tool */ + class PathExport Tool : public Base::Persistence + { + TYPESYSTEM_HEADER(); + + public: + enum ToolType { + UNDEFINED, + DRILL, + CENTERDRILL, + COUNTERSINK, + COUNTERBORE, + FLYCUTTER, + REAMER, + TAP, + ENDMILL, + SLOTCUTTER, + BALLENDMILL, + CHAMFERMILL, + CORNERROUND, + ENGRAVER }; + + enum ToolMaterial { + MATUNDEFINED, + HIGHSPEEDSTEEL, + HIGHCARBONTOOLSTEEL, + CASTALLOY, + CARBIDE, + CERAMICS, + DIAMOND, + SIALON }; + + //constructors + Tool(); + Tool(const char* name, + ToolType type=Tool::UNDEFINED, + ToolMaterial material=Tool::MATUNDEFINED, + double diameter=10.0, + double lengthoffset=100, + double flatradius=0, + double cornerradius=0, + double cuttingedgeangle=0, + double cuttingedgeheight=0); + ~Tool(); + + // from base class + virtual unsigned int getMemSize (void) const; + virtual void Save (Base::Writer &/*writer*/) const; + virtual void Restore(Base::XMLReader &/*reader*/); + + // attributes + std::string Name; + ToolType Type; + ToolMaterial Material; + double Diameter; + double LengthOffset; + double FlatRadius; + double CornerRadius; + double CuttingEdgeAngle; + double CuttingEdgeHeight; + + static const std::vector ToolTypes(void); + static const std::vector ToolMaterials(void); + static const char* TypeName(ToolType typ); + static ToolType getToolType(std::string type); + static ToolMaterial getToolMaterial(std::string mat); + static const char* MaterialName(ToolMaterial mat); + }; +} //namespace Path + +#endif // PATH_TOOL_H diff --git a/src/Mod/Path/App/ToolPy.xml b/src/Mod/Path/App/ToolPy.xml index 0f515c8491..94e56525d3 100644 --- a/src/Mod/Path/App/ToolPy.xml +++ b/src/Mod/Path/App/ToolPy.xml @@ -5,7 +5,7 @@ Name="ToolPy" Twin="Tool" TwinPointer="Tool" - Include="Mod/Path/App/Tooltable.h" + Include="Mod/Path/App/Tool.h" Namespace="Path" FatherInclude="Base/PersistencePy.h" FatherNamespace="Base" diff --git a/src/Mod/Path/App/ToolPyImp.cpp b/src/Mod/Path/App/ToolPyImp.cpp new file mode 100644 index 0000000000..c075d2c786 --- /dev/null +++ b/src/Mod/Path/App/ToolPyImp.cpp @@ -0,0 +1,302 @@ +/*************************************************************************** + * Copyright (c) Yorik van Havre (yorik@uncreated.net) 2014 * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#include "Base/Reader.h" +#include "Mod/Path/App/Tool.h" +#include "Mod/Path/App/Tooltable.h" + +// inclusion of the generated files (generated out of ToolPy.xml and TooltablePy.xml) +#include "ToolPy.h" +#include "ToolPy.cpp" + +using namespace Path; + +#if PY_MAJOR_VERSION >= 3 +# define PYSTRING_FROMSTRING(str) PyUnicode_FromString(str) +# define PYINT_TYPE PyLong_Type +# define PYINT_FROMLONG(l) PyLong_FromLong(l) +# define PYINT_ASLONG(o) PyLong_AsLong(o) +#else +# define PYSTRING_FROMSTRING(str) PyString_FromString(str) +# define PYINT_TYPE PyInt_Type +# define PYINT_FROMLONG(l) PyInt_FromLong(l) +# define PYINT_ASLONG(o) PyInt_AsLong(o) +#endif + + +// returns a string which represents the object e.g. when printed in python +std::string ToolPy::representation(void) const +{ + std::stringstream str; + str.precision(5); + str << "Tool "; + str << getToolPtr()->Name; + return str.str(); +} + +PyObject *ToolPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper +{ + // create a new instance of ToolPy and the Twin object + return new ToolPy(new Tool); +} + +// constructor method +int ToolPy::PyInit(PyObject* args, PyObject* kwd) +{ + char *name="Default tool"; + char *type = "Undefined"; + char *mat = "Undefined"; + PyObject *dia = 0; + PyObject *len = 0; + PyObject *fla = 0; + PyObject *cor = 0; + PyObject *ang = 0; + PyObject *hei = 0; + int version = 1; + + static char *kwlist[] = {"name", "tooltype", "material", "diameter", "lengthOffset", "flatRadius", "cornerRadius", "cuttingEdgeAngle", "cuttingEdgeHeight" , "version", NULL}; + + PyObject *dict = 0; + if (!kwd && (PyObject_TypeCheck(args, &PyDict_Type) || PyArg_ParseTuple(args, "O!", &PyDict_Type, &dict))) { + static PyObject *arg = PyTuple_New(0); + if (PyObject_TypeCheck(args, &PyDict_Type)) { + dict = args; + } + if (!PyArg_ParseTupleAndKeywords(arg, dict, "|sssOOOOOOi", kwlist, &name, &type, &mat, &dia, &len, &fla, &cor, &ang, &hei, &version)) { + return -1; + } + } else { + PyErr_Clear(); + if (!PyArg_ParseTupleAndKeywords(args, kwd, "|sssOOOOOO", kwlist, &name, &type, &mat, &dia, &len, &fla, &cor, &ang, &hei)) { + return -1; + } + } + + if (1 != version) { + PyErr_SetString(PyExc_TypeError, "Unsupported Tool template version"); + return -1; + } + + getToolPtr()->Name = name; + + std::string typeStr(type); + getToolPtr()->Type = Tool::getToolType(typeStr); + + std::string matStr(mat); + getToolPtr()->Material = Tool::getToolMaterial(matStr); + + getToolPtr()->Diameter = dia ? PyFloat_AsDouble(dia) : 0.0; + getToolPtr()->LengthOffset = len ? PyFloat_AsDouble(len) : 0.0; + getToolPtr()->FlatRadius = fla ? PyFloat_AsDouble(fla) : 0.0; + getToolPtr()->CornerRadius = cor ? PyFloat_AsDouble(cor) : 0.0; + getToolPtr()->CuttingEdgeAngle = ang ? PyFloat_AsDouble(ang) : 180.0; + getToolPtr()->CuttingEdgeHeight = hei ? PyFloat_AsDouble(hei) : 0.0; + + return 0; +} + +// attributes get/setters + +Py::String ToolPy::getName(void) const +{ + return Py::String(getToolPtr()->Name.c_str()); +} + +void ToolPy::setName(Py::String arg) +{ + std::string name = arg.as_std_string(); + getToolPtr()->Name = name; +} + +Py::String ToolPy::getToolType(void) const +{ + return Py::String(Tool::TypeName(getToolPtr()->Type)); +} + +void ToolPy::setToolType(Py::String arg) +{ + std::string typeStr(arg.as_std_string()); + getToolPtr()->Type = Tool::getToolType(typeStr); + +} + +Py::String ToolPy::getMaterial(void) const +{ + return Py::String(Tool::MaterialName(getToolPtr()->Material)); +} + +void ToolPy::setMaterial(Py::String arg) +{ + std::string matStr(arg.as_std_string()); + getToolPtr()->Material = Tool::getToolMaterial(matStr); +} + +Py::Float ToolPy::getDiameter(void) const +{ + return Py::Float(getToolPtr()->Diameter); +} + +void ToolPy::setDiameter(Py::Float arg) +{ + getToolPtr()->Diameter = arg.operator double(); +} + +Py::Float ToolPy::getLengthOffset(void) const +{ + return Py::Float(getToolPtr()->LengthOffset); +} + +void ToolPy::setLengthOffset(Py::Float arg) +{ + getToolPtr()->LengthOffset = arg.operator double(); +} + +Py::Float ToolPy::getFlatRadius(void) const +{ + return Py::Float(getToolPtr()->FlatRadius); +} + +void ToolPy::setFlatRadius(Py::Float arg) +{ + getToolPtr()->FlatRadius = arg.operator double(); +} + +Py::Float ToolPy::getCornerRadius(void) const +{ + return Py::Float(getToolPtr()->CornerRadius); +} + +void ToolPy::setCornerRadius(Py::Float arg) +{ + getToolPtr()->CornerRadius = arg.operator double(); +} + +Py::Float ToolPy::getCuttingEdgeAngle(void) const +{ + return Py::Float(getToolPtr()->CuttingEdgeAngle); +} + +void ToolPy::setCuttingEdgeAngle(Py::Float arg) +{ + getToolPtr()->CuttingEdgeAngle = arg.operator double(); +} + +Py::Float ToolPy::getCuttingEdgeHeight(void) const +{ + return Py::Float(getToolPtr()->CuttingEdgeHeight); +} + +void ToolPy::setCuttingEdgeHeight(Py::Float arg) +{ + getToolPtr()->CuttingEdgeHeight = arg.operator double(); +} + +// custom attributes get/set + +PyObject *ToolPy::getCustomAttributes(const char* /*attr*/) const +{ + return 0; +} + +int ToolPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} + +PyObject* ToolPy::copy(PyObject * args) +{ + if (PyArg_ParseTuple(args, "")) { + return new ToolPy(new Path::Tool(*getToolPtr())); + } + throw Py::TypeError("This method accepts no argument"); +} + +PyObject* ToolPy::setFromTemplate(PyObject * args) +{ + char *pstr = 0; + if (PyArg_ParseTuple(args, "s", &pstr)) { + // embed actual string in dummy tag so XMLReader can consume that on construction + std::ostringstream os; + os << "" << pstr << ""; + std::istringstream is(os.str()); + Base::XMLReader reader("", is); + getToolPtr()->Restore(reader); + Py_Return ; + } + + PyErr_Clear(); + if (!PyInit(args, 0)) { + Py_Return ; + } + + PyErr_SetString(PyExc_TypeError, "argument must be a string or dictionary"); + return 0; +} + +PyObject* ToolPy::templateAttrs(PyObject * args) +{ + if (!args || PyArg_ParseTuple(args, "")) { + PyObject *dict = PyDict_New(); + PyDict_SetItemString(dict, "version", PYINT_FROMLONG(1)); + PyDict_SetItemString(dict, "name", PYSTRING_FROMSTRING(getToolPtr()->Name.c_str())); + PyDict_SetItemString(dict, "tooltype",PYSTRING_FROMSTRING(Tool::TypeName(getToolPtr()->Type))); + PyDict_SetItemString(dict, "material", PYSTRING_FROMSTRING(Tool::MaterialName(getToolPtr()->Material))); + PyDict_SetItemString(dict, "diameter", PyFloat_FromDouble(getToolPtr()->Diameter)); + PyDict_SetItemString(dict, "lengthOffset", PyFloat_FromDouble(getToolPtr()->LengthOffset)); + PyDict_SetItemString(dict, "flatRadius", PyFloat_FromDouble(getToolPtr()->FlatRadius)); + PyDict_SetItemString(dict, "cornerRadius", PyFloat_FromDouble(getToolPtr()->CornerRadius)); + PyDict_SetItemString(dict, "cuttingEdgeAngle", PyFloat_FromDouble(getToolPtr()->CuttingEdgeAngle)); + PyDict_SetItemString(dict, "cuttingEdgeHeight", PyFloat_FromDouble(getToolPtr()->CuttingEdgeHeight)); + return dict; + } + throw Py::TypeError("This method accepts no argument"); +} + +PyObject* ToolPy::getToolTypes(PyObject * args) +{ + if (PyArg_ParseTuple(args, "")) { + std::vector toolTypes = Tool::ToolTypes(); + PyObject *list = PyList_New(0); + for(unsigned i = 0; i != toolTypes.size(); i++) { + + PyList_Append(list, PYSTRING_FROMSTRING(toolTypes[i].c_str())); + } + return list; + } + throw Py::TypeError("This method accepts no argument"); +} + +PyObject* ToolPy::getToolMaterials(PyObject * args) +{ + if (PyArg_ParseTuple(args, "")) { + std::vector toolMaterials = Tool::ToolMaterials(); + PyObject *list = PyList_New(0); + for(unsigned i = 0; i != toolMaterials.size(); i++) { + + PyList_Append(list, PYSTRING_FROMSTRING(toolMaterials[i].c_str())); + } + return list; + } + throw Py::TypeError("This method accepts no argument"); +} \ No newline at end of file diff --git a/src/Mod/Path/App/Tooltable.cpp b/src/Mod/Path/App/Tooltable.cpp index 85cfc89cd7..a9351899e9 100644 --- a/src/Mod/Path/App/Tooltable.cpp +++ b/src/Mod/Path/App/Tooltable.cpp @@ -34,239 +34,6 @@ using namespace Base; using namespace Path; - -// TOOL - - -TYPESYSTEM_SOURCE(Path::Tool , Base::Persistence); - -// Constructors & destructors - -Tool::Tool(const char* name, - ToolType type, - ToolMaterial /*material*/, - double diameter, - double lengthoffset, - double flatradius, - double cornerradius, - double cuttingedgeangle, - double cuttingedgeheight) -:Name(name),Type(type),Material(MATUNDEFINED),Diameter(diameter),LengthOffset(lengthoffset), -FlatRadius(flatradius),CornerRadius(cornerradius),CuttingEdgeAngle(cuttingedgeangle), -CuttingEdgeHeight(cuttingedgeheight) -{ -} - -Tool::Tool() -{ - Type = UNDEFINED; - Material = MATUNDEFINED; - Diameter = 0; - LengthOffset = 0; - FlatRadius = 0; - CornerRadius = 0; - CuttingEdgeAngle = 180; - CuttingEdgeHeight = 0; -} - -Tool::~Tool() -{ -} - -// Reimplemented from base class - -unsigned int Tool::getMemSize (void) const -{ - return 0; -} - -void Tool::Save (Writer &writer) const -{ - writer.Stream() << writer.ind() << "" << std::endl; -} - -void Tool::Restore(XMLReader &reader) -{ - reader.readElement("Tool"); - Name = reader.getAttribute("name"); - Diameter = reader.hasAttribute("diameter") ? (double) reader.getAttributeAsFloat("diameter") : 0.0; - LengthOffset = reader.hasAttribute("length") ? (double) reader.getAttributeAsFloat("length") : 0.0; - FlatRadius = reader.hasAttribute("flat") ? (double) reader.getAttributeAsFloat("flat") : 0.0; - CornerRadius = reader.hasAttribute("corner") ? (double) reader.getAttributeAsFloat("corner") : 0.0; - CuttingEdgeAngle = reader.hasAttribute("angle") ? (double) reader.getAttributeAsFloat("angle") : 180.0; - CuttingEdgeHeight = reader.hasAttribute("height") ? (double) reader.getAttributeAsFloat("height") : 0.0; - std::string type = reader.hasAttribute("type") ? reader.getAttribute("type") : ""; - std::string mat = reader.hasAttribute("mat") ? reader.getAttribute("mat") : ""; - - Type = getToolType(type); - Material = getToolMaterial(mat); - - -} - -const std::vector Tool::ToolTypes(void) -{ - std::vector toolTypes(13); - toolTypes[0] ="EndMill"; - toolTypes[1] ="Drill"; - toolTypes[2] ="CenterDrill"; - toolTypes[3] ="CounterSink"; - toolTypes[4] ="CounterBore"; - toolTypes[5] ="FlyCutter"; - toolTypes[6] ="Reamer"; - toolTypes[7] ="Tap"; - toolTypes[8] ="SlotCutter"; - toolTypes[9] ="BallEndMill"; - toolTypes[10] ="ChamferMill"; - toolTypes[11] ="CornerRound"; - toolTypes[12] ="Engraver"; - return toolTypes; - -} - -const std::vector Tool::ToolMaterials(void) -{ - std::vector toolMat(7); - toolMat[0] ="Carbide"; - toolMat[1] ="HighSpeedSteel"; - toolMat[2] ="HighCarbonToolSteel"; - toolMat[3] ="CastAlloy"; - toolMat[4] ="Ceramics"; - toolMat[5] ="Diamond"; - toolMat[6] ="Sialon"; - return toolMat; - -} - -Tool::ToolType Tool::getToolType(std::string type) -{ - Tool::ToolType Type; - if(type=="EndMill") - Type = Tool::ENDMILL; - else if(type=="Drill") - Type = Tool::DRILL; - else if(type=="CenterDrill") - Type = Tool::CENTERDRILL; - else if(type=="CounterSink") - Type = Tool::COUNTERSINK; - else if(type=="CounterBore") - Type = Tool::COUNTERBORE; - else if(type=="FlyCutter") - Type = Tool::FLYCUTTER; - else if(type=="Reamer") - Type = Tool::REAMER; - else if(type=="Tap") - Type = Tool::TAP; - else if(type=="SlotCutter") - Type = Tool::SLOTCUTTER; - else if(type=="BallEndMill") - Type = Tool::BALLENDMILL; - else if(type=="ChamferMill") - Type = Tool::CHAMFERMILL; - else if(type=="CornerRound") - Type = Tool::CORNERROUND; - else if(type=="Engraver") - Type = Tool::ENGRAVER; - else - Type = Tool::UNDEFINED; - - return Type; -} - -Tool::ToolMaterial Tool::getToolMaterial(std::string mat) -{ - Tool::ToolMaterial Material; - if(mat=="Carbide") - Material = Tool::CARBIDE; - else if(mat=="HighSpeedSteel") - Material = Tool::HIGHSPEEDSTEEL; - else if(mat=="HighCarbonToolSteel") - Material = Tool::HIGHCARBONTOOLSTEEL; - else if(mat=="CastAlloy") - Material = Tool::CASTALLOY; - else if(mat=="Ceramics") - Material = Tool::CERAMICS; - else if(mat=="Diamond") - Material = Tool::DIAMOND; - else if(mat=="Sialon") - Material = Tool::SIALON; - else - Material = Tool::MATUNDEFINED; - - return Material; -} - -const char* Tool::TypeName(Tool::ToolType typ) { - switch (typ) { - case Tool::DRILL: - return "Drill"; - case Tool::CENTERDRILL: - return "CenterDrill"; - case Tool::COUNTERSINK: - return "CounterSink"; - case Tool::COUNTERBORE: - return "CounterBore"; - case Tool::FLYCUTTER: - return "FlyCutter"; - case Tool::REAMER: - return "Reamer"; - case Tool::TAP: - return "Tap"; - case Tool::ENDMILL: - return "EndMill"; - case Tool::SLOTCUTTER: - return "SlotCutter"; - case Tool::BALLENDMILL: - return "BallEndMill"; - case Tool::CHAMFERMILL: - return "ChamferMill"; - case Tool::CORNERROUND: - return "CornerRound"; - case Tool::ENGRAVER: - return "Engraver"; - case Tool::UNDEFINED: - return "Undefined"; - } - return "Undefined"; -} - -const char* Tool::MaterialName(Tool::ToolMaterial mat) -{ - switch (mat) { - case Tool::HIGHSPEEDSTEEL: - return "HighSpeedSteel"; - case Tool::CARBIDE: - return "Carbide"; - case Tool::HIGHCARBONTOOLSTEEL: - return "HighCarbonToolSteel"; - case Tool::CASTALLOY: - return "CastAlloy"; - case Tool::CERAMICS: - return "Ceramics"; - case Tool::DIAMOND: - return "Diamond"; - case Tool::SIALON: - return "Sialon"; - case Tool::MATUNDEFINED: - return "Undefined"; - } - return "Undefined"; -} - -// TOOLTABLE - - - TYPESYSTEM_SOURCE(Path::Tooltable , Base::Persistence); Tooltable::Tooltable() diff --git a/src/Mod/Path/App/Tooltable.h b/src/Mod/Path/App/Tooltable.h index 450ea826b2..873b943afd 100644 --- a/src/Mod/Path/App/Tooltable.h +++ b/src/Mod/Path/App/Tooltable.h @@ -28,80 +28,10 @@ #include #include #include +#include "Tool.h" namespace Path -{ - - /** The representation of a single tool */ - class PathExport Tool : public Base::Persistence - { - TYPESYSTEM_HEADER(); - - public: - enum ToolType { - UNDEFINED, - DRILL, - CENTERDRILL, - COUNTERSINK, - COUNTERBORE, - FLYCUTTER, - REAMER, - TAP, - ENDMILL, - SLOTCUTTER, - BALLENDMILL, - CHAMFERMILL, - CORNERROUND, - ENGRAVER }; - - enum ToolMaterial { - MATUNDEFINED, - HIGHSPEEDSTEEL, - HIGHCARBONTOOLSTEEL, - CASTALLOY, - CARBIDE, - CERAMICS, - DIAMOND, - SIALON }; - - //constructors - Tool(); - Tool(const char* name, - ToolType type=Tool::UNDEFINED, - ToolMaterial material=Tool::MATUNDEFINED, - double diameter=10.0, - double lengthoffset=100, - double flatradius=0, - double cornerradius=0, - double cuttingedgeangle=0, - double cuttingedgeheight=0); - ~Tool(); - - // from base class - virtual unsigned int getMemSize (void) const; - virtual void Save (Base::Writer &/*writer*/) const; - virtual void Restore(Base::XMLReader &/*reader*/); - - // attributes - std::string Name; - ToolType Type; - ToolMaterial Material; - double Diameter; - double LengthOffset; - double FlatRadius; - double CornerRadius; - double CuttingEdgeAngle; - double CuttingEdgeHeight; - - static const std::vector ToolTypes(void); - static const std::vector ToolMaterials(void); - static const char* TypeName(ToolType typ); - static ToolType getToolType(std::string type); - static ToolMaterial getToolMaterial(std::string mat); - static const char* MaterialName(ToolMaterial mat); - }; - - /** The representation of a table of tools */ +{ /** The representation of a table of tools */ class PathExport Tooltable : public Base::Persistence { TYPESYSTEM_HEADER(); @@ -129,6 +59,8 @@ namespace Path // attributes std::map Tools; + int Version; + std::string Name; }; } //namespace Path diff --git a/src/Mod/Path/App/TooltablePy.xml b/src/Mod/Path/App/TooltablePy.xml index 39230d35cb..0480a83b03 100644 --- a/src/Mod/Path/App/TooltablePy.xml +++ b/src/Mod/Path/App/TooltablePy.xml @@ -15,6 +15,18 @@ The Tooltable object holds a table of CNC tools + + + the name of this tool table + + + + + + the version of this tooltable + + + the dictionary of tools of this table diff --git a/src/Mod/Path/App/TooltablePyImp.cpp b/src/Mod/Path/App/TooltablePyImp.cpp index 756ef0a23a..bb03bd8649 100644 --- a/src/Mod/Path/App/TooltablePyImp.cpp +++ b/src/Mod/Path/App/TooltablePyImp.cpp @@ -23,231 +23,16 @@ #include "PreCompiled.h" #include "Base/Reader.h" +#include "Mod/Path/App/Tool.h" #include "Mod/Path/App/Tooltable.h" // inclusion of the generated files (generated out of ToolPy.xml and TooltablePy.xml) #include "ToolPy.h" -#include "ToolPy.cpp" #include "TooltablePy.h" #include "TooltablePy.cpp" using namespace Path; - - -// ToolPy - - - -// returns a string which represents the object e.g. when printed in python -std::string ToolPy::representation(void) const -{ - std::stringstream str; - str.precision(5); - str << "Tool "; - str << getToolPtr()->Name; - return str.str(); -} - -PyObject *ToolPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper -{ - // create a new instance of ToolPy and the Twin object - return new ToolPy(new Tool); -} - -// constructor method -int ToolPy::PyInit(PyObject* args, PyObject* kwd) -{ - char *name="Default tool"; - char *type = "Undefined"; - char *mat = "Undefined"; - PyObject *dia = 0; - PyObject *len = 0; - PyObject *fla = 0; - PyObject *cor = 0; - PyObject *ang = 0; - PyObject *hei = 0; - int version = 1; - - static char *kwlist[] = {"name", "tooltype", "material", "diameter", "lengthOffset", "flatRadius", "cornerRadius", "cuttingEdgeAngle", "cuttingEdgeHeight" , "version", NULL}; - - PyObject *dict = 0; - if (!kwd && (PyObject_TypeCheck(args, &PyDict_Type) || PyArg_ParseTuple(args, "O!", &PyDict_Type, &dict))) { - static PyObject *arg = PyTuple_New(0); - if (PyObject_TypeCheck(args, &PyDict_Type)) { - dict = args; - } - if (!PyArg_ParseTupleAndKeywords(arg, dict, "|sssOOOOOOi", kwlist, &name, &type, &mat, &dia, &len, &fla, &cor, &ang, &hei, &version)) { - return -1; - } - } else { - PyErr_Clear(); - if (!PyArg_ParseTupleAndKeywords(args, kwd, "|sssOOOOOO", kwlist, &name, &type, &mat, &dia, &len, &fla, &cor, &ang, &hei)) { - return -1; - } - } - - if (1 != version) { - PyErr_SetString(PyExc_TypeError, "Unsupported Tool template version"); - return -1; - } - - getToolPtr()->Name = name; - - std::string typeStr(type); - getToolPtr()->Type = Tool::getToolType(typeStr); - - std::string matStr(mat); - getToolPtr()->Material = Tool::getToolMaterial(matStr); - - getToolPtr()->Diameter = dia ? PyFloat_AsDouble(dia) : 0.0; - getToolPtr()->LengthOffset = len ? PyFloat_AsDouble(len) : 0.0; - getToolPtr()->FlatRadius = fla ? PyFloat_AsDouble(fla) : 0.0; - getToolPtr()->CornerRadius = cor ? PyFloat_AsDouble(cor) : 0.0; - getToolPtr()->CuttingEdgeAngle = ang ? PyFloat_AsDouble(ang) : 180.0; - getToolPtr()->CuttingEdgeHeight = hei ? PyFloat_AsDouble(hei) : 0.0; - - return 0; -} - -// attributes get/setters - -Py::String ToolPy::getName(void) const -{ - return Py::String(getToolPtr()->Name.c_str()); -} - -void ToolPy::setName(Py::String arg) -{ - std::string name = arg.as_std_string(); - getToolPtr()->Name = name; -} - -Py::String ToolPy::getToolType(void) const -{ - return Py::String(Tool::TypeName(getToolPtr()->Type)); -} - -void ToolPy::setToolType(Py::String arg) -{ - std::string typeStr(arg.as_std_string()); - getToolPtr()->Type = Tool::getToolType(typeStr); - -} - -Py::String ToolPy::getMaterial(void) const -{ - return Py::String(Tool::MaterialName(getToolPtr()->Material)); -} - -void ToolPy::setMaterial(Py::String arg) -{ - std::string matStr(arg.as_std_string()); - getToolPtr()->Material = Tool::getToolMaterial(matStr); -} - -Py::Float ToolPy::getDiameter(void) const -{ - return Py::Float(getToolPtr()->Diameter); -} - -void ToolPy::setDiameter(Py::Float arg) -{ - getToolPtr()->Diameter = arg.operator double(); -} - -Py::Float ToolPy::getLengthOffset(void) const -{ - return Py::Float(getToolPtr()->LengthOffset); -} - -void ToolPy::setLengthOffset(Py::Float arg) -{ - getToolPtr()->LengthOffset = arg.operator double(); -} - -Py::Float ToolPy::getFlatRadius(void) const -{ - return Py::Float(getToolPtr()->FlatRadius); -} - -void ToolPy::setFlatRadius(Py::Float arg) -{ - getToolPtr()->FlatRadius = arg.operator double(); -} - -Py::Float ToolPy::getCornerRadius(void) const -{ - return Py::Float(getToolPtr()->CornerRadius); -} - -void ToolPy::setCornerRadius(Py::Float arg) -{ - getToolPtr()->CornerRadius = arg.operator double(); -} - -Py::Float ToolPy::getCuttingEdgeAngle(void) const -{ - return Py::Float(getToolPtr()->CuttingEdgeAngle); -} - -void ToolPy::setCuttingEdgeAngle(Py::Float arg) -{ - getToolPtr()->CuttingEdgeAngle = arg.operator double(); -} - -Py::Float ToolPy::getCuttingEdgeHeight(void) const -{ - return Py::Float(getToolPtr()->CuttingEdgeHeight); -} - -void ToolPy::setCuttingEdgeHeight(Py::Float arg) -{ - getToolPtr()->CuttingEdgeHeight = arg.operator double(); -} - -// custom attributes get/set - -PyObject *ToolPy::getCustomAttributes(const char* /*attr*/) const -{ - return 0; -} - -int ToolPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) -{ - return 0; -} - -PyObject* ToolPy::copy(PyObject * args) -{ - if (PyArg_ParseTuple(args, "")) { - return new ToolPy(new Path::Tool(*getToolPtr())); - } - throw Py::TypeError("This method accepts no argument"); -} - -PyObject* ToolPy::setFromTemplate(PyObject * args) -{ - char *pstr = 0; - if (PyArg_ParseTuple(args, "s", &pstr)) { - // embed actual string in dummy tag so XMLReader can consume that on construction - std::ostringstream os; - os << "" << pstr << ""; - std::istringstream is(os.str()); - Base::XMLReader reader("", is); - getToolPtr()->Restore(reader); - Py_Return ; - } - - PyErr_Clear(); - if (!PyInit(args, 0)) { - Py_Return ; - } - - PyErr_SetString(PyExc_TypeError, "argument must be a string or dictionary"); - return 0; -} - #if PY_MAJOR_VERSION >= 3 # define PYSTRING_FROMSTRING(str) PyUnicode_FromString(str) # define PYINT_TYPE PyLong_Type @@ -260,59 +45,6 @@ PyObject* ToolPy::setFromTemplate(PyObject * args) # define PYINT_ASLONG(o) PyInt_AsLong(o) #endif -PyObject* ToolPy::templateAttrs(PyObject * args) -{ - if (!args || PyArg_ParseTuple(args, "")) { - PyObject *dict = PyDict_New(); - PyDict_SetItemString(dict, "version", PYINT_FROMLONG(1)); - PyDict_SetItemString(dict, "name", PYSTRING_FROMSTRING(getToolPtr()->Name.c_str())); - PyDict_SetItemString(dict, "tooltype",PYSTRING_FROMSTRING(Tool::TypeName(getToolPtr()->Type))); - PyDict_SetItemString(dict, "material", PYSTRING_FROMSTRING(Tool::MaterialName(getToolPtr()->Material))); - PyDict_SetItemString(dict, "diameter", PyFloat_FromDouble(getToolPtr()->Diameter)); - PyDict_SetItemString(dict, "lengthOffset", PyFloat_FromDouble(getToolPtr()->LengthOffset)); - PyDict_SetItemString(dict, "flatRadius", PyFloat_FromDouble(getToolPtr()->FlatRadius)); - PyDict_SetItemString(dict, "cornerRadius", PyFloat_FromDouble(getToolPtr()->CornerRadius)); - PyDict_SetItemString(dict, "cuttingEdgeAngle", PyFloat_FromDouble(getToolPtr()->CuttingEdgeAngle)); - PyDict_SetItemString(dict, "cuttingEdgeHeight", PyFloat_FromDouble(getToolPtr()->CuttingEdgeHeight)); - return dict; - } - throw Py::TypeError("This method accepts no argument"); -} - -PyObject* ToolPy::getToolTypes(PyObject * args) -{ - if (PyArg_ParseTuple(args, "")) { - std::vector toolTypes = Tool::ToolTypes(); - PyObject *list = PyList_New(0); - for(unsigned i = 0; i != toolTypes.size(); i++) { - - PyList_Append(list, PYSTRING_FROMSTRING(toolTypes[i].c_str())); - } - return list; - } - throw Py::TypeError("This method accepts no argument"); -} - -PyObject* ToolPy::getToolMaterials(PyObject * args) -{ - if (PyArg_ParseTuple(args, "")) { - std::vector toolMaterials = Tool::ToolMaterials(); - PyObject *list = PyList_New(0); - for(unsigned i = 0; i != toolMaterials.size(); i++) { - - PyList_Append(list, PYSTRING_FROMSTRING(toolMaterials[i].c_str())); - } - return list; - } - throw Py::TypeError("This method accepts no argument"); -} - - -// TooltablePy - - - - // returns a string which represents the object e.g. when printed in python std::string TooltablePy::representation(void) const { @@ -331,6 +63,9 @@ PyObject *TooltablePy::PyMake(struct _typeobject *, PyObject *, PyObject *) // // constructor method int TooltablePy::PyInit(PyObject* args, PyObject* /*kwd*/) { + char *name="Tooltable"; + int version = 1; + if (PyArg_ParseTuple(args, "")) { return 0; } @@ -502,6 +237,25 @@ int TooltablePy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) return 0; } +Py::Int TooltablePy::getVersion(void) const +{ + return Py::Int(getTooltablePtr()->Version); +} + +void TooltablePy::setVersion(Py::Int version) { + getTooltablePtr()->Version = version; +} + +Py::String TooltablePy::getName(void) const +{ + return Py::String(getTooltablePtr()->Name.c_str()); +} + +void TooltablePy::setName(Py::String arg) +{ + std::string name = arg.as_std_string(); + getTooltablePtr()->Name = name; +} PyObject* TooltablePy::setFromTemplate(PyObject * args) { @@ -531,3 +285,4 @@ PyObject* TooltablePy::templateAttrs(PyObject * args) } return dict; } + diff --git a/src/Mod/Path/Gui/Resources/panels/ToolLibraryEditor.ui b/src/Mod/Path/Gui/Resources/panels/ToolLibraryEditor.ui index 01657469c4..ed3cdf6bd5 100644 --- a/src/Mod/Path/Gui/Resources/panels/ToolLibraryEditor.ui +++ b/src/Mod/Path/Gui/Resources/panels/ToolLibraryEditor.ui @@ -19,8 +19,8 @@ Tool Library - - + + @@ -38,32 +38,184 @@ - - - - true - - - QAbstractItemView::DragOnly - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - + + + + + + + 225 + 16777215 + + + + + 0 + + + 0 + + + 6 + + + 0 + + + + + + + Tool Tables + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + + + + 32 + 32 + + + + + + + + :/icons/FreeCAD-default/scalable/edit-edit.svg:/icons/FreeCAD-default/scalable/edit-edit.svg + + + + + + + + 32 + 32 + + + + + + + + :/icons/FreeCAD-default/scalable/list-remove.svg + + + + + + + + + 32 + 32 + + + + + + + + :/icons/FreeCAD-default/scalable/list-add.svg + + + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + 0 + + + + true + + + + 20 + 20 + + + + + + + + + + + false + + + false + + + QAbstractItemView::NoDragDrop + + + Qt::IgnoreAction + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + true + + + false + + + false + + + + - + @@ -140,7 +292,7 @@ - + Qt::Horizontal diff --git a/src/Mod/Path/PathScripts/PathToolLibraryManager.py b/src/Mod/Path/PathScripts/PathToolLibraryManager.py index 9580f60d25..a34f267244 100644 --- a/src/Mod/Path/PathScripts/PathToolLibraryManager.py +++ b/src/Mod/Path/PathScripts/PathToolLibraryManager.py @@ -145,6 +145,8 @@ class ToolLibraryManager(): preferences and all or part of the library can be exported to other formats ''' + #TODO: copy & Duplicate tools between lists + TooltableTypeJSON = translate("TooltableEditor", "Tooltable JSON (*.json)") TooltableTypeXML = translate("TooltableEditor", "Tooltable XML (*.xml)") TooltableTypeHeekscad = translate("TooltableEditor", "HeeksCAD tooltable (*.tooltable)") @@ -155,75 +157,183 @@ class ToolLibraryManager(): def __init__(self): self.prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Path") + self.toolTables = [] + self.currentTableName = None + self.loadToolTables() + + if len(self.toolTables): + self.currentTableName = self.toolTables[0].Name + return - def templateAttrs(self, tooltable): - attrs = {} - attrs['Version'] = 1 - attrs['Tools'] = tooltable.templateAttrs() - return attrs + def getToolTables(self): + ''' Return tool table list ''' + return self.toolTables + + def getCurrentTableName(self): + ''' return the name of the currently loaded tool table ''' + return self.currentTableName + + def getCurrentTable(self): + ''' returns an object of the current tool table ''' + return self.getTableFromName(self.currentTableName) + + def getTableFromName(self, name): + ''' get the tool table object from the name ''' + for table in self.toolTables: + if table.Name == name: + return table + + def getNextToolTableName(self, tableName='Tool Table'): + ''' get a unique name for a new tool table ''' + iter = 1 + tempName = tableName[-2:] + + if tempName[0] == '-' and tempName[-1].isdigit(): + tableName = tableName[:-2] + + while any(table.Name == tableName + '-' + str(iter) for table in self.toolTables): + iter += 1 + + return tableName + '-' + str(iter) + + def addNewToolTable(self): + ''' creates a new tool table ''' + tt = Path.Tooltable() + tt.Version = 1 + name = self.getNextToolTableName() + tt.Name = name + self.toolTables.append(tt) + self.saveMainLibrary() + return name + + def deleteToolTable(self): + ''' deletes the selected tool table ''' + index = next((index for (index, d) in enumerate(self.toolTables) if d.Name == self.currentTableName), None) + self.toolTables.pop(index) + self.saveMainLibrary() + + def renameToolTable(self, newName, index): + ''' renames a tool table with the new name''' + currentTableName = self.toolTables[index].Name + if newName == currentTableName: + PathLog.error(translate('PathToolLibraryManager', "Tool Table Same Name")) + return False + if newName in self.toolTables: + PathLog.error(translate('PathToolLibraryManager', "Tool Table Name Exists")) + return False + tt = self.getTableFromName(currentTableName) + if tt: + tt.Name = newName + self.saveMainLibrary() + return True + + + def templateAttrs(self): + ''' gets the tool table arributes ''' + toolTables = [] + for tt in self.toolTables: + tableData = {} + tableData['Version'] = 1 + tableData['TableName'] = tt.Name + + toolData = {} + for tool in tt.Tools: + toolData[tool] = tt.Tools[tool].templateAttrs() + + tableData['Tools'] = toolData + toolTables.append(tableData) + + return toolTables def tooltableFromAttrs(self, stringattrs): if stringattrs.get('Version') and 1 == int(stringattrs['Version']): - attrs = {} - for key, val in PathUtil.keyValueIter(stringattrs['Tools']): - attrs[int(key)] = val - return Path.Tooltable(attrs) + + tt = Path.Tooltable() + tt.Version = 1 + tt.Name = self.getNextToolTableName() + + if stringattrs.get('Version'): + tt.Version = stringattrs.get('Version') + + if stringattrs.get('TableName'): + tt.Name = stringattrs.get('TableName') + if any(table.Name == tt.Name for table in self.toolTables): + tt.Name = self.getNextToolTableName(tt.Name) + + for key, attrs in PathUtil.keyValueIter(stringattrs['Tools']): + tool = Path.Tool() + tool.Name = str(attrs["name"]) + tool.ToolType = str(attrs["tooltype"]) + tool.Material = str(attrs["material"]) + tool.Diameter = float(attrs["diameter"]) + tool.LengthOffset = float(attrs["lengthOffset"]) + tool.FlatRadius = float(attrs["flatRadius"]) + tool.CornerRadius = float(attrs["cornerRadius"]) + tool.CuttingEdgeAngle = float(attrs["cuttingEdgeAngle"]) + tool.CuttingEdgeHeight = float(attrs["cuttingEdgeHeight"]) + tt.setTool(int(key), tool) + + return tt else: PathLog.error(translate('PathToolLibraryManager', "Unsupported Path tooltable template version %s") % stringattrs.get('Version')) return None - def saveMainLibrary(self, tooltable): + def loadToolTables(self): + ''' loads the tool tables from the stored data ''' + self.toolTables = [] + self.currentTableName = '' + + def addTable(tt): + if tt: + self.toolTables.append(tt) + else: + PathLog.error(translate('PathToolLibraryManager', "Unsupported Path tooltable")) + + prefsData = json.loads(self.prefs.GetString(self.PreferenceMainLibraryJSON, "")) + + if isinstance(prefsData, dict): + tt = self.tooltableFromAttrs(prefsData) + addTable(tt) + + if isinstance(prefsData, list): + for table in prefsData: + tt = self.tooltableFromAttrs(table) + addTable(tt) + + def saveMainLibrary(self): '''Persists the permanent library to FreeCAD user preferences''' - tmpstring = json.dumps(self.templateAttrs(tooltable)) + tmpstring = json.dumps(self.templateAttrs()) self.prefs.SetString(self.PreferenceMainLibraryJSON, tmpstring) + self.loadToolTables() return True - def getLists(self): + def getJobList(self): '''Builds the list of all Tool Table lists''' tablelist = [] - toollist = "
" - tablelist.append(toollist) - # Get ToolTables from any open CNC jobs for o in FreeCAD.ActiveDocument.Objects: if hasattr(o, "Proxy"): if isinstance(o.Proxy, PathScripts.PathJob.ObjectJob): tablelist.append(o.Label) + return tablelist - def _findList(self, listname): - tt = None - if listname == "
": - tmpstring = self.prefs.GetString(self.PreferenceMainLibraryJSON, "") - if not tmpstring: - tmpstring = self.prefs.GetString(self.PreferenceMainLibraryXML, "") - if tmpstring: - if tmpstring[0] == '{': - tt = self.tooltableFromAttrs(json.loads(tmpstring)) - elif tmpstring[0] == '<': - # legacy XML table - Handler = FreeCADTooltableHandler() - xml.sax.parseString(tmpstring, Handler) - tt = Handler.tooltable - # store new format - self.saveMainLibrary(tt) - else: - tt = Path.Tooltable() - else: - for o in FreeCAD.ActiveDocument.Objects: - if o.Label == listname: - tt = o.Tooltable - return tt - def getTool(self, listname, toolnum): - tt = self._findList(listname) + ''' gets the tool object ''' + tt = self.getTableFromName(listname) return tt.getTool(toolnum) def getTools(self, tablename): '''returns the tool data for a given table''' tooldata = [] - tt = self._findList(tablename) + tableExists = any(table.Name == tablename for table in self.toolTables) + if tableExists: + self.currentTableName = tablename + else: + return None + + tt = self.getTableFromName(tablename) headers = ["","Tool Num.","Name","Tool Type","Material","Diameter","Length Offset","Flat Radius","Corner Radius","Cutting Edge Angle","Cutting Edge Height"] model = QtGui.QStandardItemModel() model.setHorizontalHeaderLabels(headers) @@ -247,11 +357,11 @@ class ToolLibraryManager(): itemDiameter = QtGui.QStandardItem(unitconv(t.Diameter)) itemLengthOffset = QtGui.QStandardItem(unitconv(t.LengthOffset)) itemFlatRadius = QtGui.QStandardItem(unitconv(t.FlatRadius)) - itmCornerRadius = QtGui.QStandardItem(unitconv(t.CornerRadius)) + itemCornerRadius = QtGui.QStandardItem(unitconv(t.CornerRadius)) itemCuttingEdgeAngle = QtGui.QStandardItem(str(t.CuttingEdgeAngle)) itemCuttingEdgeHeight = QtGui.QStandardItem(unitconv(t.CuttingEdgeHeight)) - row = [itemcheck, itemNumber, itemName, itemToolType, itemMaterial, itemDiameter, itemLengthOffset, itemFlatRadius, itmCornerRadius, itemCuttingEdgeAngle, itemCuttingEdgeHeight] + row = [itemcheck, itemNumber, itemName, itemToolType, itemMaterial, itemDiameter, itemLengthOffset, itemFlatRadius, itemCornerRadius, itemCuttingEdgeAngle, itemCuttingEdgeHeight] model.appendRow(row) return model @@ -260,6 +370,8 @@ class ToolLibraryManager(): def read(self, filename, listname): "imports a tooltable from a file" + importedTables = [] + try: fileExtension = os.path.splitext(filename[0])[1].lower() xmlHandler = None @@ -279,22 +391,35 @@ class ToolLibraryManager(): ht = xmlHandler.tooltable else: with open(PathUtil.toUnicode(filename[0]), "rb") as fp: - ht = self.tooltableFromAttrs(json.load(fp)) + tableData = json.load(fp) + + if isinstance(tableData, dict): + ht = self.tooltableFromAttrs(tableData) + if ht: + importedTables.append(ht) + + if isinstance(tableData, list): + for table in tableData: + ht = self.tooltableFromAttrs(table) + if ht: + importedTables.append(ht) + + if importedTables: + for tt in importedTables: + self.toolTables.append(tt) + + self.saveMainLibrary() + return True + else: + return False - tt = self._findList(listname) - for t in ht.Tools: - newt = ht.getTool(t).copy() - tt.addTools(newt) - if listname == "
": - self.saveMainLibrary(tt) - return True except Exception as e: # pylint: disable=broad-except print("could not parse file", e) def write(self, filename, listname): "exports the tooltable to a file" - tt = self._findList(listname) + tt = self.getTableFromName(listname) if tt: try: def openFileWithExtension(name, ext): @@ -314,7 +439,7 @@ class ToolLibraryManager(): fp.write("T{0} P{0} Y{1} Z{2} A{3} B{4} C{5} U{6} V{7} W{8} D{9} I{10} J{11} Q{12} ;{13}\n".format(key,0,t.LengthOffset,0,0,0,0,0,0,t.Diameter,0,0,0,t.Name)) else: fp,fname = openFileWithExtension(filename[0], '.json') - json.dump(self.templateAttrs(tt), fp, sort_keys=True, indent=2) + json.dump(self.templateAttrs(), fp, sort_keys=True, indent=2) fp.close() print("Written ", PathUtil.toUnicode(fname)) @@ -324,8 +449,7 @@ class ToolLibraryManager(): def addnew(self, listname, tool, position = None): "adds a new tool at the end of the table" - print(listname, tool, position) - tt = self._findList(listname) + tt = self.getTableFromName(listname) if not tt: tt = Path.Tooltable() if position is None: @@ -335,26 +459,26 @@ class ToolLibraryManager(): tt.setTool(position, tool) newID = position - if listname == "
": - return self.saveMainLibrary(tt) + if listname == self.getCurrentTableName(): + self.saveMainLibrary() return newID def updateTool(self, listname, toolnum, tool): '''updates tool data''' - tt = self._findList(listname) + tt = self.getTableFromName(listname) tt.deleteTool(toolnum) tt.setTool(toolnum, tool) - if listname == "
": - return self.saveMainLibrary(tt) + if listname == self.getCurrentTableName(): + return self.saveMainLibrary() return True def moveup(self, number, listname): "moves a tool to a lower number, if possible" - if number < 2: - return False target = number - 1 - tt = self._findList(listname) - + if number < 2: + return False, target + target = number - 1 + tt = self.getTableFromName(listname) t1 = tt.getTool(number).copy() tt.deleteTool(number) if target in tt.Tools.keys(): @@ -362,13 +486,13 @@ class ToolLibraryManager(): tt.deleteTool(target) tt.setTool(number, t2) tt.setTool(target, t1) - if listname == "
": - self.saveMainLibrary(tt) - return True + if listname == self.getCurrentTableName(): + self.saveMainLibrary() + return True, target def movedown(self, number, listname): "moves a tool to a higher number, if possible" - tt = self._findList(listname) + tt = self.getTableFromName(listname) target = number + 1 t1 = tt.getTool(number).copy() tt.deleteTool(number) @@ -377,16 +501,16 @@ class ToolLibraryManager(): tt.deleteTool(target) tt.setTool(number, t2) tt.setTool(target, t1) - if listname == "
": - self.saveMainLibrary(tt) - return True + if listname == self.getCurrentTableName(): + self.saveMainLibrary() + return True, target def delete(self, number, listname): '''deletes a tool from the current list''' - tt = self._findList(listname) + tt = self.getTableFromName(listname) tt.deleteTool(number) - if listname == "
": - self.saveMainLibrary(tt) + if listname == self.getCurrentTableName(): + self.saveMainLibrary() return True @@ -395,9 +519,11 @@ class EditorPanel(): def __init__(self, job, cb): self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolLibraryEditor.ui") self.TLM = ToolLibraryManager() + listname = self.TLM.getCurrentTableName() + + if listname: + self.loadToolTables() - self.loadTable() - self.form.ToolsList.resizeColumnsToContents() self.job = job self.cb = cb @@ -417,6 +543,12 @@ class EditorPanel(): def getFields(self): pass + def setFields(self): + pass + + def open(self): + pass + def getType(self, tooltype): "gets a combobox index number for a given type or viceversa" toolslist = Path.Tool.getToolTypes(Path.Tool()) @@ -429,7 +561,7 @@ class EditorPanel(): return toolslist[tooltype] def getMaterial(self, material): - "gets a combobox index number for a given material or viceversa" + '''gets a combobox index number for a given material or viceversa''' matslist = Path.Tool.getToolMaterials(Path.Tool()) if isinstance(material, str): if material in matslist: @@ -440,65 +572,34 @@ class EditorPanel(): return matslist[material] def addTool(self): + '''adds new tool to the current tool table''' tool = Path.Tool() editor = self.toolEditor(tool) r = editor.Parent.exec_() if r: editor.accept() - listname = "
" - if self.TLM.addnew(listname, editor.Tool) is True: - self.loadTable() - - def setFields(self): - pass - - def open(self): - pass - - def loadTable(self): - #tooldata = self.TLM.getTools(curr.data()) - tooldata = self.TLM.getTools("
") - self.form.ToolsList.setModel(tooldata) - - def moveUp(self): - "moves a tool to a lower number, if possible" - item = self.form.ToolsList.selectedIndexes()[1].data() - if item: - number = int(item) - listname = "
" - #listname = self.form.listView.selectedIndexes()[0].data() - if self.TLM.moveup(number, listname) is True: - self.loadTable() - - def moveDown(self): - "moves a tool to a higher number, if possible" - item = self.form.ToolsList.selectedIndexes()[1].data() - if item: - number = int(item) - listname = "
" - #listname = self.form.listView.selectedIndexes()[0].data() - if self.TLM.movedown(number, listname) is True: - self.loadTable() + listname = self.TLM.getCurrentTableName() + self.TLM.addnew(listname, editor.Tool) # is True: + self.loadTable(listname) def delete(self): - '''deletes a tool''' + '''deletes the selected tool''' #listname = self.form.listView.selectedIndexes()[0].data() - listname = "
" + listname = self.TLM.getCurrentTableName() model = self.form.ToolsList.model() for i in range(model.rowCount()): item = model.item(i, 0) if item.checkState(): t = model.index(i, 1) self.TLM.delete(int(t.data()) ,listname) - self.loadTable() + self.loadTable(listname) def editTool(self, currItem): - + '''load the tool edit dialog''' row = currItem.row() value = currItem.sibling(row, 1).data() - #listname = self.form.listView.selectedIndexes()[0].data() - listname = "
" + listname = self.TLM.getCurrentTableName() toolnum = int(value) tool = self.TLM.getTool(listname, toolnum) editor = self.toolEditor(tool) @@ -507,38 +608,89 @@ class EditorPanel(): if r: editor.accept() if self.TLM.updateTool(listname, toolnum, editor.Tool) is True: - self.loadTable() + self.loadTable(listname) + + def moveUp(self): + '''moves a tool to a lower number, if possible''' + item = self.form.ToolsList.selectedIndexes()[1].data() + if item: + number = int(item) + listname = self.TLM.getCurrentTableName() + success, newNum = self.TLM.moveup(number, listname) + if success: + self.loadTable(listname) + self.updateSelection(newNum) + + def moveDown(self): + '''moves a tool to a higher number, if possible''' + item = self.form.ToolsList.selectedIndexes()[1].data() + if item: + number = int(item) + listname = self.TLM.getCurrentTableName() + success, newNum = self.TLM.movedown(number, listname) + if success: + self.loadTable(listname) + self.updateSelection(newNum) + + def updateSelection(self, number): + '''update the tool list selection to track moves''' + model = self.form.ToolsList.model() + for i in range(model.rowCount()): + if int(model.index(i, 1).data()) == number: + self.form.ToolsList.selectRow(i) + self.form.ToolsList.model().item(i, 0).setCheckState(QtCore.Qt.Checked) + return + def importFile(self): - "imports a tooltable from a file" + '''imports a tooltable from a file''' filename = QtGui.QFileDialog.getOpenFileName(self.form, translate( "TooltableEditor", "Open tooltable", None), None, "{};;{};;{}".format(ToolLibraryManager.TooltableTypeJSON, ToolLibraryManager.TooltableTypeXML, ToolLibraryManager.TooltableTypeHeekscad)) if filename[0]: - listname = '
' + listname = self.TLM.getNextToolTableName() if self.TLM.read(filename, listname): - self.loadTable() + self.loadToolTables() + #self.loadTable(listname) def exportFile(self): - "export a tooltable to a file" + '''export a tooltable to a file''' filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("TooltableEditor", "Save tooltable", None), None, "{};;{};;{}".format(ToolLibraryManager.TooltableTypeJSON, ToolLibraryManager.TooltableTypeXML, ToolLibraryManager.TooltableTypeLinuxCNC)) - if filename[0]: - #listname = self.form.listView.selectedIndexes()[0].data() - listname = '
' + listname = self.TLM.getCurrentTableName() self.TLM.write(filename, listname) - def checkCopy(self): + def toolSelected(self, index): + ''' updates the ui when tools are selected''' + self.form.ToolsList.selectRow(index.row()) + self.form.btnCopyTools.setEnabled(False) + self.form.ButtonDelete.setEnabled(False) + self.form.ButtonUp.setEnabled(False) + self.form.ButtonDown.setEnabled(False) + model = self.form.ToolsList.model() + checkCount = 0 + checkList = [] for i in range(model.rowCount()): item = model.item(i, 0) if item.checkState(): + checkCount += 1 + checkList.append(i) self.form.btnCopyTools.setEnabled(True) + # only allow moving or deleting a single tool at a time. + if checkCount == 1: + #make sure the row is highlighted when the check box gets ticked + self.form.ToolsList.selectRow(checkList[0]) + self.form.ButtonDelete.setEnabled(True) + self.form.ButtonUp.setEnabled(True) + self.form.ButtonDown.setEnabled(True) + if len(PathUtils.GetJobs()) == 0: self.form.btnCopyTools.setEnabled(False) def copyTools(self): + ''' copy selected tool ''' tools = [] model = self.form.ToolsList.model() for i in range(model.rowCount()): @@ -549,15 +701,15 @@ class EditorPanel(): if len(tools) == 0: return - targets = self.TLM.getLists() - currList = "
" + targets = self.TLM.getJobList() + currList = self.TLM.getCurrentTableName() for target in targets: if target == currList: targets.remove(target) if len(targets) == 0: - FreeCAD.Console.PrintWarning("no place to go") + FreeCAD.Console.PrintWarning("No Path Jobs in current document") return elif len(targets) == 1: targetlist = targets[0] @@ -587,9 +739,84 @@ class EditorPanel(): self.cb() FreeCAD.ActiveDocument.recompute() + def tableSelected(self, index): + ''' loads the tools for the selected tool table ''' + name = self.form.TableList.itemFromIndex(index).text() + self.loadTable(name) + + def loadTable(self, name): + ''' loads the tools for the selected tool table ''' + tooldata = self.TLM.getTools(name) + if tooldata: + self.form.ToolsList.setModel(tooldata) + self.form.ToolsList.resizeColumnsToContents() + self.setCurrentToolTableByName(name) + + + def addNewToolTable(self): + ''' adds new tool to selected tool table ''' + name = self.TLM.addNewToolTable() + self.loadToolTables() + self.loadTable(name) + + def loadToolTables(self): + ''' Load list of available tool tables ''' + self.form.TableList.clear() + model = self.form.ToolsList.model() + if model: + model.clear() + if len(self.TLM.getToolTables()) > 0: + for table in self.TLM.getToolTables(): + listItem = QtGui.QListWidgetItem(table.Name) + listItem.setIcon(QtGui.QIcon(':/icons/Path-ToolTable.svg')) + listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsEditable) + listItem.setSizeHint(QtCore.QSize(0,40)) + self.form.TableList.addItem(listItem) + self.loadTable(self.TLM.getToolTables()[0].Name) + + def setCurrentToolTableByName(self, name): + ''' get the current tool table ''' + item = self.form.TableList.findItems(name, QtCore.Qt.MatchExactly)[0] + self.form.TableList.setCurrentItem(item) + + def removeToolTable(self): + ''' delete the selected tool table ''' + self.TLM.deleteToolTable() + self.loadToolTables() + + def initTableRename(self): + ''' update the tool table list entry to allow renaming ''' + name = self.TLM.getCurrentTableName() + item = self.form.TableList.findItems(name, QtCore.Qt.MatchExactly)[0] + self.form.TableList.editItem(item) + + def renameTable(self, listItem): + ''' rename the selected too table ''' + newName = listItem.text() + index = self.form.TableList.indexFromItem(listItem).row() + reloadTables = self.TLM.renameToolTable(newName, index) + if reloadTables: + self.loadToolTables() + def getStandardButtons(self): return int(QtGui.QDialogButtonBox.Ok) + # def openMenu(self, position): + # menu = QtGui.QMenu() + # newAction = menu.addAction("New Tool Table") + # deleteAction = menu.addAction("Delete") + # renameAction = menu.addAction("Rename") + # action = menu.exec_(self.form.TableList.mapToGlobal(position)) + # if action == newAction: + # self.addNewToolTable() + # pass + # if action == deleteAction: + # self.removeToolTable() + # pass + # if action == renameAction: + # self.initTableRename() + # pass + def setupUi(self): # Connect Signals and Slots self.form.ButtonNewTool.clicked.connect(self.addTool) @@ -598,11 +825,29 @@ class EditorPanel(): self.form.ButtonDown.clicked.connect(self.moveDown) self.form.ButtonUp.clicked.connect(self.moveUp) self.form.ButtonDelete.clicked.connect(self.delete) + self.form.ToolsList.doubleClicked.connect(self.editTool) - self.form.ToolsList.clicked.connect(self.checkCopy) + self.form.ToolsList.clicked.connect(self.toolSelected) + self.form.btnCopyTools.clicked.connect(self.copyTools) + self.form.TableList.clicked.connect(self.tableSelected) + self.form.TableList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + #self.form.TableList.customContextMenuRequested.connect(self.openMenu) + self.form.TableList.itemChanged.connect(self.renameTable) + + self.form.ButtonAddToolTable.clicked.connect(self.addNewToolTable) + self.form.ButtonAddToolTable.setToolTip(translate("TooltableEditor","Add New Tool Table")) + self.form.ButtonRemoveToolTable.clicked.connect(self.removeToolTable) + self.form.ButtonRemoveToolTable.setToolTip(translate("TooltableEditor","Delete Selected Tool Table")) + self.form.ButtonRenameToolTable.clicked.connect(self.initTableRename) + self.form.ButtonRenameToolTable.setToolTip(translate("TooltableEditor","Rename Selected Tool Table")) + + self.form.btnCopyTools.setEnabled(False) + self.form.ButtonDelete.setEnabled(False) + self.form.ButtonUp.setEnabled(False) + self.form.ButtonDown.setEnabled(False) self.setFields() diff --git a/src/Mod/TechDraw/App/DrawViewDraft.cpp b/src/Mod/TechDraw/App/DrawViewDraft.cpp index bdb58a9381..5bdd98ba2a 100644 --- a/src/Mod/TechDraw/App/DrawViewDraft.cpp +++ b/src/Mod/TechDraw/App/DrawViewDraft.cpp @@ -53,12 +53,13 @@ DrawViewDraft::DrawViewDraft(void) ADD_PROPERTY_TYPE(Source ,(0),group,App::Prop_None,"Draft object for this view"); Source.setScope(App::LinkScope::Global); - ADD_PROPERTY_TYPE(LineWidth,(0.35),group,App::Prop_None,"Line width of this view"); + ADD_PROPERTY_TYPE(LineWidth,(0.35),group,App::Prop_None,"Line width of this view. If Override Style is false, this value multiplies the object line width"); ADD_PROPERTY_TYPE(FontSize,(12.0),group,App::Prop_None,"Text size for this view"); ADD_PROPERTY_TYPE(Direction ,(0,0,1.0),group,App::Prop_None,"Projection direction. The direction you are looking from."); ADD_PROPERTY_TYPE(Color,(0.0f,0.0f,0.0f),group,App::Prop_None,"The default color of text and lines"); ADD_PROPERTY_TYPE(LineStyle,("Solid") ,group,App::Prop_None,"A line style to use for this view. Can be Solid, Dashed, Dashdot, Dot or a SVG pattern like 0.20,0.20"); ADD_PROPERTY_TYPE(LineSpacing,(1.0f),group,App::Prop_None,"The spacing between lines to use for multiline texts"); + ADD_PROPERTY_TYPE(OverrideStyle,(false),group,App::Prop_None,"If True, line color, width and style of this view will override those of rendered objects"); ScaleType.setValue("Custom"); } @@ -76,7 +77,8 @@ short DrawViewDraft::mustExecute() const Direction.isTouched() || Color.isTouched() || LineStyle.isTouched() || - LineSpacing.isTouched(); + LineSpacing.isTouched() || + OverrideStyle.isTouched(); } if ((bool) result) { return result; @@ -113,7 +115,8 @@ App::DocumentObjectExecReturn *DrawViewDraft::execute(void) << ",color=\"" << col.asCSSString() << "\"" << ",linespacing=" << LineSpacing.getValue() // We must set techdraw to "true" becausea couple of things behave differently than in Drawing - << ",techdraw=True"; + << ",techdraw=True" + << ",override=" << (OverrideStyle.getValue() ? "True" : "False"); // this is ok for a starting point, but should eventually make dedicated Draft functions that build the svg for all the special cases // (Arch section, etc) diff --git a/src/Mod/TechDraw/App/DrawViewDraft.h b/src/Mod/TechDraw/App/DrawViewDraft.h index 411c4e0853..fb2967af65 100644 --- a/src/Mod/TechDraw/App/DrawViewDraft.h +++ b/src/Mod/TechDraw/App/DrawViewDraft.h @@ -50,6 +50,7 @@ public: App::PropertyColor Color; App::PropertyString LineStyle; App::PropertyFloat LineSpacing; + App::PropertyBool OverrideStyle; /** @name methods override Feature */ //@{