chiark / gitweb /
Add uppercase STL and HEX to file dialog filters for linux/MacOS
[cura.git] / Cura / cura_sf / fabmetheus_utilities / geometry / creation / lathe.py
1 """
2 Boolean geometry extrusion.
3
4 """
5
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.
8 import __init__
9
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
16 import math
17
18
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'
23
24 def addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes):
25         "Add an indexed loop to the vertexes."
26         loops = loopLists[-1]
27         loop = []
28         loops.append(loop)
29         for point in path:
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)
37
38 def addNegatives(derivation, negatives, paths):
39         "Add pillars output to negatives."
40         for path in paths:
41                 loopListsByPath = getLoopListsByPath(derivation, 1.000001, path)
42                 geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
43                 negatives.append(geometryOutput)
44
45 def addNegativesPositives(derivation, negatives, paths, positives):
46         "Add pillars output to negatives and positives."
47         for path in paths:
48                 endMultiplier = None
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)
56                 else:
57                         negatives.append(geometryOutput)
58
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 )
64
65 def addPositives(derivation, paths, positives):
66         "Add pillars output to positives."
67         for path in paths:
68                 loopListsByPath = getLoopListsByPath(derivation, None, path)
69                 geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
70                 positives.append(geometryOutput)
71
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)
79                 return None
80         negatives = []
81         positives = []
82         addNegativesPositives(derivation, negatives, derivation.target, positives)
83         return getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives)
84
85 def getGeometryOutputByArguments(arguments, elementNode):
86         "Get triangle mesh from attribute dictionary by arguments."
87         return getGeometryOutput(None, elementNode)
88
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}})
95
96 def getLoopListsByPath(derivation, endMultiplier, path):
97         "Get loop lists from path."
98         vertexes = []
99         loopLists = [[]]
100         if len(derivation.loop) < 2:
101                 return loopLists
102         for pointIndex, pointComplex in enumerate(derivation.loop):
103                 if endMultiplier != None and not derivation.isEndCloseToStart:
104                         if pointIndex == 0:
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([])
113         return loopLists
114
115 def getNewDerivation(elementNode):
116         'Get new derivation.'
117         return LatheDerivation(elementNode)
118
119 def processElementNode(elementNode):
120         "Process the xml element."
121         solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode))
122
123
124 class LatheDerivation:
125         "Class to hold lathe variables."
126         def __init__(self, elementNode):
127                 'Set defaults.'
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:')
137                         print(elementNode)
138                         return
139                 firstPath = self.target[0]
140                 if len(firstPath) < 3:
141                         print('Warning, firstPath length is less than three in derive in lathe for:')
142                         print(elementNode)
143                         self.target = []
144                         return
145                 if self.axisStart == None:
146                         if self.axisEnd == None:
147                                 self.axisStart = firstPath[0]
148                                 self.axisEnd = firstPath[-1]
149                         else:
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:')
155                         print(elementNode)
156                         self.target = []
157                         return
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:')
163                         print(elementNode)
164                         self.target = []
165                         return
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)