2 Boolean geometry extrusion.
6 from __future__ import absolute_import
8 from fabmetheus_utilities.geometry.creation import solid
9 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
10 from fabmetheus_utilities.geometry.solids import triangle_mesh
11 from fabmetheus_utilities.vector3 import Vector3
12 from fabmetheus_utilities import euclidean
16 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
17 __credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
18 __date__ = '$Date: 2008/02/05 $'
19 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
21 def addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes):
22 "Add an indexed loop to the vertexes."
27 pointMinusBegin = point - derivation.axisStart
28 dotVector3 = derivation.axisProjectiveSpace.getDotVector3(pointMinusBegin)
29 dotVector3Complex = dotVector3.dropAxis()
30 dotPointComplex = pointComplex * dotVector3Complex
31 dotPoint = Vector3(dotPointComplex.real, dotPointComplex.imag, dotVector3.z)
32 projectedVector3 = derivation.axisProjectiveSpace.getVector3ByPoint(dotPoint) + derivation.axisStart
33 loop.append(projectedVector3)
35 def addNegatives(derivation, negatives, paths):
36 "Add pillars output to negatives."
38 loopListsByPath = getLoopListsByPath(derivation, 1.000001, path)
39 geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
40 negatives.append(geometryOutput)
42 def addNegativesPositives(derivation, negatives, paths, positives):
43 "Add pillars output to negatives and positives."
46 normal = euclidean.getNormalByPath(path)
47 if normal.dot(derivation.normal) < 0.0:
48 endMultiplier = 1.000001
49 loopListsByPath = getLoopListsByPath(derivation, endMultiplier, path)
50 geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
51 if endMultiplier == None:
52 positives.append(geometryOutput)
54 negatives.append(geometryOutput)
56 def addOffsetAddToLists( loop, offset, vector3Index, vertexes ):
57 "Add an indexed loop to the vertexes."
58 vector3Index += offset
59 loop.append( vector3Index )
60 vertexes.append( vector3Index )
62 def addPositives(derivation, paths, positives):
63 "Add pillars output to positives."
65 loopListsByPath = getLoopListsByPath(derivation, None, path)
66 geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
67 positives.append(geometryOutput)
69 def getGeometryOutput(derivation, elementNode):
70 "Get triangle mesh from attribute dictionary."
71 if derivation == None:
72 derivation = LatheDerivation(elementNode)
73 if len(euclidean.getConcatenatedList(derivation.target)) == 0:
74 print('Warning, in lathe there are no paths.')
75 print(elementNode.attributes)
79 addNegativesPositives(derivation, negatives, derivation.target, positives)
80 return getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives)
82 def getGeometryOutputByArguments(arguments, elementNode):
83 "Get triangle mesh from attribute dictionary by arguments."
84 return getGeometryOutput(None, elementNode)
86 def getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives):
87 "Get triangle mesh from derivation, elementNode, negatives and positives."
88 positiveOutput = triangle_mesh.getUnifiedOutput(positives)
89 if len(negatives) < 1:
90 return solid.getGeometryOutputByManipulation(elementNode, positiveOutput)
91 return solid.getGeometryOutputByManipulation(elementNode, {'difference' : {'shapes' : [positiveOutput] + negatives}})
93 def getLoopListsByPath(derivation, endMultiplier, path):
94 "Get loop lists from path."
97 if len(derivation.loop) < 2:
99 for pointIndex, pointComplex in enumerate(derivation.loop):
100 if endMultiplier != None and not derivation.isEndCloseToStart:
102 nextPoint = derivation.loop[1]
103 pointComplex = endMultiplier * (pointComplex - nextPoint) + nextPoint
104 elif pointIndex == len(derivation.loop) - 1:
105 previousPoint = derivation.loop[pointIndex - 1]
106 pointComplex = endMultiplier * (pointComplex - previousPoint) + previousPoint
107 addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes)
108 if derivation.isEndCloseToStart:
109 loopLists[-1].append([])
112 def getNewDerivation(elementNode):
113 'Get new derivation.'
114 return LatheDerivation(elementNode)
116 def processElementNode(elementNode):
117 "Process the xml element."
118 solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode))
121 class LatheDerivation(object):
122 "Class to hold lathe variables."
123 def __init__(self, elementNode):
125 self.axisEnd = evaluate.getVector3ByPrefix(None, elementNode, 'axisEnd')
126 self.axisStart = evaluate.getVector3ByPrefix(None, elementNode, 'axisStart')
127 self.end = evaluate.getEvaluatedFloat(360.0, elementNode, 'end')
128 self.loop = evaluate.getTransformedPathByKey([], elementNode, 'loop')
129 self.sides = evaluate.getEvaluatedInt(None, elementNode, 'sides')
130 self.start = evaluate.getEvaluatedFloat(0.0, elementNode, 'start')
131 self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
132 if len(self.target) < 1:
133 print('Warning, no target in derive in lathe for:')
136 firstPath = self.target[0]
137 if len(firstPath) < 3:
138 print('Warning, firstPath length is less than three in derive in lathe for:')
142 if self.axisStart == None:
143 if self.axisEnd == None:
144 self.axisStart = firstPath[0]
145 self.axisEnd = firstPath[-1]
147 self.axisStart = Vector3()
148 self.axis = self.axisEnd - self.axisStart
149 axisLength = abs(self.axis)
150 if axisLength <= 0.0:
151 print('Warning, axisLength is zero in derive in lathe for:')
155 self.axis /= axisLength
156 firstVector3 = firstPath[1] - self.axisStart
157 firstVector3Length = abs(firstVector3)
158 if firstVector3Length <= 0.0:
159 print('Warning, firstVector3Length is zero in derive in lathe for:')
163 firstVector3 /= firstVector3Length
164 self.axisProjectiveSpace = euclidean.ProjectiveSpace().getByBasisZFirst(self.axis, firstVector3)
165 if self.sides == None:
166 distanceToLine = euclidean.getDistanceToLineByPaths(self.axisStart, self.axisEnd, self.target)
167 self.sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, distanceToLine)
168 endRadian = math.radians(self.end)
169 startRadian = math.radians(self.start)
170 self.isEndCloseToStart = euclidean.getIsRadianClose(endRadian, startRadian)
171 if len(self.loop) < 1:
172 self.loop = euclidean.getComplexPolygonByStartEnd(endRadian, 1.0, self.sides, startRadian)
173 self.normal = euclidean.getNormalByPath(firstPath)