"""
This page is in the table of contents.
The gear script can generate a spur gear couple, a bevel gear couple, a ring gear couple and a rack & pinion couple.

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.

The couple has a pinion gear and a complement.

==Examples==
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, default_gear.svg was generated by clicking 'Carve' on the carve tool panel and choosing default_gear.xml in the file chooser.

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.

===Bevel===
Bevel gear couple.

<a href='../models/xml_models/creation/gear/bevel.svg'>gear operatingAngle=90</a>

===Collar===
Spur gear couple and each gear has a collar.

<a href='../models/xml_models/creation/gear/collar.svg'>gear complementCollarHeightOverHeight='1' pinionCollarHeightOverHeight='1' shaftRadius='5'</a>

===Default Gear===
Default spur gear with no parameters.

<a href='../models/xml_models/creation/gear/default_gear.svg'>gear</a>

===Keyway===
Spur gear couple and each gear has a collar and defined keyway.

<a href='../models/xml_models/creation/gear/keyway.svg'>gear complementCollarHeightOverHeight='1' keywayRadius='2' pinionCollarHeightOverHeight='1' shaftRadius='5'</a>

===Rack===
Rack and pinion couple.

<a href='../models/xml_models/creation/gear/rack.svg'>gear teethComplement='0'</a>

===Rack Hole===
Rack and pinion couple, with holes in the rack.

<a href='../models/xml_models/creation/gear/rack_hole.svg'>gear rackHoleRadiusOverWidth='0.2' rackWidthOverHeight='2' teethComplement='0'</a>

===Ring===
Pinion and ring gear.

<a href='../models/xml_models/creation/gear/ring.svg'>gear teethComplement='-23'</a>

===Shaft===
Spur gear couple and each gear has a square shaft hole.

<a href='../models/xml_models/creation/gear/shaft.svg'>gear shaftRadius='5'</a>

===Shaft Top===
Spur gear couple and each gear has a round shaft hole, truncated on top.

<a href='../models/xml_models/creation/gear/shaft_top.svg'>gear shaftRadius='5' shaftSides='13' shaftDepthTop='2'</a>

===Spur Helix===
Spur gear couple with the gear teeth following a helix path.

<a href='../models/xml_models/creation/gear/spur_helix.svg'>gear  helixAngle='45'</a>

===Spur Herringbone===
Spur gear couple with the gear teeth following a herringbone path.

<a href='../models/xml_models/creation/gear/spur_herringbone.svg'>gear  helixAngle='45' helixType='herringbone'</a>

===Spur Parabolic===
Spur gear couple with the gear teeth following a parabolic path.

<a href='../models/xml_models/creation/gear/spur_parabolic.svg'>gear  helixAngle='45' helixType='parabolic'</a>

===Spur Profile===
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.

<a href='../models/xml_models/creation/gear/spur_profile.svg'>gear id='spurProfile' height='0' | extrude target='=document.getElementByID(spurProfile)</a>

==Parameters==
===Center Distance===
Default is such that the pitch radius works out to twenty.

Defines the distance between the gear centers.

===Clearance Couplet===
====Clearance Over Wavelength====
Default is 0.1.

Defines the ratio of the clearance over the wavelength of the gear profile.  The wavelength is the arc distance between the gear teeth.

====Clearance====
Default is the 'Clearance Over Wavelength' times the wavelength.

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.

===Collar Width Couplet===
====Collar Width Over Radius====
Default is one.

Defines the ratio of the collar width over the shaft radius.

====Collar Width====
Default is the 'Collar Width Over Radius' times the shaft radius.

Defines the collar width.

===Complement Collar Height Couplet===
====Complement Collar Height Over Height====
Default is zero.

Defines the ratio of the complement collar height over the tooth height.

====Complement Collar Height====
Default is the 'Complement Collar Height Over Height' times the tooth height.

Defines the complement collar height.  If the complement collar height is zero, there will not be a collar on the complement gear.

===Creation Type===
Default is 'both'.

====Both====
When selected, the pinion and complement will be generated.

====Complement====
When selected, only the complement gear or rack will be generated.

====Pinion====
When selected, only the pinion will be generated.

===Gear Hole Paths===
Default is empty.

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.

===Height===
Default is ten.

Defines the tooth height.

===Helix Angle===
Default is zero.

===Helix Path===
Default is empty.

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.

===Helix Type===
Default is 'basic'.

====Basic====
When selected, the helix will be basic.

====Herringbone====
When selected, the helix will have a herringbone pattern.

====Parabolic====
When selected, the helix will have a parabolic pattern.

===Keyway Radius Couplet===
====Keyway Radius Over Radius====
Default is half.

Defines the ratio of the keyway radius over the shaft radius.

====Keyway Radius====
Default is the 'Keyway Radius Over Radius' times the shaft radius.

Defines the keyway radius.  If the keyway radius is zero, there will not be a keyway on the collar.

===Lightening Hole Margin Couplet===
====Lightening Hole Margin Over Rim Width====
Default is one.

Defines the ratio of the lightening hole margin over the rim width.

====Lightening Hole Margin====
Default is the 'Lightening Hole Margin Over Rim Width' times the rim width.

Defines the minimum margin between lightening holes.

===Lightening Hole Minimum Radius===
Default is one.

Defines the minimum radius of the lightening holes.

===Move Type===
Default is 'separate'.

====None====
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.

====Mesh====
When selected, the gears will be separated horizontally so that they just mesh.  This is useful to test if the gears mesh properly.

====Separate====
When selected, the gears will be separated horizontally with a gap between them.

====Vertical====
When selected, the gears will be separated vertically.

===Operating Angle===
Default is 180 degrees.

Defines the operating angle between the gear axes.  If the operating angle is not 180 degrees, a bevel gear couple will be generated.

===Pinion Collar Height Over Height Couplet===
====Pinion Collar Height Over Height====
Default is zero.

Defines the ratio of the pinion collar height over the tooth height.

====Pinion Collar Height====
Default is the 'Pinion Collar Height Over Height' times the tooth height.

Defines the pinion collar height.  If the pinion collar height is zero, there will not be a collar on the pinion gear.

===Pitch Radius===
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.

Defines the pinion pitch radius.

===Plate Clearance Over Height Couplet===
====Plate Clearance Over Height====
Default is 0.2.

Defines the ratio of the plate clearance over the plate height.

====Plate Clearance====
Default is the 'Plate Clearance Over Height' times the plate height.

Defines the clearance between the pinoin and the plate of the ring gear.  If the clearance is zero, they will touch.

===Plate Height Over Height Couplet===
====Plate Height Over Height====
Default is half.

Defines the ratio of the plate height over the tooth height.

====Plate Height====
Default is the 'Plate Height Over Height' times the tooth height.

Defines the height of the plate of the ring gear.

===Pressure Angle===
Default is twenty degrees.

Defines the pressure angle of the gear couple.

===Profile Surfaces===
Default is eleven.

Defines the number of profile surfaces.

===Rack Hole Radius Over Width Couplet===
====Rack Hole Radius Over Width====
Default is zero.

Defines the ratio of the rack hole radius over the rack width.

====Rack Hole Radius====
Default is the 'Rack Hole Radius Over Width' times the rack width.

Defines the radius of the rack holes.  If the rack hole radius is zero, there won't be any rack holes.

===Rack Hole Below Over Width Couplet===
====Rack Hole Below Over Width====
Default is 0.6.

Defines the ratio of the distance below the pitch of the rack holes over the rack width.

====Rack Hole Below====
Default is the 'Rack Hole Below Over Width' times the rack width.

Defines the the distance below the pitch of the rack holes.

===Rack Hole Step Over Width Couplet===
====Rack Hole Step Over Width====
Default is one.

Defines the ratio of the rack hole step over the rack width.

====Rack Hole Step====
Default is the 'Rack Hole Step Over Width' times the rack width.

Defines the horizontal step distance between the rack holes.

===Rack Length Over Radius Couplet===
====Rack Length Over Radius====
Default is two times pi.

Defines the ratio of the rack length over the pitch radius.

====Rack Length====
Default is the 'Rack Length Over Radius' times the pitch radius.

Defines the rack length.

===Rack Width Over Height Couplet===
====Rack Width Over Height====
Default is one.

Defines the ratio of the rack width over the tooth height.

====Rack Width====
Default is the 'Rack Width Over Height' times the tooth height.

Defines the rack width.

===Rim Width Over Height Couplet===
====Rim Width Over Height====
Default is 0.2.

Defines the ratio of the rim width over the pitch radius.

====Rim Width====
Default is the 'Rim Width Over Height' times the pitch radius.

Defines the rim width of the gear.

===Root Bevel Over Clearance Couplet===
====Root Bevel Over Clearance====
Default is half.

Defines the ratio of the root bevel over the clearance.

====Root Bevel====
Default is the 'Root Bevel Over Clearance' times the clearance.

Defines the bevel at the root of the gear tooth.

===Shaft Depth Bottom Couplet===
====Shaft Depth Bottom Over Radius====
Default is zero.

Defines the ratio of the bottom shaft depth over the shaft radius.

====Shaft Depth Bottom====
Default is the 'Shaft Depth Bottom Over Radius' times the shaft radius.

Defines the bottom shaft depth.

===Shaft Depth Top Couplet===
====Shaft Depth Top Over Radius====
Default is zero.

Defines the ratio of the top shaft depth over the shaft radius.

====Shaft Depth Top====
Default is the 'Shaft Depth Top Over Radius' times the shaft radius.

Defines the top shaft depth.

===Shaft Path===
Default is empty.

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.

===Shaft Radius Over Pitch Radius Couplet===
====Shaft Radius Over Pitch Radius====
Default is zero.

Defines the ratio of the shaft radius over the pitch radius.

====Shaft Radius====
Default is the 'Shaft Radius Over Pitch Radius' times the pitch radius.

Defines the shaft radius.

===Shaft Sides===
Default is four.

Defines the number of shaft sides.

===Teeth Pinion===
Default is seven.

Defines the number of teeth in the pinion.

===Teeth Complement===
Default is seventeen.

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.

===Tip Bevel Over Clearance Couplet===
====Tip Bevel Over Clearance====
Default is 0.1.

Defines the ratio of the tip bevel over the clearance.

====Tip Bevel====
Default is the 'Tip Bevel Over Clearance' times the clearance.

Defines the bevel at the tip of the gear tooth.

===Tooth Width Multiplier===
Default is 0.99999.

Defines the amount the width of the tooth will multiplied.  If when the gears are produced, they mesh too tightly, you can reduce the tooth width multiplier so that they mesh with reasonable tightness.

"""

