Merge branch 'master' into feature/coolant-management

This commit is contained in:
sliptonic
2019-08-29 09:46:32 -05:00
committed by GitHub
22 changed files with 2032 additions and 1346 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 = "<path "
if pathname is None:
svg += 'id="%s" ' % obj.Name
@@ -125,8 +142,14 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct
egroups = Part.sortEdges(edges)
else:
egroups = []
first = True
for w in wires:
w1=w.copy()
if first:
first = False
else:
# invert further wires to create holes
w1 = DraftGeomUtils.invert(w1)
w1.fixWire()
egroups.append(Part.__sortEdges__(w1.Edges))
for egroupindex, edges in enumerate(egroups):
@@ -368,7 +391,7 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct
svg += 'x2="'+ str(shootsize*-1) +'" y2="0" />\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("&","&amp;").replace("<","&lt;").replace(">","&gt;")
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 += '<text fill="' + color +'" font-size="' + str(fontsize) + '" '
svg += '<text stroke-width="0" stroke="' + tcolor + '" fill="' + tcolor +'" font-size="' + str(fontsize) + '" '
svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';'
svg += 'font-family:'+ fontname +'" '
svg += 'transform="rotate('+str(math.degrees(angle))
@@ -406,8 +429,8 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct
#svg += '" freecad:skip="1"'
svg += '>\n' + t + '</text>\n'
else:
svg = '<text fill="'
svg += color +'" font-size="'
svg = '<text stroke-width="0" stroke="' + tcolor + '" fill="'
svg += tcolor +'" font-size="'
svg += str(fontsize) + '" '
svg += 'style="text-anchor:'+anchor+';text-align:'+align.lower()+';'
svg += 'font-family:'+ fontname +'" '
@@ -426,9 +449,9 @@ def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direct
svg += '>\n'
if len(text) == 1:
try:
svg += text[0]
svg += text[0].replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")
except:
svg += text[0].decode("utf8")
svg += text[0].decode("utf8").replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")
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 += '<tspan x="0" dy="'+str(linespacing)+'">'
try:
svg += text[i]
svg += text[i].replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")
except:
svg += text[i].decode("utf8")
svg += text[i].decode("utf8").replace("&","&amp;").replace("<","&lt;").replace(">","&gt;")
svg += '</tspan>\n'
svg += '</text>\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 += '<tspan>' + obj.ViewObject.Proxy.bubbletexts[n].string.getValues()[0] + '</tspan>\n'
svg += '</text>\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 = '<g transform ="scale(1,-1)">'+svg+'</g>'
svg = '<g transform ="scale(1,-1)">\n '+svg+'</g>\n'
return svg

View File

@@ -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:

View File

