[big change] Use marker shape instead of points

Because points lost placements when saving/restoring.
This commit is contained in:
DeepSOIC 2015-10-25 16:10:58 +03:00
parent 5fe625d109
commit 651cd398c6
7 changed files with 429 additions and 54 deletions

View File

@ -25,11 +25,12 @@ __title__="Base feature module for lattice object of lattice workbench for FreeC
__author__ = "DeepSOIC" __author__ = "DeepSOIC"
__url__ = "" __url__ = ""
import FreeCAD as App
import Part import Part
from pivy import coin
from latticeCommon import * from latticeCommon import *
import latticeCompoundExplorer as LCE import latticeCompoundExplorer as LCE
import latticeMarkers
def makeLatticeFeature(name, AppClass, icon, ViewClass = None): def makeLatticeFeature(name, AppClass, icon, ViewClass = None):
'''makeLatticeFeature(name, AppClass, ViewClass = None): makes a document object for a LatticeFeature-derived object.''' '''makeLatticeFeature(name, AppClass, ViewClass = None): makes a document object for a LatticeFeature-derived object.'''
@ -43,15 +44,46 @@ def makeLatticeFeature(name, AppClass, icon, ViewClass = None):
return obj return obj
def isObjectLattice(documentObject):
'''isObjectLattice(documentObject): When operating on the object, it is to be treated as a lattice object. If False, treat as a regular shape.'''
ret = False
if hasattr(documentObject,"isLattice"):
if documentObject.isLattice == 'True':
ret = True
return ret
def getMarkerSizeEstimate(ListOfPlacements):
'''getMarkerSizeEstimate(ListOfPlacements): computes the default marker size for the array of placements'''
if len(ListOfPlacements) == 0:
return 1.0
pathLength = 0
for i in range(1, len(ListOfPlacements)):
pathLength += (ListOfPlacements[i].Base - ListOfPlacements[i-1].Base).Length
sz = pathLength/len(ListOfPlacements)/2.0
#FIXME: make hierarchy-aware
if sz < DistConfusion*10:
sz = 1.0
return sz
class LatticeFeature(): class LatticeFeature():
"Base object for lattice objects (arrays of placements)" "Base object for lattice objects (arrays of placements)"
def __init__(self,obj): def __init__(self,obj):
# please, don't override. Override derivedInit instead.
self.Type = "latticeFeature" self.Type = "latticeFeature"
prop = "NumElements" prop = "NumElements"
obj.addProperty("App::PropertyInteger",prop,"Info","Number of placements in the array") obj.addProperty("App::PropertyInteger",prop,"Info","Number of placements in the array")
obj.setEditorMode(prop, 1) # set read-only obj.setEditorMode(prop, 1) # set read-only
obj.addProperty("App::PropertyLength","MarkerSize","Lattice","Size of placement markers (set to zero for automatic).")
obj.addProperty("App::PropertyEnumeration","isLattice","Lattice","Sets whether this object should be treated as a lattice by further operations")
obj.isLattice = ['Auto','False','True']
self.derivedInit(obj) self.derivedInit(obj)
obj.Proxy = self obj.Proxy = self
@ -62,14 +94,53 @@ class LatticeFeature():
pass pass
def execute(self,obj): def execute(self,obj):
derivedExecute(self, obj) # please, don't override. Override derivedExecute instead.
obj.NumElements = LCE.CalculateNumberOfLeaves(obj.Shape)
plms = self.derivedExecute(obj)
if plms is not None:
obj.NumElements = len(plms)
shapes = []
markerSize = obj.MarkerSize
if markerSize < DistConfusion:
markerSize = getMarkerSizeEstimate(plms)
marker = latticeMarkers.getPlacementMarker(scale=markerSize)
#FIXME: make hierarchy-aware
for plm in plms:
sh = marker.copy()
sh.Placement = plm
shapes.append(sh)
if len(shapes) == 0:
obj.Shape = markers.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.
obj.Shape = Part.makeCompound(shapes)
if obj.isLattice == 'Auto':
obj.isLattice = 'True'
else:
# DerivedExecute didn't return anything. Thus we assume it
# has assigned the shape, and thus we don't do anything.
# Moreover, we assume that it is no longer a lattice object, so:
if obj.isLattice == 'Auto':
obj.isLattice = 'False'
obj.NumElements = len(obj.Shape.childShapes(False,False))
return return
def derivedExecute(): def derivedExecute(self,obj):
'''For overriding by derived class''' '''For overriding by derived class. If this returns a list of placements,
pass it's going to be used to build the shape. If returns None, it is assumed that
derivedExecute has already assigned the shape, and no further actions are needed.'''
return []
def verifyIntegrity(self,obj):
if self.__init__.__func__ is not LatticeFeature.__init__.__func__:
FreeCAD.Console.PrintError("__init__() of lattice object is overridden. Please don't! Fix it!\n")
if self.execute.__func__ is not LatticeFeature.execute.__func__:
FreeCAD.Console.PrintError("execute() of lattice object is overridden. Please don't! Fix it!\n")
class ViewProviderLatticeFeature: class ViewProviderLatticeFeature:
@ -78,10 +149,9 @@ class ViewProviderLatticeFeature:
def __init__(self,vobj): def __init__(self,vobj):
vobj.Proxy = self vobj.Proxy = self
# vobj.DisplayMode = "Markers" # vobj.DisplayMode = "Markers"
vobj.PointSize = 4
vobj.PointColor = (1.0, 0.7019608020782471, 0.0, 0.0) #orange
def getIcon(self): def getIcon(self):
self.Object.Proxy.verifyIntegrity(self.Object)
if hasattr(self, "icon"): if hasattr(self, "icon"):
if self.icon: if self.icon:
return getIconPath(self.icon) return getIconPath(self.icon)
@ -111,6 +181,7 @@ class ViewProviderLatticeFeature:
try: try:
children = self.claimChildren() children = self.claimChildren()
if children and len(children) > 0: if children and len(children) > 0:
marker = latticeMarkers
for child in children: for child in children:
child.ViewObject.show() child.ViewObject.show()
except Exception as err: except Exception as err:

