""" 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 ' __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)