2 This page is in the table of contents.
3 The xml.py script is an import translator plugin to get a carving from an Art of Illusion xml file.
5 An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
7 The getCarving function takes the file name of an xml file and returns the carving.
9 An xml file can be exported from Art of Illusion by going to the "File" menu, then going into the "Export" menu item, then picking the XML choice. This will bring up the XML file chooser window, choose a place to save the file then click "OK". Leave the "compressFile" checkbox unchecked. All the objects from the scene will be exported, this plugin will ignore the light and camera. If you want to fabricate more than one object at a time, you can have multiple objects in the Art of Illusion scene and they will all be carved, then fabricated together.
14 from __future__ import absolute_import
16 from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
17 from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid
18 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
19 from fabmetheus_utilities.geometry.solids import triangle_mesh
20 from fabmetheus_utilities.vector3 import Vector3
21 from fabmetheus_utilities import euclidean
22 from fabmetheus_utilities import xml_simple_writer
26 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
27 __credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
28 __date__ = '$Date: 2008/21/04 $'
29 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
32 def getEmptyZLoops(archivableObjects, importRadius, shouldPrintWarning, z, zoneArrangement):
33 'Get loops at empty z level.'
34 emptyZ = zoneArrangement.getEmptyZ(z)
35 visibleObjects = evaluate.getVisibleObjects(archivableObjects)
36 visibleObjectLoopsList = boolean_solid.getVisibleObjectLoopsList(importRadius, visibleObjects, emptyZ)
37 loops = euclidean.getConcatenatedList(visibleObjectLoopsList)
38 if euclidean.isLoopListIntersecting(loops):
39 loops = boolean_solid.getLoopsUnion(importRadius, visibleObjectLoopsList)
40 if shouldPrintWarning:
41 print('Warning, the triangle mesh slice intersects itself in getExtruderPaths in boolean_geometry.')
42 print('Something will still be printed, but there is no guarantee that it will be the correct shape.')
43 print('Once the gcode is saved, you should check over the layer with a z of:')
47 def getLoopLayers(archivableObjects, importRadius, layerHeight, maximumZ, shouldPrintWarning, z, zoneArrangement):
51 triangle_mesh.getLoopLayerAppend(loopLayers, z).loops = getEmptyZLoops(archivableObjects, importRadius, True, z, zoneArrangement)
55 def getMinimumZ(geometryObject):
56 'Get the minimum of the minimum z of the archivableObjects and the object.'
57 booleanGeometry = BooleanGeometry()
58 booleanGeometry.archivableObjects = geometryObject.archivableObjects
59 booleanGeometry.importRadius = setting.getImportRadius(geometryObject.elementNode)
60 booleanGeometry.layerHeight = setting.getLayerHeight(geometryObject.elementNode)
61 archivableMinimumZ = booleanGeometry.getMinimumZ()
62 geometryMinimumZ = geometryObject.getMinimumZ()
63 if archivableMinimumZ == None:
64 return geometryMinimumZ
65 if geometryMinimumZ == None:
66 return archivableMinimumZ
67 return min(archivableMinimumZ, geometryMinimumZ)
70 class BooleanGeometry(object):
71 'A boolean geometry scene.'
74 self.archivableObjects = []
76 self.importRadius = 0.6
77 self.layerHeight = 0.4
81 'Get the string representation of this carving.'
83 if len(self.archivableObjects) > 0:
84 elementNode = self.archivableObjects[0].elementNode
85 output = xml_simple_writer.getBeginGeometryXMLOutput(elementNode)
86 self.addXML( 1, output )
87 return xml_simple_writer.getEndGeometryXMLString(output)
89 def addXML(self, depth, output):
90 'Add xml for this object.'
91 xml_simple_writer.addXMLFromObjects( depth, self.archivableObjects, output )
93 def getCarveBoundaryLayers(self):
94 'Get the boundary layers.'
95 if self.getMinimumZ() == None:
97 z = self.minimumZ + 0.5 * self.layerHeight
98 self.loopLayers = getLoopLayers(self.archivableObjects, self.importRadius, self.layerHeight, self.maximumZ, True, z, self.zoneArrangement)
99 self.cornerMaximum = Vector3(-912345678.0, -912345678.0, -912345678.0)
100 self.cornerMinimum = Vector3(912345678.0, 912345678.0, 912345678.0)
101 for loopLayer in self.loopLayers:
102 for loop in loopLayer.loops:
104 pointVector3 = Vector3(point.real, point.imag, loopLayer.z)
105 self.cornerMaximum.maximize(pointVector3)
106 self.cornerMinimum.minimize(pointVector3)
107 self.cornerMaximum.z += self.halfHeight
108 self.cornerMinimum.z -= self.halfHeight
109 for loopLayerIndex in xrange(len(self.loopLayers) -1, -1, -1):
110 loopLayer = self.loopLayers[loopLayerIndex]
111 if len(loopLayer.loops) > 0:
112 return self.loopLayers[: loopLayerIndex + 1]
115 def getCarveCornerMaximum(self):
116 'Get the corner maximum of the vertexes.'
117 return self.cornerMaximum
119 def getCarveCornerMinimum(self):
120 'Get the corner minimum of the vertexes.'
121 return self.cornerMinimum
123 def getCarveLayerHeight(self):
124 'Get the layer height.'
125 return self.layerHeight
127 def getFabmetheusXML(self):
128 'Return the fabmetheus XML.'
129 if len(self.archivableObjects) > 0:
130 return self.archivableObjects[0].elementNode.getOwnerDocument().getOriginalRoot()
133 def getInterpretationSuffix(self):
134 'Return the suffix for a boolean carving.'
137 def getMatrix4X4(self):
141 def getMatrixChainTetragrid(self):
142 'Get the matrix chain tetragrid.'
145 def getMinimumZ(self):
148 for visibleObject in evaluate.getVisibleObjects(self.archivableObjects):
149 vertexes += visibleObject.getTransformedVertexes()
150 if len(vertexes) < 1:
152 self.maximumZ = -912345678.0
153 self.minimumZ = 912345678.0
154 for vertex in vertexes:
155 self.maximumZ = max(self.maximumZ, vertex.z)
156 self.minimumZ = min(self.minimumZ, vertex.z)
157 self.zoneArrangement = triangle_mesh.ZoneArrangement(self.layerHeight, vertexes)
158 self.halfHeight = 0.5 * self.layerHeight
159 self.setActualMinimumZ()
162 def getNumberOfEmptyZLoops(self, z):
163 'Get number of empty z loops.'
164 return len(getEmptyZLoops(self.archivableObjects, self.importRadius, False, z, self.zoneArrangement))
166 def setActualMinimumZ(self):
167 'Get the actual minimum z at the lowest rotated boundary layer.'
168 halfHeightOverMyriad = 0.0001 * self.halfHeight
169 while self.minimumZ < self.maximumZ:
170 if self.getNumberOfEmptyZLoops(self.minimumZ + halfHeightOverMyriad) > 0:
171 if self.getNumberOfEmptyZLoops(self.minimumZ - halfHeightOverMyriad) < 1:
173 increment = -self.halfHeight
174 while abs(increment) > halfHeightOverMyriad:
175 self.minimumZ += increment
176 increment = 0.5 * abs(increment)
177 if self.getNumberOfEmptyZLoops(self.minimumZ) > 0:
178 increment = -increment
179 self.minimumZ = round(self.minimumZ, -int(round(math.log10(halfHeightOverMyriad) + 1.5)))
181 self.minimumZ += self.layerHeight
183 def setCarveImportRadius( self, importRadius ):
184 'Set the import radius.'
185 self.importRadius = importRadius
187 def setCarveIsCorrectMesh( self, isCorrectMesh ):
188 'Set the is correct mesh flag.'
189 self.isCorrectMesh = isCorrectMesh
191 def setCarveLayerHeight( self, layerHeight ):
192 'Set the layer height.'
193 self.layerHeight = layerHeight