View File

@ -68,8 +68,8 @@ class Compose(latticeBaseFeature.LatticeFeature):
obj.ToolFlattenHierarchy = True obj.ToolFlattenHierarchy = True
def execute(self,obj): def derivedExecute(self,obj):
# Fill in (update read-only) properties that are driven by the mode. # cache stuff
base = obj.Base.Shape base = obj.Base.Shape
if base.ShapeType != 'Compound': if base.ShapeType != 'Compound':
base = Part.makeCompound([base]) base = Part.makeCompound([base])
@ -87,45 +87,82 @@ class Compose(latticeBaseFeature.LatticeFeature):
toolChildren = tool.childShapes() toolChildren = tool.childShapes()
iBase = 0 iBase = 0
isMult = obj.Operation == 'MultiplyPlacements' # cache comparisons to speed them up isMult = obj.Operation == 'MultiplyPlacements' # cache mode comparisons to speed them up
isAvg = obj.Operation == 'AveragePlacements' isAvg = obj.Operation == 'AveragePlacements'
isIgnore = obj.Operation == 'IgnoreBasePlacements' isIgnore = obj.Operation == 'IgnoreBasePlacements'
isOvrride = obj.Operation == 'OverrideBasePlacements' isOverride = obj.Operation == 'OverrideBasePlacements'
rst = []
#mode validity logic
if not latticeBaseFeature.isObjectLattice(obj.Tool):
FreeCAD.Console.PrintWarning(obj.Name+': Tool is not a lattice object. Results may be unexpected.\n')
outputIsLattice = latticeBaseFeature.isObjectLattice(obj.Base)
if isOverride and outputIsLattice:
FreeCAD.Console.PrintWarning(obj.Name+': Base is a lattice object. OverrideBasePlacements operation requires a generic compound as Base. So, the lattice is being treated as a generic compound.\n')
outputIsLattice = False
# initialize output containers and loop variables
outputShapes = [] #output list of shapes
outputPlms = [] #list of placements
bFirst = True bFirst = True
plmMatcher = App.Placement() #extra placement, that aligns first tool member and first base member plmMatcher = App.Placement() #extra placement, that aligns first tool member and first base member
for toolChild in toolChildren:
sh = baseChildren[iBase].copy()
if bFirst:
bFirst = False
if obj.BaseKeepPosOfFirst:
plmMatcher = toolChild.Placement.inverse()
toolPlm = plmMatcher.multiply(toolChild.Placement)
if isMult:
sh.Placement = toolPlm.multiply(sh.Placement)
elif isAvg:
plm1 = toolPlm
plm2 = sh.Placement
transl = plm1.Base*0.5 + plm2.Base*0.5
a1,b1,c1,d1 = plm1.Rotation.Q
a2,b2,c2,d2 = plm2.Rotation.Q
rot = App.Rotation((a1+a2,b1+b2,c1+c2,d1+d2)) #no divide-by-two, because FreeCAD will normalize the quaternion automatically
sh.Placement = App.Placement(transl,rot)
elif isIgnore:
sh.Placement = toolPlm
elif isOverride:
sh.transformShape(toolPlm.inverse.multiply(sh.Placement))
sh.Placement = toolPlm
rst.append(sh)
iBase += 1
# the essence
for toolChild in toolChildren:
# early test for termination
if iBase > len(baseChildren)-1: if iBase > len(baseChildren)-1:
if obj.BaseLoopSequence: if obj.BaseLoopSequence:
iBase = 0 iBase = 0
else: else:
FreeCAD.Console.PrintWarning(obj.Name+': There are '+str(len(toolChildren)-len(baseChildren))+
' more placements in Tool than children in Base. Those placements'+
' were dropped.\n')
break break
obj.Shape = Part.makeCompound(rst) #cache some stuff
basePlm = baseChildren[iBase].Placement
toolPlm = toolChild.Placement
if not outputIsLattice:
outputShape = baseChildren[iBase].copy()
#prepare alignment placement
if bFirst:
bFirst = False
if obj.BaseKeepPosOfFirst:
plmMatcher = toolPlm.inverse()
#mode logic
if isMult:
outPlm = toolPlm.multiply(plmMatcher.multiply(basePlm))
elif isAvg:
plm1 = toolPlm
plm2 = pltMatcher.multiply(basePlm)
transl = plm1.Base*0.5 + plm2.Base*0.5
a1,b1,c1,d1 = plm1.Rotation.Q
a2,b2,c2,d2 = plm2.Rotation.Q
rot = App.Rotation((a1+a2,b1+b2,c1+c2,d1+d2)) #no divide-by-two, because FreeCAD will normalize the quaternion automatically
outPlm = App.Placement(transl,rot)
elif isIgnore:
outPlm = toolPlm
elif isOverride:
assert(not outputIsLattice)
outputShape.transformShape(toolPlm.inverse.multiply(plmMatcher.multiply(basePlm)))
outPlm = toolPlm
if outputIsLattice:
outputPlms.append(outPlm)
else:
outputShape.Placement = outPlm
outputShapes.append(outputShape)
iBase += 1
if outputIsLattice:
return outputPlms
else:
obj.Shape = Part.makeCompound(outputShapes)
class ViewProviderCompose(latticeBaseFeature.ViewProviderLatticeFeature): class ViewProviderCompose(latticeBaseFeature.ViewProviderLatticeFeature):

