955 lines
38 KiB
Python
955 lines
38 KiB
Python
"""
|
|
Svg reader.
|
|
|
|
"""
|
|
|
|
|
|
from __future__ import absolute_import
|
|
#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.
|
|
import __init__
|
|
|
|
from fabmetheus_utilities.geometry.solids import triangle_mesh
|
|
from fabmetheus_utilities.xml_simple_reader import DocumentNode
|
|
from fabmetheus_utilities import archive
|
|
from fabmetheus_utilities import euclidean
|
|
from fabmetheus_utilities import gcodec
|
|
from fabmetheus_utilities import intercircle
|
|
from fabmetheus_utilities import settings
|
|
from fabmetheus_utilities import svg_writer
|
|
import math
|
|
import os
|
|
import sys
|
|
import traceback
|
|
|
|
|
|
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
|
|
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
|
|
__date__ = '$Date: 2008/21/04 $'
|
|
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
|
|
|
|
|
globalNumberOfCornerPoints = 11
|
|
globalNumberOfBezierPoints = globalNumberOfCornerPoints + globalNumberOfCornerPoints
|
|
globalNumberOfCirclePoints = 4 * globalNumberOfCornerPoints
|
|
|
|
|
|
def addFunctionsToDictionary( dictionary, functions, prefix ):
|
|
"Add functions to dictionary."
|
|
for function in functions:
|
|
dictionary[ function.__name__[ len( prefix ) : ] ] = function
|
|
|
|
def getArcComplexes(begin, end, largeArcFlag, radius, sweepFlag, xAxisRotation):
|
|
'Get the arc complexes, procedure at http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes'
|
|
if begin == end:
|
|
print('Warning, begin equals end in getArcComplexes in svgReader')
|
|
print(begin)
|
|
print(end)
|
|
return []
|
|
if radius.imag < 0.0:
|
|
print('Warning, radius.imag is less than zero in getArcComplexes in svgReader')
|
|
print(radius)
|
|
radius = complex(radius.real, abs(radius.imag))
|
|
if radius.real < 0.0:
|
|
print('Warning, radius.real is less than zero in getArcComplexes in svgReader')
|
|
print(radius)
|
|
radius = complex(abs(radius.real), radius.imag)
|
|
if radius.imag <= 0.0:
|
|
print('Warning, radius.imag is too small for getArcComplexes in svgReader')
|
|
print(radius)
|
|
return [end]
|
|
if radius.real <= 0.0:
|
|
print('Warning, radius.real is too small for getArcComplexes in svgReader')
|
|
print(radius)
|
|
return [end]
|
|
xAxisRotationComplex = euclidean.getWiddershinsUnitPolar(xAxisRotation)
|
|
reverseXAxisRotationComplex = complex(xAxisRotationComplex.real, -xAxisRotationComplex.imag)
|
|
beginRotated = begin * reverseXAxisRotationComplex
|
|
endRotated = end * reverseXAxisRotationComplex
|
|
beginTransformed = complex(beginRotated.real / radius.real, beginRotated.imag / radius.imag)
|
|
endTransformed = complex(endRotated.real / radius.real, endRotated.imag / radius.imag)
|
|
midpointTransformed = 0.5 * (beginTransformed + endTransformed)
|
|
midMinusBeginTransformed = midpointTransformed - beginTransformed
|
|
midMinusBeginTransformedLength = abs(midMinusBeginTransformed)
|
|
if midMinusBeginTransformedLength > 1.0:
|
|
print('The ellipse radius is too small for getArcComplexes in svgReader.')
|
|
print('So the ellipse will be scaled to fit, according to the formulas in "Step 3: Ensure radii are large enough" of:')
|
|
print('http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii')
|
|
print('')
|
|
radius *= midMinusBeginTransformedLength
|
|
beginTransformed /= midMinusBeginTransformedLength
|
|
endTransformed /= midMinusBeginTransformedLength
|
|
midpointTransformed /= midMinusBeginTransformedLength
|
|
midMinusBeginTransformed /= midMinusBeginTransformedLength
|
|
midMinusBeginTransformedLength = 1.0
|
|
midWiddershinsTransformed = complex(-midMinusBeginTransformed.imag, midMinusBeginTransformed.real)
|
|
midWiddershinsLengthSquared = 1.0 - midMinusBeginTransformedLength * midMinusBeginTransformedLength
|
|
if midWiddershinsLengthSquared < 0.0:
|
|
midWiddershinsLengthSquared = 0.0
|
|
midWiddershinsLength = math.sqrt(midWiddershinsLengthSquared)
|
|
midWiddershinsTransformed *= midWiddershinsLength / abs(midWiddershinsTransformed)
|
|
centerTransformed = midpointTransformed
|
|
if largeArcFlag == sweepFlag:
|
|
centerTransformed -= midWiddershinsTransformed
|
|
else:
|
|
centerTransformed += midWiddershinsTransformed
|
|
beginMinusCenterTransformed = beginTransformed - centerTransformed
|
|
beginMinusCenterTransformedLength = abs(beginMinusCenterTransformed)
|
|
if beginMinusCenterTransformedLength <= 0.0:
|
|
return end
|
|
beginAngle = math.atan2(beginMinusCenterTransformed.imag, beginMinusCenterTransformed.real)
|
|
endMinusCenterTransformed = endTransformed - centerTransformed
|
|
angleDifference = euclidean.getAngleDifferenceByComplex(endMinusCenterTransformed, beginMinusCenterTransformed)
|
|
if sweepFlag:
|
|
if angleDifference < 0.0:
|
|
angleDifference += 2.0 * math.pi
|
|
else:
|
|
if angleDifference > 0.0:
|
|
angleDifference -= 2.0 * math.pi
|
|
global globalSideAngle
|
|
sides = int(math.ceil(abs(angleDifference) / globalSideAngle))
|
|
sideAngle = angleDifference / float(sides)
|
|
arcComplexes = []
|
|
center = complex(centerTransformed.real * radius.real, centerTransformed.imag * radius.imag) * xAxisRotationComplex
|
|
for side in xrange(1, sides):
|
|
unitPolar = euclidean.getWiddershinsUnitPolar(beginAngle + float(side) * sideAngle)
|
|
circumferential = complex(unitPolar.real * radius.real, unitPolar.imag * radius.imag) * beginMinusCenterTransformedLength
|
|
point = center + circumferential * xAxisRotationComplex
|
|
arcComplexes.append(point)
|
|
arcComplexes.append(end)
|
|
return arcComplexes
|
|
|
|
def getChainMatrixSVG(elementNode, matrixSVG):
|
|
"Get chain matrixSVG by svgElement."
|
|
matrixSVG = matrixSVG.getOtherTimesSelf(getMatrixSVG(elementNode).tricomplex)
|
|
if elementNode.parentNode != None:
|
|
matrixSVG = getChainMatrixSVG(elementNode.parentNode, matrixSVG)
|
|
return matrixSVG
|
|
|
|
def getChainMatrixSVGIfNecessary(elementNode, yAxisPointingUpward):
|
|
"Get chain matrixSVG by svgElement and yAxisPointingUpward."
|
|
matrixSVG = MatrixSVG()
|
|
if yAxisPointingUpward:
|
|
return matrixSVG
|
|
return getChainMatrixSVG(elementNode, matrixSVG)
|
|
|
|
def getCubicPoint( along, begin, controlPoints, end ):
|
|
'Get the cubic point.'
|
|
segmentBegin = getQuadraticPoint( along, begin, controlPoints[0], controlPoints[1] )
|
|
segmentEnd = getQuadraticPoint( along, controlPoints[0], controlPoints[1], end )
|
|
return ( 1.0 - along ) * segmentBegin + along * segmentEnd
|
|
|
|
def getCubicPoints( begin, controlPoints, end, numberOfBezierPoints=globalNumberOfBezierPoints):
|
|
'Get the cubic points.'
|
|
bezierPortion = 1.0 / float(numberOfBezierPoints)
|
|
cubicPoints = []
|
|
for bezierIndex in xrange( 1, numberOfBezierPoints + 1 ):
|
|
cubicPoints.append(getCubicPoint(bezierPortion * bezierIndex, begin, controlPoints, end))
|
|
return cubicPoints
|
|
|
|
def getFontReader(fontFamily):
|
|
'Get the font reader for the fontFamily.'
|
|
fontLower = fontFamily.lower().replace(' ', '_')
|
|
global globalFontReaderDictionary
|
|
if fontLower in globalFontReaderDictionary:
|
|
return globalFontReaderDictionary[fontLower]
|
|
global globalFontFileNames
|
|
if globalFontFileNames == None:
|
|
globalFontFileNames = archive.getFileNamesByFilePaths(archive.getFilePathsByDirectory(getFontsDirectoryPath()))
|
|
if fontLower not in globalFontFileNames:
|
|
print('Warning, the %s font was not found in the fabmetheus_utilities/fonts folder, so Gentium Basic Regular will be substituted.' % fontFamily)
|
|
print('The available fonts are:')
|
|
globalFontFileNames.sort()
|
|
print(globalFontFileNames)
|
|
print('')
|
|
fontLower = 'gentium_basic_regular'
|
|
fontReader = FontReader(fontLower)
|
|
globalFontReaderDictionary[fontLower] = fontReader
|
|
return fontReader
|
|
|
|
def getFontsDirectoryPath():
|
|
"Get the fonts directory path."
|
|
return archive.getFabmetheusUtilitiesPath('fonts')
|
|
|
|
def getLabelString(dictionary):
|
|
"Get the label string for the dictionary."
|
|
for key in dictionary:
|
|
labelIndex = key.find('label')
|
|
if labelIndex >= 0:
|
|
return dictionary[key]
|
|
return ''
|
|
|
|
def getMatrixSVG(elementNode):
|
|
"Get matrixSVG by svgElement."
|
|
matrixSVG = MatrixSVG()
|
|
if 'transform' not in elementNode.attributes:
|
|
return matrixSVG
|
|
transformWords = []
|
|
for transformWord in elementNode.attributes['transform'].replace(')', '(').split('('):
|
|
transformWordStrip = transformWord.strip()
|
|
if transformWordStrip != '': # workaround for split(character) bug which leaves an extra empty element
|
|
transformWords.append(transformWordStrip)
|
|
global globalGetTricomplexDictionary
|
|
getTricomplexDictionaryKeys = globalGetTricomplexDictionary.keys()
|
|
for transformWordIndex, transformWord in enumerate(transformWords):
|
|
if transformWord in getTricomplexDictionaryKeys:
|
|
transformString = transformWords[transformWordIndex + 1].replace(',', ' ')
|
|
matrixSVG = matrixSVG.getSelfTimesOther(globalGetTricomplexDictionary[ transformWord ](transformString.split()))
|
|
return matrixSVG
|
|
|
|
def getQuadraticPoint( along, begin, controlPoint, end ):
|
|
'Get the quadratic point.'
|
|
oneMinusAlong = 1.0 - along
|
|
segmentBegin = oneMinusAlong * begin + along * controlPoint
|
|
segmentEnd = oneMinusAlong * controlPoint + along * end
|
|
return oneMinusAlong * segmentBegin + along * segmentEnd
|
|
|
|
def getQuadraticPoints(begin, controlPoint, end, numberOfBezierPoints=globalNumberOfBezierPoints):
|
|
'Get the quadratic points.'
|
|
bezierPortion = 1.0 / float(numberOfBezierPoints)
|
|
quadraticPoints = []
|
|
for bezierIndex in xrange(1, numberOfBezierPoints + 1):
|
|
quadraticPoints.append(getQuadraticPoint(bezierPortion * bezierIndex, begin, controlPoint, end))
|
|
return quadraticPoints
|
|
|
|
def getRightStripAlphabetPercent(word):
|
|
"Get word with alphabet characters and the percent sign stripped from the right."
|
|
word = word.strip()
|
|
for characterIndex in xrange(len(word) - 1, -1, -1):
|
|
character = word[characterIndex]
|
|
if not character.isalpha() and not character == '%':
|
|
return float(word[: characterIndex + 1])
|
|
return None
|
|
|
|
def getRightStripMinusSplit(lineString):
|
|
"Get string with spaces after the minus sign stripped."
|
|
oldLineStringLength = -1
|
|
while oldLineStringLength < len(lineString):
|
|
oldLineStringLength = len(lineString)
|
|
lineString = lineString.replace('- ', '-')
|
|
return lineString.split()
|
|
|
|
def getStrokeRadius(elementNode):
|
|
"Get the stroke radius."
|
|
return 0.5 * getRightStripAlphabetPercent(getStyleValue('1.0', elementNode, 'stroke-width'))
|
|
|
|
def getStyleValue(defaultValue, elementNode, key):
|
|
"Get the stroke value string."
|
|
if 'style' in elementNode.attributes:
|
|
line = elementNode.attributes['style']
|
|
strokeIndex = line.find(key)
|
|
if strokeIndex > -1:
|
|
words = line[strokeIndex :].replace(':', ' ').replace(';', ' ').split()
|
|
if len(words) > 1:
|
|
return words[1]
|
|
if key in elementNode.attributes:
|
|
return elementNode.attributes[key]
|
|
if elementNode.parentNode == None:
|
|
return defaultValue
|
|
return getStyleValue(defaultValue, elementNode.parentNode, key)
|
|
|
|
def getTextComplexLoops(fontFamily, fontSize, text, yAxisPointingUpward=True):
|
|
"Get text as complex loops."
|
|
textComplexLoops = []
|
|
fontReader = getFontReader(fontFamily)
|
|
horizontalAdvanceX = 0.0
|
|
for character in text:
|
|
glyph = fontReader.getGlyph(character, yAxisPointingUpward)
|
|
textComplexLoops += glyph.getSizedAdvancedLoops(fontSize, horizontalAdvanceX, yAxisPointingUpward)
|
|
horizontalAdvanceX += glyph.horizontalAdvanceX
|
|
return textComplexLoops
|
|
|
|
def getTransformedFillOutline(elementNode, loop, yAxisPointingUpward):
|
|
"Get the loops if fill is on, otherwise get the outlines."
|
|
fillOutlineLoops = None
|
|
if getStyleValue('none', elementNode, 'fill').lower() == 'none':
|
|
fillOutlineLoops = intercircle.getAroundsFromLoop(loop, getStrokeRadius(elementNode))
|
|
else:
|
|
fillOutlineLoops = [loop]
|
|
return getChainMatrixSVGIfNecessary(elementNode, yAxisPointingUpward).getTransformedPaths(fillOutlineLoops)
|
|
|
|
def getTransformedOutlineByPath(elementNode, path, yAxisPointingUpward):
|
|
"Get the outline from the path."
|
|
aroundsFromPath = intercircle.getAroundsFromPath(path, getStrokeRadius(elementNode))
|
|
return getChainMatrixSVGIfNecessary(elementNode, yAxisPointingUpward).getTransformedPaths(aroundsFromPath)
|
|
|
|
def getTransformedOutlineByPaths(elementNode, paths, yAxisPointingUpward):
|
|
"Get the outline from the paths."
|
|
aroundsFromPaths = intercircle.getAroundsFromPaths(paths, getStrokeRadius(elementNode))
|
|
return getChainMatrixSVGIfNecessary(elementNode, yAxisPointingUpward).getTransformedPaths(aroundsFromPaths)
|
|
|
|
def getTricomplexmatrix(transformWords):
|
|
"Get matrixSVG by transformWords."
|
|
tricomplex = [euclidean.getComplexByWords(transformWords)]
|
|
tricomplex.append(euclidean.getComplexByWords(transformWords, 2))
|
|
tricomplex.append(euclidean.getComplexByWords(transformWords, 4))
|
|
return tricomplex
|
|
|
|
def getTricomplexrotate(transformWords):
|
|
"Get matrixSVG by transformWords."
|
|
rotate = euclidean.getWiddershinsUnitPolar(math.radians(float(transformWords[0])))
|
|
return [rotate, complex(-rotate.imag,rotate.real), complex()]
|
|
|
|
def getTricomplexscale(transformWords):
|
|
"Get matrixSVG by transformWords."
|
|
scale = euclidean.getComplexByWords(transformWords)
|
|
return [complex(scale.real,0.0), complex(0.0,scale.imag), complex()]
|
|
|
|
def getTricomplexskewX(transformWords):
|
|
"Get matrixSVG by transformWords."
|
|
skewX = math.tan(math.radians(float(transformWords[0])))
|
|
return [complex(1.0, 0.0), complex(skewX, 1.0), complex()]
|
|
|
|
def getTricomplexskewY(transformWords):
|
|
"Get matrixSVG by transformWords."
|
|
skewY = math.tan(math.radians(float(transformWords[0])))
|
|
return [complex(1.0, skewY), complex(0.0, 1.0), complex()]
|
|
|
|
def getTricomplexTimesColumn(firstTricomplex, otherColumn):
|
|
"Get this matrix multiplied by the otherColumn."
|
|
dotProductX = firstTricomplex[0].real * otherColumn.real + firstTricomplex[1].real * otherColumn.imag
|
|
dotProductY = firstTricomplex[0].imag * otherColumn.real + firstTricomplex[1].imag * otherColumn.imag
|
|
return complex(dotProductX, dotProductY)
|
|
|
|
def getTricomplexTimesOther(firstTricomplex, otherTricomplex):
|
|
"Get the first tricomplex multiplied by the other tricomplex."
|
|
#A down, B right from http://en.wikipedia.org/wiki/Matrix_multiplication
|
|
tricomplexTimesOther = [getTricomplexTimesColumn(firstTricomplex, otherTricomplex[0])]
|
|
tricomplexTimesOther.append(getTricomplexTimesColumn(firstTricomplex, otherTricomplex[1]))
|
|
tricomplexTimesOther.append(getTricomplexTimesColumn(firstTricomplex, otherTricomplex[2]) + firstTricomplex[2])
|
|
return tricomplexTimesOther
|
|
|
|
def getTricomplextranslate(transformWords):
|
|
"Get matrixSVG by transformWords."
|
|
translate = euclidean.getComplexByWords(transformWords)
|
|
return [complex(1.0, 0.0), complex(0.0, 1.0), translate]
|
|
|
|
def processSVGElementcircle( elementNode, svgReader ):
|
|
"Process elementNode by svgReader."
|
|
attributes = elementNode.attributes
|
|
center = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributes, 'cx', 'cy')
|
|
radius = euclidean.getFloatDefaultByDictionary( 0.0, attributes, 'r')
|
|
if radius == 0.0:
|
|
print('Warning, in processSVGElementcircle in svgReader radius is zero in:')
|
|
print(attributes)
|
|
return
|
|
global globalNumberOfCirclePoints
|
|
global globalSideAngle
|
|
loop = []
|
|
loopLayer = svgReader.getLoopLayer()
|
|
for side in xrange( globalNumberOfCirclePoints ):
|
|
unitPolar = euclidean.getWiddershinsUnitPolar( float(side) * globalSideAngle )
|
|
loop.append( center + radius * unitPolar )
|
|
loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward)
|
|
|
|
def processSVGElementellipse( elementNode, svgReader ):
|
|
"Process elementNode by svgReader."
|
|
attributes = elementNode.attributes
|
|
center = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributes, 'cx', 'cy')
|
|
radius = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributes, 'rx', 'ry')
|
|
if radius.real == 0.0 or radius.imag == 0.0:
|
|
print('Warning, in processSVGElementellipse in svgReader radius is zero in:')
|
|
print(attributes)
|
|
return
|
|
global globalNumberOfCirclePoints
|
|
global globalSideAngle
|
|
loop = []
|
|
loopLayer = svgReader.getLoopLayer()
|
|
for side in xrange( globalNumberOfCirclePoints ):
|
|
unitPolar = euclidean.getWiddershinsUnitPolar( float(side) * globalSideAngle )
|
|
loop.append( center + complex( unitPolar.real * radius.real, unitPolar.imag * radius.imag ) )
|
|
loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward)
|
|
|
|
def processSVGElementg(elementNode, svgReader):
|
|
'Process elementNode by svgReader.'
|
|
if 'id' not in elementNode.attributes:
|
|
return
|
|
idString = elementNode.attributes['id']
|
|
if 'beginningOfControlSection' in elementNode.attributes:
|
|
if elementNode.attributes['beginningOfControlSection'].lower()[: 1] == 't':
|
|
svgReader.stopProcessing = True
|
|
return
|
|
idStringLower = idString.lower()
|
|
zIndex = idStringLower.find('z:')
|
|
if zIndex < 0:
|
|
idStringLower = getLabelString(elementNode.attributes)
|
|
zIndex = idStringLower.find('z:')
|
|
if zIndex < 0:
|
|
return
|
|
floatFromValue = euclidean.getFloatFromValue(idStringLower[zIndex + len('z:') :].strip())
|
|
if floatFromValue != None:
|
|
svgReader.z = floatFromValue
|
|
|
|
def processSVGElementline(elementNode, svgReader):
|
|
"Process elementNode by svgReader."
|
|
begin = euclidean.getComplexDefaultByDictionaryKeys(complex(), elementNode.attributes, 'x1', 'y1')
|
|
end = euclidean.getComplexDefaultByDictionaryKeys(complex(), elementNode.attributes, 'x2', 'y2')
|
|
loopLayer = svgReader.getLoopLayer()
|
|
loopLayer.loops += getTransformedOutlineByPath(elementNode, [begin, end], svgReader.yAxisPointingUpward)
|
|
|
|
def processSVGElementpath( elementNode, svgReader ):
|
|
"Process elementNode by svgReader."
|
|
if 'd' not in elementNode.attributes:
|
|
print('Warning, in processSVGElementpath in svgReader can not get a value for d in:')
|
|
print(elementNode.attributes)
|
|
return
|
|
loopLayer = svgReader.getLoopLayer()
|
|
PathReader(elementNode, loopLayer.loops, svgReader.yAxisPointingUpward)
|
|
|
|
def processSVGElementpolygon( elementNode, svgReader ):
|
|
"Process elementNode by svgReader."
|
|
if 'points' not in elementNode.attributes:
|
|
print('Warning, in processSVGElementpolygon in svgReader can not get a value for d in:')
|
|
print(elementNode.attributes)
|
|
return
|
|
loopLayer = svgReader.getLoopLayer()
|
|
words = getRightStripMinusSplit(elementNode.attributes['points'].replace(',', ' '))
|
|
loop = []
|
|
for wordIndex in xrange( 0, len(words), 2 ):
|
|
loop.append(euclidean.getComplexByWords(words[wordIndex :]))
|
|
loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward)
|
|
|
|
def processSVGElementpolyline(elementNode, svgReader):
|
|
"Process elementNode by svgReader."
|
|
if 'points' not in elementNode.attributes:
|
|
print('Warning, in processSVGElementpolyline in svgReader can not get a value for d in:')
|
|
print(elementNode.attributes)
|
|
return
|
|
loopLayer = svgReader.getLoopLayer()
|
|
words = getRightStripMinusSplit(elementNode.attributes['points'].replace(',', ' '))
|
|
path = []
|
|
for wordIndex in xrange(0, len(words), 2):
|
|
path.append(euclidean.getComplexByWords(words[wordIndex :]))
|
|
loopLayer.loops += getTransformedOutlineByPath(elementNode, path, svgReader.yAxisPointingUpward)
|
|
|
|
def processSVGElementrect( elementNode, svgReader ):
|
|
"Process elementNode by svgReader."
|
|
attributes = elementNode.attributes
|
|
height = euclidean.getFloatDefaultByDictionary( 0.0, attributes, 'height')
|
|
if height == 0.0:
|
|
print('Warning, in processSVGElementrect in svgReader height is zero in:')
|
|
print(attributes)
|
|
return
|
|
width = euclidean.getFloatDefaultByDictionary( 0.0, attributes, 'width')
|
|
if width == 0.0:
|
|
print('Warning, in processSVGElementrect in svgReader width is zero in:')
|
|
print(attributes)
|
|
return
|
|
center = euclidean.getComplexDefaultByDictionaryKeys(complex(), attributes, 'x', 'y')
|
|
inradius = 0.5 * complex( width, height )
|
|
cornerRadius = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributes, 'rx', 'ry')
|
|
loopLayer = svgReader.getLoopLayer()
|
|
if cornerRadius.real == 0.0 and cornerRadius.imag == 0.0:
|
|
inradiusMinusX = complex( - inradius.real, inradius.imag )
|
|
loop = [center + inradius, center + inradiusMinusX, center - inradius, center - inradiusMinusX]
|
|
loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward)
|
|
return
|
|
if cornerRadius.real == 0.0:
|
|
cornerRadius = complex( cornerRadius.imag, cornerRadius.imag )
|
|
elif cornerRadius.imag == 0.0:
|
|
cornerRadius = complex( cornerRadius.real, cornerRadius.real )
|
|
cornerRadius = complex( min( cornerRadius.real, inradius.real ), min( cornerRadius.imag, inradius.imag ) )
|
|
ellipsePath = [ complex( cornerRadius.real, 0.0 ) ]
|
|
inradiusMinusCorner = inradius - cornerRadius
|
|
loop = []
|
|
global globalNumberOfCornerPoints
|
|
global globalSideAngle
|
|
for side in xrange( 1, globalNumberOfCornerPoints ):
|
|
unitPolar = euclidean.getWiddershinsUnitPolar( float(side) * globalSideAngle )
|
|
ellipsePath.append( complex( unitPolar.real * cornerRadius.real, unitPolar.imag * cornerRadius.imag ) )
|
|
ellipsePath.append( complex( 0.0, cornerRadius.imag ) )
|
|
cornerPoints = []
|
|
for point in ellipsePath:
|
|
cornerPoints.append( point + inradiusMinusCorner )
|
|
cornerPointsReversed = cornerPoints[: : -1]
|
|
for cornerPoint in cornerPoints:
|
|
loop.append( center + cornerPoint )
|
|
for cornerPoint in cornerPointsReversed:
|
|
loop.append( center + complex( - cornerPoint.real, cornerPoint.imag ) )
|
|
for cornerPoint in cornerPoints:
|
|
loop.append( center - cornerPoint )
|
|
for cornerPoint in cornerPointsReversed:
|
|
loop.append( center + complex( cornerPoint.real, - cornerPoint.imag ) )
|
|
loop = euclidean.getLoopWithoutCloseSequentialPoints( 0.0001 * abs(inradius), loop )
|
|
loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward)
|
|
|
|
def processSVGElementtext(elementNode, svgReader):
|
|
"Process elementNode by svgReader."
|
|
if svgReader.yAxisPointingUpward:
|
|
return
|
|
fontFamily = getStyleValue('Gentium Basic Regular', elementNode, 'font-family')
|
|
fontSize = getRightStripAlphabetPercent(getStyleValue('12.0', elementNode, 'font-size'))
|
|
matrixSVG = getChainMatrixSVGIfNecessary(elementNode, svgReader.yAxisPointingUpward)
|
|
loopLayer = svgReader.getLoopLayer()
|
|
translate = euclidean.getComplexDefaultByDictionaryKeys(complex(), elementNode.attributes, 'x', 'y')
|
|
for textComplexLoop in getTextComplexLoops(fontFamily, fontSize, elementNode.getTextContent(), svgReader.yAxisPointingUpward):
|
|
translatedLoop = []
|
|
for textComplexPoint in textComplexLoop:
|
|
translatedLoop.append(textComplexPoint + translate )
|
|
loopLayer.loops.append(matrixSVG.getTransformedPath(translatedLoop))
|
|
|
|
|
|
class FontReader:
|
|
"Class to read a font in the fonts folder."
|
|
def __init__(self, fontFamily):
|
|
"Initialize."
|
|
self.fontFamily = fontFamily
|
|
self.glyphDictionary = {}
|
|
self.glyphElementNodeDictionary = {}
|
|
self.missingGlyph = None
|
|
fileName = os.path.join(getFontsDirectoryPath(), fontFamily + '.svg')
|
|
documentElement = DocumentNode(fileName, archive.getFileText(fileName)).getDocumentElement()
|
|
self.fontElementNode = documentElement.getFirstChildByLocalName('defs').getFirstChildByLocalName('font')
|
|
self.fontFaceElementNode = self.fontElementNode.getFirstChildByLocalName('font-face')
|
|
self.unitsPerEM = float(self.fontFaceElementNode.attributes['units-per-em'])
|
|
glyphElementNodes = self.fontElementNode.getChildElementsByLocalName('glyph')
|
|
for glyphElementNode in glyphElementNodes:
|
|
self.glyphElementNodeDictionary[glyphElementNode.attributes['unicode']] = glyphElementNode
|
|
|
|
def getGlyph(self, character, yAxisPointingUpward):
|
|
"Get the glyph for the character."
|
|
if character not in self.glyphElementNodeDictionary:
|
|
if self.missingGlyph == None:
|
|
missingGlyphElementNode = self.fontElementNode.getFirstChildByLocalName('missing-glyph')
|
|
self.missingGlyph = Glyph(missingGlyphElementNode, self.unitsPerEM, yAxisPointingUpward)
|
|
return self.missingGlyph
|
|
if character not in self.glyphDictionary:
|
|
self.glyphDictionary[character] = Glyph(self.glyphElementNodeDictionary[character], self.unitsPerEM, yAxisPointingUpward)
|
|
return self.glyphDictionary[character]
|
|
|
|
|
|
class Glyph:
|
|
"Class to handle a glyph."
|
|
def __init__(self, elementNode, unitsPerEM, yAxisPointingUpward):
|
|
"Initialize."
|
|
self.horizontalAdvanceX = float(elementNode.attributes['horiz-adv-x'])
|
|
self.loops = []
|
|
self.unitsPerEM = unitsPerEM
|
|
elementNode.attributes['fill'] = ''
|
|
if 'd' not in elementNode.attributes:
|
|
return
|
|
PathReader(elementNode, self.loops, yAxisPointingUpward)
|
|
|
|
def getSizedAdvancedLoops(self, fontSize, horizontalAdvanceX, yAxisPointingUpward=True):
|
|
"Get loops for font size, advanced horizontally."
|
|
multiplierX = fontSize / self.unitsPerEM
|
|
multiplierY = multiplierX
|
|
if not yAxisPointingUpward:
|
|
multiplierY = -multiplierY
|
|
sizedLoops = []
|
|
for loop in self.loops:
|
|
sizedLoop = []
|
|
sizedLoops.append(sizedLoop)
|
|
for point in loop:
|
|
sizedLoop.append( complex(multiplierX * (point.real + horizontalAdvanceX), multiplierY * point.imag))
|
|
return sizedLoops
|
|
|
|
|
|
class MatrixSVG:
|
|
"Two by three svg matrix."
|
|
def __init__(self, tricomplex=None):
|
|
"Initialize."
|
|
self.tricomplex = tricomplex
|
|
|
|
def __repr__(self):
|
|
"Get the string representation of this two by three svg matrix."
|
|
return str(self.tricomplex)
|
|
|
|
def getOtherTimesSelf(self, otherTricomplex):
|
|
"Get the other matrix multiplied by this matrix."
|
|
if otherTricomplex == None:
|
|
return MatrixSVG(self.tricomplex)
|
|
if self.tricomplex == None:
|
|
return MatrixSVG(otherTricomplex)
|
|
return MatrixSVG(getTricomplexTimesOther(otherTricomplex, self.tricomplex))
|
|
|
|
def getSelfTimesOther(self, otherTricomplex):
|
|
"Get this matrix multiplied by the other matrix."
|
|
if otherTricomplex == None:
|
|
return MatrixSVG(self.tricomplex)
|
|
if self.tricomplex == None:
|
|
return MatrixSVG(otherTricomplex)
|
|
return MatrixSVG(getTricomplexTimesOther(self.tricomplex, otherTricomplex))
|
|
|
|
def getTransformedPath(self, path):
|
|
"Get transformed path."
|
|
if self.tricomplex == None:
|
|
return path
|
|
complexX = self.tricomplex[0]
|
|
complexY = self.tricomplex[1]
|
|
complexTranslation = self.tricomplex[2]
|
|
transformedPath = []
|
|
for point in path:
|
|
x = complexX.real * point.real + complexY.real * point.imag
|
|
y = complexX.imag * point.real + complexY.imag * point.imag
|
|
transformedPath.append(complex(x, y) + complexTranslation)
|
|
return transformedPath
|
|
|
|
def getTransformedPaths(self, paths):
|
|
"Get transformed paths."
|
|
if self.tricomplex == None:
|
|
return paths
|
|
transformedPaths = []
|
|
for path in paths:
|
|
transformedPaths.append(self.getTransformedPath(path))
|
|
return transformedPaths
|
|
|
|
|
|
class PathReader:
|
|
"Class to read svg path."
|
|
def __init__(self, elementNode, loops, yAxisPointingUpward):
|
|
"Add to path string to loops."
|
|
self.controlPoints = None
|
|
self.elementNode = elementNode
|
|
self.loops = loops
|
|
self.oldPoint = None
|
|
self.outlinePaths = []
|
|
self.path = []
|
|
self.yAxisPointingUpward = yAxisPointingUpward
|
|
pathString = elementNode.attributes['d'].replace(',', ' ')
|
|
global globalProcessPathWordDictionary
|
|
processPathWordDictionaryKeys = globalProcessPathWordDictionary.keys()
|
|
for processPathWordDictionaryKey in processPathWordDictionaryKeys:
|
|
pathString = pathString.replace( processPathWordDictionaryKey, ' %s ' % processPathWordDictionaryKey )
|
|
self.words = getRightStripMinusSplit(pathString)
|
|
for self.wordIndex in xrange( len( self.words ) ):
|
|
word = self.words[ self.wordIndex ]
|
|
if word in processPathWordDictionaryKeys:
|
|
globalProcessPathWordDictionary[word](self)
|
|
if len(self.path) > 0:
|
|
self.outlinePaths.append(self.path)
|
|
self.loops += getTransformedOutlineByPaths(elementNode, self.outlinePaths, yAxisPointingUpward)
|
|
|
|
def addPathArc( self, end ):
|
|
"Add an arc to the path."
|
|
begin = self.getOldPoint()
|
|
self.controlPoints = None
|
|
radius = self.getComplexByExtraIndex(1)
|
|
xAxisRotation = math.radians(float(self.words[self.wordIndex + 3]))
|
|
largeArcFlag = euclidean.getBooleanFromValue(self.words[ self.wordIndex + 4 ])
|
|
sweepFlag = euclidean.getBooleanFromValue(self.words[ self.wordIndex + 5 ])
|
|
self.path += getArcComplexes(begin, end, largeArcFlag, radius, sweepFlag, xAxisRotation)
|
|
self.wordIndex += 8
|
|
|
|
def addPathCubic( self, controlPoints, end ):
|
|
"Add a cubic curve to the path."
|
|
begin = self.getOldPoint()
|
|
self.controlPoints = controlPoints
|
|
self.path += getCubicPoints( begin, controlPoints, end )
|
|
self.wordIndex += 7
|
|
|
|
def addPathCubicReflected( self, controlPoint, end ):
|
|
"Add a cubic curve to the path from a reflected control point."
|
|
begin = self.getOldPoint()
|
|
controlPointBegin = begin
|
|
if self.controlPoints != None:
|
|
if len(self.controlPoints) == 2:
|
|
controlPointBegin = begin + begin - self.controlPoints[-1]
|
|
self.controlPoints = [controlPointBegin, controlPoint]
|
|
self.path += getCubicPoints(begin, self.controlPoints, end)
|
|
self.wordIndex += 5
|
|
|
|
def addPathLine(self, lineFunction, point):
|
|
"Add a line to the path."
|
|
self.controlPoints = None
|
|
self.path.append(point)
|
|
self.wordIndex += 3
|
|
self.addPathLineByFunction(lineFunction)
|
|
|
|
def addPathLineAxis(self, point):
|
|
"Add an axis line to the path."
|
|
self.controlPoints = None
|
|
self.path.append(point)
|
|
self.wordIndex += 2
|
|
|
|
def addPathLineByFunction( self, lineFunction ):
|
|
"Add a line to the path by line function."
|
|
while 1:
|
|
if self.getFloatByExtraIndex() == None:
|
|
return
|
|
self.path.append(lineFunction())
|
|
self.wordIndex += 2
|
|
|
|
def addPathMove( self, lineFunction, point ):
|
|
"Add an axis line to the path."
|
|
self.controlPoints = None
|
|
if len(self.path) > 0:
|
|
self.outlinePaths.append(self.path)
|
|
self.oldPoint = self.path[-1]
|
|
self.path = [point]
|
|
self.wordIndex += 3
|
|
self.addPathLineByFunction(lineFunction)
|
|
|
|
def addPathQuadratic( self, controlPoint, end ):
|
|
"Add a quadratic curve to the path."
|
|
begin = self.getOldPoint()
|
|
self.controlPoints = [controlPoint]
|
|
self.path += getQuadraticPoints(begin, controlPoint, end)
|
|
self.wordIndex += 5
|
|
|
|
def addPathQuadraticReflected( self, end ):
|
|
"Add a quadratic curve to the path from a reflected control point."
|
|
begin = self.getOldPoint()
|
|
controlPoint = begin
|
|
if self.controlPoints != None:
|
|
if len( self.controlPoints ) == 1:
|
|
controlPoint = begin + begin - self.controlPoints[-1]
|
|
self.controlPoints = [ controlPoint ]
|
|
self.path += getQuadraticPoints(begin, controlPoint, end)
|
|
self.wordIndex += 3
|
|
|
|
def getComplexByExtraIndex( self, extraIndex=0 ):
|
|
'Get complex from the extraIndex.'
|
|
return euclidean.getComplexByWords(self.words, self.wordIndex + extraIndex)
|
|
|
|
def getComplexRelative(self):
|
|
"Get relative complex."
|
|
return self.getComplexByExtraIndex() + self.getOldPoint()
|
|
|
|
def getFloatByExtraIndex( self, extraIndex=0 ):
|
|
'Get float from the extraIndex.'
|
|
totalIndex = self.wordIndex + extraIndex
|
|
if totalIndex >= len(self.words):
|
|
return None
|
|
word = self.words[totalIndex]
|
|
if word[: 1].isalpha():
|
|
return None
|
|
return euclidean.getFloatFromValue(word)
|
|
|
|
def getOldPoint(self):
|
|
'Get the old point.'
|
|
if len(self.path) > 0:
|
|
return self.path[-1]
|
|
return self.oldPoint
|
|
|
|
def processPathWordA(self):
|
|
'Process path word A.'
|
|
self.addPathArc( self.getComplexByExtraIndex( 6 ) )
|
|
|
|
def processPathWorda(self):
|
|
'Process path word a.'
|
|
self.addPathArc(self.getComplexByExtraIndex(6) + self.getOldPoint())
|
|
|
|
def processPathWordC(self):
|
|
'Process path word C.'
|
|
end = self.getComplexByExtraIndex( 5 )
|
|
self.addPathCubic( [ self.getComplexByExtraIndex( 1 ), self.getComplexByExtraIndex(3) ], end )
|
|
|
|
def processPathWordc(self):
|
|
'Process path word C.'
|
|
begin = self.getOldPoint()
|
|
end = self.getComplexByExtraIndex( 5 )
|
|
self.addPathCubic( [ self.getComplexByExtraIndex( 1 ) + begin, self.getComplexByExtraIndex(3) + begin ], end + begin )
|
|
|
|
def processPathWordH(self):
|
|
"Process path word H."
|
|
beginY = self.getOldPoint().imag
|
|
self.addPathLineAxis(complex(float(self.words[self.wordIndex + 1]), beginY))
|
|
while 1:
|
|
floatByExtraIndex = self.getFloatByExtraIndex()
|
|
if floatByExtraIndex == None:
|
|
return
|
|
self.path.append(complex(floatByExtraIndex, beginY))
|
|
self.wordIndex += 1
|
|
|
|
def processPathWordh(self):
|
|
"Process path word h."
|
|
begin = self.getOldPoint()
|
|
self.addPathLineAxis(complex(float(self.words[self.wordIndex + 1]) + begin.real, begin.imag))
|
|
while 1:
|
|
floatByExtraIndex = self.getFloatByExtraIndex()
|
|
if floatByExtraIndex == None:
|
|
return
|
|
self.path.append(complex(floatByExtraIndex + self.getOldPoint().real, begin.imag))
|
|
self.wordIndex += 1
|
|
|
|
def processPathWordL(self):
|
|
"Process path word L."
|
|
self.addPathLine(self.getComplexByExtraIndex, self.getComplexByExtraIndex( 1 ))
|
|
|
|
def processPathWordl(self):
|
|
"Process path word l."
|
|
self.addPathLine(self.getComplexRelative, self.getComplexByExtraIndex(1) + self.getOldPoint())
|
|
|
|
def processPathWordM(self):
|
|
"Process path word M."
|
|
self.addPathMove(self.getComplexByExtraIndex, self.getComplexByExtraIndex(1))
|
|
|
|
def processPathWordm(self):
|
|
"Process path word m."
|
|
self.addPathMove(self.getComplexRelative, self.getComplexByExtraIndex(1) + self.getOldPoint())
|
|
|
|
def processPathWordQ(self):
|
|
'Process path word Q.'
|
|
self.addPathQuadratic( self.getComplexByExtraIndex( 1 ), self.getComplexByExtraIndex(3) )
|
|
|
|
def processPathWordq(self):
|
|
'Process path word q.'
|
|
begin = self.getOldPoint()
|
|
self.addPathQuadratic(self.getComplexByExtraIndex(1) + begin, self.getComplexByExtraIndex(3) + begin)
|
|
|
|
def processPathWordS(self):
|
|
'Process path word S.'
|
|
self.addPathCubicReflected( self.getComplexByExtraIndex( 1 ), self.getComplexByExtraIndex(3) )
|
|
|
|
def processPathWords(self):
|
|
'Process path word s.'
|
|
begin = self.getOldPoint()
|
|
self.addPathCubicReflected(self.getComplexByExtraIndex(1) + begin, self.getComplexByExtraIndex(3) + begin)
|
|
|
|
def processPathWordT(self):
|
|
'Process path word T.'
|
|
self.addPathQuadraticReflected( self.getComplexByExtraIndex( 1 ) )
|
|
|
|
def processPathWordt(self):
|
|
'Process path word t.'
|
|
self.addPathQuadraticReflected(self.getComplexByExtraIndex(1) + self.getOldPoint())
|
|
|
|
def processPathWordV(self):
|
|
"Process path word V."
|
|
beginX = self.getOldPoint().real
|
|
self.addPathLineAxis(complex(beginX, float(self.words[self.wordIndex + 1])))
|
|
while 1:
|
|
floatByExtraIndex = self.getFloatByExtraIndex()
|
|
if floatByExtraIndex == None:
|
|
return
|
|
self.path.append(complex(beginX, floatByExtraIndex))
|
|
self.wordIndex += 1
|
|
|
|
def processPathWordv(self):
|
|
"Process path word v."
|
|
begin = self.getOldPoint()
|
|
self.addPathLineAxis(complex(begin.real, float(self.words[self.wordIndex + 1]) + begin.imag))
|
|
while 1:
|
|
floatByExtraIndex = self.getFloatByExtraIndex()
|
|
if floatByExtraIndex == None:
|
|
return
|
|
self.path.append(complex(begin.real, floatByExtraIndex + self.getOldPoint().imag))
|
|
self.wordIndex += 1
|
|
|
|
def processPathWordZ(self):
|
|
"Process path word Z."
|
|
self.controlPoints = None
|
|
if len(self.path) < 1:
|
|
return
|
|
self.loops.append(getChainMatrixSVGIfNecessary(self.elementNode, self.yAxisPointingUpward).getTransformedPath(self.path))
|
|
self.oldPoint = self.path[0]
|
|
self.path = []
|
|
|
|
def processPathWordz(self):
|
|
"Process path word z."
|
|
self.processPathWordZ()
|
|
|
|
|
|
class SVGReader:
|
|
"An svg carving."
|
|
def __init__(self):
|
|
"Add empty lists."
|
|
self.loopLayers = []
|
|
self.sliceDictionary = None
|
|
self.stopProcessing = False
|
|
self.z = 0.0
|
|
|
|
def flipDirectLayer(self, loopLayer):
|
|
"Flip the y coordinate of the layer and direct the loops."
|
|
for loop in loopLayer.loops:
|
|
for pointIndex, point in enumerate(loop):
|
|
loop[pointIndex] = complex(point.real, -point.imag)
|
|
triangle_mesh.sortLoopsInOrderOfArea(True, loopLayer.loops)
|
|
for loopIndex, loop in enumerate(loopLayer.loops):
|
|
isInsideLoops = euclidean.getIsInFilledRegion(loopLayer.loops[: loopIndex], euclidean.getLeftPoint(loop))
|
|
intercircle.directLoop((not isInsideLoops), loop)
|
|
|
|
def getLoopLayer(self):
|
|
"Return the rotated loop layer."
|
|
if self.z != None:
|
|
loopLayer = euclidean.LoopLayer(self.z)
|
|
self.loopLayers.append(loopLayer)
|
|
self.z = None
|
|
return self.loopLayers[-1]
|
|
|
|
def parseSVG(self, fileName, svgText):
|
|
"Parse SVG text and store the layers."
|
|
self.fileName = fileName
|
|
xmlParser = DocumentNode(fileName, svgText)
|
|
self.documentElement = xmlParser.getDocumentElement()
|
|
if self.documentElement == None:
|
|
print('Warning, documentElement was None in parseSVG in SVGReader, so nothing will be done for:')
|
|
print(fileName)
|
|
return
|
|
self.parseSVGByElementNode(self.documentElement)
|
|
|
|
def parseSVGByElementNode(self, elementNode):
|
|
"Parse SVG by elementNode."
|
|
self.sliceDictionary = svg_writer.getSliceDictionary(elementNode)
|
|
self.yAxisPointingUpward = euclidean.getBooleanFromDictionary(False, self.sliceDictionary, 'yAxisPointingUpward')
|
|
self.processElementNode(elementNode)
|
|
if not self.yAxisPointingUpward:
|
|
for loopLayer in self.loopLayers:
|
|
self.flipDirectLayer(loopLayer)
|
|
|
|
def processElementNode(self, elementNode):
|
|
'Process the xml element.'
|
|
if self.stopProcessing:
|
|
return
|
|
lowerLocalName = elementNode.getNodeName().lower()
|
|
global globalProcessSVGElementDictionary
|
|
if lowerLocalName in globalProcessSVGElementDictionary:
|
|
try:
|
|
globalProcessSVGElementDictionary[lowerLocalName](elementNode, self)
|
|
except:
|
|
print('Warning, in processElementNode in svg_reader, could not process:')
|
|
print(elementNode)
|
|
traceback.print_exc(file=sys.stdout)
|
|
for childNode in elementNode.childNodes:
|
|
self.processElementNode(childNode)
|
|
|
|
|
|
globalFontFileNames = None
|
|
globalFontReaderDictionary = {}
|
|
globalGetTricomplexDictionary = {}
|
|
globalGetTricomplexFunctions = [
|
|
getTricomplexmatrix,
|
|
getTricomplexrotate,
|
|
getTricomplexscale,
|
|
getTricomplexskewX,
|
|
getTricomplexskewY,
|
|
getTricomplextranslate ]
|
|
globalProcessPathWordFunctions = [
|
|
PathReader.processPathWordA,
|
|
PathReader.processPathWorda,
|
|
PathReader.processPathWordC,
|
|
PathReader.processPathWordc,
|
|
PathReader.processPathWordH,
|
|
PathReader.processPathWordh,
|
|
PathReader.processPathWordL,
|
|
PathReader.processPathWordl,
|
|
PathReader.processPathWordM,
|
|
PathReader.processPathWordm,
|
|
PathReader.processPathWordQ,
|
|
PathReader.processPathWordq,
|
|
PathReader.processPathWordS,
|
|
PathReader.processPathWords,
|
|
PathReader.processPathWordT,
|
|
PathReader.processPathWordt,
|
|
PathReader.processPathWordV,
|
|
PathReader.processPathWordv,
|
|
PathReader.processPathWordZ,
|
|
PathReader.processPathWordz ]
|
|
globalProcessPathWordDictionary = {}
|
|
globalProcessSVGElementDictionary = {}
|
|
globalProcessSVGElementFunctions = [
|
|
processSVGElementcircle,
|
|
processSVGElementellipse,
|
|
processSVGElementg,
|
|
processSVGElementline,
|
|
processSVGElementpath,
|
|
processSVGElementpolygon,
|
|
processSVGElementpolyline,
|
|
processSVGElementrect,
|
|
processSVGElementtext ]
|
|
globalSideAngle = 0.5 * math.pi / float( globalNumberOfCornerPoints )
|
|
|
|
|
|
addFunctionsToDictionary( globalGetTricomplexDictionary, globalGetTricomplexFunctions, 'getTricomplex')
|
|
addFunctionsToDictionary( globalProcessPathWordDictionary, globalProcessPathWordFunctions, 'processPathWord')
|
|
addFunctionsToDictionary( globalProcessSVGElementDictionary, globalProcessSVGElementFunctions, 'processSVGElement')
|