from __future__ import absolute_import
#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
import __init__

from fabmetheus_utilities.geometry.creation import extrude
from fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import shaft
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.creation import teardrop
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities.vector3index import Vector3Index
from fabmetheus_utilities import euclidean
import math


__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'


def addBevelGear(derivation, extrudeDerivation, pitchRadius, positives, teeth, vector3GearProfile):
	"Get extrude output for a cylinder gear."
	totalPitchRadius = derivation.pitchRadiusComplement + derivation.pitchRadius
	totalTeeth = derivation.teethPinion + derivation.teethComplement
	portionDirections = extrude.getSpacedPortionDirections(extrudeDerivation.interpolationDictionary)
	loopLists = extrude.getLoopListsByPath(extrudeDerivation, None, vector3GearProfile[0], portionDirections)
	firstLoopList = loopLists[0]
	gearOverPinion = float(totalTeeth - teeth) / float(teeth)
	thirdLayerHeight = 0.33333333333 * setting.getLayerHeight(derivation.xmlElement)
	pitchRadian = math.atan(math.sin(derivation.operatingRadian) / (gearOverPinion + math.cos(derivation.operatingRadian)))
	coneDistance = pitchRadius / math.sin(pitchRadian)
	apex = Vector3(0.0, 0.0, math.sqrt(coneDistance * coneDistance - pitchRadius * pitchRadius))
	cosPitch = apex.z / coneDistance
	sinPitch = math.sin(pitchRadian)
	for loop in firstLoopList:
		for point in loop:
			alongWay = point.z / coneDistance
			oneMinusAlongWay = 1.0 - alongWay
			pointComplex = point.dropAxis()
			pointComplexLength = abs(pointComplex)
			deltaRadius = pointComplexLength - pitchRadius
			cosDeltaRadius = cosPitch * deltaRadius
			sinDeltaRadius = sinPitch * deltaRadius
			pointComplex *= (cosDeltaRadius + pitchRadius) / pointComplexLength
			point.x = pointComplex.real
			point.y = pointComplex.imag
			point.z += sinDeltaRadius
			point.x *= oneMinusAlongWay
			point.y *= oneMinusAlongWay
	addBottomLoop(-thirdLayerHeight, firstLoopList)
	topLoop = firstLoopList[-1]
	topAddition = []
	topZ = euclidean.getTopPath(topLoop) + thirdLayerHeight
	oldIndex = topLoop[-1].index
	for point in topLoop:
		oldIndex += 1
		topAddition.append(Vector3Index(oldIndex, 0.8 * point.x, 0.8 * point.y, topZ))
	firstLoopList.append(topAddition)
	translation = Vector3(0.0, 0.0, -euclidean.getBottomByPaths(firstLoopList))
	euclidean.translateVector3Paths(firstLoopList, translation)
	geometryOutput = triangle_mesh.getPillarsOutput(loopLists)
	positives.append(geometryOutput)

def addBottomLoop(deltaZ, loops):
	"Add bottom loop to loops."
	bottomLoop = loops[0]
	bottomAddition = []
	bottomZ = euclidean.getBottomByPath(bottomLoop) + deltaZ
	for point in bottomLoop:
		bottomAddition.append(Vector3Index(len(bottomAddition), point.x, point.y, bottomZ))
	loops.insert(0, bottomAddition)
	numberOfVertexes = 0
	for loop in loops:
		for point in loop:
			point.index = numberOfVertexes
			numberOfVertexes += 1

