2 Boolean geometry extrusion.
6 from __future__ import absolute_import
7 #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
10 from fabmetheus_utilities.geometry.creation import lineation
11 from fabmetheus_utilities.geometry.creation import solid
12 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
13 from fabmetheus_utilities.geometry.solids import triangle_mesh
14 from fabmetheus_utilities.vector3 import Vector3
15 from fabmetheus_utilities import euclidean
19 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
20 __credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
21 __date__ = '$Date: 2008/02/05 $'
22 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
24 def addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes):
25 "Add an indexed loop to the vertexes."
30 pointMinusBegin = point - derivation.axisStart
31 dotVector3 = derivation.axisProjectiveSpace.getDotVector3(pointMinusBegin)
32 dotVector3Complex = dotVector3.dropAxis()
33 dotPointComplex = pointComplex * dotVector3Complex
34 dotPoint = Vector3(dotPointComplex.real, dotPointComplex.imag, dotVector3.z)
35 projectedVector3 = derivation.axisProjectiveSpace.getVector3ByPoint(dotPoint) + derivation.axisStart
36 loop.append(projectedVector3)
38 def addNegatives(derivation, negatives, paths):
39 "Add pillars output to negatives."
41 loopListsByPath = getLoopListsByPath(derivation, 1.000001, path)
42 geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
43 negatives.append(geometryOutput)
45 def addNegativesPositives(derivation, negatives, paths, positives):
46 "Add pillars output to negatives and positives."
49 normal = euclidean.getNormalByPath(path)
50 if normal.dot(derivation.normal) < 0.0:
51 endMultiplier = 1.000001
52 loopListsByPath = getLoopListsByPath(derivation, endMultiplier, path)
53 geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
54 if endMultiplier == None:
55 positives.append(geometryOutput)
57 negatives.append(geometryOutput)
59 def addOffsetAddToLists( loop, offset, vector3Index, vertexes ):
60 "Add an indexed loop to the vertexes."
61 vector3Index += offset
62 loop.append( vector3Index )
63 vertexes.append( vector3Index )
65 def addPositives(derivation, paths, positives):
66 "Add pillars output to positives."
68 loopListsByPath = getLoopListsByPath(derivation, None, path)
69 geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
70 positives.append(geometryOutput)
72 def getGeometryOutput(derivation, elementNode):
73 "Get triangle mesh from attribute dictionary."
74 if derivation == None:
75 derivation = LatheDerivation(elementNode)
76 if len(euclidean.getConcatenatedList(derivation.target)) == 0:
77 print('Warning, in lathe there are no paths.')
78 print(elementNode.attributes)
82 addNegativesPositives(derivation, negatives, derivation.target, positives)
83 return getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives)
85 def getGeometryOutputByArguments(arguments, elementNode):
86 "Get triangle mesh from attribute dictionary by arguments."
87 return getGeometryOutput(None, elementNode)
89 def getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives):
90 "Get triangle mesh from derivation, elementNode, negatives and positives."
91 positiveOutput = triangle_mesh.getUnifiedOutput(positives)
92 if len(negatives) < 1:
93 return solid.getGeometryOutputByManipulation(elementNode, positiveOutput)
94 return solid.getGeometryOutputByManipulation(elementNode, {'difference' : {'shapes' : [positiveOutput] + negatives}})
96 def getLoopListsByPath(derivation, endMultiplier, path):
97 "Get loop lists from path."
100 if len(derivation.loop) < 2:
102 for pointIndex, pointComplex in enumerate(derivation.loop):
103 if endMultiplier != None and not derivation.isEndCloseToStart:
105 nextPoint = derivation.loop[1]
106 pointComplex = endMultiplier * (pointComplex - nextPoint) + nextPoint
107 elif pointIndex == len(derivation.loop) - 1:
108 previousPoint = derivation.loop[pointIndex - 1]
109 pointComplex = endMultiplier * (pointComplex - previousPoint) + previousPoint
110 addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes)
111 if derivation.isEndCloseToStart:
112 loopLists[-1].append([])
115 def getNewDerivation(elementNode):
116 'Get new derivation.'
117 return LatheDerivation(elementNode)
119 def processElementNode(elementNode):
120 "Process the xml element."
121 solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode))
124 class LatheDerivation:
125 "Class to hold lathe variables."
126 def __init__(self, elementNode):
128 self.axisEnd = evaluate.getVector3ByPrefix(None, elementNode, 'axisEnd')
129 self.axisStart = evaluate.getVector3ByPrefix(None, elementNode, 'axisStart')
130 self.end = evaluate.getEvaluatedFloat(360.0, elementNode, 'end')
131 self.loop = evaluate.getTransformedPathByKey([], elementNode, 'loop')
132 self.sides = evaluate.getEvaluatedInt(None, elementNode, 'sides')
133 self.start = evaluate.getEvaluatedFloat(0.0, elementNode, 'start')
134 self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
135 if len(self.target) < 1:
136 print('Warning, no target in derive in lathe for:')
139 firstPath = self.target[0]
140 if len(firstPath) < 3:
141 print('Warning, firstPath length is less than three in derive in lathe for:')
145 if self.axisStart == None:
146 if self.axisEnd == None:
147 self.axisStart = firstPath[0]
148 self.axisEnd = firstPath[-1]
150 self.axisStart = Vector3()
151 self.axis = self.axisEnd - self.axisStart
152 axisLength = abs(self.axis)
153 if axisLength <= 0.0:
154 print('Warning, axisLength is zero in derive in lathe for:')
158 self.axis /= axisLength
159 firstVector3 = firstPath[1] - self.axisStart
160 firstVector3Length = abs(firstVector3)
161 if firstVector3Length <= 0.0:
162 print('Warning, firstVector3Length is zero in derive in lathe for:')
166 firstVector3 /= firstVector3Length
167 self.axisProjectiveSpace = euclidean.ProjectiveSpace().getByBasisZFirst(self.axis, firstVector3)
168 if self.sides == None:
169 distanceToLine = euclidean.getDistanceToLineByPaths(self.axisStart, self.axisEnd, self.target)
170 self.sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, distanceToLine)
171 endRadian = math.radians(self.end)
172 startRadian = math.radians(self.start)
173 self.isEndCloseToStart = euclidean.getIsRadianClose(endRadian, startRadian)
174 if len(self.loop) < 1:
175 self.loop = euclidean.getComplexPolygonByStartEnd(endRadian, 1.0, self.sides, startRadian)
176 self.normal = euclidean.getNormalByPath(firstPath)