diff --git a/Lattice2CodeModules.py b/Lattice2CodeModules.py index 804eb2d..5b73d89 100644 --- a/Lattice2CodeModules.py +++ b/Lattice2CodeModules.py @@ -6,3 +6,4 @@ import lattice2GeomUtils as GeomUtils import lattice2InterpolatorUtil as InterpolatorUtil import lattice2Markers as Markers import lattice2ValueSeriesGenerator as ValueSeriesGenerator +import lattice2ShapeCopy as ShapeCopy \ No newline at end of file diff --git a/lattice2BaseFeature.py b/lattice2BaseFeature.py index df257eb..611bc48 100644 --- a/lattice2BaseFeature.py +++ b/lattice2BaseFeature.py @@ -32,6 +32,7 @@ from lattice2Common import * import lattice2CompoundExplorer as LCE import lattice2Markers import lattice2Executer +from lattice2ShapeCopy import shallowCopy def getDefLatticeFaceColor(): @@ -111,6 +112,21 @@ class LatticeFeature(): self.derivedInit(obj) obj.Proxy = self + + def assureProperty(self, selfobj, proptype, propname, defvalue, group, tooltip): + """assureProperty(selfobj, proptype, propname, defvalue, group, tooltip): adds + a property if one is missing, and sets its value to default. Does nothing if property + already exists. Returns True if property was created, or False if not.""" + + if hasattr(selfobj, propname): + #todo: check type match + return False + + selfobj.addProperty(proptype, propname, group, tooltip) + if defvalue is not None: + setattr(selfobj, propname, defvalue) + return True + def derivedInit(self, obj): @@ -141,17 +157,17 @@ class LatticeFeature(): obj.Placement = App.Placement() if bExposing: - obj.Shape = marker.copy() + obj.Shape = shallowCopy(marker) obj.Placement = plms[0] else: for plm in plms: - sh = marker.copy() + sh = shallowCopy(marker) sh.Placement = plm shapes.append(sh) if len(shapes) == 0: obj.Shape = lattice2Markers.getNullShapeShape(markerSize) - raise ValueError('Lattice object is null') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing. + raise ValueError('Lattice object is null') sh = Part.makeCompound(shapes) obj.Shape = sh diff --git a/lattice2Common.py b/lattice2Common.py index ab4af1d..3ac203f 100644 --- a/lattice2Common.py +++ b/lattice2Common.py @@ -80,7 +80,7 @@ def deselect(sel): '''deselect(sel): remove objects in sel from selection''' for selobj in sel: FreeCADGui.Selection.removeSelection(selobj.Object) - + # OCC's Precision::Confusion; should have taken this from FreeCAD but haven't found; unlikely to ever change. DistConfusion = 1e-7 ParaConfusion = 1e-8 diff --git a/lattice2PopulateChildren.py b/lattice2PopulateChildren.py index 17475ac..7bc5eb8 100644 --- a/lattice2PopulateChildren.py +++ b/lattice2PopulateChildren.py @@ -35,6 +35,7 @@ import lattice2BaseFeature import lattice2CompoundExplorer as LCE import lattice2Executer from lattice2PopulateCopies import DereferenceArray +import lattice2ShapeCopy as ShapeCopy # -------------------------- document object -------------------------------------------------- @@ -62,10 +63,18 @@ class LatticePopulateChildren(lattice2BaseFeature.LatticeFeature): obj.addProperty("App::PropertyLink","PlacementsTo","Lattice PopulateChildren", "Placement or array of placements, containing target locations.") obj.addProperty("App::PropertyLink","PlacementsFrom", "Lattice PopulateChildren","Placement or array of placements to be treated as origins for PlacementsTo.") + + self.initNewProperties(obj) + def initNewProperties(self, obj): + # properties that can be missing on objects made with earlier version of Lattice2 + if self.assureProperty(obj, "App::PropertyEnumeration","Copying", ShapeCopy.copy_types, "Lattice PopulateChildren", "Sets, what method to use for copying shapes."): + self.Copying = ShapeCopy.copy_types[0] def derivedExecute(self,obj): + self.initNewProperties(obj) + outputIsLattice = lattice2BaseFeature.isObjectLattice(obj.Object) if not lattice2BaseFeature.isObjectLattice(obj.Object): @@ -84,13 +93,13 @@ class LatticePopulateChildren(lattice2BaseFeature.LatticeFeature): # Precompute referencing placements = DereferenceArray(obj, placements, obj.PlacementsFrom, obj.Referencing) - - + # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements iChild = 0 numChildren = len(objectPlms) if outputIsLattice else len(objectShapes) + copy_method_index = ShapeCopy.getCopyTypeIndex(obj.Copying) # the essence for iPlm in range(len(placements)): @@ -106,8 +115,8 @@ class LatticePopulateChildren(lattice2BaseFeature.LatticeFeature): objectPlm = objectPlms[iChild] outputPlms.append(plm.multiply(objectPlm)) else: - outputShape = objectShapes[iChild].copy() - outputShape.Placement = plm.multiply(outputShape.Placement) + outputShape = ShapeCopy.copyShape(objectShapes[iChild], copy_method_index, plm) + # outputShape.Placement = plm.multiply(outputShape.Placement) #now done by shape copy routine outputShapes.append(outputShape) iChild += 1 diff --git a/lattice2PopulateCopies.py b/lattice2PopulateCopies.py index 2bd0238..7eb17fc 100644 --- a/lattice2PopulateCopies.py +++ b/lattice2PopulateCopies.py @@ -34,6 +34,7 @@ from lattice2Common import * import lattice2BaseFeature import lattice2CompoundExplorer as LCE import lattice2Executer +import lattice2ShapeCopy as ShapeCopy # ---------------------------shared code-------------------------------------- def DereferenceArray(obj,placements, lnkFrom, refmode): @@ -100,6 +101,9 @@ class LatticePopulateCopies(lattice2BaseFeature.LatticeFeature): obj.addProperty("App::PropertyEnumeration", propname, "Lattice PopulateCopies","In case single object copy is made, this property controls, if it's packed into compoud or not.") setattr(obj,propname,["(autosettle)","always", "only if many"]) setattr(obj,propname,"always") # this is to match the old behavior. This is not the default setting for new features. + if self.assureProperty(obj, "App::PropertyEnumeration","Copying", ShapeCopy.copy_types, "Lattice PopulateChildren", "Sets, what method to use for copying shapes."): + self.Copying = ShapeCopy.copy_types[0] + def derivedExecute(self,obj): self.assureProperties(obj) @@ -119,6 +123,8 @@ class LatticePopulateCopies(lattice2BaseFeature.LatticeFeature): # initialize output containers and loop variables outputShapes = [] #output list of shapes outputPlms = [] #list of placements + copy_method_index = ShapeCopy.getCopyTypeIndex(obj.Copying) + # the essence for plm in placements: @@ -127,8 +133,8 @@ class LatticePopulateCopies(lattice2BaseFeature.LatticeFeature): for objectPlm in objectPlms: outputPlms.append(plm.multiply(objectPlm)) else: - outputShape = objectShape.copy() - outputShape.Placement = plm.multiply(outputShape.Placement) + outputShape = ShapeCopy.copyShape(objectShape, copy_method_index, plm) + #outputShape.Placement = plm.multiply(outputShape.Placement) # now handled by copyShape outputShapes.append(outputShape) if outputIsLattice: @@ -144,8 +150,7 @@ class LatticePopulateCopies(lattice2BaseFeature.LatticeFeature): #now, set the result shape if len(outputShapes) == 1 and obj.OutputCompounding == "only if many": sh = outputShapes[0] - sh.transformShape(sh.Placement.toMatrix(),True) #True = make copy - sh.Placement = App.Placement() + sh = ShapeCopy.transformCopy(sh) obj.Shape = sh else: obj.Shape = Part.makeCompound(outputShapes) diff --git a/lattice2ShapeCopy.py b/lattice2ShapeCopy.py new file mode 100644 index 0000000..253325c --- /dev/null +++ b/lattice2ShapeCopy.py @@ -0,0 +1,87 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2016 - Victor Titov (DeepSOIC) * +#* * +#* * +#* This program is free software; you can redistribute it and/or modify * +#* it under the terms of the GNU Lesser General Public License (LGPL) * +#* as published by the Free Software Foundation; either version 2 of * +#* the License, or (at your option) any later version. * +#* for detail see the LICENCE text file. * +#* * +#* This program 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 program; if not, write to the Free Software * +#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +#* USA * +#* * +#*************************************************************************** + +__title__="ShapeCopy module for Lattice2" +__author__ = "DeepSOIC" +__url__ = "" +__doc__ = "Utility methods to copy shapes" + +import FreeCAD + +def shallowCopy(shape, extra_placement = None): + """shallowCopy(shape, extra_placement = None): creates a shallow copy of a shape. The + copy will match by isSame/isEqual/isPartner tests, but will have an independent placement.""" + + copiers = { + "Vertex": lambda(sh): sh.Vertexes[0], + "Edge": lambda(sh): sh.Edges[0], + "Wire": lambda(sh): sh.Wires[0], + "Face": lambda(sh): sh.Faces[0], + "Shell": lambda(sh): sh.Shells[0], + "Solid": lambda(sh): sh.Solids[0], + "CompSolid": lambda(sh): sh.CompSolids[0], + "Compound": lambda(sh): sh.Compounds[0], + } + copier = copiers.get(shape.ShapeType) + if copier is None: + copier = lambda(sh): sh.copy() + FreeCAD.Console.PrintWarning("Lattice2: shallowCopy: unexpected shape type '{typ}'. Using deep copy instead.\n".format(typ= shape.ShapeType)) + ret = copier(shape) + if extra_placement is not None: + ret.Placement = extra_placement.multiply(ret.Placement) + return ret + +def deepCopy(shape, extra_placement = None): + """deepCopy(shape, extra_placement = None): Copies all subshapes. The copy will not match by isSame/isEqual/ + isPartner tests.""" + + ret = shape.copy() + if extra_placement is not None: + ret.Placement = extra_placement.multiply(ret.Placement) + return ret + +def transformCopy(shape, extra_placement = None): + """transformCopy(shape, extra_placement = None): creates a deep copy shape with shape's placement applied to + the subelements (the placement of returned shape is zero).""" + + if extra_placement is None: + extra_placement = FreeCAD.Placement() + ret = shape.copy() + ret.transformShape(extra_placement.multiply(ret.Placement).toMatrix(), True) + ret.Placement = FreeCAD.Placement() #reset placement + return ret + + +copy_types = ["Shallow copy", "Deep copy", "Transformed deep copy"] +copy_functions = [shallowCopy, deepCopy, transformCopy] + +def getCopyTypeIndex(copy_type_string): + return copy_types.index(str(copy_type_string)) + +def copyShape(shape, copy_type_index, extra_placement = None): + """copyShape(shape, copy_type_index, extra_placement = None): copies a shape (or creates + a moved copy of shape, if extra_placement is given). copy_type_index should be obtained + from string by getCopyTypeIndex() function.""" + + global copy_functions + return copy_functions[copy_type_index](shape, extra_placement)