def addCollarShaft(collarHeight, derivation, negatives, positives, xmlElement):
	'Add collar.'
	if collarHeight <= 0.0:
		addShaft(derivation, negatives, positives)
		return
	connectionEnd = Vector3(0.0, 0.0, derivation.height + collarHeight)
	copyShallow = derivation.xmlElement.getCopyShallow()
	copyShallow.attributeDictionary['path'] = [Vector3(0.0, 0.0, derivation.height), connectionEnd]
	collarDerivation = extrude.ExtrudeDerivation(copyShallow)
	addCollarShaftSetDerivation(collarDerivation, collarHeight, derivation, negatives, positives, xmlElement)

def addCollarShaftSetDerivation(collarDerivation, collarHeight, derivation, negatives, positives, xmlElement):
	'Add collar and shaft.'
	collarSides = evaluate.getSidesMinimumThreeBasedOnPrecision(derivation.shaftRimRadius, xmlElement)
	collarProfile = euclidean.getComplexPolygon(complex(), derivation.shaftRimRadius, collarSides)
	vector3CollarProfile = euclidean.getVector3Path(collarProfile)
	extrude.addPositives(collarDerivation, [vector3CollarProfile], positives)
	addShaft(derivation, negatives, positives)
	drillZ = derivation.height + 0.5 * collarHeight
	drillEnd = Vector3(0.0, derivation.shaftRimRadius, drillZ)
	drillStart = Vector3(0.0, 0.0, drillZ)
	teardrop.addNegativesByRadius(drillEnd, negatives, derivation.keywayRadius, drillStart, xmlElement)

def addLighteningHoles(derivation, gearHolePaths, negatives, pitchRadius, positives):
	"Add lightening holes."
	positiveVertexes = matrix.getVertexes(positives)
	bottomPath = euclidean.getTopPath(positiveVertexes)
	topPath = euclidean.getBottomByPath(positiveVertexes)
	copyShallow = derivation.xmlElement.getCopyShallow()
	copyShallow.attributeDictionary['path'] = [Vector3(0.0, 0.0, bottomPath), Vector3(0.0, 0.0, topPath)]
	extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
	vector3LighteningHoles = getLighteningHoles(derivation, gearHolePaths, pitchRadius)
	extrude.addNegativesPositives(extrudeDerivation, negatives, vector3LighteningHoles, positives)

def addRackHole(derivation, vector3RackProfiles, x, xmlElement):
	"Add rack hole to vector3RackProfiles."
	rackHole = euclidean.getComplexPolygon(complex(x, -derivation.rackHoleBelow), derivation.rackHoleRadius, -13)
	vector3RackProfiles.append(euclidean.getVector3Path(rackHole))

def addRackHoles(derivation, vector3RackProfiles, xmlElement):
	"Add rack holes to vector3RackProfiles."
	if len(derivation.gearHolePaths) > 0:
		vector3RackProfiles += derivation.gearHolePaths
		return
	if derivation.rackHoleRadius <= 0.0:
		return
	addRackHole(derivation, vector3RackProfiles, 0.0, xmlElement)
	rackHoleMargin = derivation.rackHoleRadius + derivation.rackHoleRadius
	rackHoleSteps = int(math.ceil((derivation.rackDemilength - rackHoleMargin) / derivation.rackHoleStep))
	for rackHoleIndex in xrange(1, rackHoleSteps):
		x = float(rackHoleIndex) * derivation.rackHoleStep
		addRackHole(derivation, vector3RackProfiles, -x, xmlElement)
		addRackHole(derivation, vector3RackProfiles, x, xmlElement)

def addShaft(derivation, negatives, positives):
	"Add shaft."
	if len(derivation.shaftPath) < 3:
		return
	positiveVertexes = matrix.getVertexes(positives)
	bottomPath = euclidean.getTopPath(positiveVertexes)
	topPath = euclidean.getBottomByPath(positiveVertexes)
	copyShallow = derivation.xmlElement.getCopyShallow()
	copyShallow.attributeDictionary['path'] = [Vector3(0.0, 0.0, bottomPath), Vector3(0.0, 0.0, topPath)]
	extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
	extrude.addNegativesPositives(extrudeDerivation, negatives, [derivation.shaftPath], positives)

def getAxialMargin(circleRadius, numberOfSides, polygonRadius):
	'Get axial margin.'
	return polygonRadius * math.sin(math.pi / float(numberOfSides)) - circleRadius

def getBevelPath(begin, bevel, center, end):
	'Get bevel path.'
	centerMinusBegin = center - begin
	centerMinusBeginLength = abs(centerMinusBegin)
	endMinusCenter = end - center
	endMinusCenterLength = abs(endMinusCenter)
	endMinusCenter /= endMinusCenterLength
	maximumExtensionLength = 0.333333333 * endMinusCenterLength
	if centerMinusBeginLength <= bevel * 1.5:
		extensionLength = min(maximumExtensionLength, centerMinusBeginLength)
		return [complex(center.real, center.imag) + extensionLength * endMinusCenter]
	centerMinusBegin *= (centerMinusBeginLength - bevel) / centerMinusBeginLength
	extensionLength = min(maximumExtensionLength, bevel)
	bevelPath = [complex(center.real, center.imag) + extensionLength * endMinusCenter]
	bevelPath.append(begin + centerMinusBegin)
	return bevelPath

def getGearPaths(derivation, pitchRadius, teeth, toothProfile):
	'Get gear paths.'
	if teeth < 0:
		return getGearProfileAnnulus(derivation, pitchRadius, teeth, toothProfile)
	if teeth == 0:
		return [getGearProfileRack(derivation, toothProfile)]
	return [getGearProfileCylinder(teeth, toothProfile)]

def getGearProfileAnnulus(derivation, pitchRadius, teeth, toothProfile):
	'Get gear profile for an annulus gear.'
	gearProfileCylinder = getGearProfileCylinder(teeth, toothProfile)
	annulusRadius = derivation.dedendum + derivation.rimWidth - pitchRadius
	return [euclidean.getComplexPolygon(complex(), annulusRadius, -teeth, 0.5 * math.pi), gearProfileCylinder]

def getGearProfileCylinder(teeth, toothProfile):
	'Get gear profile for a cylinder gear.'
	gearProfile = []
	toothAngleRadian = 2.0 * math.pi / float(teeth)
	totalToothAngle = 0.0
	for toothIndex in xrange(abs(teeth)):
		for toothPoint in toothProfile:
			gearProfile.append(toothPoint * euclidean.getWiddershinsUnitPolar(totalToothAngle))
		totalToothAngle += toothAngleRadian
	return gearProfile

def getGearProfileRack(derivation, toothProfile):
	'Get gear profile for rack.'
	derivation.extraRackDemilength = 0.0
	for complexPoint in derivation.helixPath:
		derivation.extraRackDemilength = max(abs(derivation.helixHeight * complexPoint.imag), derivation.extraRackDemilength)
	rackDemilengthPlus = derivation.rackDemilength
	if derivation.height > 0.0:
		derivation.extraRackDemilength *= 1.1
		rackDemilengthPlus += derivation.extraRackDemilength
	teethRack = int(math.ceil(rackDemilengthPlus / derivation.wavelength))
	gearProfile = []
	for toothIndex in xrange(-teethRack, teethRack + 1):
		translateComplex = complex(-toothIndex * derivation.wavelength, 0.0)
		translatedPath = euclidean.getTranslatedComplexPath(toothProfile, translateComplex)
		gearProfile += translatedPath
	gearProfile = euclidean.getHorizontallyBoundedPath(rackDemilengthPlus, -rackDemilengthPlus, gearProfile)
	firstPoint = gearProfile[0]
	lastPoint = gearProfile[-1]
	rackWidth = derivation.rackWidth
	minimumRackWidth = 1.1 * derivation.dedendum
	if rackWidth < minimumRackWidth:
		rackWidth = minimumRackWidth
		print('Warning, rackWidth is too small in getGearProfileRack in gear.')
		print('RackWidth will be set to a bit more than the dedendum.')
	gearProfile += [complex(lastPoint.real, -rackWidth),complex(firstPoint.real, -rackWidth)]
	return gearProfile