View File

@ -144,7 +144,7 @@ def CalculateNumberOfLeaves(compound):
return cnt return cnt
def AllLeaves(compound): def AllLeaves(compound):
'AllLeaves(compound): Traverses the compound and collects all the leaves into a single list' 'AllLeaves(compound): Traverses the compound and collects all the leaves into a single list. Returns list of shapes.'
output = [] output = []
for (child, msg, it) in CompoundExplorer(compound): for (child, msg, it) in CompoundExplorer(compound):
if msg == it.MSG_LEAF: if msg == it.MSG_LEAF:

View File

@ -29,6 +29,7 @@ __url__ = ""
__doc__ = "Module for loading marker shapes for Lattice workbench" __doc__ = "Module for loading marker shapes for Lattice workbench"
_nullShapeShape = 0 _nullShapeShape = 0
_ShapeDict = {}
def getShapePath(shapeName): def getShapePath(shapeName):
""" """
@ -56,3 +57,31 @@ def getNullShapeShape(scale = 1.0):
ret.scale(scale) ret.scale(scale)
return ret return ret
def loadShape(shapeID):
global _ShapeDict
sh = _ShapeDict.get(shapeID)
if sh is None:
try:
sh = Part.Shape()
f = open(getShapePath(shapeID + '.brep'))
sh.importBrep(f)
f.close()
except Exception as err:
FreeCAD.Console.PrintError('Failed to load standard shape "'+shapeID+'". \n' + err.message + '\n')
sh = Part.Point() #Create at least something!
_ShapeDict[shapeID] = sh
return sh
def getPlacementMarker(scale = 1.0, markerID = None):
'''getPlacementMarker(scale = 1.0, markerID = None): returns a placement marker shape.
The shape is scaled according to "scale" argument.
markerID sets the marker file name. If omitted, default placement marker is returned.'''
if markerID is None:
markerID = 'tetra-orimarker'
sh = loadShape(markerID)
if scale != 1.0:
sh = sh.copy()
sh.scale(scale)
return sh

