chiark / gitweb /
Move SF into its own directory, to seperate SF and Cura. Rename newui to gui.
[cura.git] / Cura / cura_sf / fabmetheus_utilities / geometry / creation / teardrop.py
1 """
2 Teardrop path.
3
4 """
5
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.
8 import __init__
9
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
17 import math
18
19
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'
24
25
26 def addNegativesByRadius(elementNode, end, negatives, radius, start):
27         "Add teardrop drill hole to negatives."
28         if radius <= 0.0:
29                 return
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)])
34
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))
42
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)
47
48 def getInclination(end, start):
49         "Get inclination."
50         if end == None or start == None:
51                 return 0.0
52         endMinusStart = end - start
53         return math.atan2(endMinusStart.z, abs(endMinusStart.dropAxis()))
54
55 def getNewDerivation(elementNode):
56         'Get new derivation.'
57         return TeardropDerivation(elementNode)
58
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)
69         teardropPath = []
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))
77         else:
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)
85
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)
92
93 def processElementNode(elementNode):
94         "Process the xml element."
95         path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
96
97
98 class TeardropDerivation:
99         "Class to hold teardrop variables."
100         def __init__(self, elementNode):
101                 'Set defaults.'
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')
111                 if size != None:
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)