def getGeometryOutput(derivation, xmlElement):
	"Get vector3 vertexes from attribute dictionary."
	if derivation == None:
		derivation = GearDerivation(xmlElement)
	creationFirst = derivation.creationType.lower()[: 1]
	toothProfileComplement = getToothProfile(derivation, derivation.pitchRadiusComplement, derivation.teethComplement)
	pinionProfile = getGearProfileCylinder(derivation.teethPinion, derivation.pinionToothProfile)
	complementPaths = getGearPaths(
		derivation, derivation.pitchRadiusComplement, derivation.teethComplement, toothProfileComplement)
	vector3PinionProfile = euclidean.getVector3Path(pinionProfile)
	vector3ComplementPaths = euclidean.getVector3Paths(complementPaths)
	translation = Vector3()
	moveFirst = derivation.moveType.lower()[: 1]
	if moveFirst != 'n':
		distance = derivation.pitchRadius
		if moveFirst == 'm':
			distance += derivation.pitchRadiusComplement
		else:
			distance += abs(derivation.pitchRadiusComplement)
			decimalPlaces = 1 - int(math.floor(math.log10(distance)))
			distance += derivation.halfWavelength + derivation.halfWavelength
			distance = round(1.15 * distance, decimalPlaces)
		translation = Vector3(0.0, -distance)
	if derivation.height <=0.0:
		return getPathOutput(
			creationFirst, derivation, translation, vector3ComplementPaths, vector3PinionProfile, xmlElement)
	pitchRadius = derivation.pitchRadius
	teeth = derivation.teethPinion
	twist = derivation.helixHeight / derivation.pitchRadius
	extrudeOutputPinion = getOutputCylinder(
		derivation.pinionCollarHeight, derivation, None, pitchRadius, teeth, twist, [vector3PinionProfile], xmlElement)
	if creationFirst == 'p':
		return extrudeOutputPinion
	teeth = derivation.teethComplement
	extrudeOutputSecond = None
	if teeth == 0:
		extrudeOutputSecond = getOutputRack(derivation, vector3ComplementPaths[0], xmlElement)
	else:
		twist = -derivation.helixHeight / derivation.pitchRadiusComplement
		extrudeOutputSecond = getOutputCylinder(
			derivation.complementCollarHeight,
			derivation,
			derivation.gearHolePaths,
			derivation.pitchRadiusComplement,
			teeth,
			twist,
			vector3ComplementPaths,
			xmlElement)
	if creationFirst == 'c':
		return extrudeOutputSecond
	gearVertexes = matrix.getVertexes(extrudeOutputSecond)
	if moveFirst == 'v':
		translation = Vector3(0.0, 0.0, euclidean.getTopPath(gearVertexes))
		euclidean.translateVector3Path(matrix.getVertexes(extrudeOutputPinion), translation)
	else:
		euclidean.translateVector3Path(gearVertexes, translation)
	return {'group' : {'shapes' : [extrudeOutputPinion, extrudeOutputSecond]}}

def getGeometryOutputByArguments(arguments, xmlElement):
	"Get vector3 vertexes from attribute dictionary by arguments."
	return getGeometryOutput(None, xmlElement)

def getHalfwave(pitchRadius, teeth):
	'Get tooth halfwave.'
	return pitchRadius * math.pi / float(teeth)

def getHelixComplexPath(derivation, xmlElement):
	'Set gear helix path.'
	helixTypeFirstCharacter = derivation.helixType.lower()[: 1]
	if helixTypeFirstCharacter == 'b':
		return [complex(), complex(1.0, 1.0)]
	if helixTypeFirstCharacter == 'h':
		return [complex(), complex(0.5, 0.5), complex(1.0, 0.0)]
	if helixTypeFirstCharacter == 'p':
		helixComplexPath = []
		x = 0.0
		xStep = setting.getLayerHeight(xmlElement) / derivation.height
		justBelowOne = 1.0 - 0.5 * xStep
		while x < justBelowOne:
			distanceFromCenter = 0.5 - x
			parabolicTwist = 0.25 - distanceFromCenter * distanceFromCenter
			helixComplexPath.append(complex(x, parabolicTwist))
			x += xStep
		helixComplexPath.append(complex(1.0, 0.0))
		return helixComplexPath
	print('Warning, the helix type was not one of (basic, herringbone or parabolic) in getHelixComplexPath in gear for:')
	print(derivation.helixType)
	print(derivation.xmlElement)

def getLiftedOutput(derivation, geometryOutput, xmlElement):
	"Get extrude output for a rack."
	if derivation.moveType.lower()[: 1] == 'm':
		return geometryOutput
	geometryOutputVertexes = matrix.getVertexes(geometryOutput)
	translation = Vector3(0.0, 0.0, -euclidean.getBottomByPath(geometryOutputVertexes))
	euclidean.translateVector3Path(geometryOutputVertexes, translation)
	return geometryOutput

def getLighteningHoles(derivation, gearHolePaths, pitchRadius):
	'Get cutout circles.'
	if gearHolePaths != None:
		if len(gearHolePaths) > 0:
			return gearHolePaths
	innerRadius = abs(pitchRadius) - derivation.dedendum
	lighteningHoleOuterRadius = innerRadius - derivation.rimWidth
	shaftRimRadius = max(derivation.shaftRimRadius, (lighteningHoleOuterRadius) * (0.5 - math.sqrt(0.1875)))
	lighteningHoleRadius = 0.5 * (lighteningHoleOuterRadius - derivation.shaftRimRadius)
	if lighteningHoleRadius < derivation.lighteningHoleMinimumRadius:
		return []
	lighteningHoles = []
	numberOfLighteningHoles = 3
	polygonRadius = lighteningHoleOuterRadius - lighteningHoleRadius
	rimDemiwidth = 0.5 * derivation.lighteningHoleMargin
	axialMargin = getAxialMargin(lighteningHoleRadius, numberOfLighteningHoles, polygonRadius)
	if axialMargin < rimDemiwidth:
		while axialMargin < rimDemiwidth:
			lighteningHoleRadius *= 0.999
			if lighteningHoleRadius < derivation.lighteningHoleMinimumRadius:
				return []
			axialMargin = getAxialMargin(lighteningHoleRadius, numberOfLighteningHoles, polygonRadius)
	else:
		newNumberOfLighteningHoles = numberOfLighteningHoles
		while axialMargin > rimDemiwidth:
			numberOfLighteningHoles = newNumberOfLighteningHoles
			newNumberOfLighteningHoles += 2
			axialMargin = getAxialMargin(lighteningHoleRadius, newNumberOfLighteningHoles, polygonRadius)
	sideAngle = 2.0 * math.pi / float(numberOfLighteningHoles)
	startAngle = 0.0
	for lighteningHoleIndex in xrange(numberOfLighteningHoles):
		unitPolar = euclidean.getWiddershinsUnitPolar(startAngle)
		lighteningHole = euclidean.getComplexPolygon(unitPolar * polygonRadius, lighteningHoleRadius, -13)
		lighteningHoles.append(lighteningHole)
		startAngle += sideAngle
	return euclidean.getVector3Paths(lighteningHoles)