@@ -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 :-)
'''
"""

File diff suppressed because it is too large Load Diff

View File

@@ -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"

View File

@@ -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

View File

@@ -24,7 +24,7 @@
#ifndef PROPERTYTOOL_H
#define PROPERTYTOOL_H
#include "Tooltable.h"
#include "Tool.h"
#include <App/Property.h>
namespace Path

258
src/Mod/Path/App/Tool.cpp Normal file
View File

@@ -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 <Base/Writer.h>
#include <Base/Reader.h>
#include <Base/Exception.h>
#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() << "<Tool "
<< "name=\"" << encodeAttribute(Name) << "\" "
<< "diameter=\"" << Diameter << "\" "
<< "length=\"" << LengthOffset << "\" "
<< "flat=\"" << FlatRadius << "\" "
<< "corner=\"" << CornerRadius << "\" "
<< "angle=\"" << CuttingEdgeAngle << "\" "
<< "height=\"" << CuttingEdgeHeight << "\" "
<< "type=\"" << TypeName(Type) << "\" "
<< "mat=\"" << MaterialName(Material) << "\" "
<< "/>" << 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<std::string> Tool::ToolTypes(void)
{
std::vector<std::string> 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<std::string> Tool::ToolMaterials(void)
{
std::vector<std::string> 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";
}

104
src/Mod/Path/App/Tool.h Normal file
View File

@@ -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 <vector>
#include <string>
#include <map>
#include <Base/Persistence.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<std::string> ToolTypes(void);
static const std::vector<std::string> 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

View File

@@ -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"

View File

@@ -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 << "<snippet>" << pstr << "</snippet>";
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<std::string> 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<std::string> 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");
}

View File

@@ -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() << "<Tool "
<< "name=\"" << encodeAttribute(Name) << "\" "
<< "diameter=\"" << Diameter << "\" "
<< "length=\"" << LengthOffset << "\" "
<< "flat=\"" << FlatRadius << "\" "
<< "corner=\"" << CornerRadius << "\" "
<< "angle=\"" << CuttingEdgeAngle << "\" "
<< "height=\"" << CuttingEdgeHeight << "\" "
<< "type=\"" << TypeName(Type) << "\" "
<< "mat=\"" << MaterialName(Material) << "\" "
<< "/>" << 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<std::string> Tool::ToolTypes(void)
{
std::vector<std::string> 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<std::string> Tool::ToolMaterials(void)
{
std::vector<std::string> 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()

View File

@@ -28,80 +28,10 @@
#include <string>
#include <map>
#include <Base/Persistence.h>
#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<std::string> ToolTypes(void);
static const std::vector<std::string> 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<int,Tool*> Tools;
int Version;
std::string Name;
};
} //namespace Path

View File

@@ -15,6 +15,18 @@
<Author Licence="LGPL" Name="Yorik van Havre" EMail="yorik@uncreated.net" />
<UserDocu>The Tooltable object holds a table of CNC tools</UserDocu>
</Documentation>
<Attribute Name="Name" ReadOnly="false">
<Documentation>
<UserDocu>the name of this tool table</UserDocu>
</Documentation>
<Parameter Name="Name" Type="String"/>
</Attribute>
<Attribute Name="Version" ReadOnly="false">
<Documentation>
<UserDocu>the version of this tooltable</UserDocu>
</Documentation>
<Parameter Name="Version" Type="Int"/>
</Attribute>
<Attribute Name="Tools" ReadOnly="false">
<Documentation>
<UserDocu>the dictionary of tools of this table</UserDocu>

View File

@@ -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 << "<snippet>" << pstr << "</snippet>";
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<std::string> 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<std::string> 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;
}

View File

@@ -19,8 +19,8 @@
<property name="windowTitle">
<string>Tool Library</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="ButtonImport">
@@ -38,32 +38,184 @@
</item>
</layout>
</item>
<item>
<widget class="QTableView" name="ToolsList">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragOnly</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QWidget" name="widget" native="true">
<property name="maximumSize">
<size>
<width>225</width>
<height>16777215</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Tool Tables</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="ButtonRenameToolTable">
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../../../Gui/Icons/resource.qrc">
<normaloff>:/icons/FreeCAD-default/scalable/edit-edit.svg</normaloff>:/icons/FreeCAD-default/scalable/edit-edit.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ButtonRemoveToolTable">
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normalon>:/icons/FreeCAD-default/scalable/list-remove.svg</normalon>
</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ButtonAddToolTable">
<property name="maximumSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normalon>:/icons/FreeCAD-default/scalable/list-add.svg</normalon>
</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="TableList">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTableView" name="ToolsList">
<property name="dragEnabled">
<bool>false</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::NoDragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::IgnoreAction</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="cornerButtonEnabled">
<bool>false</bool>
</property>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="verticalHeaderStretchLastSection">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btnCopyTools">
@@ -140,7 +292,7 @@
</item>
</layout>
</item>
<item>
<item row="3" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>

View File

@@ -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 = "<Main>"
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 == "<Main>":
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 == "<Main>":
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 == "<Main>":
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 == "<Main>":
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 == "<Main>":
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 == "<Main>":
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 == "<Main>":
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 = "<Main>"
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("<Main>")
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 = "<Main>"
#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 = "<Main>"
#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 = "<Main>"
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 = "<Main>"
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 = '<Main>'
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 = '<Main>'
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 = "<Main>"
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()

View File

@@ -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)

View File

@@ -50,6 +50,7 @@ public:
App::PropertyColor Color;
App::PropertyString LineStyle;
App::PropertyFloat LineSpacing;
App::PropertyBool OverrideStyle;
/** @name methods override Feature */
//@{