chiark / gitweb /
Add uppercase STL and HEX to file dialog filters for linux/MacOS
[cura.git] / Cura / cura_sf / fabmetheus_utilities / geometry / creation / gear.py
1 """
2 This page is in the table of contents.
3 The gear script can generate a spur gear couple, a bevel gear couple, a ring gear couple and a rack & pinion couple.
4
5 A helix pattern can be added to each gear type.  All the gear types have a clearance and all the teeth can be beveled.  A keyway, shaft and lightening holes can be added to all the round gears, and rack holes can be added to the rack.  The script can output solid gears or only the gear profiles.  Both gears of the couple can be generated or just one.
6
7 The couple has a pinion gear and a complement.
8
9 ==Examples==
10 The link text includes the distinguishing parameters.  Each svg page was generated from an xml page of the same root name using carve.  For example, gear.svg was generated by clicking 'Carve' on the carve tool panel and choosing gear.xml in the file chooser.
11
12 Each generated svg file has the xml fabmetheus element without comments towards the end of the file.  To see it, open the svg file in a text editor and search for 'fabmetheus'   If you copy that into a new text document, add the line '<?xml version='1.0' ?>' at the beginning and then give it a file name with the extension '.xml', you could then generate another svg file using carve.
13
14 ===Bevel===
15 Bevel gear couple.
16
17 <a href='../models/xml_models/creation/gear/bevel.svg'>gear operatingAngle=90</a>
18
19 ===Collar===
20 Spur gear couple and each gear has a collar.
21
22 <a href='../models/xml_models/creation/gear/collar.svg'>gear complementCollarLengthOverFaceWidth='1' pinionCollarLengthOverFaceWidth='1' shaftRadius='5'</a>
23
24 ===Gear===
25 Default spur gear with no parameters.
26
27 <a href='../models/xml_models/creation/gear/gear.svg'>gear</a>
28
29 ===Keyway===
30 Spur gear couple and each gear has a collar and defined keyway.
31
32 <a href='../models/xml_models/creation/gear/keyway.svg'>gear complementCollarLengthOverFaceWidth='1' keywayRadius='2' pinionCollarLengthOverFaceWidth='1' shaftRadius='5'</a>
33
34 ===Rack===
35 Rack and pinion couple.
36
37 <a href='../models/xml_models/creation/gear/rack.svg'>gear teethComplement='0'</a>
38
39 ===Rack Hole===
40 Rack and pinion couple, with holes in the rack.
41
42 <a href='../models/xml_models/creation/gear/rack_hole.svg'>gear rackHoleRadiusOverWidth='0.2' rackWidthOverFaceWidth='2' teethComplement='0'</a>
43
44 ===Ring===
45 Pinion and ring gear.
46
47 <a href='../models/xml_models/creation/gear/ring.svg'>gear teethComplement='-23'</a>
48
49 ===Shaft===
50 Spur gear couple and each gear has a square shaft hole.
51
52 <a href='../models/xml_models/creation/gear/shaft.svg'>gear shaftRadius='5'</a>
53
54 ===Shaft Top===
55 Spur gear couple and each gear has a round shaft hole, truncated on top.
56
57 <a href='../models/xml_models/creation/gear/shaft_top.svg'>gear shaftRadius='5' shaftSides='13' shaftDepthTop='2'</a>
58
59 ===Spur Helix===
60 Spur gear couple with the gear teeth following a helix path.
61
62 <a href='../models/xml_models/creation/gear/spur_helix.svg'>gear  helixAngle='45'</a>
63
64 ===Spur Herringbone===
65 Spur gear couple with the gear teeth following a herringbone path.
66
67 <a href='../models/xml_models/creation/gear/spur_herringbone.svg'>gear  helixAngle='45' helixType='herringbone'</a>
68
69 ===Spur Parabolic===
70 Spur gear couple with the gear teeth following a parabolic path.
71
72 <a href='../models/xml_models/creation/gear/spur_parabolic.svg'>gear  helixAngle='45' helixType='parabolic'</a>
73
74 ===Spur Profile===
75 Spur gear couple profile.  Since this is just a horizontal path, it can not be sliced, so the path is then extruded to create a solid which can be sliced and viewed.
76
77 <a href='../models/xml_models/creation/gear/spur_profile.svg'>gear id='spurProfile' faceWidth='0' | extrude target='=document.getElementByID(spurProfile)</a>
78
79 ==Parameters==
80 ===Center Distance===
81 Default is such that the pitch radius works out to twenty.
82
83 Defines the distance between the gear centers.
84
85 ===Clearance Couplet===
86 ====Clearance Over Wavelength====
87 Default is 0.1.
88
89 Defines the ratio of the clearance over the wavelength of the gear profile.  The wavelength is the arc distance between the gear teeth.
90
91 ====Clearance====
92 Default is the 'Clearance Over Wavelength' times the wavelength.
93
94 Defines the clearance between the gear tooth and the other gear of the couple.  If the clearance is zero, the outside of the gear tooth will touch the other gear.  If the clearance is too high, the gear teeth will be long and weak.
95
96 ===Collar Addendum Couplet===
97 ====Collar Addendum Over Radius====
98 Default is one.
99
100 Defines the ratio of the collar addendum over the shaft radius.
101
102 ====Collar Addendum====
103 Default is the 'Collar Addendum Over Radius' times the shaft radius.
104
105 Defines the collar addendum.
106
107 ===Complement Collar Length Couplet===
108 ====Complement Collar Length Over Face Width====
109 Default is zero.
110
111 Defines the ratio of the complement collar length over the face width.
112
113 ====Complement Collar Length====
114 Default is the 'Complement Collar Length Over Face Width' times the face width.
115
116 Defines the complement collar length.  If the complement collar length is zero, there will not be a collar on the complement gear.
117
118 ===Creation Type===
119 Default is 'both'.
120
121 ====Both====
122 When selected, the pinion and complement will be generated.
123
124 ====Complement====
125 When selected, only the complement gear or rack will be generated.
126
127 ====Pinion====
128 When selected, only the pinion will be generated.
129
130 ===Face Width===
131 Default is ten.
132
133 Defines the face width.
134
135 ===Gear Hole Paths===
136 Default is empty.
137
138 Defines the centers of the gear holes.  If the gear hole paths parameter is the default empty, then the centers of the gear holes will be generated from other parameters.
139
140 ===Helix Angle===
141 Default is zero.
142
143 ===Helix Path===
144 Default is empty.
145
146 Defines the helix path of the gear teeth.  If the helix path is the default empty, then the helix will be generated from the helix angle and helix type.
147
148 ===Helix Type===
149 Default is 'basic'.
150
151 ====Basic====
152 When selected, the helix will be basic.
153
154 ====Herringbone====
155 When selected, the helix will have a herringbone pattern.
156
157 ====Parabolic====
158 When selected, the helix will have a parabolic pattern.
159
160 ===Keyway Radius Couplet===
161 ====Keyway Radius Over Radius====
162 Default is half.
163
164 Defines the ratio of the keyway radius over the shaft radius.
165
166 ====Keyway Radius====
167 Default is the 'Keyway Radius Over Radius' times the shaft radius.
168
169 Defines the keyway radius.  If the keyway radius is zero, there will not be a keyway on the collar.
170
171 ===Lightening Hole Margin Couplet===
172 ====Lightening Hole Margin Over Rim Dedendum====
173 Default is one.
174
175 Defines the ratio of the lightening hole margin over the rim dedendum.
176
177 ====Lightening Hole Margin====
178 Default is the 'Lightening Hole Margin Over Rim Dedendum' times the rim dedendum.
179
180 Defines the minimum margin between lightening holes.
181
182 ===Lightening Hole Minimum Radius===
183 Default is one.
184
185 Defines the minimum radius of the lightening holes.
186
187 ===Move Type===
188 Default is 'separate'.
189
190 ====None====
191 When selected, the gears will be not be moved and will therefore overlap.  Afterwards the write plugin could be used to write each gear to a different file, so they can be fabricated in separate operations.
192
193 ====Mesh====
194 When selected, the gears will be separated horizontally so that they just mesh.  This is useful to test if the gears mesh properly.
195
196 ====Separate====
197 When selected, the gears will be separated horizontally with a gap between them.
198
199 ====Vertical====
200 When selected, the gears will be separated vertically.
201
202 ===Operating Angle===
203 Default is 180 degrees.
204
205 Defines the operating angle between the gear axes.  If the operating angle is not 180 degrees, a bevel gear couple will be generated.
206
207 ===Pinion Collar Length Couplet===
208 ====Pinion Collar Length Over Face Width====
209 Default is zero.
210
211 Defines the ratio of the pinion collar length over the face width.
212
213 ====Pinion Collar Length====
214 Default is the 'Pinion Collar Length Over Face Width' times the face width.
215
216 Defines the pinion collar length.  If the pinion collar length is zero, there will not be a collar on the pinion gear.
217
218 ===Pitch Radius===
219 Default is twenty if the pitch radius has not been set.  If the center distance is set, the default pitch radius is the center distance times the number of pinion teeth divided by the total number of gear teeth.
220
221 Defines the pinion pitch radius.
222
223 ===Plate Clearance Couplet===
224 ====Plate Clearance Over Length====
225 Default is 0.2.
226
227 Defines the ratio of the plate clearance over the plate length.
228
229 ====Plate Clearance====
230 Default is the 'Plate Clearance Over Length' times the plate length.
231
232 Defines the clearance between the pinion and the plate of the ring gear.  If the clearance is zero, they will touch.
233
234 ===Plate Length Couplet===
235 ====Plate Length Over Face Width====
236 Default is half.
237
238 Defines the ratio of the plate length over the face width.
239
240 ====Plate Length====
241 Default is the 'Plate Length Over Face Width' times the face width.
242
243 Defines the length of the plate of the ring gear.
244
245 ===Pressure Angle===
246 Default is twenty degrees.
247
248 Defines the pressure angle of the gear couple.
249
250 ===Profile Surfaces===
251 Default is eleven.
252
253 Defines the number of profile surfaces.
254
255 ===Rack Hole Below Over Width Couplet===
256 ====Rack Hole Below Over Width====
257 Default is 0.6.
258
259 Defines the ratio of the distance below the pitch of the rack holes over the rack width.
260
261 ====Rack Hole Below====
262 Default is the 'Rack Hole Below Over Width' times the rack width.
263
264 Defines the the distance below the pitch of the rack holes.
265
266 ===Rack Hole Radius Couplet===
267 ====Rack Hole Radius Over Width====
268 Default is zero.
269
270 Defines the ratio of the rack hole radius over the rack width.
271
272 ====Rack Hole Radius====
273 Default is the 'Rack Hole Radius Over Width' times the rack width.
274
275 Defines the radius of the rack holes.  If the rack hole radius is zero, there won't be any rack holes.
276
277 ===Rack Hole Step Over Width Couplet===
278 ====Rack Hole Step Over Width====
279 Default is one.
280
281 Defines the ratio of the rack hole step over the rack width.
282
283 ====Rack Hole Step====
284 Default is the 'Rack Hole Step Over Width' times the rack width.
285
286 Defines the horizontal step distance between the rack holes.
287
288 ===Rack Length Over Radius Couplet===
289 ====Rack Length Over Radius====
290 Default is two times pi.
291
292 Defines the ratio of the rack length over the pitch radius.
293
294 ====Rack Length====
295 Default is the 'Rack Length Over Radius' times the pitch radius.
296
297 Defines the rack length.
298
299 ===Rack Width Couplet===
300 ====Rack Width Over Face Width====
301 Default is one.
302
303 Defines the ratio of the rack width over the face width.
304
305 ====Rack Width====
306 Default is the 'Rack Width Over Face Width' times the face width.
307
308 Defines the rack width.
309
310 ===Rim Dedendum Couplet===
311 ====Rim Dedendum Over Radius====
312 Default is 0.2.
313
314 Defines the ratio of the rim dedendum over the pitch radius.
315
316 ====Rim Dedendum====
317 Default is the 'Rim Dedendum Over Radius' times the pitch radius.
318
319 Defines the rim dedendum of the gear.
320
321 ===Root Bevel Couplet===
322 ====Root Bevel Over Clearance====
323 Default is half.
324
325 Defines the ratio of the root bevel over the clearance.
326
327 ====Root Bevel====
328 Default is the 'Root Bevel Over Clearance' times the clearance.
329
330 Defines the bevel at the root of the gear tooth.
331
332 ===Shaft Depth Bottom Couplet===
333 ====Shaft Depth Bottom Over Radius====
334 Default is zero.
335
336 Defines the ratio of the bottom shaft depth over the shaft radius.
337
338 ====Shaft Depth Bottom====
339 Default is the 'Shaft Depth Bottom Over Radius' times the shaft radius.
340
341 Defines the bottom shaft depth.
342
343 ===Shaft Depth Top Couplet===
344 ====Shaft Depth Top Over Radius====
345 Default is zero.
346
347 Defines the ratio of the top shaft depth over the shaft radius.
348
349 ====Shaft Depth Top====
350 Default is the 'Shaft Depth Top Over Radius' times the shaft radius.
351
352 Defines the top shaft depth.
353
354 ===Shaft Path===
355 Default is empty.
356
357 Defines the path of the shaft hole.  If the shaft path is the default empty, then the shaft path will be generated from the shaft depth bottom, shaft depth top, shaft radius and shaft sides.
358
359 ===Shaft Radius Couplet===
360 ====Shaft Radius Over Pitch Radius====
361 Default is zero.
362
363 Defines the ratio of the shaft radius over the pitch radius.
364
365 ====Shaft Radius====
366 Default is the 'Shaft Radius Over Pitch Radius' times the pitch radius.
367
368 Defines the shaft radius.  If the shaft radius is zero there will not be a shaft hole.
369
370 ===Shaft Sides===
371 Default is four.
372
373 Defines the number of shaft sides.
374
375 ===Teeth Pinion===
376 Default is seven.
377
378 Defines the number of teeth in the pinion.
379
380 ===Teeth Complement===
381 Default is seventeen.
382
383 Defines the number of teeth in the complement of the gear couple.  If the number of teeth is positive, the gear couple will be a spur or bevel type.  If the number of teeth is zero, the gear couple will be a rack and pinion.  If the number of teeth is negative, the gear couple will be a spur and ring.
384
385 ===Tip Bevel Couplet===
386 ====Tip Bevel Over Clearance====
387 Default is 0.1.
388
389 Defines the ratio of the tip bevel over the clearance.
390
391 ====Tip Bevel====
392 Default is the 'Tip Bevel Over Clearance' times the clearance.
393
394 Defines the bevel at the tip of the gear tooth.
395
396 ===Tooth Thickness Multiplier===
397 Default is 0.99999.
398
399 Defines the amount the thickness of the tooth will multiplied.  If when the gears are produced, they mesh too tightly, you can reduce the tooth thickness multiplier so that they mesh with reasonable tightness.
400
401 """
402
403 from __future__ import absolute_import
404 #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.
405 import __init__
406
407 from fabmetheus_utilities.geometry.creation import extrude
408 from fabmetheus_utilities.geometry.creation import lineation
409 from fabmetheus_utilities.geometry.creation import shaft
410 from fabmetheus_utilities.geometry.creation import solid
411 from fabmetheus_utilities.geometry.creation import teardrop
412 from fabmetheus_utilities.geometry.geometry_tools import path
413 from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
414 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
415 from fabmetheus_utilities.geometry.geometry_utilities import matrix
416 from fabmetheus_utilities.geometry.solids import triangle_mesh
417 from fabmetheus_utilities.vector3 import Vector3
418 from fabmetheus_utilities.vector3index import Vector3Index
419 from fabmetheus_utilities import euclidean
420 import math
421
422
423 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
424 __credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
425 __date__ = '$Date: 2008/02/05 $'
426 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
427
428
429 def addBevelGear(derivation, extrudeDerivation, pitchRadius, positives, teeth, vector3GearProfile):
430         "Get extrude output for a cylinder gear."
431         totalPitchRadius = derivation.pitchRadiusComplement + derivation.pitchRadius
432         totalTeeth = derivation.teethPinion + derivation.teethComplement
433         portionDirections = extrude.getSpacedPortionDirections(extrudeDerivation.interpolationDictionary)
434         loopLists = extrude.getLoopListsByPath(extrudeDerivation, None, vector3GearProfile[0], portionDirections)
435         firstLoopList = loopLists[0]
436         gearOverPinion = float(totalTeeth - teeth) / float(teeth)
437         thirdLayerHeight = 0.33333333333 * setting.getLayerHeight(derivation.elementNode)
438         pitchRadian = math.atan(math.sin(derivation.operatingRadian) / (gearOverPinion + math.cos(derivation.operatingRadian)))
439         coneDistance = pitchRadius / math.sin(pitchRadian)
440         apex = Vector3(0.0, 0.0, math.sqrt(coneDistance * coneDistance - pitchRadius * pitchRadius))
441         cosPitch = apex.z / coneDistance
442         sinPitch = math.sin(pitchRadian)
443         for loop in firstLoopList:
444                 for point in loop:
445                         alongWay = point.z / coneDistance
446                         oneMinusAlongWay = 1.0 - alongWay
447                         pointComplex = point.dropAxis()
448                         pointComplexLength = abs(pointComplex)
449                         deltaRadius = pointComplexLength - pitchRadius
450                         cosDeltaRadius = cosPitch * deltaRadius
451                         sinDeltaRadius = sinPitch * deltaRadius
452                         pointComplex *= (cosDeltaRadius + pitchRadius) / pointComplexLength
453                         point.x = pointComplex.real
454                         point.y = pointComplex.imag
455                         point.z += sinDeltaRadius
456                         point.x *= oneMinusAlongWay
457                         point.y *= oneMinusAlongWay
458         addBottomLoop(-thirdLayerHeight, firstLoopList)
459         topLoop = firstLoopList[-1]
460         topAddition = []
461         topZ = euclidean.getTopPath(topLoop) + thirdLayerHeight
462         oldIndex = topLoop[-1].index
463         for point in topLoop:
464                 oldIndex += 1
465                 topAddition.append(Vector3Index(oldIndex, 0.8 * point.x, 0.8 * point.y, topZ))
466         firstLoopList.append(topAddition)
467         translation = Vector3(0.0, 0.0, -euclidean.getBottomByPaths(firstLoopList))
468         euclidean.translateVector3Paths(firstLoopList, translation)
469         geometryOutput = triangle_mesh.getPillarsOutput(loopLists)
470         positives.append(geometryOutput)
471
472 def addBottomLoop(deltaZ, loops):
473         "Add bottom loop to loops."
474         bottomLoop = loops[0]
475         bottomAddition = []
476         bottomZ = euclidean.getBottomByPath(bottomLoop) + deltaZ
477         for point in bottomLoop:
478                 bottomAddition.append(Vector3Index(len(bottomAddition), point.x, point.y, bottomZ))
479         loops.insert(0, bottomAddition)
480         numberOfVertexes = 0
481         for loop in loops:
482                 for point in loop:
483                         point.index = numberOfVertexes
484                         numberOfVertexes += 1
485
486 def addCollarShaft(collarLength, derivation, elementNode, negatives, positives):
487         'Add collar.'
488         if collarLength <= 0.0:
489                 addShaft(derivation, negatives, positives)
490                 return
491         connectionEnd = Vector3(0.0, 0.0, derivation.faceWidth + collarLength)
492         copyShallow = derivation.elementNode.getCopyShallow()
493         copyShallow.attributes['path'] = [Vector3(0.0, 0.0, derivation.faceWidth), connectionEnd]
494         collarDerivation = extrude.ExtrudeDerivation(copyShallow)
495         addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives)
496
497 def addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives):
498         'Add collar and shaft.'
499         collarSides = evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, derivation.shaftRimRadius)
500         collarProfile = euclidean.getComplexPolygon(complex(), derivation.shaftRimRadius, collarSides)
501         vector3CollarProfile = euclidean.getVector3Path(collarProfile)
502         extrude.addPositives(collarDerivation, [vector3CollarProfile], positives)
503         addShaft(derivation, negatives, positives)
504         drillZ = derivation.faceWidth + 0.5 * collarLength
505         drillEnd = Vector3(0.0, derivation.shaftRimRadius, drillZ)
506         drillStart = Vector3(0.0, 0.0, drillZ)
507         teardrop.addNegativesByRadius(elementNode, drillEnd, negatives, derivation.keywayRadius, drillStart)
508
509 def addLighteningHoles(derivation, gearHolePaths, negatives, pitchRadius, positives):
510         "Add lightening holes."
511         positiveVertexes = matrix.getVertexes(positives)
512         bottomPath = euclidean.getTopPath(positiveVertexes)
513         topPath = euclidean.getBottomByPath(positiveVertexes)
514         copyShallow = derivation.elementNode.getCopyShallow()
515         copyShallow.attributes['path'] = [Vector3(0.0, 0.0, bottomPath), Vector3(0.0, 0.0, topPath)]
516         extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
517         vector3LighteningHoles = getLighteningHoles(derivation, gearHolePaths, pitchRadius)
518         extrude.addNegativesPositives(extrudeDerivation, negatives, vector3LighteningHoles, positives)
519
520 def addRackHole(derivation, elementNode, vector3RackProfiles, x):
521         "Add rack hole to vector3RackProfiles."
522         rackHole = euclidean.getComplexPolygon(complex(x, -derivation.rackHoleBelow), derivation.rackHoleRadius, -13)
523         vector3RackProfiles.append(euclidean.getVector3Path(rackHole))
524
525 def addRackHoles(derivation, elementNode, vector3RackProfiles):
526         "Add rack holes to vector3RackProfiles."
527         if len(derivation.gearHolePaths) > 0:
528                 vector3RackProfiles += derivation.gearHolePaths
529                 return
530         if derivation.rackHoleRadius <= 0.0:
531                 return
532         addRackHole(derivation, elementNode, vector3RackProfiles, 0.0)
533         rackHoleMargin = derivation.rackHoleRadius + derivation.rackHoleRadius
534         rackHoleSteps = int(math.ceil((derivation.rackDemilength - rackHoleMargin) / derivation.rackHoleStep))
535         for rackHoleIndex in xrange(1, rackHoleSteps):
536                 x = float(rackHoleIndex) * derivation.rackHoleStep
537                 addRackHole(derivation, elementNode, vector3RackProfiles, -x)
538                 addRackHole(derivation, elementNode, vector3RackProfiles, x)
539
540 def addShaft(derivation, negatives, positives):
541         "Add shaft."
542         if len(derivation.shaftPath) < 3:
543                 return
544         positiveVertexes = matrix.getVertexes(positives)
545         bottomPath = euclidean.getTopPath(positiveVertexes)
546         topPath = euclidean.getBottomByPath(positiveVertexes)
547         copyShallow = derivation.elementNode.getCopyShallow()
548         copyShallow.attributes['path'] = [Vector3(0.0, 0.0, bottomPath), Vector3(0.0, 0.0, topPath)]
549         extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
550         extrude.addNegativesPositives(extrudeDerivation, negatives, [derivation.shaftPath], positives)
551
552 def getAxialMargin(circleRadius, numberOfSides, polygonRadius):
553         'Get axial margin.'
554         return polygonRadius * math.sin(math.pi / float(numberOfSides)) - circleRadius
555
556 def getBevelPath(begin, bevel, center, end):
557         'Get bevel path.'
558         centerMinusBegin = center - begin
559         centerMinusBeginLength = abs(centerMinusBegin)
560         endMinusCenter = end - center
561         endMinusCenterLength = abs(endMinusCenter)
562         endMinusCenter /= endMinusCenterLength
563         maximumExtensionLength = 0.333333333 * endMinusCenterLength
564         if centerMinusBeginLength <= bevel * 1.5:
565                 extensionLength = min(maximumExtensionLength, centerMinusBeginLength)
566                 return [complex(center.real, center.imag) + extensionLength * endMinusCenter]
567         centerMinusBegin *= (centerMinusBeginLength - bevel) / centerMinusBeginLength
568         extensionLength = min(maximumExtensionLength, bevel)
569         bevelPath = [complex(center.real, center.imag) + extensionLength * endMinusCenter]
570         bevelPath.append(begin + centerMinusBegin)
571         return bevelPath
572
573 def getGearPaths(derivation, pitchRadius, teeth, toothProfile):
574         'Get gear paths.'
575         if teeth < 0:
576                 return getGearProfileAnnulus(derivation, pitchRadius, teeth, toothProfile)
577         if teeth == 0:
578                 return [getGearProfileRack(derivation, toothProfile)]
579         return [getGearProfileCylinder(teeth, toothProfile)]
580
581 def getGearProfileAnnulus(derivation, pitchRadius, teeth, toothProfile):
582         'Get gear profile for an annulus gear.'
583         gearProfileCylinder = getGearProfileCylinder(teeth, toothProfile)
584         annulusRadius = derivation.dedendum + derivation.rimDedendum - pitchRadius
585         return [euclidean.getComplexPolygon(complex(), annulusRadius, -teeth, 0.5 * math.pi), gearProfileCylinder]
586
587 def getGearProfileCylinder(teeth, toothProfile):
588         'Get gear profile for a cylinder gear.'
589         gearProfile = []
590         toothAngleRadian = 2.0 * math.pi / float(teeth)
591         totalToothAngle = 0.0
592         for toothIndex in xrange(abs(teeth)):
593                 for toothPoint in toothProfile:
594                         gearProfile.append(toothPoint * euclidean.getWiddershinsUnitPolar(totalToothAngle))
595                 totalToothAngle += toothAngleRadian
596         return gearProfile
597
598 def getGearProfileRack(derivation, toothProfile):
599         'Get gear profile for rack.'
600         derivation.extraRackDemilength = 0.0
601         for complexPoint in derivation.helixPath:
602                 derivation.extraRackDemilength = max(abs(derivation.helixHeight * complexPoint.imag), derivation.extraRackDemilength)
603         rackDemilengthPlus = derivation.rackDemilength
604         if derivation.faceWidth > 0.0:
605                 derivation.extraRackDemilength *= 1.1
606                 rackDemilengthPlus += derivation.extraRackDemilength
607         teethRack = int(math.ceil(rackDemilengthPlus / derivation.wavelength))
608         gearProfile = []
609         for toothIndex in xrange(-teethRack, teethRack + 1):
610                 translateComplex = complex(-toothIndex * derivation.wavelength, 0.0)
611                 translatedPath = euclidean.getTranslatedComplexPath(toothProfile, translateComplex)
612                 gearProfile += translatedPath
613         gearProfile = euclidean.getHorizontallyBoundedPath(rackDemilengthPlus, -rackDemilengthPlus, gearProfile)
614         firstPoint = gearProfile[0]
615         lastPoint = gearProfile[-1]
616         rackWidth = derivation.rackWidth
617         minimumRackWidth = 1.1 * derivation.dedendum
618         if rackWidth < minimumRackWidth:
619                 rackWidth = minimumRackWidth
620                 print('Warning, rackWidth is too small in getGearProfileRack in gear.')
621                 print('RackWidth will be set to a bit more than the dedendum.')
622         gearProfile += [complex(lastPoint.real, -rackWidth),complex(firstPoint.real, -rackWidth)]
623         return gearProfile
624
625 def getGeometryOutput(derivation, elementNode):
626         "Get vector3 vertexes from attribute dictionary."
627         if derivation == None:
628                 derivation = GearDerivation(elementNode)
629         creationFirst = derivation.creationType.lower()[: 1]
630         toothProfileComplement = getToothProfile(derivation, derivation.pitchRadiusComplement, derivation.teethComplement)
631         pinionProfile = getGearProfileCylinder(derivation.teethPinion, derivation.pinionToothProfile)
632         complementPaths = getGearPaths(
633                 derivation, derivation.pitchRadiusComplement, derivation.teethComplement, toothProfileComplement)
634         vector3PinionProfile = euclidean.getVector3Path(pinionProfile)
635         vector3ComplementPaths = euclidean.getVector3Paths(complementPaths)
636         translation = Vector3()
637         moveFirst = derivation.moveType.lower()[: 1]
638         if moveFirst != 'n':
639                 distance = derivation.pitchRadius
640                 if moveFirst == 'm':
641                         distance += derivation.pitchRadiusComplement
642                 else:
643                         distance += abs(derivation.pitchRadiusComplement)
644                         decimalPlaces = 1 - int(math.floor(math.log10(distance)))
645                         distance += derivation.halfWavelength + derivation.halfWavelength
646                         distance = round(1.15 * distance, decimalPlaces)
647                 translation = Vector3(0.0, -distance)
648         if derivation.faceWidth <=0.0:
649                 return getPathOutput(
650                         creationFirst, derivation, elementNode, translation, vector3ComplementPaths, vector3PinionProfile)
651         pitchRadius = derivation.pitchRadius
652         teeth = derivation.teethPinion
653         twist = derivation.helixHeight / derivation.pitchRadius
654         extrudeOutputPinion = getOutputCylinder(
655                 derivation.pinionCollarLength, derivation, elementNode, None, pitchRadius, teeth, twist, [vector3PinionProfile])
656         if creationFirst == 'p':
657                 return extrudeOutputPinion
658         teeth = derivation.teethComplement
659         extrudeOutputSecond = None
660         if teeth == 0:
661                 extrudeOutputSecond = getOutputRack(derivation, elementNode, vector3ComplementPaths[0])
662         else:
663                 twist = -derivation.helixHeight / derivation.pitchRadiusComplement
664                 extrudeOutputSecond = getOutputCylinder(
665                         derivation.complementCollarLength,
666                         derivation,
667                         elementNode,
668                         derivation.gearHolePaths,
669                         derivation.pitchRadiusComplement,
670                         teeth,
671                         twist,
672                         vector3ComplementPaths)
673         if creationFirst == 'c':
674                 return extrudeOutputSecond
675         gearVertexes = matrix.getVertexes(extrudeOutputSecond)
676         if moveFirst == 'v':
677                 translation = Vector3(0.0, 0.0, euclidean.getTopPath(gearVertexes))
678                 euclidean.translateVector3Path(matrix.getVertexes(extrudeOutputPinion), translation)
679         else:
680                 euclidean.translateVector3Path(gearVertexes, translation)
681         return {'group' : {'shapes' : [extrudeOutputPinion, extrudeOutputSecond]}}
682
683 def getGeometryOutputByArguments(arguments, elementNode):
684         "Get vector3 vertexes from attribute dictionary by arguments."
685         return getGeometryOutput(None, elementNode)
686
687 def getHalfwave(pitchRadius, teeth):
688         'Get tooth halfwave.'
689         return pitchRadius * math.pi / float(teeth)
690
691 def getHelixComplexPath(derivation, elementNode):
692         'Set gear helix path.'
693         helixTypeFirstCharacter = derivation.helixType.lower()[: 1]
694         if helixTypeFirstCharacter == 'b':
695                 return [complex(), complex(1.0, 1.0)]
696         if helixTypeFirstCharacter == 'h':
697                 return [complex(), complex(0.5, 0.5), complex(1.0, 0.0)]
698         if helixTypeFirstCharacter == 'p':
699                 helixComplexPath = []
700                 x = 0.0
701                 xStep = setting.getLayerHeight(elementNode) / derivation.faceWidth
702                 justBelowOne = 1.0 - 0.5 * xStep
703                 while x < justBelowOne:
704                         distanceFromCenter = 0.5 - x
705                         parabolicTwist = 0.25 - distanceFromCenter * distanceFromCenter
706                         helixComplexPath.append(complex(x, parabolicTwist))
707                         x += xStep
708                 helixComplexPath.append(complex(1.0, 0.0))
709                 return helixComplexPath
710         print('Warning, the helix type was not one of (basic, herringbone or parabolic) in getHelixComplexPath in gear for:')
711         print(derivation.helixType)
712         print(derivation.elementNode)
713
714 def getLiftedOutput(derivation, geometryOutput):
715         "Get extrude output for a rack."
716         if derivation.moveType.lower()[: 1] == 'm':
717                 return geometryOutput
718         geometryOutputVertexes = matrix.getVertexes(geometryOutput)
719         translation = Vector3(0.0, 0.0, -euclidean.getBottomByPath(geometryOutputVertexes))
720         euclidean.translateVector3Path(geometryOutputVertexes, translation)
721         return geometryOutput
722
723 def getLighteningHoles(derivation, gearHolePaths, pitchRadius):
724         'Get cutout circles.'
725         if gearHolePaths != None:
726                 if len(gearHolePaths) > 0:
727                         return gearHolePaths
728         innerRadius = abs(pitchRadius) - derivation.dedendum
729         lighteningHoleOuterRadius = innerRadius - derivation.rimDedendum
730         shaftRimRadius = max(derivation.shaftRimRadius, (lighteningHoleOuterRadius) * (0.5 - math.sqrt(0.1875)))
731         lighteningHoleRadius = 0.5 * (lighteningHoleOuterRadius - derivation.shaftRimRadius)
732         if lighteningHoleRadius < derivation.lighteningHoleMinimumRadius:
733                 return []
734         lighteningHoles = []
735         numberOfLighteningHoles = 3
736         polygonRadius = lighteningHoleOuterRadius - lighteningHoleRadius
737         rimDemiwidth = 0.5 * derivation.lighteningHoleMargin
738         axialMargin = getAxialMargin(lighteningHoleRadius, numberOfLighteningHoles, polygonRadius)
739         if axialMargin < rimDemiwidth:
740                 while axialMargin < rimDemiwidth:
741                         lighteningHoleRadius *= 0.999
742                         if lighteningHoleRadius < derivation.lighteningHoleMinimumRadius:
743                                 return []
744                         axialMargin = getAxialMargin(lighteningHoleRadius, numberOfLighteningHoles, polygonRadius)
745         else:
746                 newNumberOfLighteningHoles = numberOfLighteningHoles
747                 while axialMargin > rimDemiwidth:
748                         numberOfLighteningHoles = newNumberOfLighteningHoles
749                         newNumberOfLighteningHoles += 2
750                         axialMargin = getAxialMargin(lighteningHoleRadius, newNumberOfLighteningHoles, polygonRadius)
751         sideAngle = 2.0 * math.pi / float(numberOfLighteningHoles)
752         startAngle = 0.0
753         for lighteningHoleIndex in xrange(numberOfLighteningHoles):
754                 unitPolar = euclidean.getWiddershinsUnitPolar(startAngle)
755                 lighteningHole = euclidean.getComplexPolygon(unitPolar * polygonRadius, lighteningHoleRadius, -13)
756                 lighteningHoles.append(lighteningHole)
757                 startAngle += sideAngle
758         return euclidean.getVector3Paths(lighteningHoles)
759
760 def getNewDerivation(elementNode):
761         'Get new derivation.'
762         return GearDerivation(elementNode)
763
764 def getOutputCylinder(
765                 collarLength, derivation, elementNode, gearHolePaths, pitchRadius, teeth, twist, vector3GearProfile):
766         "Get extrude output for a cylinder gear."
767         copyShallow = derivation.elementNode.getCopyShallow()
768         copyShallow.attributes['path'] = [Vector3(), Vector3(0.0, 0.0, derivation.faceWidth)]
769         extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
770         negatives = []
771         positives = []
772         if twist != 0.0:
773                 twistDegrees = math.degrees(twist)
774                 extrudeDerivation.twistPathDefault = []
775                 for complexPoint in derivation.helixPath:
776                         extrudeDerivation.twistPathDefault.append(Vector3(complexPoint.real, twistDegrees * complexPoint.imag))
777                 extrude.insertTwistPortions(extrudeDerivation, elementNode)
778         if derivation.operatingAngle != 180.0:
779                 addBevelGear(derivation, extrudeDerivation, pitchRadius, positives, teeth, vector3GearProfile)
780                 addCollarShaft(collarLength, derivation, elementNode, negatives, positives)
781                 return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
782         if pitchRadius > 0:
783                 extrude.addNegativesPositives(extrudeDerivation, negatives, vector3GearProfile, positives)
784                 addLighteningHoles(derivation, gearHolePaths, negatives, pitchRadius, positives)
785                 addCollarShaft(collarLength, derivation, elementNode, negatives, positives)
786                 return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
787         if derivation.plateLength <= 0.0:
788                 extrude.addNegativesPositives(extrudeDerivation, negatives, vector3GearProfile, positives)
789                 return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
790         portionDirections = extrude.getSpacedPortionDirections(extrudeDerivation.interpolationDictionary)
791         outerGearProfile = vector3GearProfile[0]
792         outerLoopLists = extrude.getLoopListsByPath(extrudeDerivation, None, outerGearProfile, portionDirections)
793         addBottomLoop(-derivation.plateClearance, outerLoopLists[0])
794         geometryOutput = triangle_mesh.getPillarsOutput(outerLoopLists)
795         positives.append(geometryOutput)
796         innerLoopLists = extrude.getLoopListsByPath(extrudeDerivation, None, vector3GearProfile[1], portionDirections)
797         addBottomLoop(-derivation.plateClearance, innerLoopLists[0])
798         geometryOutput = triangle_mesh.getPillarsOutput(innerLoopLists)
799         negatives.append(geometryOutput)
800         connectionStart = Vector3(0.0, 0.0, -derivation.plateLength)
801         copyShallow = derivation.elementNode.getCopyShallow()
802         copyShallow.attributes['path'] = [connectionStart, Vector3(0.0, 0.0, -derivation.plateClearance)]
803         plateDerivation = extrude.ExtrudeDerivation(copyShallow)
804         extrude.addNegativesPositives(plateDerivation, negatives, [outerGearProfile], positives)
805         vector3LighteningHoles = getLighteningHoles(derivation, gearHolePaths, pitchRadius)
806         extrude.addNegativesPositives(plateDerivation, negatives, vector3LighteningHoles, positives)
807         addShaft(derivation, negatives, positives)
808         positiveOutput = triangle_mesh.getUnifiedOutput(positives)
809         annulusPlateOutput = {'difference' : {'shapes' : [positiveOutput] + negatives}}
810         if collarLength <= 0.0:
811                 outputCylinder = solid.getGeometryOutputByManipulation(elementNode, annulusPlateOutput)
812                 return getLiftedOutput(derivation, outputCylinder)
813         negatives = []
814         positives = []
815         connectionEnd = Vector3(0.0, 0.0, derivation.faceWidth + collarLength)
816         copyShallow = derivation.elementNode.getCopyShallow()
817         copyShallow.attributes['path'] = [Vector3(0.0, 0.0, -derivation.plateClearance), connectionEnd]
818         collarDerivation = extrude.ExtrudeDerivation(copyShallow)
819         addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives)
820         collarOutput = {'difference' : {'shapes' : positives + negatives}}
821         cylinderOutput = {'union' : {'shapes' : [annulusPlateOutput, collarOutput]}}
822         outputCylinder = solid.getGeometryOutputByManipulation(elementNode, cylinderOutput)
823         return getLiftedOutput(derivation, outputCylinder)
824
825 def getOutputRack(derivation, elementNode, vector3GearProfile):
826         "Get extrude output for a rack."
827         path = []
828         for complexPoint in derivation.helixPath:
829                 point = Vector3(derivation.helixHeight * complexPoint.imag, 0.0, derivation.faceWidth * complexPoint.real)
830                 path.append(point)
831         copyShallow = derivation.elementNode.getCopyShallow()
832         copyShallow.attributes['path'] = path
833         extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
834         negatives = []
835         positives = []
836         vector3RackProfiles = [vector3GearProfile]
837         if derivation.extraRackDemilength > 0.0:
838                 yMaximum = -912345678.0
839                 yMinimum = 912345678.0
840                 for point in vector3GearProfile:
841                         yMaximum = max(point.y, yMaximum)
842                         yMinimum = min(point.y, yMinimum)
843                 muchLessThanWidth = 0.01 * derivation.rackWidth
844                 yMaximum += muchLessThanWidth
845                 yMinimum -= muchLessThanWidth
846                 extraRackLength = derivation.extraRackDemilength + derivation.extraRackDemilength
847                 rackDemilengthPlus = derivation.rackDemilength + extraRackLength
848                 leftNegative = [
849                         Vector3(-derivation.rackDemilength, yMaximum),
850                         Vector3(-derivation.rackDemilength, yMinimum),
851                         Vector3(-rackDemilengthPlus, yMinimum),
852                         Vector3(-rackDemilengthPlus, yMaximum)]
853                 vector3RackProfiles.append(leftNegative)
854                 rightNegative = [
855                         Vector3(rackDemilengthPlus, yMaximum),
856                         Vector3(rackDemilengthPlus, yMinimum),
857                         Vector3(derivation.rackDemilength, yMinimum),
858                         Vector3(derivation.rackDemilength, yMaximum)]
859                 vector3RackProfiles.append(rightNegative)
860         addRackHoles(derivation, elementNode, vector3RackProfiles)
861         extrude.addNegativesPositives(extrudeDerivation, negatives, vector3RackProfiles, positives)
862         return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
863
864 def getPathOutput(creationFirst, derivation, elementNode, translation, vector3ComplementPaths, vector3PinionProfile):
865         "Get gear path output."
866         vector3PinionProfile = lineation.getPackedGeometryOutputByLoop(elementNode, lineation.SideLoop(vector3PinionProfile))
867         if creationFirst == 'p':
868                 return vector3PinionProfile
869         packedGearGeometry = []
870         for vector3ComplementPath in vector3ComplementPaths:
871                 sideLoop = lineation.SideLoop(vector3ComplementPath)
872                 packedGearGeometry += lineation.getPackedGeometryOutputByLoop(elementNode, sideLoop)
873         if creationFirst == 'c':
874                 return packedGearGeometry
875         euclidean.translateVector3Paths(packedGearGeometry, translation)
876         return vector3PinionProfile + packedGearGeometry
877
878 def getThicknessMultipliedPath(path, thicknessMultiplier):
879         "Get thickness multiplied path."
880         for pointIndex, point in enumerate(path):
881                 path[pointIndex] = complex(point.real * thicknessMultiplier, point.imag)
882         return path
883
884 def getToothProfile(derivation, pitchRadius, teeth):
885         'Get profile for one tooth.'
886         if teeth < 0:
887                 return getToothProfileAnnulus(derivation, pitchRadius, teeth)
888         if teeth == 0:
889                 return getToothProfileRack(derivation)
890         return getToothProfileCylinder(derivation, pitchRadius, teeth)
891
892 def getToothProfileAnnulus(derivation, pitchRadius, teeth):
893         'Get profile for one tooth of an annulus.'
894         toothProfileHalf = []
895         toothProfileHalfCylinder = getToothProfileHalfCylinder(derivation, pitchRadius)
896         pitchRadius = -pitchRadius
897         innerRadius = pitchRadius - derivation.addendum
898         # tooth is multiplied by 1.02 because at around 1.01 for a 7/-23/20.0 test case, there is intersection since the paths are bending together
899         for point in getThicknessMultipliedPath(toothProfileHalfCylinder, 1.02 / derivation.toothThicknessMultiplier):
900                 if abs(point) >= innerRadius:
901                         toothProfileHalf.append(point)
902         profileFirst = toothProfileHalf[0]
903         profileSecond = toothProfileHalf[1]
904         firstMinusSecond = profileFirst - profileSecond
905         remainingAddendum = abs(profileFirst) - innerRadius
906         firstMinusSecond *= remainingAddendum / abs(firstMinusSecond)
907         extensionPoint = profileFirst + firstMinusSecond
908         if derivation.tipBevel > 0.0:
909                 unitPolar = euclidean.getWiddershinsUnitPolar(2.0 / float(teeth) * math.pi)
910                 mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag) * unitPolar
911                 bevelPath = getBevelPath(profileFirst, derivation.tipBevel, extensionPoint, mirrorPoint)
912                 toothProfileHalf = bevelPath + toothProfileHalf
913         else:
914                 toothProfileHalf.insert(0, extensionPoint)
915         profileLast = toothProfileHalf[-1]
916         profilePenultimate = toothProfileHalf[-2]
917         lastMinusPenultimate = profileLast - profilePenultimate
918         remainingDedendum = pitchRadius - abs(profileLast) + derivation.dedendum
919         lastMinusPenultimate *= remainingDedendum / abs(lastMinusPenultimate)
920         extensionPoint = profileLast + lastMinusPenultimate
921         if derivation.rootBevel > 0.0:
922                 mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag)
923                 bevelPath = getBevelPath(profileLast, derivation.rootBevel, extensionPoint, mirrorPoint)
924                 bevelPath.reverse()
925                 toothProfileHalf += bevelPath
926         else:
927                 toothProfileHalf.append(extensionPoint)
928         toothProfileAnnulus = euclidean.getMirrorPath(toothProfileHalf)
929         toothProfileAnnulus.reverse()
930         return toothProfileAnnulus
931
932 def getToothProfileCylinder(derivation, pitchRadius, teeth):
933         'Get profile for one tooth of a cylindrical gear.'
934         toothProfileHalfCylinder = getToothProfileHalfCylinder(derivation, pitchRadius)
935         toothProfileHalfCylinder = getThicknessMultipliedPath(toothProfileHalfCylinder, derivation.toothThicknessMultiplier)
936         toothProfileHalf = []
937         innerRadius = pitchRadius - derivation.dedendum
938         for point in toothProfileHalfCylinder:
939                 if abs(point) >= innerRadius:
940                         toothProfileHalf.append(point)
941         return getToothProfileCylinderByProfile(derivation, pitchRadius, teeth, toothProfileHalf)
942
943 def getToothProfileCylinderByProfile(derivation, pitchRadius, teeth, toothProfileHalf):
944         'Get profile for one tooth of a cylindrical gear.'
945         profileFirst = toothProfileHalf[0]
946         profileSecond = toothProfileHalf[1]
947         firstMinusSecond = profileFirst - profileSecond
948         remainingDedendum = abs(profileFirst) - pitchRadius + derivation.dedendum
949         firstMinusSecond *= remainingDedendum / abs(firstMinusSecond)
950         extensionPoint = profileFirst + firstMinusSecond
951         if derivation.rootBevel > 0.0:
952                 unitPolar = euclidean.getWiddershinsUnitPolar(-2.0 / float(teeth) * math.pi)
953                 mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag) * unitPolar
954                 bevelPath = getBevelPath(profileFirst, derivation.rootBevel, extensionPoint, mirrorPoint)
955                 toothProfileHalf = bevelPath + toothProfileHalf
956         else:
957                 toothProfileHalf.insert(0, extensionPoint)
958         if derivation.tipBevel > 0.0:
959                 profileLast = toothProfileHalf[-1]
960                 profilePenultimate = toothProfileHalf[-2]
961                 mirrorPoint = complex(-profileLast.real, profileLast.imag)
962                 bevelPath = getBevelPath(profilePenultimate, derivation.tipBevel, profileLast, mirrorPoint)
963                 bevelPath.reverse()
964                 toothProfileHalf = toothProfileHalf[: -1] + bevelPath
965         return euclidean.getMirrorPath(toothProfileHalf)
966
967 def getToothProfileHalfCylinder(derivation, pitchRadius):
968         'Get profile for half of a one tooth of a cylindrical gear.'
969         toothProfile=[]
970 #       x = -y * tan(p) + 1
971 #       x*x + y*y = (2-cos(p))^2
972 #       y*y*t*t-2yt+1+y*y=4-4c-c*c
973 #       y*y*(t*t+1)-2yt=3-4c-c*c
974 #       y*y*(t*t+1)-2yt-3+4c-c*c=0
975 #       a=tt+1
976 #       b=-2t
977 #       c=c(4-c)-3
978         a = derivation.tanPressure * derivation.tanPressure + 1.0
979         b = -derivation.tanPressure - derivation.tanPressure
980         cEnd = derivation.cosPressure * (4.0 - derivation.cosPressure) - 3.0
981         yEnd = (-b - math.sqrt(b*b - 4 * a * cEnd)) * 0.5 / a
982         yEnd *= derivation.pitchRadius / abs(pitchRadius)
983         yEnd -= derivation.clearance / abs(pitchRadius)
984         # to prevent intersections, yBegin is moved towards the base circle, giving a thinner tooth
985         yBegin = -yEnd
986         if pitchRadius > 0.0:
987                 yBegin = 0.5 * derivation.sinPressure + 0.5 * yBegin
988         beginComplex = complex(1.0 - yBegin * derivation.tanPressure, yBegin)
989         endComplex = complex(1.0 - yEnd * derivation.tanPressure, yEnd)
990         endMinusBeginComplex = endComplex - beginComplex
991         wholeAngle = -abs(endMinusBeginComplex) / derivation.cosPressure
992         wholeAngleIncrement = wholeAngle / float(derivation.profileSurfaces)
993         stringStartAngle = abs(beginComplex - complex(1.0, 0.0)) / derivation.cosPressure
994         wholeDepthIncrementComplex = endMinusBeginComplex / float(derivation.profileSurfaces)
995         for profileIndex in xrange(derivation.profileSurfaces + 1):
996                 contactPoint = beginComplex + wholeDepthIncrementComplex * float(profileIndex)
997                 stringAngle = stringStartAngle + wholeAngleIncrement * float(profileIndex)
998                 angle = math.atan2(contactPoint.imag, contactPoint.real) - stringAngle
999                 angle += 0.5 * math.pi - derivation.quarterWavelength / abs(pitchRadius)
1000                 toothPoint = abs(contactPoint) * euclidean.getWiddershinsUnitPolar(angle) * abs(pitchRadius)
1001                 toothProfile.append(toothPoint)
1002         return toothProfile
1003
1004 def getToothProfileRack(derivation):
1005         'Get profile for one rack tooth.'
1006         addendumSide = derivation.quarterWavelength - derivation.addendum * derivation.tanPressure
1007         addendumComplex = complex(addendumSide, derivation.addendum)
1008         dedendumSide = derivation.quarterWavelength + derivation.dedendum * derivation.tanPressure
1009         dedendumComplex = complex(dedendumSide, -derivation.dedendum)
1010         toothProfile = [dedendumComplex]
1011         if derivation.rootBevel > 0.0:
1012                 mirrorPoint = complex(derivation.wavelength - dedendumSide, -derivation.dedendum)
1013                 toothProfile = getBevelPath(addendumComplex, derivation.rootBevel, dedendumComplex, mirrorPoint)
1014         if derivation.tipBevel > 0.0:
1015                 mirrorPoint = complex(-addendumComplex.real, addendumComplex.imag)
1016                 bevelPath = getBevelPath(dedendumComplex, derivation.tipBevel, addendumComplex, mirrorPoint)
1017                 bevelPath.reverse()
1018                 toothProfile += bevelPath
1019         else:
1020                 toothProfile.append(addendumComplex)
1021         return euclidean.getMirrorPath(getThicknessMultipliedPath(toothProfile, derivation.toothThicknessMultiplier))
1022
1023 def processElementNode(elementNode):
1024         "Process the xml element."
1025         geometryOutput = getGeometryOutput(None, elementNode)
1026         if geometryOutput.__class__ == list:
1027                 path.convertElementNode(elementNode, geometryOutput)
1028         else:
1029                 solid.processElementNodeByGeometry(elementNode, geometryOutput)
1030
1031
1032 class GearDerivation:
1033         "Class to hold gear variables."
1034         def __init__(self, elementNode):
1035                 'Set defaults.'
1036                 self.clearanceOverWavelength = evaluate.getEvaluatedFloat(0.1, elementNode, 'clearanceOverWavelength')
1037                 self.collarAddendumOverRadius = evaluate.getEvaluatedFloat(1.0, elementNode, 'collarAddendumOverRadius')
1038                 self.complementCollarLengthOverFaceWidth = evaluate.getEvaluatedFloat(
1039                         0.0, elementNode, 'complementCollarLengthOverFaceWidth')
1040                 self.copyShallow = elementNode.getCopyShallow()
1041                 self.creationType = evaluate.getEvaluatedString('both', elementNode, 'creationType')
1042                 self.creationTypeMenuRadioStrings = 'both complement pinion'.split()
1043                 self.elementNode = elementNode
1044                 self.faceWidth = evaluate.getEvaluatedFloat(10.0, elementNode, 'faceWidth')
1045                 self.helixAngle = evaluate.getEvaluatedFloat(0.0, elementNode, 'helixAngle')
1046                 self.helixType = evaluate.getEvaluatedString('basic', elementNode, 'helixType')
1047                 self.helixTypeMenuRadioStrings = 'basic herringbone parabolic'.split()
1048                 self.keywayRadiusOverRadius = evaluate.getEvaluatedFloat(0.5, elementNode, 'keywayRadiusOverRadius')
1049                 self.lighteningHoleMarginOverRimDedendum = evaluate.getEvaluatedFloat(
1050                         1.0, elementNode, 'lighteningHoleMarginOverRimDedendum')
1051                 self.lighteningHoleMinimumRadius = evaluate.getEvaluatedFloat(
1052                         1.0, elementNode, 'lighteningHoleMinimumRadius')
1053                 self.moveType = evaluate.getEvaluatedString('separate', elementNode, 'moveType')
1054                 self.moveTypeMenuRadioStrings = 'mesh none separate vertical'.split()
1055                 self.operatingAngle = evaluate.getEvaluatedFloat(180.0, elementNode, 'operatingAngle')
1056                 self.pinionCollarLengthOverFaceWidth = evaluate.getEvaluatedFloat(
1057                         0.0, elementNode, 'pinionCollarLengthOverFaceWidth')
1058                 self.plateClearanceOverLength = evaluate.getEvaluatedFloat(0.2, elementNode, 'plateClearanceOverLength')
1059                 self.plateLengthOverFaceWidth = evaluate.getEvaluatedFloat(0.5, elementNode, 'plateLengthOverFaceWidth')
1060                 self.pressureAngle = evaluate.getEvaluatedFloat(20.0, elementNode, 'pressureAngle')
1061                 self.profileSurfaces = evaluate.getEvaluatedInt(11, elementNode, 'profileSurfaces')
1062                 self.rackHoleBelowOverWidth = evaluate.getEvaluatedFloat(0.6, elementNode, 'rackHoleBelowOverWidth')
1063                 self.rackHoleRadiusOverWidth = evaluate.getEvaluatedFloat(0.0, elementNode, 'rackHoleRadiusOverWidth')
1064                 self.rackHoleStepOverWidth = evaluate.getEvaluatedFloat(1.0, elementNode, 'rackHoleStepOverWidth')
1065                 self.rackLengthOverRadius = evaluate.getEvaluatedFloat(math.pi + math.pi, elementNode, 'rackLengthOverRadius')
1066                 self.rackWidthOverFaceWidth = evaluate.getEvaluatedFloat(1.0, elementNode, 'rackWidthOverFaceWidth')
1067                 self.rimDedendumOverRadius = evaluate.getEvaluatedFloat(0.2, elementNode, 'rimDedendumOverRadius')
1068                 self.rootBevelOverClearance = evaluate.getEvaluatedFloat(0.5, elementNode, 'rootBevelOverClearance')
1069                 self.shaftDepthBottomOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'shaftDepthBottomOverRadius')
1070                 self.shaftDepthTopOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'shaftDepthOverRadius')
1071                 self.shaftRadiusOverPitchRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'shaftRadiusOverPitchRadius')
1072                 self.shaftSides = evaluate.getEvaluatedInt(4, elementNode, 'shaftSides')
1073                 self.teethComplement = evaluate.getEvaluatedInt(17, elementNode, 'teethComplement')
1074                 self.teethPinion = evaluate.getEvaluatedInt(7, elementNode, 'teeth')
1075                 totalTeethOverPinionTeeth = float(self.teethComplement + self.teethPinion) / float(self.teethPinion)
1076                 self.centerDistance = evaluate.getEvaluatedFloat(20.0 * totalTeethOverPinionTeeth, elementNode, 'centerDistance')
1077                 derivedPitchRadius = self.centerDistance / totalTeethOverPinionTeeth
1078                 self.pitchRadius = evaluate.getEvaluatedFloat(derivedPitchRadius, elementNode, 'pitchRadius')
1079                 self.tipBevelOverClearance = evaluate.getEvaluatedFloat(0.1, elementNode, 'tipBevelOverClearance')
1080                 # tooth multiplied by 0.99999 to avoid an intersection
1081                 self.toothThicknessMultiplier = evaluate.getEvaluatedFloat(0.99999, elementNode, 'toothThicknessMultiplier')
1082                 # Set derived variables.
1083                 self.wavelength = self.pitchRadius * 2.0 * math.pi / float(self.teethPinion)
1084                 self.clearance = self.wavelength * self.clearanceOverWavelength
1085                 self.clearance = evaluate.getEvaluatedFloat(self.clearance, elementNode, 'clearance')
1086                 self.complementCollarLength = self.faceWidth * self.complementCollarLengthOverFaceWidth
1087                 self.complementCollarLength = evaluate.getEvaluatedFloat(self.complementCollarLength, elementNode, 'complementCollarLength')
1088                 self.gearHolePaths = evaluate.getTransformedPathsByKey([], elementNode, 'gearHolePaths')
1089                 self.pinionCollarLength = self.faceWidth * self.pinionCollarLengthOverFaceWidth
1090                 self.pinionCollarLength = evaluate.getEvaluatedFloat(self.pinionCollarLength, elementNode, 'pinionCollarLength')
1091                 self.plateLength = self.faceWidth * self.plateLengthOverFaceWidth
1092                 self.plateLength = evaluate.getEvaluatedFloat(self.plateLength, elementNode, 'plateLength')
1093                 self.plateClearance = self.plateLength * self.plateClearanceOverLength
1094                 self.plateClearance = evaluate.getEvaluatedFloat(self.plateClearance, elementNode, 'plateClearance')
1095                 self.rackLength = self.pitchRadius * self.rackLengthOverRadius
1096                 self.rackLength = evaluate.getEvaluatedFloat(self.rackLength, elementNode, 'rackLength')
1097                 self.rackDemilength = 0.5 * self.rackLength
1098                 self.rackWidth = self.faceWidth * self.rackWidthOverFaceWidth
1099                 self.rackWidth = evaluate.getEvaluatedFloat(self.rackWidth, elementNode, 'rackWidth')
1100                 self.rimDedendum = self.pitchRadius * self.rimDedendumOverRadius
1101                 self.rimDedendum = evaluate.getEvaluatedFloat(self.rimDedendum, elementNode, 'rimDedendum')
1102                 self.rootBevel = self.clearance * self.rootBevelOverClearance
1103                 self.rootBevel = evaluate.getEvaluatedFloat(self.rootBevel, elementNode, 'rootBevel')
1104                 self.shaftRadius = self.pitchRadius * self.shaftRadiusOverPitchRadius
1105                 self.shaftRadius = evaluate.getEvaluatedFloat(self.shaftRadius, elementNode, 'shaftRadius')
1106                 self.collarAddendum = self.shaftRadius * self.collarAddendumOverRadius
1107                 self.collarAddendum = evaluate.getEvaluatedFloat(self.collarAddendum, elementNode, 'collarWidth')
1108                 self.keywayRadius = self.shaftRadius * self.keywayRadiusOverRadius
1109                 self.keywayRadius = lineation.getFloatByPrefixBeginEnd(elementNode, 'keywayRadius', 'keywayDiameter', self.keywayRadius)
1110                 self.lighteningHoleMargin = self.rimDedendum * self.lighteningHoleMarginOverRimDedendum
1111                 self.lighteningHoleMargin = evaluate.getEvaluatedFloat(
1112                         self.lighteningHoleMargin, elementNode, 'lighteningHoleMargin')
1113                 self.rackHoleBelow = self.rackWidth * self.rackHoleBelowOverWidth
1114                 self.rackHoleBelow = evaluate.getEvaluatedFloat(self.rackHoleBelow, elementNode, 'rackHoleBelow')
1115                 self.rackHoleRadius = self.rackWidth * self.rackHoleRadiusOverWidth
1116                 self.rackHoleRadius = lineation.getFloatByPrefixBeginEnd(elementNode, 'rackHoleRadius', 'rackHoleDiameter', self.rackHoleRadius)
1117                 self.rackHoleStep = self.rackWidth * self.rackHoleStepOverWidth
1118                 self.rackHoleStep = evaluate.getEvaluatedFloat(self.rackHoleStep, elementNode, 'rackHoleStep')
1119                 self.shaftDepthBottom = self.shaftRadius * self.shaftDepthBottomOverRadius
1120                 self.shaftDepthBottom = evaluate.getEvaluatedFloat(self.shaftDepthBottom, elementNode, 'shaftDepthBottom')
1121                 self.shaftDepthTop = self.shaftRadius * self.shaftDepthTopOverRadius
1122                 self.shaftDepthTop = evaluate.getEvaluatedFloat(self.shaftDepthTop, elementNode, 'shaftDepthTop')
1123                 self.shaftPath = evaluate.getTransformedPathByKey([], elementNode, 'shaftPath')
1124                 if len(self.shaftPath) < 3:
1125                         self.shaftPath = shaft.getShaftPath(self.shaftDepthBottom, self.shaftDepthTop, self.shaftRadius, -self.shaftSides)
1126                 self.tipBevel = self.clearance * self.tipBevelOverClearance
1127                 self.tipBevel = evaluate.getEvaluatedFloat(self.tipBevel, elementNode, 'tipBevel')
1128                 # Set derived values.
1129                 self.helixRadian = math.radians(self.helixAngle)
1130                 if self.teethComplement <= 0.0 and self.operatingAngle != 180.0:
1131                         print('Warning, an operatingAngle other than 180 degrees can only work with a positive number of gear teeth.')
1132                         print('Therefore the operatingAngle will be reset to 180 degrees.')
1133                         self.operatingAngle = 180.0
1134                 self.tanHelix = math.tan(self.helixRadian)
1135                 self.helixHeight = self.tanHelix * self.faceWidth
1136                 self.operatingRadian = math.radians(self.operatingAngle)
1137                 self.pitchRadiusComplement = self.pitchRadius * float(self.teethComplement) / float(self.teethPinion)
1138                 self.pressureRadian = math.radians(self.pressureAngle)
1139                 self.cosPressure = math.cos(self.pressureRadian)
1140                 self.sinPressure = math.sin(self.pressureRadian)
1141                 self.tanPressure = math.tan(self.pressureRadian)
1142                 self.halfWavelength = 0.5 * self.wavelength
1143                 self.helixPath = euclidean.getComplexPath(evaluate.getTransformedPathByKey([], elementNode, 'helixPath'))
1144                 if len(self.helixPath) < 1:
1145                         self.helixPath = getHelixComplexPath(self, elementNode)
1146                 self.quarterWavelength = 0.25 * self.wavelength
1147                 self.shaftRimRadius = self.shaftRadius + self.collarAddendum
1148                 self.toothProfileHalf = getToothProfileHalfCylinder(self, self.pitchRadius)
1149                 self.toothProfileHalf = getThicknessMultipliedPath(self.toothProfileHalf, self.toothThicknessMultiplier)
1150                 self.addendum = self.toothProfileHalf[-1].imag - self.pitchRadius
1151                 self.dedendum = abs(self.toothProfileHalf[-1]) - self.pitchRadius + self.clearance
1152                 self.pinionToothProfile = getToothProfileCylinderByProfile(self, self.pitchRadius, self.teethPinion, self.toothProfileHalf)