def getNewDerivation(xmlElement):
	'Get new derivation.'
	return GearDerivation(xmlElement)

def getOutputCylinder(
		collarHeight, derivation, gearHolePaths, pitchRadius, teeth, twist, vector3GearProfile, xmlElement):
	"Get extrude output for a cylinder gear."
	copyShallow = derivation.xmlElement.getCopyShallow()
	copyShallow.attributeDictionary['path'] = [Vector3(), Vector3(0.0, 0.0, derivation.height)]
	extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
	negatives = []
	positives = []
	if twist != 0.0:
		twistDegrees = math.degrees(twist)
		extrudeDerivation.twistPathDefault = []
		for complexPoint in derivation.helixPath:
			extrudeDerivation.twistPathDefault.append(Vector3(complexPoint.real, twistDegrees * complexPoint.imag))
		extrude.insertTwistPortions(extrudeDerivation, xmlElement)
	if derivation.operatingAngle != 180.0:
		addBevelGear(derivation, extrudeDerivation, pitchRadius, positives, teeth, vector3GearProfile)
		addCollarShaft(collarHeight, derivation, negatives, positives, xmlElement)
		return extrude.getGeometryOutputByNegativesPositives(negatives, positives, xmlElement)
	if pitchRadius > 0:
		extrude.addNegativesPositives(extrudeDerivation, negatives, vector3GearProfile, positives)
		addLighteningHoles(derivation, gearHolePaths, negatives, pitchRadius, positives)
		addCollarShaft(collarHeight, derivation, negatives, positives, xmlElement)
		return extrude.getGeometryOutputByNegativesPositives(negatives, positives, xmlElement)
	if derivation.plateHeight <= 0.0:
		extrude.addNegativesPositives(extrudeDerivation, negatives, vector3GearProfile, positives)
		return extrude.getGeometryOutputByNegativesPositives(negatives, positives, xmlElement)
	portionDirections = extrude.getSpacedPortionDirections(extrudeDerivation.interpolationDictionary)
	outerGearProfile = vector3GearProfile[0]
	outerLoopLists = extrude.getLoopListsByPath(extrudeDerivation, None, outerGearProfile, portionDirections)
	addBottomLoop(-derivation.plateClearance, outerLoopLists[0])
	geometryOutput = triangle_mesh.getPillarsOutput(outerLoopLists)
	positives.append(geometryOutput)
	innerLoopLists = extrude.getLoopListsByPath(extrudeDerivation, None, vector3GearProfile[1], portionDirections)
	addBottomLoop(-derivation.plateClearance, innerLoopLists[0])
	geometryOutput = triangle_mesh.getPillarsOutput(innerLoopLists)
	negatives.append(geometryOutput)
	connectionStart = Vector3(0.0, 0.0, -derivation.plateHeight)
	copyShallow = derivation.xmlElement.getCopyShallow()
	copyShallow.attributeDictionary['path'] = [connectionStart, Vector3(0.0, 0.0, -derivation.plateClearance)]
	plateDerivation = extrude.ExtrudeDerivation(copyShallow)
	extrude.addNegativesPositives(plateDerivation, negatives, [outerGearProfile], positives)
	vector3LighteningHoles = getLighteningHoles(derivation, gearHolePaths, pitchRadius)
	extrude.addNegativesPositives(plateDerivation, negatives, vector3LighteningHoles, positives)
	addShaft(derivation, negatives, positives)
	positiveOutput = triangle_mesh.getUnifiedOutput(positives)
	annulusPlateOutput = {'difference' : {'shapes' : [positiveOutput] + negatives}}
	if collarHeight <= 0.0:
		outputCylinder = solid.getGeometryOutputByManipulation(annulusPlateOutput, xmlElement)
		return getLiftedOutput(derivation, outputCylinder, xmlElement)
	negatives = []
	positives = []
	connectionEnd = Vector3(0.0, 0.0, derivation.height + collarHeight)
	copyShallow = derivation.xmlElement.getCopyShallow()
	copyShallow.attributeDictionary['path'] = [Vector3(0.0, 0.0, -derivation.plateClearance), connectionEnd]
	collarDerivation = extrude.ExtrudeDerivation(copyShallow)
	addCollarShaftSetDerivation(collarDerivation, collarHeight, derivation, negatives, positives, xmlElement)
	collarOutput = {'difference' : {'shapes' : positives + negatives}}
	cylinderOutput = {'union' : {'shapes' : [annulusPlateOutput, collarOutput]}}
	outputCylinder = solid.getGeometryOutputByManipulation(cylinderOutput, xmlElement)
	return getLiftedOutput(derivation, outputCylinder, xmlElement)

def getOutputRack(derivation, vector3GearProfile, xmlElement):
	"Get extrude output for a rack."
	path = []
	for complexPoint in derivation.helixPath:
		point = Vector3(derivation.helixHeight * complexPoint.imag, 0.0, derivation.height * complexPoint.real)
		path.append(point)
	copyShallow = derivation.xmlElement.getCopyShallow()
	copyShallow.attributeDictionary['path'] = path
	extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
	negatives = []
	positives = []
	vector3RackProfiles = [vector3GearProfile]
	if derivation.extraRackDemilength > 0.0:
		yMaximum = -912345678.0
		yMinimum = 912345678.0
		for point in vector3GearProfile:
			yMaximum = max(point.y, yMaximum)
			yMinimum = min(point.y, yMinimum)
		muchLessThanWidth = 0.01 * derivation.rackWidth
		yMaximum += muchLessThanWidth
		yMinimum -= muchLessThanWidth
		extraRackLength = derivation.extraRackDemilength + derivation.extraRackDemilength
		rackDemilengthPlus = derivation.rackDemilength + extraRackLength
		leftNegative = [
			Vector3(-derivation.rackDemilength, yMaximum),
			Vector3(-derivation.rackDemilength, yMinimum),
			Vector3(-rackDemilengthPlus, yMinimum),
			Vector3(-rackDemilengthPlus, yMaximum)]
		vector3RackProfiles.append(leftNegative)
		rightNegative = [
			Vector3(rackDemilengthPlus, yMaximum),
			Vector3(rackDemilengthPlus, yMinimum),
			Vector3(derivation.rackDemilength, yMinimum),
			Vector3(derivation.rackDemilength, yMaximum)]
		vector3RackProfiles.append(rightNegative)
	addRackHoles(derivation, vector3RackProfiles, xmlElement)
	extrude.addNegativesPositives(extrudeDerivation, negatives, vector3RackProfiles, positives)
	return extrude.getGeometryOutputByNegativesPositives(negatives, positives, xmlElement)

