6 from __future__ import absolute_import
7 #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.
10 from fabmetheus_utilities.geometry.creation import extrude
11 from fabmetheus_utilities.geometry.creation import lineation
12 from fabmetheus_utilities.geometry.geometry_tools import path
13 from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
14 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
15 from fabmetheus_utilities.vector3 import Vector3
16 from fabmetheus_utilities import euclidean
20 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
21 __credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
22 __date__ = '$Date: 2008/02/05 $'
23 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
26 def addNegativesByRadius(elementNode, end, negatives, radius, start):
27 "Add teardrop drill hole to negatives."
30 copyShallow = elementNode.getCopyShallow()
31 extrude.setElementNodeToEndStart(copyShallow, end, start)
32 extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
33 extrude.addNegatives(extrudeDerivation, negatives, [getTeardropPathByEndStart(elementNode, end, radius, start)])
35 def getGeometryOutput(derivation, elementNode):
36 "Get vector3 vertexes from attribute dictionary."
37 if derivation == None:
38 derivation = TeardropDerivation(elementNode)
39 teardropPath = getTeardropPath(
40 derivation.inclination, derivation.overhangRadians, derivation.overhangSpan, derivation.radiusArealized, derivation.sides)
41 return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(teardropPath))
43 def getGeometryOutputByArguments(arguments, elementNode):
44 "Get vector3 vertexes from attribute dictionary by arguments."
45 evaluate.setAttributesByArguments(['radius', 'inclination'], arguments, elementNode)
46 return getGeometryOutput(None, elementNode)
48 def getInclination(end, start):
50 if end == None or start == None:
52 endMinusStart = end - start
53 return math.atan2(endMinusStart.z, abs(endMinusStart.dropAxis()))
55 def getNewDerivation(elementNode):
57 return TeardropDerivation(elementNode)
59 def getTeardropPath(inclination, overhangRadians, overhangSpan, radiusArealized, sides):
60 "Get vector3 teardrop path."
61 sideAngle = 2.0 * math.pi / float(sides)
62 overhangPlaneAngle = euclidean.getWiddershinsUnitPolar(overhangRadians)
63 overhangRadians = math.atan2(overhangPlaneAngle.imag, overhangPlaneAngle.real * math.cos(inclination))
64 tanOverhangAngle = math.tan(overhangRadians)
65 beginAngle = overhangRadians
66 beginMinusEndAngle = math.pi + overhangRadians + overhangRadians
67 withinSides = int(math.ceil(beginMinusEndAngle / sideAngle))
68 withinSideAngle = -beginMinusEndAngle / float(withinSides)
70 for side in xrange(withinSides + 1):
71 unitPolar = euclidean.getWiddershinsUnitPolar(beginAngle)
72 teardropPath.append(unitPolar * radiusArealized)
73 beginAngle += withinSideAngle
74 firstPoint = teardropPath[0]
75 if overhangSpan <= 0.0:
76 teardropPath.append(complex(0.0, firstPoint.imag + firstPoint.real / tanOverhangAngle))
78 deltaX = (radiusArealized - firstPoint.imag) * tanOverhangAngle
79 overhangPoint = complex(firstPoint.real - deltaX, radiusArealized)
80 remainingDeltaX = max(0.0, overhangPoint.real - 0.5 * overhangSpan )
81 overhangPoint += complex(-remainingDeltaX, remainingDeltaX / tanOverhangAngle)
82 teardropPath.append(complex(-overhangPoint.real, overhangPoint.imag))
83 teardropPath.append(overhangPoint)
84 return euclidean.getVector3Path(teardropPath)
86 def getTeardropPathByEndStart(elementNode, end, radius, start):
87 "Get vector3 teardrop path by end and start."
88 inclination = getInclination(end, start)
89 sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, radius)
90 radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNoderadius, sides)
91 return getTeardropPath(inclination, setting.getOverhangRadians(elementNode), setting.getOverhangSpan(elementNode), radiusArealized, sides)
93 def processElementNode(elementNode):
94 "Process the xml element."
95 path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
98 class TeardropDerivation:
99 "Class to hold teardrop variables."
100 def __init__(self, elementNode):
102 end = evaluate.getVector3ByPrefix(None, elementNode, 'end')
103 start = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'start')
104 inclinationDegree = math.degrees(getInclination(end, start))
105 self.elementNode = elementNode
106 self.inclination = math.radians(evaluate.getEvaluatedFloat(inclinationDegree, elementNode, 'inclination'))
107 self.overhangRadians = setting.getOverhangRadians(elementNode)
108 self.overhangSpan = setting.getOverhangSpan(elementNode)
109 self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'diameter', 1.0)
110 size = evaluate.getEvaluatedFloat(None, elementNode, 'size')
112 self.radius = 0.5 * size
113 self.sides = evaluate.getEvaluatedFloat(None, elementNode, 'sides')
114 if self.sides == None:
115 self.sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, self.radius)
116 self.radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.radius, self.sides)