MrDraw/SkeinPyPy_NewUI/fabmetheus_utilities/geometry/creation/lineation.py
Daid a414a80837 Adding SkeinPyPy_NewUI as development for a brand new user interface.
Experimental, doesn't slice yet, loads of work ahead.
2012-02-20 00:30:49 +01:00

308 lines
12 KiB
Python

"""
Polygon path.
"""
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.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getComplexByDictionary(dictionary, valueComplex):
'Get complex by dictionary.'
if 'x' in dictionary:
valueComplex = complex(euclidean.getFloatFromValue(dictionary['x']),valueComplex.imag)
if 'y' in dictionary:
valueComplex = complex(valueComplex.real, euclidean.getFloatFromValue(dictionary['y']))
return valueComplex
def getComplexByDictionaryListValue(value, valueComplex):
'Get complex by dictionary, list or value.'
if value.__class__ == complex:
return value
if value.__class__ == dict:
return getComplexByDictionary(value, valueComplex)
if value.__class__ == list:
return getComplexByFloatList(value, valueComplex)
floatFromValue = euclidean.getFloatFromValue(value)
if floatFromValue == None:
return valueComplex
return complex( floatFromValue, floatFromValue )
def getComplexByFloatList( floatList, valueComplex ):
'Get complex by float list.'
if len(floatList) > 0:
valueComplex = complex(euclidean.getFloatFromValue(floatList[0]), valueComplex.imag)
if len(floatList) > 1:
valueComplex = complex(valueComplex.real, euclidean.getFloatFromValue(floatList[1]))
return valueComplex
def getComplexByMultiplierPrefix(elementNode, multiplier, prefix, valueComplex):
'Get complex from multiplier, prefix and xml element.'
if multiplier == 0.0:
return valueComplex
oldMultipliedValueComplex = valueComplex * multiplier
complexByPrefix = getComplexByPrefix(elementNode, prefix, oldMultipliedValueComplex)
if complexByPrefix == oldMultipliedValueComplex:
return valueComplex
return complexByPrefix / multiplier
def getComplexByMultiplierPrefixes(elementNode, multiplier, prefixes, valueComplex):
'Get complex from multiplier, prefixes and xml element.'
for prefix in prefixes:
valueComplex = getComplexByMultiplierPrefix(elementNode, multiplier, prefix, valueComplex)
return valueComplex
def getComplexByPrefix(elementNode, prefix, valueComplex):
'Get complex from prefix and xml element.'
value = evaluate.getEvaluatedValue(None, elementNode, prefix)
if value != None:
valueComplex = getComplexByDictionaryListValue(value, valueComplex)
x = evaluate.getEvaluatedFloat(None, elementNode, prefix + '.x')
if x != None:
valueComplex = complex( x, getComplexIfNone( valueComplex ).imag )
y = evaluate.getEvaluatedFloat(None, elementNode, prefix + '.y')
if y != None:
valueComplex = complex( getComplexIfNone( valueComplex ).real, y )
return valueComplex
def getComplexByPrefixBeginEnd(elementNode, prefixBegin, prefixEnd, valueComplex):
'Get complex from element node, prefixBegin and prefixEnd.'
valueComplex = getComplexByPrefix(elementNode, prefixBegin, valueComplex)
if prefixEnd in elementNode.attributes:
return 0.5 * getComplexByPrefix(elementNode, valueComplex + valueComplex, prefixEnd)
else:
return valueComplex
def getComplexByPrefixes(elementNode, prefixes, valueComplex):
'Get complex from prefixes and xml element.'
for prefix in prefixes:
valueComplex = getComplexByPrefix(elementNode, prefix, valueComplex)
return valueComplex
def getComplexIfNone( valueComplex ):
'Get new complex if the original complex is none.'
if valueComplex == None:
return complex()
return valueComplex
def getFloatByPrefixBeginEnd(elementNode, prefixBegin, prefixEnd, valueFloat):
'Get float from prefixBegin, prefixEnd and xml element.'
valueFloat = evaluate.getEvaluatedFloat(valueFloat, elementNode, prefixBegin)
if prefixEnd in elementNode.attributes:
return 0.5 * evaluate.getEvaluatedFloat(valueFloat + valueFloat, elementNode, prefixEnd)
return valueFloat
def getFloatByPrefixSide(defaultValue, elementNode, prefix, side):
'Get float by prefix and side.'
if elementNode == None:
return defaultValue
if side != None:
key = prefix + 'OverSide'
if key in elementNode.attributes:
defaultValue = euclidean.getFloatFromValue(evaluate.getEvaluatedValueObliviously(elementNode, key)) * side
return evaluate.getEvaluatedFloat(defaultValue, elementNode, prefix)
def getGeometryOutput(derivation, elementNode):
'Get geometry output from paths.'
if derivation == None:
derivation = LineationDerivation(elementNode)
geometryOutput = []
for path in derivation.target:
sideLoop = SideLoop(path)
geometryOutput += getGeometryOutputByLoop(elementNode, sideLoop)
return geometryOutput
def getGeometryOutputByArguments(arguments, elementNode):
'Get vector3 vertexes from attribute dictionary by arguments.'
return getGeometryOutput(None, elementNode)
def getGeometryOutputByLoop(elementNode, sideLoop):
'Get geometry output by side loop.'
sideLoop.rotate(elementNode)
return getGeometryOutputByManipulation(elementNode, sideLoop)
def getGeometryOutputByManipulation(elementNode, sideLoop):
'Get geometry output by manipulation.'
sideLoop.loop = euclidean.getLoopWithoutCloseSequentialPoints( sideLoop.close, sideLoop.loop )
return sideLoop.getManipulationPluginLoops(elementNode)
def getInradius(defaultInradius, elementNode):
'Get inradius.'
defaultInradius = getComplexByPrefixes(elementNode, ['demisize', 'inradius'], defaultInradius)
return getComplexByMultiplierPrefix(elementNode, 2.0, 'size', defaultInradius)
def getMinimumRadius(beginComplexSegmentLength, endComplexSegmentLength, radius):
'Get minimum radius.'
return min(abs(radius), 0.5 * min(beginComplexSegmentLength, endComplexSegmentLength))
def getNewDerivation(elementNode):
'Get new derivation.'
return LineationDerivation(elementNode)
def getNumberOfBezierPoints(begin, elementNode, end):
'Get the numberOfBezierPoints.'
numberOfBezierPoints = int(math.ceil(0.5 * evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, abs(end - begin))))
return evaluate.getEvaluatedInt(numberOfBezierPoints, elementNode, 'sides')
def getPackedGeometryOutputByLoop(elementNode, sideLoop):
'Get packed geometry output by side loop.'
sideLoop.rotate(elementNode)
return getGeometryOutputByManipulation(elementNode, sideLoop)
def getRadiusAverage(radiusComplex):
'Get average radius from radiusComplex.'
return math.sqrt(radiusComplex.real * radiusComplex.imag)
def getRadiusComplex(elementNode, radius):
'Get radius complex for elementNode.'
radius = getComplexByPrefixes(elementNode, ['demisize', 'radius'], radius)
return getComplexByMultiplierPrefixes(elementNode, 2.0, ['diameter', 'size'], radius)
def getStrokeRadiusByPrefix(elementNode, prefix):
'Get strokeRadius by prefix.'
strokeRadius = getFloatByPrefixBeginEnd(elementNode, prefix + 'strokeRadius', prefix + 'strokeWidth', 1.0)
return getFloatByPrefixBeginEnd(elementNode, prefix + 'radius', prefix + 'diameter', strokeRadius)
def processElementNode(elementNode):
'Process the xml element.'
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
def processElementNodeByFunction(elementNode, manipulationFunction):
'Process the xml element by the manipulationFunction.'
elementAttributesCopy = elementNode.attributes.copy()
targets = evaluate.getElementNodesByKey(elementNode, 'target')
for target in targets:
targetAttributesCopy = target.attributes.copy()
target.attributes = elementAttributesCopy
processTargetByFunction(manipulationFunction, target)
target.attributes = targetAttributesCopy
def processTargetByFunction(manipulationFunction, target):
'Process the target by the manipulationFunction.'
if target.xmlObject == None:
print('Warning, there is no object in processTargetByFunction in lineation for:')
print(target)
return
geometryOutput = []
transformedPaths = target.xmlObject.getTransformedPaths()
for transformedPath in transformedPaths:
sideLoop = SideLoop(transformedPath)
sideLoop.rotate(target)
sideLoop.loop = euclidean.getLoopWithoutCloseSequentialPoints( sideLoop.close, sideLoop.loop )
geometryOutput += manipulationFunction(sideLoop.close, target, sideLoop.loop, '', sideLoop.sideLength)
if len(geometryOutput) < 1:
print('Warning, there is no geometryOutput in processTargetByFunction in lineation for:')
print(target)
return
removeChildNodesFromElementObject(target)
path.convertElementNode(target, geometryOutput)
def removeChildNodesFromElementObject(elementNode):
'Process the xml element by manipulationFunction.'
elementNode.removeChildNodesFromIDNameParent()
if elementNode.xmlObject != None:
if elementNode.parentNode.xmlObject != None:
if elementNode.xmlObject in elementNode.parentNode.xmlObject.archivableObjects:
elementNode.parentNode.xmlObject.archivableObjects.remove(elementNode.xmlObject)
def setClosedAttribute(elementNode, revolutions):
'Set the closed attribute of the elementNode.'
closedBoolean = evaluate.getEvaluatedBoolean(revolutions <= 1, elementNode, 'closed')
elementNode.attributes['closed'] = str(closedBoolean).lower()
class LineationDerivation:
'Class to hold lineation variables.'
def __init__(self, elementNode):
'Set defaults.'
self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
class SideLoop:
'Class to handle loop, side angle and side length.'
def __init__(self, loop, sideAngle=None, sideLength=None):
'Initialize.'
if sideAngle == None:
if len(loop) > 0:
sideAngle = 2.0 * math.pi / float(len(loop))
else:
sideAngle = 1.0
print('Warning, loop has no sides in SideLoop in lineation.')
if sideLength == None:
if len(loop) > 0:
sideLength = euclidean.getLoopLength(loop) / float(len(loop))
else:
sideLength = 1.0
print('Warning, loop has no length in SideLoop in lineation.')
self.loop = loop
self.sideAngle = abs(sideAngle)
self.sideLength = abs(sideLength)
self.close = 0.001 * sideLength
def getManipulationPluginLoops(self, elementNode):
'Get loop manipulated by the plugins in the manipulation paths folder.'
xmlProcessor = elementNode.getXMLProcessor()
matchingPlugins = evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationMatrixDictionary)
matchingPlugins += evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationPathDictionary)
matchingPlugins += evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationShapeDictionary)
matchingPlugins.sort(evaluate.compareExecutionOrderAscending)
loops = [self.loop]
for matchingPlugin in matchingPlugins:
matchingLoops = []
prefix = matchingPlugin.__name__.replace('_', '') + '.'
for loop in loops:
matchingLoops += matchingPlugin.getManipulatedPaths(self.close, elementNode, loop, prefix, self.sideLength)
loops = matchingLoops
return loops
def rotate(self, elementNode):
'Rotate.'
rotation = math.radians(evaluate.getEvaluatedFloat(0.0, elementNode, 'rotation'))
rotation += evaluate.getEvaluatedFloat(0.0, elementNode, 'rotationOverSide') * self.sideAngle
if rotation != 0.0:
planeRotation = euclidean.getWiddershinsUnitPolar( rotation )
for vertex in self.loop:
rotatedComplex = vertex.dropAxis() * planeRotation
vertex.x = rotatedComplex.real
vertex.y = rotatedComplex.imag
if 'clockwise' in elementNode.attributes:
isClockwise = euclidean.getBooleanFromValue(evaluate.getEvaluatedValueObliviously(elementNode, 'clockwise'))
if isClockwise == euclidean.getIsWiddershinsByVector3( self.loop ):
self.loop.reverse()
class Spiral:
'Class to add a spiral.'
def __init__(self, spiral, stepRatio):
'Initialize.'
self.spiral = spiral
if self.spiral == None:
return
self.spiralIncrement = self.spiral * stepRatio
self.spiralTotal = Vector3()
def __repr__(self):
'Get the string representation of this Spiral.'
return self.spiral
def getSpiralPoint(self, unitPolar, vector3):
'Add spiral to the vector.'
if self.spiral == None:
return vector3
vector3 += Vector3(unitPolar.real * self.spiralTotal.x, unitPolar.imag * self.spiralTotal.y, self.spiralTotal.z)
self.spiralTotal += self.spiralIncrement
return vector3