Package fabmetheus_utilities :: Module svg_writer
[hide private]
[frames] | no frames]

Source Code for Module fabmetheus_utilities.svg_writer

  1  """ 
  2  Svg_writer is a class and collection of utilities to read from and write to an svg file. 
  3   
  4  Svg_writer uses the layer_template.svg file in the templates folder in the same folder as svg_writer, to output an svg file. 
  5   
  6  """ 
  7   
  8  from fabmetheus_utilities.vector3 import Vector3 
  9  from fabmetheus_utilities.xml_simple_reader import XMLSimpleReader 
 10  from fabmetheus_utilities import archive 
 11  from fabmetheus_utilities import euclidean 
 12  from fabmetheus_utilities import xml_simple_reader 
 13  from fabmetheus_utilities import xml_simple_writer 
 14  import os, sys, math, StringIO 
 15  from importlib import import_module 
 16   
 17  __author__ = 'Enrique Perez (perez_enrique@yahoo.com)' 
 18  __date__ = '$Date: 2008/02/05 $' 
 19  __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' 
 20   
 21   
 22  globalOriginalTextString = '<!-- Original XML Text:\n' 
 23   
 24  __interpret_plugins_path__ = 'fabmetheus_utilities/fabmetheus_tools/interpret_plugins' 
 25   