def getPathOutput(creationFirst, derivation, translation, vector3ComplementPaths, vector3PinionProfile, xmlElement):
	"Get gear path output."
	vector3PinionProfile = lineation.getPackedGeometryOutputByLoop(lineation.SideLoop(vector3PinionProfile), xmlElement)
	if creationFirst == 'p':
		return vector3PinionProfile
	packedGearGeometry = []
	for vector3ComplementPath in vector3ComplementPaths:
		sideLoop = lineation.SideLoop(vector3ComplementPath)
		packedGearGeometry += lineation.getPackedGeometryOutputByLoop(sideLoop, xmlElement)
	if creationFirst == 'c':
		return packedGearGeometry
	euclidean.translateVector3Paths(packedGearGeometry, translation)
	return vector3PinionProfile + packedGearGeometry

def getToothProfile(derivation, pitchRadius, teeth):
	'Get profile for one tooth.'
	if teeth < 0:
		return getToothProfileAnnulus(derivation, pitchRadius, teeth)
	if teeth == 0:
		return getToothProfileRack(derivation)
	return getToothProfileCylinder(derivation, pitchRadius, teeth)

def getToothProfileAnnulus(derivation, pitchRadius, teeth):
	'Get profile for one tooth of an annulus.'
	toothProfileHalf = []
	toothProfileHalfCylinder = getToothProfileHalfCylinder(derivation, pitchRadius)
	pitchRadius = -pitchRadius
	innerRadius = pitchRadius - derivation.addendum
	# 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
	for point in getWidthMultipliedPath(toothProfileHalfCylinder, 1.02 / derivation.toothWidthMultiplier):
		if abs(point) >= innerRadius:
			toothProfileHalf.append(point)
	profileFirst = toothProfileHalf[0]
	profileSecond = toothProfileHalf[1]
	firstMinusSecond = profileFirst - profileSecond
	remainingAddendum = abs(profileFirst) - innerRadius
	firstMinusSecond *= remainingAddendum / abs(firstMinusSecond)
	extensionPoint = profileFirst + firstMinusSecond
	if derivation.tipBevel > 0.0:
		unitPolar = euclidean.getWiddershinsUnitPolar(2.0 / float(teeth) * math.pi)
		mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag) * unitPolar
		bevelPath = getBevelPath(profileFirst, derivation.tipBevel, extensionPoint, mirrorPoint)
		toothProfileHalf = bevelPath + toothProfileHalf
	else:
		toothProfileHalf.insert(0, extensionPoint)
	profileLast = toothProfileHalf[-1]
	profilePenultimate = toothProfileHalf[-2]
	lastMinusPenultimate = profileLast - profilePenultimate
	remainingDedendum = pitchRadius - abs(profileLast) + derivation.dedendum
	lastMinusPenultimate *= remainingDedendum / abs(lastMinusPenultimate)
	extensionPoint = profileLast + lastMinusPenultimate
	if derivation.rootBevel > 0.0:
		mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag)
		bevelPath = getBevelPath(profileLast, derivation.rootBevel, extensionPoint, mirrorPoint)
		bevelPath.reverse()
		toothProfileHalf += bevelPath
	else:
		toothProfileHalf.append(extensionPoint)
	toothProfileAnnulus = euclidean.getMirrorPath(toothProfileHalf)
	toothProfileAnnulus.reverse()
	return toothProfileAnnulus

def getToothProfileCylinder(derivation, pitchRadius, teeth):
	'Get profile for one tooth of a cylindrical gear.'
	toothProfileHalfCylinder = getToothProfileHalfCylinder(derivation, pitchRadius)
	toothProfileHalfCylinder = getWidthMultipliedPath(toothProfileHalfCylinder, derivation.toothWidthMultiplier)
	toothProfileHalf = []
	innerRadius = pitchRadius - derivation.dedendum
	for point in toothProfileHalfCylinder:
		if abs(point) >= innerRadius:
			toothProfileHalf.append(point)
	return getToothProfileCylinderByProfile(derivation, pitchRadius, teeth, toothProfileHalf)

def getToothProfileCylinderByProfile(derivation, pitchRadius, teeth, toothProfileHalf):
	'Get profile for one tooth of a cylindrical gear.'
	profileFirst = toothProfileHalf[0]
	profileSecond = toothProfileHalf[1]
	firstMinusSecond = profileFirst - profileSecond
	remainingDedendum = abs(profileFirst) - pitchRadius + derivation.dedendum
	firstMinusSecond *= remainingDedendum / abs(firstMinusSecond)
	extensionPoint = profileFirst + firstMinusSecond
	if derivation.rootBevel > 0.0:
		unitPolar = euclidean.getWiddershinsUnitPolar(-2.0 / float(teeth) * math.pi)
		mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag) * unitPolar
		bevelPath = getBevelPath(profileFirst, derivation.rootBevel, extensionPoint, mirrorPoint)
		toothProfileHalf = bevelPath + toothProfileHalf
	else:
		toothProfileHalf.insert(0, extensionPoint)
	if derivation.tipBevel > 0.0:
		profileLast = toothProfileHalf[-1]
		profilePenultimate = toothProfileHalf[-2]
		mirrorPoint = complex(-profileLast.real, profileLast.imag)
		bevelPath = getBevelPath(profilePenultimate, derivation.tipBevel, profileLast, mirrorPoint)
		bevelPath.reverse()
		toothProfileHalf = toothProfileHalf[: -1] + bevelPath
	return euclidean.getMirrorPath(toothProfileHalf)

def getToothProfileHalfCylinder(derivation, pitchRadius):
	'Get profile for half of a one tooth of a cylindrical gear.'
	toothProfile=[]
#	x = -y * tan(p) + 1
#	x*x + y*y = (2-cos(p))^2
#	y*y*t*t-2yt+1+y*y=4-4c-c*c
#	y*y*(t*t+1)-2yt=3-4c-c*c
#	y*y*(t*t+1)-2yt-3+4c-c*c=0
#	a=tt+1
#	b=-2t
#	c=c(4-c)-3
	a = derivation.tanPressure * derivation.tanPressure + 1.0
	b = -derivation.tanPressure - derivation.tanPressure
	cEnd = derivation.cosPressure * (4.0 - derivation.cosPressure) - 3.0
	yEnd = (-b - math.sqrt(b*b - 4 * a * cEnd)) * 0.5 / a
	yEnd *= derivation.pitchRadius / abs(pitchRadius)
	yEnd -= derivation.clearance / abs(pitchRadius)
	# to prevent intersections, yBegin is moved towards the base circle, giving a thinner tooth
	yBegin = -yEnd
	if pitchRadius > 0.0:
		yBegin = 0.5 * derivation.sinPressure + 0.5 * yBegin
	beginComplex = complex(1.0 - yBegin * derivation.tanPressure, yBegin)
	endComplex = complex(1.0 - yEnd * derivation.tanPressure, yEnd)
	endMinusBeginComplex = endComplex - beginComplex
	wholeAngle = -abs(endMinusBeginComplex) / derivation.cosPressure
	wholeAngleIncrement = wholeAngle / float(derivation.profileSurfaces)
	stringStartAngle = abs(beginComplex - complex(1.0, 0.0)) / derivation.cosPressure
	wholeDepthIncrementComplex = endMinusBeginComplex / float(derivation.profileSurfaces)
	for profileIndex in xrange(derivation.profileSurfaces + 1):
		contactPoint = beginComplex + wholeDepthIncrementComplex * float(profileIndex)
		stringAngle = stringStartAngle + wholeAngleIncrement * float(profileIndex)
		angle = math.atan2(contactPoint.imag, contactPoint.real) - stringAngle
		angle += 0.5 * math.pi - derivation.quarterWavelength / abs(pitchRadius)
		toothPoint = abs(contactPoint) * euclidean.getWiddershinsUnitPolar(angle) * abs(pitchRadius)
		toothProfile.append(toothPoint)
	return toothProfile

