chiark / gitweb /
Add back the ultimaker platform, and made the platform mesh simpler.
[cura.git] / Cura / slice / 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
405 from fabmetheus_utilities.geometry.creation import extrude
406 from fabmetheus_utilities.geometry.creation import lineation
407 from fabmetheus_utilities.geometry.creation import shaft
408 from fabmetheus_utilities.geometry.creation import solid
409 from fabmetheus_utilities.geometry.creation import teardrop
410 from fabmetheus_utilities.geometry.geometry_tools import path
411 from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
412 from fabmetheus_utilities.geometry.geometry_utilities import evaluate
413 from fabmetheus_utilities.geometry.geometry_utilities import matrix
414 from fabmetheus_utilities.geometry.solids import triangle_mesh
415 from fabmetheus_utilities.vector3 import Vector3
416 from fabmetheus_utilities.vector3index import Vector3Index
417 from fabmetheus_utilities import euclidean
418 import math
419
420
421 __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
422 __credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
423 __date__ = '$Date: 2008/02/05 $'
424 __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
425
426
427 def addBevelGear(derivation, extrudeDerivation, pitchRadius, positives, teeth, vector3GearProfile):
428         "Get extrude output for a cylinder gear."
429         totalPitchRadius = derivation.pitchRadiusComplement + derivation.pitchRadius
430         totalTeeth = derivation.teethPinion + derivation.teethComplement
431         portionDirections = extrude.getSpacedPortionDirections(extrudeDerivation.interpolationDictionary)
432         loopLists = extrude.getLoopListsByPath(extrudeDerivation, None, vector3GearProfile[0], portionDirections)
433         firstLoopList = loopLists[0]
434         gearOverPinion = float(totalTeeth - teeth) / float(teeth)
435         thirdLayerHeight = 0.33333333333 * setting.getLayerHeight(derivation.elementNode)
436         pitchRadian = math.atan(math.sin(derivation.operatingRadian) / (gearOverPinion + math.cos(derivation.operatingRadian)))
437         coneDistance = pitchRadius / math.sin(pitchRadian)
438         apex = Vector3(0.0, 0.0, math.sqrt(coneDistance * coneDistance - pitchRadius * pitchRadius))
439         cosPitch = apex.z / coneDistance
440         sinPitch = math.sin(pitchRadian)
441         for loop in firstLoopList:
442                 for point in loop:
443                         alongWay = point.z / coneDistance
444                         oneMinusAlongWay = 1.0 - alongWay
445                         pointComplex = point.dropAxis()
446                         pointComplexLength = abs(pointComplex)
447                         deltaRadius = pointComplexLength - pitchRadius
448                         cosDeltaRadius = cosPitch * deltaRadius
449                         sinDeltaRadius = sinPitch * deltaRadius
450                         pointComplex *= (cosDeltaRadius + pitchRadius) / pointComplexLength
451                         point.x = pointComplex.real
452                         point.y = pointComplex.imag
453                         point.z += sinDeltaRadius
454                         point.x *= oneMinusAlongWay
455                         point.y *= oneMinusAlongWay
456         addBottomLoop(-thirdLayerHeight, firstLoopList)
457         topLoop = firstLoopList[-1]
458         topAddition = []
459         topZ = euclidean.getTopPath(topLoop) + thirdLayerHeight
460         oldIndex = topLoop[-1].index
461         for point in topLoop:
462                 oldIndex += 1
463                 topAddition.append(Vector3Index(oldIndex, 0.8 * point.x, 0.8 * point.y, topZ))
464         firstLoopList.append(topAddition)
465         translation = Vector3(0.0, 0.0, -euclidean.getBottomByPaths(firstLoopList))
466         euclidean.translateVector3Paths(firstLoopList, translation)
467         geometryOutput = triangle_mesh.getPillarsOutput(loopLists)
468         positives.append(geometryOutput)
469
470 def addBottomLoop(deltaZ, loops):
471         "Add bottom loop to loops."
472         bottomLoop = loops[0]
473         bottomAddition = []
474         bottomZ = euclidean.getBottomByPath(bottomLoop) + deltaZ
475         for point in bottomLoop:
476                 bottomAddition.append(Vector3Index(len(bottomAddition), point.x, point.y, bottomZ))
477         loops.insert(0, bottomAddition)
478         numberOfVertexes = 0
479         for loop in loops:
480                 for point in loop:
481                         point.index = numberOfVertexes
482                         numberOfVertexes += 1
483
484 def addCollarShaft(collarLength, derivation, elementNode, negatives, positives):
485         'Add collar.'
486         if collarLength <= 0.0:
487                 addShaft(derivation, negatives, positives)
488                 return
489         connectionEnd = Vector3(0.0, 0.0, derivation.faceWidth + collarLength)
490         copyShallow = derivation.elementNode.getCopyShallow()
491         copyShallow.attributes['path'] = [Vector3(0.0, 0.0, derivation.faceWidth), connectionEnd]
492         collarDerivation = extrude.ExtrudeDerivation(copyShallow)
493         addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives)
494
495 def addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives):
496         'Add collar and shaft.'
497         collarSides = evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, derivation.shaftRimRadius)
498         collarProfile = euclidean.getComplexPolygon(complex(), derivation.shaftRimRadius, collarSides)
499         vector3CollarProfile = euclidean.getVector3Path(collarProfile)
500         extrude.addPositives(collarDerivation, [vector3CollarProfile], positives)
501         addShaft(derivation, negatives, positives)
502         drillZ = derivation.faceWidth + 0.5 * collarLength
503         drillEnd = Vector3(0.0, derivation.shaftRimRadius, drillZ)
504         drillStart = Vector3(0.0, 0.0, drillZ)
505         teardrop.addNegativesByRadius(elementNode, drillEnd, negatives, derivation.keywayRadius, drillStart)
506
507 def addLighteningHoles(derivation, gearHolePaths, negatives, pitchRadius, positives):
508         "Add lightening holes."
509         positiveVertexes = matrix.getVertexes(positives)
510         bottomPath = euclidean.getTopPath(positiveVertexes)
511         topPath = euclidean.getBottomByPath(positiveVertexes)
512         copyShallow = derivation.elementNode.getCopyShallow()
513         copyShallow.attributes['path'] = [Vector3(0.0, 0.0, bottomPath), Vector3(0.0, 0.0, topPath)]
514         extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
515         vector3LighteningHoles = getLighteningHoles(derivation, gearHolePaths, pitchRadius)
516         extrude.addNegativesPositives(extrudeDerivation, negatives, vector3LighteningHoles, positives)
517
518 def addRackHole(derivation, elementNode, vector3RackProfiles, x):
519         "Add rack hole to vector3RackProfiles."
520         rackHole = euclidean.getComplexPolygon(complex(x, -derivation.rackHoleBelow), derivation.rackHoleRadius, -13)
521         vector3RackProfiles.append(euclidean.getVector3Path(rackHole))
522
523 def addRackHoles(derivation, elementNode, vector3RackProfiles):
524         "Add rack holes to vector3RackProfiles."
525         if len(derivation.gearHolePaths) > 0:
526                 vector3RackProfiles += derivation.gearHolePaths
527                 return
528         if derivation.rackHoleRadius <= 0.0:
529                 return
530         addRackHole(derivation, elementNode, vector3RackProfiles, 0.0)
531         rackHoleMargin = derivation.rackHoleRadius + derivation.rackHoleRadius
532         rackHoleSteps = int(math.ceil((derivation.rackDemilength - rackHoleMargin) / derivation.rackHoleStep))
533         for rackHoleIndex in xrange(1, rackHoleSteps):
534                 x = float(rackHoleIndex) * derivation.rackHoleStep
535                 addRackHole(derivation, elementNode, vector3RackProfiles, -x)
536                 addRackHole(derivation, elementNode, vector3RackProfiles, x)
537
538 def addShaft(derivation, negatives, positives):
539         "Add shaft."
540         if len(derivation.shaftPath) < 3:
541                 return
542         positiveVertexes = matrix.getVertexes(positives)
543         bottomPath = euclidean.getTopPath(positiveVertexes)
544         topPath = euclidean.getBottomByPath(positiveVertexes)
545         copyShallow = derivation.elementNode.getCopyShallow()
546         copyShallow.attributes['path'] = [Vector3(0.0, 0.0, bottomPath), Vector3(0.0, 0.0, topPath)]
547         extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
548         extrude.addNegativesPositives(extrudeDerivation, negatives, [derivation.shaftPath], positives)
549
550 def getAxialMargin(circleRadius, numberOfSides, polygonRadius):
551         'Get axial margin.'
552         return polygonRadius * math.sin(math.pi / float(numberOfSides)) - circleRadius
553
554 def getBevelPath(begin, bevel, center, end):
555         'Get bevel path.'
556         centerMinusBegin = center - begin
557         centerMinusBeginLength = abs(centerMinusBegin)
558         endMinusCenter = end - center
559         endMinusCenterLength = abs(endMinusCenter)
560         endMinusCenter /= endMinusCenterLength
561         maximumExtensionLength = 0.333333333 * endMinusCenterLength
562         if centerMinusBeginLength <= bevel * 1.5:
563                 extensionLength = min(maximumExtensionLength, centerMinusBeginLength)
564                 return [complex(center.real, center.imag) + extensionLength * endMinusCenter]
565         centerMinusBegin *= (centerMinusBeginLength - bevel) / centerMinusBeginLength
566         extensionLength = min(maximumExtensionLength, bevel)
567         bevelPath = [complex(center.real, center.imag) + extensionLength * endMinusCenter]
568         bevelPath.append(begin + centerMinusBegin)
569         return bevelPath
570
571 def getGearPaths(derivation, pitchRadius, teeth, toothProfile):
572         'Get gear paths.'
573         if teeth < 0:
574                 return getGearProfileAnnulus(derivation, pitchRadius, teeth, toothProfile)
575         if teeth == 0:
576                 return [getGearProfileRack(derivation, toothProfile)]
577         return [getGearProfileCylinder(teeth, toothProfile)]
578
579 def getGearProfileAnnulus(derivation, pitchRadius, teeth, toothProfile):
580         'Get gear profile for an annulus gear.'
581         gearProfileCylinder = getGearProfileCylinder(teeth, toothProfile)
582         annulusRadius = derivation.dedendum + derivation.rimDedendum - pitchRadius
583         return [euclidean.getComplexPolygon(complex(), annulusRadius, -teeth, 0.5 * math.pi), gearProfileCylinder]
584
585 def getGearProfileCylinder(teeth, toothProfile):
586         'Get gear profile for a cylinder gear.'
587         gearProfile = []
588         toothAngleRadian = 2.0 * math.pi / float(teeth)
589         totalToothAngle = 0.0
590         for toothIndex in xrange(abs(teeth)):
591                 for toothPoint in toothProfile:
592                         gearProfile.append(toothPoint * euclidean.getWiddershinsUnitPolar(totalToothAngle))
593                 totalToothAngle += toothAngleRadian
594         return gearProfile
595
596 def getGearProfileRack(derivation, toothProfile):
597         'Get gear profile for rack.'
598         derivation.extraRackDemilength = 0.0
599         for complexPoint in derivation.helixPath:
600                 derivation.extraRackDemilength = max(abs(derivation.helixHeight * complexPoint.imag), derivation.extraRackDemilength)
601         rackDemilengthPlus = derivation.rackDemilength
602         if derivation.faceWidth > 0.0:
603                 derivation.extraRackDemilength *= 1.1
604                 rackDemilengthPlus += derivation.extraRackDemilength
605         teethRack = int(math.ceil(rackDemilengthPlus / derivation.wavelength))
606         gearProfile = []
607         for toothIndex in xrange(-teethRack, teethRack + 1):
608                 translateComplex = complex(-toothIndex * derivation.wavelength, 0.0)
609                 translatedPath = euclidean.getTranslatedComplexPath(toothProfile, translateComplex)
610                 gearProfile += translatedPath
611         gearProfile = euclidean.getHorizontallyBoundedPath(rackDemilengthPlus, -rackDemilengthPlus, gearProfile)
612         firstPoint = gearProfile[0]
613         lastPoint = gearProfile[-1]
614         rackWidth = derivation.rackWidth
615         minimumRackWidth = 1.1 * derivation.dedendum
616         if rackWidth < minimumRackWidth:
617                 rackWidth = minimumRackWidth
618                 print('Warning, rackWidth is too small in getGearProfileRack in gear.')
619                 print('RackWidth will be set to a bit more than the dedendum.')
620         gearProfile += [complex(lastPoint.real, -rackWidth),complex(firstPoint.real, -rackWidth)]
621         return gearProfile
622
623 def getGeometryOutput(derivation, elementNode):
624         "Get vector3 vertexes from attribute dictionary."
625         if derivation == None:
626                 derivation = GearDerivation(elementNode)
627         creationFirst = derivation.creationType.lower()[: 1]
628         toothProfileComplement = getToothProfile(derivation, derivation.pitchRadiusComplement, derivation.teethComplement)
629         pinionProfile = getGearProfileCylinder(derivation.teethPinion, derivation.pinionToothProfile)
630         complementPaths = getGearPaths(
631                 derivation, derivation.pitchRadiusComplement, derivation.teethComplement, toothProfileComplement)
632         vector3PinionProfile = euclidean.getVector3Path(pinionProfile)
633         vector3ComplementPaths = euclidean.getVector3Paths(complementPaths)
634         translation = Vector3()
635         moveFirst = derivation.moveType.lower()[: 1]
636         if moveFirst != 'n':
637                 distance = derivation.pitchRadius
638                 if moveFirst == 'm':
639                         distance += derivation.pitchRadiusComplement
640                 else:
641                         distance += abs(derivation.pitchRadiusComplement)
642                         decimalPlaces = 1 - int(math.floor(math.log10(distance)))
643                         distance += derivation.halfWavelength + derivation.halfWavelength
644                         distance = round(1.15 * distance, decimalPlaces)
645                 translation = Vector3(0.0, -distance)
646         if derivation.faceWidth <=0.0:
647                 return getPathOutput(
648                         creationFirst, derivation, elementNode, translation, vector3ComplementPaths, vector3PinionProfile)
649         pitchRadius = derivation.pitchRadius
650         teeth = derivation.teethPinion
651         twist = derivation.helixHeight / derivation.pitchRadius
652         extrudeOutputPinion = getOutputCylinder(
653                 derivation.pinionCollarLength, derivation, elementNode, None, pitchRadius, teeth, twist, [vector3PinionProfile])
654         if creationFirst == 'p':
655                 return extrudeOutputPinion
656         teeth = derivation.teethComplement
657         extrudeOutputSecond = None
658         if teeth == 0:
659                 extrudeOutputSecond = getOutputRack(derivation, elementNode, vector3ComplementPaths[0])
660         else:
661                 twist = -derivation.helixHeight / derivation.pitchRadiusComplement
662                 extrudeOutputSecond = getOutputCylinder(
663                         derivation.complementCollarLength,
664                         derivation,
665                         elementNode,
666                         derivation.gearHolePaths,
667                         derivation.pitchRadiusComplement,
668                         teeth,
669                         twist,
670                         vector3ComplementPaths)
671         if creationFirst == 'c':
672                 return extrudeOutputSecond
673         gearVertexes = matrix.getVertexes(extrudeOutputSecond)
674         if moveFirst == 'v':
675                 translation = Vector3(0.0, 0.0, euclidean.getTopPath(gearVertexes))
676                 euclidean.translateVector3Path(matrix.getVertexes(extrudeOutputPinion), translation)
677         else:
678                 euclidean.translateVector3Path(gearVertexes, translation)
679         return {'group' : {'shapes' : [extrudeOutputPinion, extrudeOutputSecond]}}
680
681 def getGeometryOutputByArguments(arguments, elementNode):
682         "Get vector3 vertexes from attribute dictionary by arguments."
683         return getGeometryOutput(None, elementNode)
684
685 def getHalfwave(pitchRadius, teeth):
686         'Get tooth halfwave.'
687         return pitchRadius * math.pi / float(teeth)
688
689 def getHelixComplexPath(derivation, elementNode):
690         'Set gear helix path.'
691         helixTypeFirstCharacter = derivation.helixType.lower()[: 1]
692         if helixTypeFirstCharacter == 'b':
693                 return [complex(), complex(1.0, 1.0)]
694         if helixTypeFirstCharacter == 'h':
695                 return [complex(), complex(0.5, 0.5), complex(1.0, 0.0)]
696         if helixTypeFirstCharacter == 'p':
697                 helixComplexPath = []
698                 x = 0.0
699                 xStep = setting.getLayerHeight(elementNode) / derivation.faceWidth
700                 justBelowOne = 1.0 - 0.5 * xStep
701                 while x < justBelowOne:
702                         distanceFromCenter = 0.5 - x
703                         parabolicTwist = 0.25 - distanceFromCenter * distanceFromCenter
704                         helixComplexPath.append(complex(x, parabolicTwist))
705                         x += xStep
706                 helixComplexPath.append(complex(1.0, 0.0))
707                 return helixComplexPath
708         print('Warning, the helix type was not one of (basic, herringbone or parabolic) in getHelixComplexPath in gear for:')
709         print(derivation.helixType)
710         print(derivation.elementNode)
711
712 def getLiftedOutput(derivation, geometryOutput):
713         "Get extrude output for a rack."
714         if derivation.moveType.lower()[: 1] == 'm':
715                 return geometryOutput
716         geometryOutputVertexes = matrix.getVertexes(geometryOutput)
717         translation = Vector3(0.0, 0.0, -euclidean.getBottomByPath(geometryOutputVertexes))
718         euclidean.translateVector3Path(geometryOutputVertexes, translation)
719         return geometryOutput
720
721 def getLighteningHoles(derivation, gearHolePaths, pitchRadius):
722         'Get cutout circles.'
723         if gearHolePaths != None:
724                 if len(gearHolePaths) > 0:
725                         return gearHolePaths
726         innerRadius = abs(pitchRadius) - derivation.dedendum
727         lighteningHoleOuterRadius = innerRadius - derivation.rimDedendum
728         shaftRimRadius = max(derivation.shaftRimRadius, (lighteningHoleOuterRadius) * (0.5 - math.sqrt(0.1875)))
729         lighteningHoleRadius = 0.5 * (lighteningHoleOuterRadius - derivation.shaftRimRadius)
730         if lighteningHoleRadius < derivation.lighteningHoleMinimumRadius:
731                 return []
732         lighteningHoles = []
733         numberOfLighteningHoles = 3
734         polygonRadius = lighteningHoleOuterRadius - lighteningHoleRadius
735         rimDemiwidth = 0.5 * derivation.lighteningHoleMargin
736         axialMargin = getAxialMargin(lighteningHoleRadius, numberOfLighteningHoles, polygonRadius)
737         if axialMargin < rimDemiwidth:
738                 while axialMargin < rimDemiwidth:
739                         lighteningHoleRadius *= 0.999
740                         if lighteningHoleRadius < derivation.lighteningHoleMinimumRadius:
741                                 return []
742                         axialMargin = getAxialMargin(lighteningHoleRadius, numberOfLighteningHoles, polygonRadius)
743         else:
744                 newNumberOfLighteningHoles = numberOfLighteningHoles
745                 while axialMargin > rimDemiwidth:
746                         numberOfLighteningHoles = newNumberOfLighteningHoles
747                         newNumberOfLighteningHoles += 2
748                         axialMargin = getAxialMargin(lighteningHoleRadius, newNumberOfLighteningHoles, polygonRadius)
749         sideAngle = 2.0 * math.pi / float(numberOfLighteningHoles)
750         startAngle = 0.0
751         for lighteningHoleIndex in xrange(numberOfLighteningHoles):
752                 unitPolar = euclidean.getWiddershinsUnitPolar(startAngle)
753                 lighteningHole = euclidean.getComplexPolygon(unitPolar * polygonRadius, lighteningHoleRadius, -13)
754                 lighteningHoles.append(lighteningHole)
755                 startAngle += sideAngle
756         return euclidean.getVector3Paths(lighteningHoles)
757
758 def getNewDerivation(elementNode):
759         'Get new derivation.'
760         return GearDerivation(elementNode)
761
762 def getOutputCylinder(
763                 collarLength, derivation, elementNode, gearHolePaths, pitchRadius, teeth, twist, vector3GearProfile):
764         "Get extrude output for a cylinder gear."
765         copyShallow = derivation.elementNode.getCopyShallow()
766         copyShallow.attributes['path'] = [Vector3(), Vector3(0.0, 0.0, derivation.faceWidth)]
767         extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
768         negatives = []
769         positives = []
770         if twist != 0.0:
771                 twistDegrees = math.degrees(twist)
772                 extrudeDerivation.twistPathDefault = []
773                 for complexPoint in derivation.helixPath:
774                         extrudeDerivation.twistPathDefault.append(Vector3(complexPoint.real, twistDegrees * complexPoint.imag))
775                 extrude.insertTwistPortions(extrudeDerivation, elementNode)
776         if derivation.operatingAngle != 180.0:
777                 addBevelGear(derivation, extrudeDerivation, pitchRadius, positives, teeth, vector3GearProfile)
778                 addCollarShaft(collarLength, derivation, elementNode, negatives, positives)
779                 return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
780         if pitchRadius > 0:
781                 extrude.addNegativesPositives(extrudeDerivation, negatives, vector3GearProfile, positives)
782                 addLighteningHoles(derivation, gearHolePaths, negatives, pitchRadius, positives)
783                 addCollarShaft(collarLength, derivation, elementNode, negatives, positives)
784                 return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
785         if derivation.plateLength <= 0.0:
786                 extrude.addNegativesPositives(extrudeDerivation, negatives, vector3GearProfile, positives)
787                 return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
788         portionDirections = extrude.getSpacedPortionDirections(extrudeDerivation.interpolationDictionary)
789         outerGearProfile = vector3GearProfile[0]
790         outerLoopLists = extrude.getLoopListsByPath(extrudeDerivation, None, outerGearProfile, portionDirections)
791         addBottomLoop(-derivation.plateClearance, outerLoopLists[0])
792         geometryOutput = triangle_mesh.getPillarsOutput(outerLoopLists)
793         positives.append(geometryOutput)
794         innerLoopLists = extrude.getLoopListsByPath(extrudeDerivation, None, vector3GearProfile[1], portionDirections)
795         addBottomLoop(-derivation.plateClearance, innerLoopLists[0])
796         geometryOutput = triangle_mesh.getPillarsOutput(innerLoopLists)
797         negatives.append(geometryOutput)
798         connectionStart = Vector3(0.0, 0.0, -derivation.plateLength)
799         copyShallow = derivation.elementNode.getCopyShallow()
800         copyShallow.attributes['path'] = [connectionStart, Vector3(0.0, 0.0, -derivation.plateClearance)]
801         plateDerivation = extrude.ExtrudeDerivation(copyShallow)
802         extrude.addNegativesPositives(plateDerivation, negatives, [outerGearProfile], positives)
803         vector3LighteningHoles = getLighteningHoles(derivation, gearHolePaths, pitchRadius)
804         extrude.addNegativesPositives(plateDerivation, negatives, vector3LighteningHoles, positives)
805         addShaft(derivation, negatives, positives)
806         positiveOutput = triangle_mesh.getUnifiedOutput(positives)
807         annulusPlateOutput = {'difference' : {'shapes' : [positiveOutput] + negatives}}
808         if collarLength <= 0.0:
809                 outputCylinder = solid.getGeometryOutputByManipulation(elementNode, annulusPlateOutput)
810                 return getLiftedOutput(derivation, outputCylinder)
811         negatives = []
812         positives = []
813         connectionEnd = Vector3(0.0, 0.0, derivation.faceWidth + collarLength)
814         copyShallow = derivation.elementNode.getCopyShallow()
815         copyShallow.attributes['path'] = [Vector3(0.0, 0.0, -derivation.plateClearance), connectionEnd]
816         collarDerivation = extrude.ExtrudeDerivation(copyShallow)
817         addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives)
818         collarOutput = {'difference' : {'shapes' : positives + negatives}}
819         cylinderOutput = {'union' : {'shapes' : [annulusPlateOutput, collarOutput]}}
820         outputCylinder = solid.getGeometryOutputByManipulation(elementNode, cylinderOutput)
821         return getLiftedOutput(derivation, outputCylinder)
822
823 def getOutputRack(derivation, elementNode, vector3GearProfile):
824         "Get extrude output for a rack."
825         path = []
826         for complexPoint in derivation.helixPath:
827                 point = Vector3(derivation.helixHeight * complexPoint.imag, 0.0, derivation.faceWidth * complexPoint.real)
828                 path.append(point)
829         copyShallow = derivation.elementNode.getCopyShallow()
830         copyShallow.attributes['path'] = path
831         extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
832         negatives = []
833         positives = []
834         vector3RackProfiles = [vector3GearProfile]
835         if derivation.extraRackDemilength > 0.0:
836                 yMaximum = -912345678.0
837                 yMinimum = 912345678.0
838                 for point in vector3GearProfile:
839                         yMaximum = max(point.y, yMaximum)
840                         yMinimum = min(point.y, yMinimum)
841                 muchLessThanWidth = 0.01 * derivation.rackWidth
842                 yMaximum += muchLessThanWidth
843                 yMinimum -= muchLessThanWidth
844                 extraRackLength = derivation.extraRackDemilength + derivation.extraRackDemilength
845                 rackDemilengthPlus = derivation.rackDemilength + extraRackLength
846                 leftNegative = [
847                         Vector3(-derivation.rackDemilength, yMaximum),
848                         Vector3(-derivation.rackDemilength, yMinimum),
849                         Vector3(-rackDemilengthPlus, yMinimum),
850                         Vector3(-rackDemilengthPlus, yMaximum)]
851                 vector3RackProfiles.append(leftNegative)
852                 rightNegative = [
853                         Vector3(rackDemilengthPlus, yMaximum),
854                         Vector3(rackDemilengthPlus, yMinimum),
855                         Vector3(derivation.rackDemilength, yMinimum),
856                         Vector3(derivation.rackDemilength, yMaximum)]
857                 vector3RackProfiles.append(rightNegative)
858         addRackHoles(derivation, elementNode, vector3RackProfiles)
859         extrude.addNegativesPositives(extrudeDerivation, negatives, vector3RackProfiles, positives)
860         return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
861
862 def getPathOutput(creationFirst, derivation, elementNode, translation, vector3ComplementPaths, vector3PinionProfile):
863         "Get gear path output."
864         vector3PinionProfile = lineation.getPackedGeometryOutputByLoop(elementNode, lineation.SideLoop(vector3PinionProfile))
865         if creationFirst == 'p':
866                 return vector3PinionProfile
867         packedGearGeometry = []
868         for vector3ComplementPath in vector3ComplementPaths:
869                 sideLoop = lineation.SideLoop(vector3ComplementPath)
870                 packedGearGeometry += lineation.getPackedGeometryOutputByLoop(elementNode, sideLoop)
871         if creationFirst == 'c':
872                 return packedGearGeometry
873         euclidean.translateVector3Paths(packedGearGeometry, translation)
874         return vector3PinionProfile + packedGearGeometry
875
876 def getThicknessMultipliedPath(path, thicknessMultiplier):
877         "Get thickness multiplied path."
878         for pointIndex, point in enumerate(path):
879                 path[pointIndex] = complex(point.real * thicknessMultiplier, point.imag)
880         return path
881
882 def getToothProfile(derivation, pitchRadius, teeth):
883         'Get profile for one tooth.'
884         if teeth < 0:
885                 return getToothProfileAnnulus(derivation, pitchRadius, teeth)
886         if teeth == 0:
887                 return getToothProfileRack(derivation)
888         return getToothProfileCylinder(derivation, pitchRadius, teeth)
889
890 def getToothProfileAnnulus(derivation, pitchRadius, teeth):
891         'Get profile for one tooth of an annulus.'
892         toothProfileHalf = []
893         toothProfileHalfCylinder = getToothProfileHalfCylinder(derivation, pitchRadius)
894         pitchRadius = -pitchRadius
895         innerRadius = pitchRadius - derivation.addendum
896         # 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
897         for point in getThicknessMultipliedPath(toothProfileHalfCylinder, 1.02 / derivation.toothThicknessMultiplier):
898                 if abs(point) >= innerRadius:
899                         toothProfileHalf.append(point)
900         profileFirst = toothProfileHalf[0]
901         profileSecond = toothProfileHalf[1]
902         firstMinusSecond = profileFirst - profileSecond
903         remainingAddendum = abs(profileFirst) - innerRadius
904         firstMinusSecond *= remainingAddendum / abs(firstMinusSecond)
905         extensionPoint = profileFirst + firstMinusSecond
906         if derivation.tipBevel > 0.0:
907                 unitPolar = euclidean.getWiddershinsUnitPolar(2.0 / float(teeth) * math.pi)
908                 mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag) * unitPolar
909                 bevelPath = getBevelPath(profileFirst, derivation.tipBevel, extensionPoint, mirrorPoint)
910                 toothProfileHalf = bevelPath + toothProfileHalf
911         else:
912                 toothProfileHalf.insert(0, extensionPoint)
913         profileLast = toothProfileHalf[-1]
914         profilePenultimate = toothProfileHalf[-2]
915         lastMinusPenultimate = profileLast - profilePenultimate
916         remainingDedendum = pitchRadius - abs(profileLast) + derivation.dedendum
917         lastMinusPenultimate *= remainingDedendum / abs(lastMinusPenultimate)
918         extensionPoint = profileLast + lastMinusPenultimate
919         if derivation.rootBevel > 0.0:
920                 mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag)
921                 bevelPath = getBevelPath(profileLast, derivation.rootBevel, extensionPoint, mirrorPoint)
922                 bevelPath.reverse()
923                 toothProfileHalf += bevelPath
924         else:
925                 toothProfileHalf.append(extensionPoint)
926         toothProfileAnnulus = euclidean.getMirrorPath(toothProfileHalf)
927         toothProfileAnnulus.reverse()
928         return toothProfileAnnulus
929
930 def getToothProfileCylinder(derivation, pitchRadius, teeth):
931         'Get profile for one tooth of a cylindrical gear.'
932         toothProfileHalfCylinder = getToothProfileHalfCylinder(derivation, pitchRadius)
933         toothProfileHalfCylinder = getThicknessMultipliedPath(toothProfileHalfCylinder, derivation.toothThicknessMultiplier)
934         toothProfileHalf = []
935         innerRadius = pitchRadius - derivation.dedendum
936         for point in toothProfileHalfCylinder:
937                 if abs(point) >= innerRadius:
938                         toothProfileHalf.append(point)
939         return getToothProfileCylinderByProfile(derivation, pitchRadius, teeth, toothProfileHalf)
940
941 def getToothProfileCylinderByProfile(derivation, pitchRadius, teeth, toothProfileHalf):
942         'Get profile for one tooth of a cylindrical gear.'
943         profileFirst = toothProfileHalf[0]
944         profileSecond = toothProfileHalf[1]
945         firstMinusSecond = profileFirst - profileSecond
946         remainingDedendum = abs(profileFirst) - pitchRadius + derivation.dedendum
947         firstMinusSecond *= remainingDedendum / abs(firstMinusSecond)
948         extensionPoint = profileFirst + firstMinusSecond
949         if derivation.rootBevel > 0.0:
950                 unitPolar = euclidean.getWiddershinsUnitPolar(-2.0 / float(teeth) * math.pi)
951                 mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag) * unitPolar
952                 bevelPath = getBevelPath(profileFirst, derivation.rootBevel, extensionPoint, mirrorPoint)
953                 toothProfileHalf = bevelPath + toothProfileHalf
954         else:
955                 toothProfileHalf.insert(0, extensionPoint)
956         if derivation.tipBevel > 0.0:
957                 profileLast = toothProfileHalf[-1]
958                 profilePenultimate = toothProfileHalf[-2]
959                 mirrorPoint = complex(-profileLast.real, profileLast.imag)
960                 bevelPath = getBevelPath(profilePenultimate, derivation.tipBevel, profileLast, mirrorPoint)
961                 bevelPath.reverse()
962                 toothProfileHalf = toothProfileHalf[: -1] + bevelPath
963         return euclidean.getMirrorPath(toothProfileHalf)
964
965 def getToothProfileHalfCylinder(derivation, pitchRadius):
966         'Get profile for half of a one tooth of a cylindrical gear.'
967         toothProfile=[]
968 #       x = -y * tan(p) + 1
969 #       x*x + y*y = (2-cos(p))^2
970 #       y*y*t*t-2yt+1+y*y=4-4c-c*c
971 #       y*y*(t*t+1)-2yt=3-4c-c*c
972 #       y*y*(t*t+1)-2yt-3+4c-c*c=0
973 #       a=tt+1
974 #       b=-2t
975 #       c=c(4-c)-3
976         a = derivation.tanPressure * derivation.tanPressure + 1.0
977         b = -derivation.tanPressure - derivation.tanPressure
978         cEnd = derivation.cosPressure * (4.0 - derivation.cosPressure) - 3.0
979         yEnd = (-b - math.sqrt(b*b - 4 * a * cEnd)) * 0.5 / a
980         yEnd *= derivation.pitchRadius / abs(pitchRadius)
981         yEnd -= derivation.clearance / abs(pitchRadius)
982         # to prevent intersections, yBegin is moved towards the base circle, giving a thinner tooth
983         yBegin = -yEnd
984         if pitchRadius > 0.0:
985                 yBegin = 0.5 * derivation.sinPressure + 0.5 * yBegin
986         beginComplex = complex(1.0 - yBegin * derivation.tanPressure, yBegin)
987         endComplex = complex(1.0 - yEnd * derivation.tanPressure, yEnd)
988         endMinusBeginComplex = endComplex - beginComplex
989         wholeAngle = -abs(endMinusBeginComplex) / derivation.cosPressure
990         wholeAngleIncrement = wholeAngle / float(derivation.profileSurfaces)
991         stringStartAngle = abs(beginComplex - complex(1.0, 0.0)) / derivation.cosPressure
992         wholeDepthIncrementComplex = endMinusBeginComplex / float(derivation.profileSurfaces)
993         for profileIndex in xrange(derivation.profileSurfaces + 1):
994                 contactPoint = beginComplex + wholeDepthIncrementComplex * float(profileIndex)
995                 stringAngle = stringStartAngle + wholeAngleIncrement * float(profileIndex)
996                 angle = math.atan2(contactPoint.imag, contactPoint.real) - stringAngle
997                 angle += 0.5 * math.pi - derivation.quarterWavelength / abs(pitchRadius)
998                 toothPoint = abs(contactPoint) * euclidean.getWiddershinsUnitPolar(angle) * abs(pitchRadius)
999                 toothProfile.append(toothPoint)
1000         return toothProfile
1001
1002 def getToothProfileRack(derivation):
1003         'Get profile for one rack tooth.'
1004         addendumSide = derivation.quarterWavelength - derivation.addendum * derivation.tanPressure
1005         addendumComplex = complex(addendumSide, derivation.addendum)
1006         dedendumSide = derivation.quarterWavelength + derivation.dedendum * derivation.tanPressure
1007         dedendumComplex = complex(dedendumSide, -derivation.dedendum)
1008         toothProfile = [dedendumComplex]
1009         if derivation.rootBevel > 0.0:
1010                 mirrorPoint = complex(derivation.wavelength - dedendumSide, -derivation.dedendum)
1011                 toothProfile = getBevelPath(addendumComplex, derivation.rootBevel, dedendumComplex, mirrorPoint)
1012         if derivation.tipBevel > 0.0:
1013                 mirrorPoint = complex(-addendumComplex.real, addendumComplex.imag)
1014                 bevelPath = getBevelPath(dedendumComplex, derivation.tipBevel, addendumComplex, mirrorPoint)
1015                 bevelPath.reverse()
1016                 toothProfile += bevelPath
1017         else:
1018                 toothProfile.append(addendumComplex)
1019         return euclidean.getMirrorPath(getThicknessMultipliedPath(toothProfile, derivation.toothThicknessMultiplier))
1020
1021 def processElementNode(elementNode):
1022         "Process the xml element."
1023         geometryOutput = getGeometryOutput(None, elementNode)
1024         if geometryOutput.__class__ == list:
1025                 path.convertElementNode(elementNode, geometryOutput)
1026         else:
1027                 solid.processElementNodeByGeometry(elementNode, geometryOutput)
1028
1029
1030 class GearDerivation(object):
1031         "Class to hold gear variables."
1032         def __init__(self, elementNode):
1033                 'Set defaults.'
1034                 self.clearanceOverWavelength = evaluate.getEvaluatedFloat(0.1, elementNode, 'clearanceOverWavelength')
1035                 self.collarAddendumOverRadius = evaluate.getEvaluatedFloat(1.0, elementNode, 'collarAddendumOverRadius')
1036                 self.complementCollarLengthOverFaceWidth = evaluate.getEvaluatedFloat(
1037                         0.0, elementNode, 'complementCollarLengthOverFaceWidth')
1038                 self.copyShallow = elementNode.getCopyShallow()
1039                 self.creationType = evaluate.getEvaluatedString('both', elementNode, 'creationType')
1040                 self.creationTypeMenuRadioStrings = 'both complement pinion'.split()
1041                 self.elementNode = elementNode
1042                 self.faceWidth = evaluate.getEvaluatedFloat(10.0, elementNode, 'faceWidth')
1043                 self.helixAngle = evaluate.getEvaluatedFloat(0.0, elementNode, 'helixAngle')
1044                 self.helixType = evaluate.getEvaluatedString('basic', elementNode, 'helixType')
1045                 self.helixTypeMenuRadioStrings = 'basic herringbone parabolic'.split()
1046                 self.keywayRadiusOverRadius = evaluate.getEvaluatedFloat(0.5, elementNode, 'keywayRadiusOverRadius')
1047                 self.lighteningHoleMarginOverRimDedendum = evaluate.getEvaluatedFloat(
1048                         1.0, elementNode, 'lighteningHoleMarginOverRimDedendum')
1049                 self.lighteningHoleMinimumRadius = evaluate.getEvaluatedFloat(
1050                         1.0, elementNode, 'lighteningHoleMinimumRadius')
1051                 self.moveType = evaluate.getEvaluatedString('separate', elementNode, 'moveType')
1052                 self.moveTypeMenuRadioStrings = 'mesh none separate vertical'.split()
1053                 self.operatingAngle = evaluate.getEvaluatedFloat(180.0, elementNode, 'operatingAngle')
1054                 self.pinionCollarLengthOverFaceWidth = evaluate.getEvaluatedFloat(
1055                         0.0, elementNode, 'pinionCollarLengthOverFaceWidth')
1056                 self.plateClearanceOverLength = evaluate.getEvaluatedFloat(0.2, elementNode, 'plateClearanceOverLength')
1057                 self.plateLengthOverFaceWidth = evaluate.getEvaluatedFloat(0.5, elementNode, 'plateLengthOverFaceWidth')
1058                 self.pressureAngle = evaluate.getEvaluatedFloat(20.0, elementNode, 'pressureAngle')
1059                 self.profileSurfaces = evaluate.getEvaluatedInt(11, elementNode, 'profileSurfaces')
1060                 self.rackHoleBelowOverWidth = evaluate.getEvaluatedFloat(0.6, elementNode, 'rackHoleBelowOverWidth')
1061                 self.rackHoleRadiusOverWidth = evaluate.getEvaluatedFloat(0.0, elementNode, 'rackHoleRadiusOverWidth')
1062                 self.rackHoleStepOverWidth = evaluate.getEvaluatedFloat(1.0, elementNode, 'rackHoleStepOverWidth')
1063                 self.rackLengthOverRadius = evaluate.getEvaluatedFloat(math.pi + math.pi, elementNode, 'rackLengthOverRadius')
1064                 self.rackWidthOverFaceWidth = evaluate.getEvaluatedFloat(1.0, elementNode, 'rackWidthOverFaceWidth')
1065                 self.rimDedendumOverRadius = evaluate.getEvaluatedFloat(0.2, elementNode, 'rimDedendumOverRadius')
1066                 self.rootBevelOverClearance = evaluate.getEvaluatedFloat(0.5, elementNode, 'rootBevelOverClearance')
1067                 self.shaftDepthBottomOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'shaftDepthBottomOverRadius')
1068                 self.shaftDepthTopOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'shaftDepthOverRadius')
1069                 self.shaftRadiusOverPitchRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'shaftRadiusOverPitchRadius')
1070                 self.shaftSides = evaluate.getEvaluatedInt(4, elementNode, 'shaftSides')
1071                 self.teethComplement = evaluate.getEvaluatedInt(17, elementNode, 'teethComplement')
1072                 self.teethPinion = evaluate.getEvaluatedInt(7, elementNode, 'teeth')
1073                 totalTeethOverPinionTeeth = float(self.teethComplement + self.teethPinion) / float(self.teethPinion)
1074                 self.centerDistance = evaluate.getEvaluatedFloat(20.0 * totalTeethOverPinionTeeth, elementNode, 'centerDistance')
1075                 derivedPitchRadius = self.centerDistance / totalTeethOverPinionTeeth
1076                 self.pitchRadius = evaluate.getEvaluatedFloat(derivedPitchRadius, elementNode, 'pitchRadius')
1077                 self.tipBevelOverClearance = evaluate.getEvaluatedFloat(0.1, elementNode, 'tipBevelOverClearance')
1078                 # tooth multiplied by 0.99999 to avoid an intersection
1079                 self.toothThicknessMultiplier = evaluate.getEvaluatedFloat(0.99999, elementNode, 'toothThicknessMultiplier')
1080                 # Set derived variables.
1081                 self.wavelength = self.pitchRadius * 2.0 * math.pi / float(self.teethPinion)
1082                 self.clearance = self.wavelength * self.clearanceOverWavelength
1083                 self.clearance = evaluate.getEvaluatedFloat(self.clearance, elementNode, 'clearance')
1084                 self.complementCollarLength = self.faceWidth * self.complementCollarLengthOverFaceWidth
1085                 self.complementCollarLength = evaluate.getEvaluatedFloat(self.complementCollarLength, elementNode, 'complementCollarLength')
1086                 self.gearHolePaths = evaluate.getTransformedPathsByKey([], elementNode, 'gearHolePaths')
1087                 self.pinionCollarLength = self.faceWidth * self.pinionCollarLengthOverFaceWidth
1088                 self.pinionCollarLength = evaluate.getEvaluatedFloat(self.pinionCollarLength, elementNode, 'pinionCollarLength')
1089                 self.plateLength = self.faceWidth * self.plateLengthOverFaceWidth
1090                 self.plateLength = evaluate.getEvaluatedFloat(self.plateLength, elementNode, 'plateLength')
1091                 self.plateClearance = self.plateLength * self.plateClearanceOverLength
1092                 self.plateClearance = evaluate.getEvaluatedFloat(self.plateClearance, elementNode, 'plateClearance')
1093                 self.rackLength = self.pitchRadius * self.rackLengthOverRadius
1094                 self.rackLength = evaluate.getEvaluatedFloat(self.rackLength, elementNode, 'rackLength')
1095                 self.rackDemilength = 0.5 * self.rackLength
1096                 self.rackWidth = self.faceWidth * self.rackWidthOverFaceWidth
1097                 self.rackWidth = evaluate.getEvaluatedFloat(self.rackWidth, elementNode, 'rackWidth')
1098                 self.rimDedendum = self.pitchRadius * self.rimDedendumOverRadius
1099                 self.rimDedendum = evaluate.getEvaluatedFloat(self.rimDedendum, elementNode, 'rimDedendum')
1100                 self.rootBevel = self.clearance * self.rootBevelOverClearance
1101                 self.rootBevel = evaluate.getEvaluatedFloat(self.rootBevel, elementNode, 'rootBevel')
1102                 self.shaftRadius = self.pitchRadius * self.shaftRadiusOverPitchRadius
1103                 self.shaftRadius = evaluate.getEvaluatedFloat(self.shaftRadius, elementNode, 'shaftRadius')
1104                 self.collarAddendum = self.shaftRadius * self.collarAddendumOverRadius
1105                 self.collarAddendum = evaluate.getEvaluatedFloat(self.collarAddendum, elementNode, 'collarWidth')
1106                 self.keywayRadius = self.shaftRadius * self.keywayRadiusOverRadius
1107                 self.keywayRadius = lineation.getFloatByPrefixBeginEnd(elementNode, 'keywayRadius', 'keywayDiameter', self.keywayRadius)
1108                 self.lighteningHoleMargin = self.rimDedendum * self.lighteningHoleMarginOverRimDedendum
1109                 self.lighteningHoleMargin = evaluate.getEvaluatedFloat(
1110                         self.lighteningHoleMargin, elementNode, 'lighteningHoleMargin')
1111                 self.rackHoleBelow = self.rackWidth * self.rackHoleBelowOverWidth
1112                 self.rackHoleBelow = evaluate.getEvaluatedFloat(self.rackHoleBelow, elementNode, 'rackHoleBelow')
1113                 self.rackHoleRadius = self.rackWidth * self.rackHoleRadiusOverWidth
1114                 self.rackHoleRadius = lineation.getFloatByPrefixBeginEnd(elementNode, 'rackHoleRadius', 'rackHoleDiameter', self.rackHoleRadius)
1115                 self.rackHoleStep = self.rackWidth * self.rackHoleStepOverWidth
1116                 self.rackHoleStep = evaluate.getEvaluatedFloat(self.rackHoleStep, elementNode, 'rackHoleStep')
1117                 self.shaftDepthBottom = self.shaftRadius * self.shaftDepthBottomOverRadius
1118                 self.shaftDepthBottom = evaluate.getEvaluatedFloat(self.shaftDepthBottom, elementNode, 'shaftDepthBottom')
1119                 self.shaftDepthTop = self.shaftRadius * self.shaftDepthTopOverRadius
1120                 self.shaftDepthTop = evaluate.getEvaluatedFloat(self.shaftDepthTop, elementNode, 'shaftDepthTop')
1121                 self.shaftPath = evaluate.getTransformedPathByKey([], elementNode, 'shaftPath')
1122                 if len(self.shaftPath) < 3:
1123                         self.shaftPath = shaft.getShaftPath(self.shaftDepthBottom, self.shaftDepthTop, self.shaftRadius, -self.shaftSides)
1124                 self.tipBevel = self.clearance * self.tipBevelOverClearance
1125                 self.tipBevel = evaluate.getEvaluatedFloat(self.tipBevel, elementNode, 'tipBevel')
1126                 # Set derived values.
1127                 self.helixRadian = math.radians(self.helixAngle)
1128                 if self.teethComplement <= 0.0 and self.operatingAngle != 180.0:
1129                         print('Warning, an operatingAngle other than 180 degrees can only work with a positive number of gear teeth.')
1130                         print('Therefore the operatingAngle will be reset to 180 degrees.')
1131                         self.operatingAngle = 180.0
1132                 self.tanHelix = math.tan(self.helixRadian)
1133                 self.helixHeight = self.tanHelix * self.faceWidth
1134                 self.operatingRadian = math.radians(self.operatingAngle)
1135                 self.pitchRadiusComplement = self.pitchRadius * float(self.teethComplement) / float(self.teethPinion)
1136                 self.pressureRadian = math.radians(self.pressureAngle)
1137                 self.cosPressure = math.cos(self.pressureRadian)
1138                 self.sinPressure = math.sin(self.pressureRadian)
1139                 self.tanPressure = math.tan(self.pressureRadian)
1140                 self.halfWavelength = 0.5 * self.wavelength
1141                 self.helixPath = euclidean.getComplexPath(evaluate.getTransformedPathByKey([], elementNode, 'helixPath'))
1142                 if len(self.helixPath) < 1:
1143                         self.helixPath = getHelixComplexPath(self, elementNode)
1144                 self.quarterWavelength = 0.25 * self.wavelength
1145                 self.shaftRimRadius = self.shaftRadius + self.collarAddendum
1146                 self.toothProfileHalf = getToothProfileHalfCylinder(self, self.pitchRadius)
1147                 self.toothProfileHalf = getThicknessMultipliedPath(self.toothProfileHalf, self.toothThicknessMultiplier)
1148                 self.addendum = self.toothProfileHalf[-1].imag - self.pitchRadius
1149                 self.dedendum = abs(self.toothProfileHalf[-1]) - self.pitchRadius + self.clearance
1150                 self.pinionToothProfile = getToothProfileCylinderByProfile(self, self.pitchRadius, self.teethPinion, self.toothProfileHalf)