26 -def getCarving(fileName):
27 'Get a carving for the file using an import plugin.' 28 29 fileExtension = os.path.splitext(fileName)[1][1:].lower() 30 sys.path.insert(0, __interpret_plugins_path__) 31 pluginModule = import_module(fileExtension) 32 if pluginModule == None: 33 return None 34 return pluginModule.getCarving(fileName)
35
36 -def getCommentElement(xmlElement):
37 'Get a carving for the file using an import plugin.' 38 for childNode in xmlElement.childNodes: 39 if childNode.localName == 'comment': 40 if childNode.text.startswith(globalOriginalTextString): 41 return childNode 42 return None
43
44 -def getSliceDictionary(xmlElement):
45 'Get the metadata slice attribute dictionary.' 46 for metadataElement in xmlElement.getChildNodesByLocalName('metadata'): 47 for childNode in metadataElement.childNodes: 48 if childNode.localName.lower() == 'slice:layers': 49 return childNode.attributeDictionary 50 return {}
51
52 -def getSliceXMLElements(xmlElement):
53 'Get the slice elements.' 54 gXMLElements = xmlElement.getChildNodesByLocalNameRecursively('g') 55 sliceXMLElements = [] 56 for gXMLElement in gXMLElements: 57 if 'id' in gXMLElement.attributeDictionary: 58 idValue = gXMLElement.attributeDictionary['id'].strip() 59 if idValue.startswith('z:'): 60 sliceXMLElements.append(gXMLElement) 61 return sliceXMLElements
62
63 -def getSVGByLoopLayers(addLayerTemplateToSVG, carving, rotatedLoopLayers):
64 'Get the svg text.' 65 if len(rotatedLoopLayers) < 1: 66 return '' 67 decimalPlacesCarried = max(0, 2 - int(math.floor(math.log10(carving.layerThickness)))) 68 svgWriter = SVGWriter( 69 addLayerTemplateToSVG, 70 carving.getCarveCornerMaximum(), 71 carving.getCarveCornerMinimum(), 72 decimalPlacesCarried, 73 carving.getCarveLayerThickness()) 74 return svgWriter.getReplacedSVGTemplate(carving.fileName, 'basic', rotatedLoopLayers, carving.getFabmetheusXML())
75
76 -def setSVGCarvingCorners(cornerMaximum, cornerMinimum, layerThickness, rotatedLoopLayers):
77 'Parse SVG text and store the layers.' 78 for rotatedLoopLayer in rotatedLoopLayers: 79 for loop in rotatedLoopLayer.loops: 80 for point in loop: 81 pointVector3 = Vector3(point.real, point.imag, rotatedLoopLayer.z) 82 cornerMaximum.maximize(pointVector3) 83 cornerMinimum.minimize(pointVector3) 84 halfLayerThickness = 0.5 * layerThickness 85 cornerMaximum.z += halfLayerThickness 86 cornerMinimum.z -= halfLayerThickness
87 88
89 -class SVGWriter:
90 'A base class to get an svg skein from a carving.'
91 - def __init__(self, 92 addLayerTemplateToSVG, 93 cornerMaximum, 94 cornerMinimum, 95 decimalPlacesCarried, 96 layerThickness, 97 perimeterWidth=None):
98 'Initialize.' 99 self.addLayerTemplateToSVG = addLayerTemplateToSVG 100 self.cornerMaximum = cornerMaximum 101 self.cornerMinimum = cornerMinimum 102 self.decimalPlacesCarried = decimalPlacesCarried 103 self.layerThickness = layerThickness 104 self.perimeterWidth = perimeterWidth 105 self.textHeight = 22.5 106 self.unitScale = 3.7
107
108 - def addLayerBegin(self, layerIndex, rotatedLoopLayer):
109 'Add the start lines for the layer.' 110 zRounded = self.getRounded(rotatedLoopLayer.z) 111 self.graphicsCopy = self.graphicsXMLElement.getCopy(zRounded, self.graphicsXMLElement.parentNode) 112 if self.addLayerTemplateToSVG: 113 translateXRounded = self.getRounded(self.controlBoxWidth + self.margin + self.margin) 114 layerTranslateY = self.marginTop 115 layerTranslateY += layerIndex * self.textHeight + (layerIndex + 1) * (self.extent.y * self.unitScale + self.margin) 116 translateYRounded = self.getRounded(layerTranslateY) 117 self.graphicsCopy.attributeDictionary['transform'] = 'translate(%s, %s)' % (translateXRounded, translateYRounded) 118 layerString = 'Layer %s, z:%s' % (layerIndex, zRounded) 119 self.graphicsCopy.getFirstChildByLocalName('text').text = layerString 120 self.graphicsCopy.attributeDictionary['inkscape:groupmode'] = 'layer' 121 self.graphicsCopy.attributeDictionary['inkscape:label'] = layerString 122 self.pathXMLElement = self.graphicsCopy.getFirstChildByLocalName('path') 123 self.pathDictionary = self.pathXMLElement.attributeDictionary
124
125 - def addOriginalAsComment(self, xmlElement):
126 'Add original xmlElement as a comment.' 127 if xmlElement == None: 128 return 129 if xmlElement.localName == 'comment': 130 xmlElement.setParentAddToChildNodes(self.svgElement) 131 return 132 commentElement = xml_simple_reader.XMLElement() 133 commentElement.localName = 'comment' 134 xmlElementOutput = StringIO.StringIO() 135 xmlElement.addXML(0, xmlElementOutput) 136 textLines = archive.getTextLines(xmlElementOutput.getvalue()) 137 commentElementOutput = StringIO.StringIO() 138 isComment = False 139 for textLine in textLines: 140 lineStripped = textLine.strip() 141 if lineStripped[: len('<!--')] == '<!--': 142 isComment = True 143 if not isComment: 144 if len(textLine) > 0: 145 commentElementOutput.write(textLine + '\n') 146 if '-->' in lineStripped: 147 isComment = False 148 commentElement.text = '%s%s-->\n' % (globalOriginalTextString, commentElementOutput.getvalue()) 149 commentElement.setParentAddToChildNodes(self.svgElement)
150
151 - def addRotatedLoopLayerToOutput(self, layerIndex, rotatedLoopLayer):
152 'Add rotated boundary layer to the output.' 153 self.addLayerBegin(layerIndex, rotatedLoopLayer) 154 if rotatedLoopLayer.rotation != None: 155 self.graphicsCopy.attributeDictionary['bridgeRotation'] = str(rotatedLoopLayer.rotation) 156 if self.addLayerTemplateToSVG: 157 self.pathDictionary['transform'] = self.getTransformString() 158 else: 159 del self.pathDictionary['transform'] 160 self.pathDictionary['d'] = self.getSVGStringForLoops(rotatedLoopLayer.loops)
161
162 - def addRotatedLoopLayersToOutput(self, rotatedLoopLayers):
163 'Add rotated boundary layers to the output.' 164 for rotatedLoopLayerIndex, rotatedLoopLayer in enumerate(rotatedLoopLayers): 165 self.addRotatedLoopLayerToOutput(rotatedLoopLayerIndex, rotatedLoopLayer)
166
167 - def getReplacedSVGTemplate(self, fileName, procedureName, rotatedLoopLayers, xmlElement=None):
168 'Get the lines of text from the layer_template.svg file.' 169 self.extent = self.cornerMaximum - self.cornerMinimum 170 svgTemplateText = archive.getFileText(archive.getTemplatesPath('layer_template.svg')) 171 self.xmlParser = XMLSimpleReader( fileName, None, svgTemplateText ) 172 self.svgElement = self.xmlParser.getRoot() 173 svgElementDictionary = self.svgElement.attributeDictionary 174 self.sliceDictionary = getSliceDictionary(self.svgElement) 175 self.controlBoxHeight = float(self.sliceDictionary['controlBoxHeight']) 176 self.controlBoxWidth = float(self.sliceDictionary['controlBoxWidth']) 177 self.margin = float(self.sliceDictionary['margin']) 178 self.marginTop = float(self.sliceDictionary['marginTop']) 179 self.textHeight = float(self.sliceDictionary['textHeight']) 180 self.unitScale = float(self.sliceDictionary['unitScale']) 181 svgMinWidth = float(self.sliceDictionary['svgMinWidth']) 182 self.controlBoxHeightMargin = self.controlBoxHeight + self.marginTop 183 if not self.addLayerTemplateToSVG: 184 self.svgElement.getXMLElementByID('layerTextTemplate').removeFromIDNameParent() 185 del self.svgElement.getXMLElementByID('sliceElementTemplate').attributeDictionary['transform'] 186 self.graphicsXMLElement = self.svgElement.getXMLElementByID('sliceElementTemplate') 187 self.graphicsXMLElement.attributeDictionary['id'] = 'z:' 188 self.addRotatedLoopLayersToOutput(rotatedLoopLayers) 189 self.setMetadataNoscriptElement('layerThickness', 'Layer Thickness: ', self.layerThickness) 190 self.setMetadataNoscriptElement('maxX', 'X: ', self.cornerMaximum.x) 191 self.setMetadataNoscriptElement('minX', 'X: ', self.cornerMinimum.x) 192 self.setMetadataNoscriptElement('maxY', 'Y: ', self.cornerMaximum.y) 193 self.setMetadataNoscriptElement('minY', 'Y: ', self.cornerMinimum.y) 194 self.setMetadataNoscriptElement('maxZ', 'Z: ', self.cornerMaximum.z) 195 self.setMetadataNoscriptElement('minZ', 'Z: ', self.cornerMinimum.z) 196 self.textHeight = float( self.sliceDictionary['textHeight'] ) 197 controlTop = len(rotatedLoopLayers) * (self.margin + self.extent.y * self.unitScale + self.textHeight) + self.marginTop + self.textHeight 198 self.svgElement.getFirstChildByLocalName('title').text = os.path.basename(fileName) + ' - Slice Layers' 199 svgElementDictionary['height'] = '%spx' % self.getRounded(max(controlTop, self.controlBoxHeightMargin)) 200 width = max(self.extent.x * self.unitScale, svgMinWidth) 201 svgElementDictionary['width'] = '%spx' % self.getRounded( width ) 202 self.sliceDictionary['decimalPlacesCarried'] = str( self.decimalPlacesCarried ) 203 if self.perimeterWidth != None: 204 self.sliceDictionary['perimeterWidth'] = self.getRounded( self.perimeterWidth ) 205 self.sliceDictionary['yAxisPointingUpward'] = 'true' 206 self.sliceDictionary['procedureName'] = procedureName 207 self.setDimensionTexts('dimX', 'X: ' + self.getRounded(self.extent.x)) 208 self.setDimensionTexts('dimY', 'Y: ' + self.getRounded(self.extent.y)) 209 self.setDimensionTexts('dimZ', 'Z: ' + self.getRounded(self.extent.z)) 210 self.setTexts('numberOfLayers', 'Number of Layers: %s' % len(rotatedLoopLayers)) 211 volume = 0.0 212 for rotatedLoopLayer in rotatedLoopLayers: 213 volume += euclidean.getAreaLoops(rotatedLoopLayer.loops) 214 volume *= 0.001 215 self.setTexts('volume', 'Volume: %s cm3' % self.getRounded(volume)) 216 if not self.addLayerTemplateToSVG: 217 self.svgElement.getFirstChildByLocalName('script').removeFromIDNameParent() 218 self.svgElement.getXMLElementByID('isoControlBox').removeFromIDNameParent() 219 self.svgElement.getXMLElementByID('layerControlBox').removeFromIDNameParent() 220 self.svgElement.getXMLElementByID('scrollControlBox').removeFromIDNameParent() 221 self.graphicsXMLElement.removeFromIDNameParent() 222 self.addOriginalAsComment(xmlElement) 223 output = StringIO.StringIO() 224 output.write(self.xmlParser.beforeRoot) 225 self.svgElement.addXML(0, output) 226 return xml_simple_writer.getBeforeRootOutput(self.xmlParser)
227
228 - def getRounded(self, number):
229 'Get number rounded to the number of carried decimal places as a string.' 230 return euclidean.getRoundedToPlacesString(self.decimalPlacesCarried, number)
231
232 - def getRoundedComplexString(self, point):
233 'Get the rounded complex string.' 234 return self.getRounded( point.real ) + ' ' + self.getRounded( point.imag )
235
236 - def getSVGStringForLoop( self, loop ):
237 'Get the svg loop string.' 238 if len(loop) < 1: 239 return '' 240 return self.getSVGStringForPath(loop) + ' z'
241
242 - def getSVGStringForLoops( self, loops ):
243 'Get the svg loops string.' 244 loopString = '' 245 if len(loops) > 0: 246 loopString += self.getSVGStringForLoop( loops[0] ) 247 for loop in loops[1 :]: 248 loopString += ' ' + self.getSVGStringForLoop(loop) 249 return loopString
250
251 - def getSVGStringForPath( self, path ):
252 'Get the svg path string.' 253 svgLoopString = '' 254 for point in path: 255 stringBeginning = 'M ' 256 if len( svgLoopString ) > 0: 257 stringBeginning = ' L ' 258 svgLoopString += stringBeginning + self.getRoundedComplexString(point) 259 return svgLoopString
260
261 - def setMetadataNoscriptElement(self, key, prefix, value):
262 'Set the metadata value and the text.' 263 valueString = self.getRounded(value) 264 self.sliceDictionary[key] = valueString 265 self.setDimensionTexts(key, prefix + valueString)
266
267 - def setDimensionTexts(self, key, valueString):
268 'Set the texts to the valueString followed by mm.' 269 self.setTexts(key, valueString + ' mm')
270
271 - def setTexts(self, key, valueString):
272 'Set the texts to the valueString.' 273 self.svgElement.getXMLElementByID(key + 'Iso').text = valueString 274 self.svgElement.getXMLElementByID(key + 'Layer').text = valueString 275 self.svgElement.getXMLElementByID(key + 'Scroll').text = valueString
276
277 - def getTransformString(self):
278 'Get the svg transform string.' 279 cornerMinimumXString = self.getRounded(-self.cornerMinimum.x) 280 cornerMinimumYString = self.getRounded(-self.cornerMinimum.y) 281 return 'scale(%s, %s) translate(%s, %s)' % (self.unitScale, - self.unitScale, cornerMinimumXString, cornerMinimumYString)
282