View File

@ -86,7 +86,7 @@ class PolarArray(latticeBaseFeature.LatticeFeature):
obj.setEditorMode("AxisLinkIgnorePoint", 0 if obj.AxisLink else 1) obj.setEditorMode("AxisLinkIgnorePoint", 0 if obj.AxisLink else 1)
def execute(self,obj): def derivedExecute(self,obj):
# Fill in (update read-only) properties that are driven by the mode. # Fill in (update read-only) properties that are driven by the mode.
self.updateReadOnlyness(obj) self.updateReadOnlyness(obj)
if obj.Mode == 'SpanN': if obj.Mode == 'SpanN':
@ -151,27 +151,16 @@ class PolarArray(latticeBaseFeature.LatticeFeature):
overallPlacement = App.Placement(obj.AxisPoint, rot_ini) overallPlacement = App.Placement(obj.AxisPoint, rot_ini)
# Make the array # Make the array
rst = [] # Shapes for holding placements (each shape is a vertex with placement) output = [] # list of placements
for i in range(0, n): for i in range(0, n):
ang = startAng + step*i ang = startAng + step*i
p = Part.Vertex() p = Part.Vertex()
localrot = App.Rotation(App.Vector(0,0,1), ang) localrot = App.Rotation(App.Vector(0,0,1), ang)
localtransl = localrot.multVec(App.Vector(radius,0,0)) localtransl = localrot.multVec(App.Vector(radius,0,0))
localplm = App.Placement(localtransl, localrot) localplm = App.Placement(localtransl, localrot)
p.Placement = overallPlacement.multiply(localplm) output.append( overallPlacement.multiply(localplm) )
rst.append(p)
# Make final compound (or empty-set marker) return output
if len(rst) == 0:
scale = 1.0
if not obj.Base.Shape.isNull():
scale = abs(obj.Radius)
if scale < DistConfusion * 100:
scale = 1.0
obj.Shape = markers.getNullShapeShape(scale)
raise ValueError('Array output is null') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.
obj.Shape = Part.makeCompound(rst)
else: else:
raise ValueError("Spreadsheet mode not implemeted yet") raise ValueError("Spreadsheet mode not implemeted yet")

Binary file not shown.

249
shapes/tetra-orimarker.brep Normal file
View File