def getToothProfileRack(derivation):
	'Get profile for one rack tooth.'
	addendumSide = derivation.quarterWavelength - derivation.addendum * derivation.tanPressure
	addendumComplex = complex(addendumSide, derivation.addendum)
	dedendumSide = derivation.quarterWavelength + derivation.dedendum * derivation.tanPressure
	dedendumComplex = complex(dedendumSide, -derivation.dedendum)
	toothProfile = [dedendumComplex]
	if derivation.rootBevel > 0.0:
		mirrorPoint = complex(derivation.wavelength - dedendumSide, -derivation.dedendum)
		toothProfile = getBevelPath(addendumComplex, derivation.rootBevel, dedendumComplex, mirrorPoint)
	if derivation.tipBevel > 0.0:
		mirrorPoint = complex(-addendumComplex.real, addendumComplex.imag)
		bevelPath = getBevelPath(dedendumComplex, derivation.tipBevel, addendumComplex, mirrorPoint)
		bevelPath.reverse()
		toothProfile += bevelPath
	else:
		toothProfile.append(addendumComplex)
	return euclidean.getMirrorPath(getWidthMultipliedPath(toothProfile, derivation.toothWidthMultiplier))

def getWidthMultipliedPath(path, widthMultiplier):
	"Get width multiplied path."
	for pointIndex, point in enumerate(path):
		path[pointIndex] = complex(point.real * widthMultiplier, point.imag)
	return path

def processXMLElement(xmlElement):
	"Process the xml element."
	geometryOutput = getGeometryOutput(None, xmlElement)
	if geometryOutput.__class__ == list:
		path.convertXMLElement(geometryOutput, xmlElement)
	else:
		solid.processXMLElementByGeometry(geometryOutput, xmlElement)


