MrDraw/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/segment.py

166 lines
6.8 KiB
Python

"""
Add material to support overhang or remove material at the overhang angle.
"""
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.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
__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'
globalExecutionOrder = 60
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get segment loop."
if len(loop) < 3:
return [loop]
derivation = SegmentDerivation(elementNode, prefix)
if derivation.path == getSegmentPathDefault():
return [loop]
path = getXNormalizedVector3Path(derivation.path)
if euclidean.getIsWiddershinsByVector3(loop):
path = path[: : -1]
for point in path:
point.x = 1.0 - point.x
if derivation.center == None:
point.y = - point.y
segmentLoop = []
startEnd = StartEnd(elementNode, len(loop), prefix)
for pointIndex in xrange(len(loop)):
if pointIndex >= startEnd.start and pointIndex < startEnd.end:
segmentLoop += getSegmentPath(derivation.center, loop, path, pointIndex)
else:
segmentLoop.append(loop[pointIndex])
return [euclidean.getLoopWithoutCloseSequentialPoints( close, segmentLoop)]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return SegmentDerivation(elementNode, prefix)
def getRadialPath(begin, center, end, path):
"Get radial path."
beginComplex = begin.dropAxis()
endComplex = end.dropAxis()
centerComplex = center.dropAxis()
beginMinusCenterComplex = beginComplex - centerComplex
endMinusCenterComplex = endComplex - centerComplex
beginMinusCenterComplexRadius = abs( beginMinusCenterComplex )
endMinusCenterComplexRadius = abs( endMinusCenterComplex )
if beginMinusCenterComplexRadius == 0.0 or endMinusCenterComplexRadius == 0.0:
return [ begin ]
beginMinusCenterComplex /= beginMinusCenterComplexRadius
endMinusCenterComplex /= endMinusCenterComplexRadius
angleDifference = euclidean.getAngleDifferenceByComplex( endMinusCenterComplex, beginMinusCenterComplex )
radialPath = []
for point in path:
weightEnd = point.x
weightBegin = 1.0 - weightEnd
weightedRadius = beginMinusCenterComplexRadius * weightBegin + endMinusCenterComplexRadius * weightEnd * ( 1.0 + point.y )
radialComplex = weightedRadius * euclidean.getWiddershinsUnitPolar( angleDifference * point.x ) * beginMinusCenterComplex
polygonPoint = center + Vector3( radialComplex.real, radialComplex.imag, point.z )
radialPath.append( polygonPoint )
return radialPath
def getSegmentPath(center, loop, path, pointIndex):
"Get segment path."
centerBegin = loop[pointIndex]
centerEnd = loop[(pointIndex + 1) % len(loop)]
centerEndMinusBegin = centerEnd - centerBegin
if abs( centerEndMinusBegin ) <= 0.0:
return [ centerBegin ]
if center != None:
return getRadialPath(centerBegin, center, centerEnd, path)
begin = loop[(pointIndex + len(loop) - 1) % len(loop)]
end = loop[(pointIndex + 2) % len(loop)]
return getWedgePath(begin, centerBegin, centerEnd, centerEndMinusBegin, end, path)
def getSegmentPathDefault():
"Get segment path default."
return [Vector3(), Vector3(0.0, 1.0)]
def getWedgePath( begin, centerBegin, centerEnd, centerEndMinusBegin, end, path ):
"Get segment path."
beginComplex = begin.dropAxis()
centerBeginComplex = centerBegin.dropAxis()
centerEndComplex = centerEnd.dropAxis()
endComplex = end.dropAxis()
wedgePath = []
centerBeginMinusBeginComplex = euclidean.getNormalized( centerBeginComplex - beginComplex )
centerEndMinusCenterBeginComplexOriginal = centerEndComplex - centerBeginComplex
centerEndMinusCenterBeginComplexLength = abs( centerEndMinusCenterBeginComplexOriginal )
if centerEndMinusCenterBeginComplexLength <= 0.0:
return [ centerBegin ]
centerEndMinusCenterBeginComplex = centerEndMinusCenterBeginComplexOriginal / centerEndMinusCenterBeginComplexLength
endMinusCenterEndComplex = euclidean.getNormalized( endComplex - centerEndComplex )
widdershinsBegin = getWiddershinsAverageByVector3( centerBeginMinusBeginComplex, centerEndMinusCenterBeginComplex )
widdershinsEnd = getWiddershinsAverageByVector3( centerEndMinusCenterBeginComplex, endMinusCenterEndComplex )
for point in path:
weightEnd = point.x
weightBegin = 1.0 - weightEnd
polygonPoint = centerBegin + centerEndMinusBegin * point.x
weightedWiddershins = widdershinsBegin * weightBegin + widdershinsEnd * weightEnd
polygonPoint += weightedWiddershins * point.y * centerEndMinusCenterBeginComplexLength
polygonPoint.z += point.z
wedgePath.append( polygonPoint )
return wedgePath
def getWiddershinsAverageByVector3( centerMinusBeginComplex, endMinusCenterComplex ):
"Get the normalized average of the widdershins vectors."
centerMinusBeginWiddershins = Vector3( - centerMinusBeginComplex.imag, centerMinusBeginComplex.real )
endMinusCenterWiddershins = Vector3( - endMinusCenterComplex.imag, endMinusCenterComplex.real )
return ( centerMinusBeginWiddershins + endMinusCenterWiddershins ).getNormalized()
def getXNormalizedVector3Path(path):
"Get path where the x ranges from 0 to 1."
if len(path) < 1:
return path
minimumX = path[0].x
for point in path[1 :]:
minimumX = min( minimumX, point.x )
for point in path:
point.x -= minimumX
maximumX = path[0].x
for point in path[1 :]:
maximumX = max( maximumX, point.x )
for point in path:
point.x /= maximumX
return path
def processElementNode(elementNode):
"Process the xml element."
lineation.processElementNodeByFunction(elementNode, getManipulatedPaths)
class SegmentDerivation:
"Class to hold segment variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.center = evaluate.getVector3ByPrefix(None, elementNode, prefix + 'center')
self.path = evaluate.getPathByPrefix(elementNode, getSegmentPathDefault(), prefix)
class StartEnd:
'Class to get a start through end range.'
def __init__(self, elementNode, modulo, prefix):
"Initialize."
self.start = evaluate.getEvaluatedInt(0, elementNode, prefix + 'start')
self.extent = evaluate.getEvaluatedInt(modulo - self.start, elementNode, prefix + 'extent')
self.end = evaluate.getEvaluatedInt(self.start + self.extent, elementNode, prefix + 'end')
self.revolutions = evaluate.getEvaluatedInt(1, elementNode, prefix + 'revolutions')
if self.revolutions > 1:
self.end += modulo * (self.revolutions - 1)
def __repr__(self):
"Get the string representation of this StartEnd."
return '%s, %s, %s' % (self.start, self.end, self.revolutions)