@ -0,0 +1,249 @@
DBRep_DrawableShape
CASCADE Topology V1, (c) Matra-Datavision
Locations 15
1
1 0 0 0
0 1 0 0
0 0 1 0
1
1 0 0 0
0 1 0 0
0 0 1 0
1
1 0 0 0
0 1 0 0
0 0 1 0
1
0 0 -1 -0
-1 0 0 -0
0 1 0 0
2 4 1 3 1 0
2 3 -1 4 -1 0
1
1 0 0 0
0 1 0 0
0 0 1 0
1
-0.978231976089037 2.77555756156289e-017 0.207514339159822 0.043062200956938
0.146734796413355 -0.707106781186547 0.691714463866075 0.14354066985646
0.146734796413356 0.707106781186547 0.691714463866075 0.14354066985646
1
1 0 0 0
0 1 0 0
0 0 1 0
2 8 1 9 1 0
2 9 -1 8 -1 0
1
0.978231976089037 2.77555756156289e-017 0.207514339159822 0.043062200956938
0.146734796413355 0.707106781186547 -0.691714463866075 -0.14354066985646
-0.146734796413356 0.707106781186547 0.691714463866075 0.14354066985646
1
1 0 0 0
0 1 0 0
0 0 1 0
2 12 1 13 1 0
2 13 -1 12 -1 0
Curve2ds 0
Curves 7
1 0 0 0 1 0 0
1 -0.29999999999998128 -4.2695787882546599e-015 0 1 1.4231929294183087e-014 0
1 0.29999999999999999 0 0 -0.70710678118654524 0.70710678118654957 0
1 2.2233792906188499e-017 0.30000000000000182 0 -0.70710678118651826 -0.70710678118657666 0
1 0.044020438924006873 -0.21213203435596539 0 -0.9791402332302942 0.20318563844357879 0
1 -0.04402043892400688 -0.21213203435596534 0 0.9791402332302942 0.20318563844357873 0
1 -0.97823197608904322 1.214306433183765e-016 0 0.97914023323029442 0.20318563844357873 0
Polygon3D 0
PolygonOnTriangulations 14
2 1 2
p 0.00316666666666669 1 0 0.3
2 2 4
p 0.00316666666666669 1 0 0.3
2 3 1
p 0.00316666666666669 1 0 0.299999999999981
2 1 2
p 0.00316666666666669 1 0 0.299999999999981
2 2 4
p 0.00316666666666669 1 0 0.42426406871193
2 1 3
p 0.00316666666666669 1 0 0.42426406871193
2 4 3
p 0.00316666666666669 1 0 0.42426406871192
2 2 3
p 0.00316666666666669 1 0 0.42426406871192
2 1 3
p 0.00316666666666669 1 0 1.04403065089106
2 3 1
p 0.00316666666666669 1 0 1.04403065089106
2 4 3
p 0.00316666666666669 1 0 1.04403065089106
2 1 2
p 0.00316666666666669 1 0 1.04403065089106
2 2 3
p 0.00316666666666669 1 0 1.04403065089106
2 1 2
p 0.00316666666666669 1 0 1.04403065089106
Surfaces 4
1 0 0 0 1 0 -0 0 0 1 0 -1 0
1 0 0 0 0 0 1 1 0 -0 -0 1 0
1 0.41556347085957146 -0.087665479371065044 0.087665479371065141 -0.20751433915982237 0.6917144638660746 -0.69171446386607471 0 -0.70710678118654757 -0.70710678118654746 -0.978231976089037 -0.14673479641335552 0.14673479641335554
1 0.41556347085957152 0.087665479371065058 0.087665479371065114 -0.20751433915982231 -0.69171446386607471 -0.69171446386607471 0 -0.70710678118654757 0.70710678118654757 -0.97823197608903711 0.14673479641335549 0.14673479641335549
Triangulations 4
4 2 1 4.2491376305591e-018
0 0 0 -1.73472347597681e-018 -0.300000000000001 1.38777878078145e-017 1.73472347597681e-018 0.299999999999991 -2.13598435909256e-015 -1.73472347597681e-018 8.31933993028464e-018 0.300000000000002 0 0 1.38777878078145e-017 0.300000000000001 -2.13598435909256e-015 -0.299999999999991 0.300000000000002 0 4 2 1 4 1 3
4 2 1 7.18933680268093e-016
1.73472347597681e-018 0.299999999999991 -2.13598435909256e-015 0 0 0 1.00000000000001 -2.77555756156289e-017 -2.08166817117217e-017 -1.73472347597681e-018 -0.300000000000001 1.38777878078145e-017 1.73472347597681e-018 0.299999999999992 0 0 1.00000000000001 0 -1.73472347597681e-018 -0.300000000000001 3 1 2 3 2 4
3 1 1 6.93889390390723e-017
-1.73472347597681e-018 -0.300000000000001 1.38777878078145e-017 1.00000000000001 -2.77555756156289e-017 -2.08166817117217e-017 -1.73472347597681e-018 8.31933993028464e-018 0.300000000000002 0.212132034355965 0.424810761677399 8.32667268468867e-017 -0.59744165333565 -0.212132034355965 0.4248107616774 1 3 2
3 1 1 2.692290834716e-015
1.00000000000001 -2.77555756156289e-017 -2.08166817117217e-017 -1.73472347597681e-018 8.31933993028464e-018 0.300000000000002 1.73472347597681e-018 0.299999999999991 -2.13598435909256e-015 -2.77555756156289e-017 -0.59744165333565 0.212132034355966 0.4248107616774 -0.21213203435596 0.424810761677398 2 3 1
TShapes 23
Ve
1e-007
0 0 0
0 0
0101101
*
Ve
1.00000000839442e-007
-1.73472347597681e-018 -0.300000000000001 1.38777878078145e-017
0 0
0101101
*
Ed
1e-007 1 1 0
1 1 0 0 0.3
6 1 1 6
6 2 2 6
0
0101000
+23 6 -22 6 *
Ve
1.00000010384143e-007
1.73472347597681e-018 0.299999999999991 -2.13598435909256e-015
0 0
0101101
*
Ed
1e-007 1 1 0
1 2 0 0 0.299999999999981
6 3 1 6
6 4 2 6
0
0101000
+20 6 -23 6 *
Ve
1.00000000178043e-007
-1.73472347597681e-018 8.31933993028464e-018 0.300000000000002
0 0
0101101
*
Ed
1e-007 1 1 0
1 3 0 0 0.42426406871193
6 5 1 6
6 6 3 6
0
0101000
+22 6 -18 6 *
Ed
1e-007 1 1 0
1 4 0 0 0.42426406871192
6 7 1 6
6 8 4 6
0
0101000
+18 6 -20 6 *
Wi
0101000
-21 5 -19 5 -17 5 -16 5 *
Fa
0 1e-007 1 3
2 1
0111000
+15 0 *
Ve
1.00000000466544e-007
1.00000000000001 -2.77555756156289e-017 -2.08166817117217e-017
0 0
0101101
*
Ed
1e-007 1 1 0
1 5 0 0 1.04403065089106
6 9 2 11
6 10 4 11
0
0101000
+20 11 -13 11 *
Ed
1e-007 1 1 0
1 6 0 0 1.04403065089106
6 11 2 15
6 12 3 15
0
0101000
+22 15 -13 15 *
Wi
0101000
+19 5 -12 10 +21 5 +11 14 *
Fa
0 1e-007 2 7
2 2
0111000
+10 0 *
Ed
1e-007 1 1 0
1 7 0 0 1.04403065089106
6 13 3 11
6 14 4 11
0
0101000
+13 11 -18 11 *
Wi
0101000
-11 14 +17 5 -8 10 *
Fa
0 1e-007 3 13
2 3
0111000
+7 0 *
Wi
0101000
+8 10 +12 10 +16 5 *
Fa
0 1e-007 4 9
2 4
0111000
+5 0 *
Sh
0101000
+14 2 +9 2 +6 2 +4 2 *
So
0100000
+3 0 *
Co
1100000
-2 1 *
+1 0