class GearDerivation:
	"Class to hold gear variables."
	def __init__(self, xmlElement):
		'Set defaults.'
		self.clearanceOverWavelength = evaluate.getEvaluatedFloat(0.1, 'clearanceOverWavelength', xmlElement)
		self.collarWidthOverRadius = evaluate.getEvaluatedFloat(1.0, 'collarWidthOverRadius', xmlElement)
		self.complementCollarHeightOverHeight = evaluate.getEvaluatedFloat(
			0.0, 'complementCollarHeightOverHeight', xmlElement)
		self.copyShallow = xmlElement.getCopyShallow()
		self.creationType = evaluate.getEvaluatedString('both', 'creationType', xmlElement)
		self.creationTypeMenuRadioStrings = 'both complement pinion'.split()
		self.helixAngle = evaluate.getEvaluatedFloat(0.0, 'helixAngle', xmlElement)
		self.helixType = evaluate.getEvaluatedString('basic', 'helixType', xmlElement)
		self.helixTypeMenuRadioStrings = 'basic herringbone parabolic'.split()
		self.keywayRadiusOverRadius = evaluate.getEvaluatedFloat(0.5, 'keywayRadiusOverRadius', xmlElement)
		self.lighteningHoleMarginOverRimWidth = evaluate.getEvaluatedFloat(
			1.0, 'lighteningHoleMarginOverRimWidth', xmlElement)
		self.lighteningHoleMinimumRadius = evaluate.getEvaluatedFloat(
			1.0, 'lighteningHoleMinimumRadius', xmlElement)
		self.moveType = evaluate.getEvaluatedString('separate', 'moveType', xmlElement)
		self.moveTypeMenuRadioStrings = 'mesh none separate vertical'.split()
		self.operatingAngle = evaluate.getEvaluatedFloat(180.0, 'operatingAngle', xmlElement)
		self.pinionCollarHeightOverHeight = evaluate.getEvaluatedFloat(
			0.0, 'pinionCollarHeightOverHeight', xmlElement)
		self.plateClearanceOverHeight = evaluate.getEvaluatedFloat(0.2, 'plateClearanceOverHeight', xmlElement)
		self.plateHeightOverHeight = evaluate.getEvaluatedFloat(0.5, 'plateHeightOverHeight', xmlElement)
		self.pressureAngle = evaluate.getEvaluatedFloat(20.0, 'pressureAngle', xmlElement)
		self.profileSurfaces = evaluate.getEvaluatedInt(11, 'profileSurfaces', xmlElement)
		self.rackHoleBelowOverWidth = evaluate.getEvaluatedFloat(0.6, 'rackHoleBelowOverWidth', xmlElement)
		self.rackHoleRadiusOverWidth = evaluate.getEvaluatedFloat(0.0, 'rackHoleRadiusOverWidth', xmlElement)
		self.rackHoleStepOverWidth = evaluate.getEvaluatedFloat(1.0, 'rackHoleStepOverWidth', xmlElement)
		self.rackLengthOverRadius = evaluate.getEvaluatedFloat(math.pi + math.pi, 'rackLengthOverRadius', xmlElement)
		self.rackWidthOverHeight = evaluate.getEvaluatedFloat(1.0, 'rackWidthOverHeight', xmlElement)
		self.rimWidthOverRadius = evaluate.getEvaluatedFloat(0.2, 'rimWidthOverRadius', xmlElement)
		self.rootBevelOverClearance = evaluate.getEvaluatedFloat(0.5, 'rootBevelOverClearance', xmlElement)
		self.shaftDepthBottomOverRadius = evaluate.getEvaluatedFloat(0.0, 'shaftDepthBottomOverRadius', xmlElement)
		self.shaftDepthTopOverRadius = evaluate.getEvaluatedFloat(0.0, 'shaftDepthOverRadius', xmlElement)
		self.shaftRadiusOverPitchRadius = evaluate.getEvaluatedFloat(0.0, 'shaftRadiusOverPitchRadius', xmlElement)
		self.shaftSides = evaluate.getEvaluatedInt(4, 'shaftSides', xmlElement)
		self.teethComplement = evaluate.getEvaluatedInt(17, 'teethComplement', xmlElement)
		self.teethPinion = evaluate.getEvaluatedInt(7, 'teeth', xmlElement)
		totalTeethOverPinionTeeth = float(self.teethComplement + self.teethPinion) / float(self.teethPinion)
		self.centerDistance = evaluate.getEvaluatedFloat(20.0 * totalTeethOverPinionTeeth, 'centerDistance', xmlElement)
		derivedPitchRadius = self.centerDistance / totalTeethOverPinionTeeth
		self.pitchRadius = evaluate.getEvaluatedFloat(derivedPitchRadius, 'pitchRadius', xmlElement)
		self.tipBevelOverClearance = evaluate.getEvaluatedFloat(0.1, 'tipBevelOverClearance', xmlElement)
		self.height = evaluate.getEvaluatedFloat(10.0, 'height', xmlElement)
		# tooth multiplied by 0.99999 to avoid an intersection
		self.toothWidthMultiplier = evaluate.getEvaluatedFloat(0.99999, 'toothWidthMultiplier', xmlElement)
		# Set derived variables.
		self.wavelength = self.pitchRadius * 2.0 * math.pi / float(self.teethPinion)
		self.clearance = self.wavelength * self.clearanceOverWavelength
		self.clearance = evaluate.getEvaluatedFloat(self.clearance, 'clearance', xmlElement)
		self.complementCollarHeight = self.height * self.complementCollarHeightOverHeight
		self.complementCollarHeight = evaluate.getEvaluatedFloat(self.complementCollarHeight, 'complementCollarHeight', xmlElement)
		self.gearHolePaths = evaluate.getTransformedPathsByKey([], 'gearHolePaths', xmlElement)
		self.pinionCollarHeight = self.height * self.pinionCollarHeightOverHeight
		self.pinionCollarHeight = evaluate.getEvaluatedFloat(self.pinionCollarHeight, 'pinionCollarHeight', xmlElement)
		self.plateHeight = self.height * self.plateHeightOverHeight
		self.plateHeight = evaluate.getEvaluatedFloat(self.plateHeight, 'plateHeight', xmlElement)
		self.plateClearance = self.plateHeight * self.plateClearanceOverHeight
		self.plateClearance = evaluate.getEvaluatedFloat(self.plateClearance, 'plateClearance', xmlElement)
		self.rackLength = self.pitchRadius * self.rackLengthOverRadius
		self.rackLength = evaluate.getEvaluatedFloat(self.rackLength, 'rackLength', xmlElement)
		self.rackDemilength = 0.5 * self.rackLength
		self.rackWidth = self.height * self.rackWidthOverHeight
		self.rackWidth = evaluate.getEvaluatedFloat(self.rackWidth, 'rackWidth', xmlElement)
		self.rimWidth = self.pitchRadius * self.rimWidthOverRadius
		self.rimWidth = evaluate.getEvaluatedFloat(self.rimWidth, 'rimWidth', xmlElement)
		self.rootBevel = self.clearance * self.rootBevelOverClearance
		self.rootBevel = evaluate.getEvaluatedFloat(self.rootBevel, 'rootBevel', xmlElement)
		self.shaftRadius = self.pitchRadius * self.shaftRadiusOverPitchRadius
		self.shaftRadius = evaluate.getEvaluatedFloat(self.shaftRadius, 'shaftRadius', xmlElement)
		self.collarWidth = self.shaftRadius * self.collarWidthOverRadius
		self.collarWidth = evaluate.getEvaluatedFloat(self.collarWidth, 'collarWidth', xmlElement)
		self.keywayRadius = self.shaftRadius * self.keywayRadiusOverRadius
		self.keywayRadius = lineation.getFloatByPrefixBeginEnd('keywayRadius', 'keywayDiameter', self.keywayRadius, xmlElement)
		self.lighteningHoleMargin = self.rimWidth * self.lighteningHoleMarginOverRimWidth
		self.lighteningHoleMargin = evaluate.getEvaluatedFloat(
			self.lighteningHoleMargin, 'lighteningHoleMargin', xmlElement)
		self.rackHoleBelow = self.rackWidth * self.rackHoleBelowOverWidth
		self.rackHoleBelow = evaluate.getEvaluatedFloat(self.rackHoleBelow, 'rackHoleBelow', xmlElement)
		self.rackHoleRadius = self.rackWidth * self.rackHoleRadiusOverWidth
		self.rackHoleRadius = lineation.getFloatByPrefixBeginEnd('rackHoleRadius', 'rackHoleDiameter', self.rackHoleRadius, xmlElement)
		self.rackHoleStep = self.rackWidth * self.rackHoleStepOverWidth
		self.rackHoleStep = evaluate.getEvaluatedFloat(self.rackHoleStep, 'rackHoleStep', xmlElement)
		self.shaftDepthBottom = self.shaftRadius * self.shaftDepthBottomOverRadius
		self.shaftDepthBottom = evaluate.getEvaluatedFloat(self.shaftDepthBottom, 'shaftDepthBottom', xmlElement)
		self.shaftDepthTop = self.shaftRadius * self.shaftDepthTopOverRadius
		self.shaftDepthTop = evaluate.getEvaluatedFloat(self.shaftDepthTop, 'shaftDepthTop', xmlElement)
		self.shaftPath = evaluate.getTransformedPathByKey([], 'shaftPath', xmlElement)
		if len(self.shaftPath) < 3:
			self.shaftPath = shaft.getShaftPath(self.shaftDepthBottom, self.shaftDepthTop, self.shaftRadius, -self.shaftSides)
		self.tipBevel = self.clearance * self.tipBevelOverClearance
		self.tipBevel = evaluate.getEvaluatedFloat(self.tipBevel, 'tipBevel', xmlElement)
		# Set derived values.
		self.helixRadian = math.radians(self.helixAngle)
		if self.teethComplement <= 0.0 and self.operatingAngle != 180.0:
			print('Warning, an operatingAngle other than 180 degrees can only work with a positive number of gear teeth.')
			print('Therefore the operatingAngle will be reset to 180 degrees.')
			self.operatingAngle = 180.0
		self.tanHelix = math.tan(self.helixRadian)
		self.helixHeight = self.tanHelix * self.height
		self.operatingRadian = math.radians(self.operatingAngle)
		self.pitchRadiusComplement = self.pitchRadius * float(self.teethComplement) / float(self.teethPinion)
		self.pressureRadian = math.radians(self.pressureAngle)
		self.cosPressure = math.cos(self.pressureRadian)
		self.sinPressure = math.sin(self.pressureRadian)
		self.tanPressure = math.tan(self.pressureRadian)
		self.halfWavelength = 0.5 * self.wavelength
		self.helixPath = euclidean.getComplexPath(evaluate.getTransformedPathByKey([], 'helixPath', xmlElement))
		if len(self.helixPath) < 1:
			self.helixPath = getHelixComplexPath(self, xmlElement)
		self.quarterWavelength = 0.25 * self.wavelength
		self.shaftRimRadius = self.shaftRadius + self.collarWidth
		self.toothProfileHalf = getToothProfileHalfCylinder(self, self.pitchRadius)
		self.toothProfileHalf = getWidthMultipliedPath(self.toothProfileHalf, self.toothWidthMultiplier)
		self.addendum = self.toothProfileHalf[-1].imag - self.pitchRadius
		self.dedendum = abs(self.toothProfileHalf[-1]) - self.pitchRadius + self.clearance
		self.pinionToothProfile = getToothProfileCylinderByProfile(self, self.pitchRadius, self.teethPinion, self.toothProfileHalf)
		self.xmlElement = xmlElement

	def __repr__(self):
		"Get the string representation of this GearDerivation."
		return str(self.__dict__)
