@jwc/jscad-gears
Version:
jscad parts library to create gears
776 lines (683 loc) • 32.3 kB
JavaScript
// Involute spur gear builder jscad script. Licensed under the MIT license (http://opensource.org/licenses/mit-license.php). Copyright 2014 Dr. Rainer Hessmer
var g_ExpandToCAGParams = {
pathradius: 0.01,
resolution: 2
};
var GearType = {
Regular: 0,
Internal: 1,
Rack: 2
};
function main(params) {
// Main entry point; here we construct our solid:
var qualitySettings = {
resolution: params.resolution,
stepsPerToothAngle: params.stepsPerToothAngle
};
var gear1 = new Gear({
circularPitch: params.circularPitch,
pressureAngle: params.pressureAngle,
clearance: params.clearance,
backlash: params.backlash,
toothCount: params.wheel1ToothCount,
centerHoleDiameter: params.wheel1CenterHoleDiamater,
profileShift: -params.profileShift,
qualitySettings: qualitySettings
});
var gear2 = new Gear({
circularPitch: params.circularPitch,
pressureAngle: params.pressureAngle,
clearance: params.clearance,
backlash: params.backlash,
toothCount: params.wheel2ToothCount,
centerHoleDiameter: params.wheel2CenterHoleDiamater,
profileShift: params.profileShift,
qualitySettings: qualitySettings
});
var gearSet = new GearSet(
gear1,
gear2,
params.showOption);
var shape = gearSet.createShape();
OpenJsCad.log('returning gear set shape');
return shape;
}
function getParameterDefinitions() {
return [
{
name: 'circularPitch',
caption: 'Circular pitch (the circumference of the pitch circle divided by the number of teeth):',
type: 'float',
initial: 8
},
{
name: 'pressureAngle',
caption: 'Pressure Angle (common values are 14.5, 20 and 25 degrees):',
type: 'float',
initial: 20
},
{
name: 'clearance',
caption: 'Clearance (minimal distance between the apex of a tooth and the trough of the other gear; in length units):',
type: 'float',
initial: 0.05
},
{
name: 'backlash',
caption: 'Backlash (minimal distance between meshing gears; in length units):',
type: 'float',
initial: 0.05
},
{
name: 'profileShift',
caption: 'Profile Shift (indicates what portion of gear one\'s addendum height should be shifted to gear two. E.g., a value of 0.1 means the adddendum of gear two is increased by a factor of 1.1 while the height of the addendum of gear one is reduced to 0.9 of its normal height.):',
type: 'float',
initial: 0.0
},
{
name: 'wheel1ToothCount',
caption: 'Wheel 1 Tooth Count (n1 > 0: external gear; n1 = 0: rack; n1 < 0: internal gear):',
type: 'int',
initial: 30
},
{
name: 'wheel1CenterHoleDiamater',
caption: 'Wheel 1 Center Hole Diameter (0 for no hole):',
type: 'float',
initial: 4
},
{
name: 'wheel2ToothCount',
caption: 'Wheel 2 Tooth Count:',
type: 'int',
initial: 8
},
{
name: 'wheel2CenterHoleDiamater',
caption: 'Wheel 2 Center Hole Diameter (0 for no hole):',
type: 'float',
initial: 4
},
{
name: 'showOption',
caption: 'Show:',
type: 'choice',
values: [3, 1, 2],
initial: 3,
captions: ['Wheel 1 and Wheel 2', 'Wheel 1 Only', 'Wheel 2 Only']
},
{
name: 'stepsPerToothAngle',
caption: 'Rotation steps per tooth angle when assembling the tooth profile (3 = draft, 10 = good quality). Increasing the value will result in smoother profiles at the cost of significantly higher calcucation time. Incease in small increments and check the result by zooming in.',
type: 'int',
initial: 3
},
{
name: 'resolution',
caption: 'Number of segments per 360 degree of rotation (only used for circles and arcs); 90 is plenty:',
type: 'int',
initial: 30
},
];
}
// Generic support for 'class' inheritance (see TypeScript playground example 'Simple Inheritance' for details (http://www.typescriptlang.org/Playground/)
var __extends = this.__extends || function (d, b) {
function __() {
this.constructor = d;
}
__.prototype = b.prototype;
d.prototype = new __();
};
// End generic support for 'class' inheritance
// Start base class Gear
var Gear = (function () {
function Gear(options) {
var options = options || {};
this.toothCount = options.toothCount == null ? 15 : options.toothCount;
if (this.toothCount > 0) {
this.gearType = GearType.Regular;
} else if (this.toothCount < 0) {
this.gearType = GearType.Internal;
this.toothCount = -this.toothCount;
} else {
// this.toothCount == 0
this.gearType = GearType.Rack;
}
this.circularPitch = options.circularPitch; // Distance from one face of a tooth to the corresponding face of an adjacent tooth on the same gear, measured along the pitch circle.
this.diametralPitch = options.diametralPitch; // Ratio of the number of teeth to the pitch diameter
this.pressureAngle = options.pressureAngle || 20; // Most common stock gears have a 20° pressure angle, with 14½° and 25° pressure angle gears being much less
// common. Increasing the pressure angle increases the width of the base of the gear tooth, leading to greater strength and load carrying capacity. Decreasing
// the pressure angle provides lower backlash, smoother operation and less sensitivity to manufacturing errors. (reference: http://en.wikipedia.org/wiki/Involute_gear)
this.centerHoleDiameter = options.centerHoleDiameter || 0;
// Given either circular pitch or diametral pitch we calculate the other value
if (this.circularPitch) {
// convert circular pitch to diametral pitch
this.diametralPitch = Math.PI / this.circularPitch;
} else if (this.circularPitch) {
// convert diametral pitch to circular pitch
this.circularPitch = Math.PI / this.diametralPitch;
} else {
throw 'gear module needs either a diametralPitch or circularPitch';
}
this.clearance = options.clearance == null ? 0 : options.clearance;
this.backlash = options.backlash == null ? 0 : options.backlash;
this.center = [0, 0]; // center of the gear
this.angle = 0; // angle in degrees of the complete gear (changes during rotation animation)
// Pitch diameter: Diameter of pitch circle.
this.pitchDiameter = this.toothCount / this.diametralPitch;
this.pitchRadius = this.pitchDiameter / 2;
// Addendum: Radial distance from pitch circle to outside circle.
this.addendum = 1 / this.diametralPitch;
this.profileShift = options.profileShift || 0;
// Typically no profile shift is used meaning that this.shiftedAddendum = this.addendum
this.shiftedAddendum = this.addendum * (1 + this.profileShift);
//Outer Circle
this.outerRadius = this.pitchRadius + this.shiftedAddendum;
this.angleToothToTooth = 360 / this.toothCount;
this.qualitySettings = options.qualitySettings;
//OpenJsCad.log("qualitySettings.resolution: " + this.qualitySettings.resolution);
//OpenJsCad.log("qualitySettings.stepsPerToothAngle: " + this.qualitySettings.stepsPerToothAngle);
}
Gear.prototype.getZeroedShape = function () {
// return the gear shape center on the origin and rotation angle 0.
if (this.zeroedShape == null) {
this.zeroedShape = this._createZeroedShape();
}
return this.zeroedShape;
};
Gear.prototype._createZeroedShape = function () {
if (this.gearType == GearType.Regular) {
return this._createRegularGearShape();
} else if (this.gearType == GearType.Internal) {
return this._createInternalGearShape();
} else if (this.gearType == GearType.Rack) {
return this._createRackShape();
}
};
Gear.prototype._createRegularGearShape = function () {
var tooth = this._createSingleTooth();
//OpenJsCad.log("-1");
// we could now take the tooth cutout, rotate it tooth count times and union the various slices together into a complete gear.
// However, the union operations become more and more complex as the complete gear is built up.
// So instead we capture the outer path of the tooth and concatenate rotated versions of this path into a complete outer gear path.
// Concatenating paths is inexpensive resulting in significantly faster execution.
var outlinePaths = tooth.getOutlinePaths();
var corners = outlinePaths[0].points;
// first we need to find the corner that sits at the center
var centerCornerIndex;
for (var i = 0; i < corners.length; i++) {
var corner = corners[i];
if (corner.lengthSquared() < 0.0000001) {
centerCornerIndex = i;
break;
}
}
var outerPoints = [];
var outerCorners = [];
var outterPointsCount = corners.length - 2;
for (var i = 1; i < corners.length - 1; i++) {
var corner = corners[(i + centerCornerIndex) % corners.length];
outerCorners.push(corner);
outerPoints.push([corner.x, corner.y]);
}
for (var i = 1; i < this.toothCount; i++) {
var angle = i * this.angleToothToTooth;
var roatationMatrix = CSG.Matrix4x4.rotationZ(angle);
for (var j = 0; j < outerCorners.length; j++) {
var rotatedCorner = outerCorners[j].transform(roatationMatrix);
outerPoints.push([rotatedCorner.x, rotatedCorner.y]);
}
//OpenJsCad.log(i);
}
var gearShape = CAG.fromPointsNoCheck(outerPoints);
if (this.centerHoleDiameter > 0) {
var centerhole = CAG.circle({
center: [-0, -0],
radius: this.centerHoleDiameter / 2,
resolution: this.qualitySettings.resolution
});
gearShape = gearShape.subtract(centerhole);
}
return gearShape.rotateZ(-90);
};
Gear.prototype._createSingleTooth = function () {
// create outer circle sector covering one tooth
var toothSectorPath = new CSG.Path2D([[0, 0]], /* closed = */ false);
var toothSectorArc = CSG.Path2D.arc({
center: [0, 0],
radius: this.outerRadius,
startangle: 90,
endangle: 90 - this.angleToothToTooth,
resolution: this.qualitySettings.resolution,
});
toothSectorPath = toothSectorPath.concat(toothSectorArc);
toothSectorPath = toothSectorPath.close();
var toothSector = toothSectorPath.innerToCAG();
var toothCutout = this.createToothCutout(false);
//OpenJsCad.log("-2");
var tooth = toothSector.subtract(toothCutout);
return tooth;
};
Gear.prototype.createCutoutDemo = function () {
// create outer circle
var outerCirclePath = CSG.Path2D.arc({
center: [0, 0],
radius: this.outerRadius,
startangle: 0,
endangle: 360,
resolution: this.qualitySettings.resolution,
});
outerCirclePath = outerCirclePath.close();
var gearShape = new CAG();
gearShape = gearShape.union(outerCirclePath.expandToCAG(g_ExpandToCAGParams.pathradius, g_ExpandToCAGParams.resolution));
var firstCutoutHalf = this.createHalfToothCutout(true);
// for illustration purposes we mirror the cutout and rotate it so that we can see a completely formed tooth
var secondCutoutHalf = firstCutoutHalf.mirroredX();
gearShape = gearShape.union(firstCutoutHalf);
gearShape = gearShape.union(secondCutoutHalf);
// apply gear rotation
gearShape = gearShape.rotateZ(this.angle);
// move to correct center
gearShape = gearShape.translate(this.center);
return gearShape;
};
Gear.prototype.createToothCutout = function (asPath) {
var angleToothToTooth = 360 / this.toothCount;
var angleStepSize = this.angleToothToTooth / this.qualitySettings.stepsPerToothAngle;
//OpenJsCad.log("angleToothToTooth: " + this.angleToothToTooth);
//OpenJsCad.log("angleStepSize: " + angleStepSize);
var toothCutout = new CAG();
var toothCutter = this.createToothCutter(asPath);
var toothCutterShape = toothCutter.shape;
var lowerLeftCorner = toothCutter.lowerLeftCorner;
// To create the tooth profile we move the (virtual) infinite gear and then turn the resulting cutter position back.
// For illustration see http://lcamtuf.coredump.cx/gcnc/ch6/, section 'Putting it all together'
// We continue until the moved tooth cutter's lower left corner is outside of the outer circle of the gear.
// Going any further will no longer influence the shape of the tooth
var lowerLeftCornerDistance = 0;
var stepCounter = 0;
while (true) {
var angle = stepCounter * angleStepSize;
var xTranslation = [angle * Math.PI / 180 * this.pitchRadius, 0];
var movedLowerLeftCorner = lowerLeftCorner.translate(xTranslation);
movedLowerLeftCorner = movedLowerLeftCorner.rotateZ(angle);
lowerLeftCornerDistance = movedLowerLeftCorner.length();
if (movedLowerLeftCorner.length() > this.outerRadius) {
// the cutter is now completely outside the gear and no longer influences the shape of the gear tooth
break;
}
// we move in both directions
var movedToothCutterShape = toothCutterShape.translate(xTranslation);
var movedToothCutterShape = movedToothCutterShape.rotateZ(angle);
toothCutout = toothCutout.union(movedToothCutterShape);
if (xTranslation[0] > 0) {
//OpenJsCad.log("xTranslation: " + xTranslation);
movedToothCutterShape = toothCutterShape.translate([-xTranslation[0], xTranslation[1]]);
movedToothCutterShape = movedToothCutterShape.rotateZ(-angle);
toothCutout = toothCutout.union(movedToothCutterShape);
}
stepCounter++;
}
var outlinePaths = toothCutout.getOutlinePaths();
var corners = outlinePaths[0].points;
var cleanedUpCorners = this._smoothConcaveCorners(corners);
var points = [];
cleanedUpCorners.map(function (corner) {
points.push([corner.x, corner.y]);
});
var toothCutout = CAG.fromPoints(points);
return toothCutout.rotateZ(-this.angleToothToTooth / 2);
};
Gear.prototype.createToothCutter = function (asPath) {
// we create a trapezoidal cutter as described at http://lcamtuf.coredump.cx/gcnc/ch6/ under the section 'Putting it all together'
var toothWidth = this.circularPitch / 2;
//OpenJsCad.log("toothWidth: " + toothWidth);
OpenJsCad.log('addendum: ' + this.addendum);
OpenJsCad.log('shiftedAddendum: ' + this.shiftedAddendum);
OpenJsCad.log('clearance: ' + this.clearance);
var cutterDepth = this.addendum + this.clearance;
var cutterOutsideLength = 3 * this.addendum;
OpenJsCad.log('cutterDepth: ' + cutterDepth);
//OpenJsCad.log("cutterOutsideLength: " + cutterOutsideLength);
var sinPressureAngle = Math.sin(this.pressureAngle * Math.PI / 180);
var cosPressureAngle = Math.cos(this.pressureAngle * Math.PI / 180);
// if a positive backlash is defined then we widen the trapezoid accordingly.
// Each side of the tooth needs to widened by a fourth of the backlash (vertical to cutter faces).
var dx = this.backlash / 2 / cosPressureAngle;
//OpenJsCad.log("backlash: " + this.backlash);
//OpenJsCad.log("dx: " + dx);
var lowerRightCorner = [toothWidth / 2 + dx - cutterDepth * sinPressureAngle, this.pitchRadius + this.profileShift * this.addendum - cutterDepth];
var upperRightCorner = [toothWidth / 2 + dx + cutterOutsideLength * sinPressureAngle, this.pitchRadius + this.profileShift * this.addendum + cutterOutsideLength];
var upperLeftCorner = [-upperRightCorner[0], upperRightCorner[1]];
var lowerLeftCorner = [-lowerRightCorner[0], lowerRightCorner[1]];
//this.logPoints([lowerRightCorner, upperRightCorner, upperLeftCorner, lowerLeftCorner]);
var cutterPath = new CSG.Path2D(
[lowerLeftCorner, upperLeftCorner, upperRightCorner, lowerRightCorner],
/* closed = */
true
);
var cutterShape;
if (asPath) {
cutterShape = cutterPath.expandToCAG(g_ExpandToCAGParams.pathradius, g_ExpandToCAGParams.resolution);
} else {
cutterShape = cutterPath.innerToCAG();
}
return {
shape: cutterShape,
lowerLeftCorner: cutterPath.points[0]
};
};
Gear.prototype._createInternalGearShape = function () {
var singleTooth = this._createInternalToothProfile();
//return singleTooth;
var outlinePaths = singleTooth.getOutlinePaths();
var corners = outlinePaths[0].points;
// first we need to find the corner that sits at the center
var centerCornerIndex;
var radius = this.pitchRadius + (1 + this.profileShift) * this.addendum + this.clearance;
var delta = 0.0000001;
for (var i = 0; i < corners.length; i++) {
var corner = corners[i];
if (corner.y < delta && (corner.x + radius) < delta) {
centerCornerIndex = i;
break;
}
}
var outerCorners = [];
for (var i = 2; i < corners.length - 2; i++) {
var corner = corners[(i + centerCornerIndex) % corners.length];
outerCorners.push(corner);
}
outerCorners.reverse();
var cornersCount = outerCorners.length;
for (var i = 1; i < this.toothCount; i++) {
var angle = i * this.angleToothToTooth;
var roatationMatrix = CSG.Matrix4x4.rotationZ(angle);
for (var j = 0; j < cornersCount; j++) {
var rotatedCorner = outerCorners[j].transform(roatationMatrix);
outerCorners.push(rotatedCorner);
}
}
var outerCorners = this._smoothConcaveCorners(outerCorners);
var outerPoints = [];
outerCorners.map(function (corner) {
outerPoints.push([corner.x, corner.y]);
});
var innerRadius = this.pitchRadius + (1 - this.profileShift) * this.addendum + this.clearance;
var outerRadius = innerRadius + 4 * this.addendum;
var outerCircle = CAG.circle({
center: this.center,
radius: outerRadius,
resolution: this.qualitySettings.resolution
});
//return outerCircle;
var gearCutout = CAG.fromPointsNoCheck(outerPoints);
//return gearCutout;
return outerCircle.subtract(gearCutout);
};
Gear.prototype._createInternalToothProfile = function () {
var radius = this.pitchRadius + (1 - this.profileShift) * this.addendum + this.clearance;
var angleToothToTooth = 360 / this.toothCount;
var sin = Math.sin(angleToothToTooth / 2 * Math.PI / 180);
var cos = Math.cos(angleToothToTooth / 2 * Math.PI / 180);
var fullSector = CAG.fromPoints(
[
[0, 0],
[-(radius * cos), radius * sin],
[-radius, 0],
[-(radius * cos), -radius * sin]
]
);
var innerCircle = CAG.circle({
center: this.center,
radius: radius - (2 * this.addendum + this.clearance),
resolution: this.qualitySettings.resolution
});
var sector = fullSector.subtract(innerCircle);
var cutterTemplate = this._createInternalToothCutter();
var pinion = this.connectedGear;
var angleToothToTooth = 360 / pinion.toothCount;
var stepsPerTooth = this.qualitySettings.stepsPerToothAngle;
var angleStepSize = angleToothToTooth / stepsPerTooth;
//OpenJsCad.log("angleToothToTooth: " + angleToothToTooth);
//OpenJsCad.log("this.qualitySettings.stepsPerToothAngle: " + this.qualitySettings.stepsPerToothAngle);
//OpenJsCad.log("count: " + this.qualitySettings.stepsPerToothAngle * this.toothCount / pinion.toothCount);
//OpenJsCad.log("angleStepSize: " + angleStepSize);
var toothShape = sector;
var cutter = cutterTemplate.translate([-this.pitchRadius + this.connectedGear.pitchRadius, 0]);
toothShape = toothShape.subtract(cutter);
for (var i = 1; i < stepsPerTooth; i++) {
//OpenJsCad.log("i: " + i);
var pinionRotationAngle = i * angleStepSize;
var pinionCenterRayAngle = -pinionRotationAngle * pinion.toothCount / this.toothCount;
//OpenJsCad.log("pinionRotationAngle: " + pinionRotationAngle);
//OpenJsCad.log("pinionCenterRayAngle: " + pinionCenterRayAngle);
//var cutter = cutterTemplate;
cutter = cutterTemplate.rotateZ(pinionRotationAngle);
cutter = cutter.translate([-this.pitchRadius + this.connectedGear.pitchRadius, 0]);
cutter = cutter.rotateZ(pinionCenterRayAngle);
toothShape = toothShape.subtract(cutter);
cutter = cutterTemplate.rotateZ(-pinionRotationAngle);
cutter = cutter.translate([-this.pitchRadius + this.connectedGear.pitchRadius, 0]);
cutter = cutter.rotateZ(-pinionCenterRayAngle);
toothShape = toothShape.subtract(cutter);
}
return toothShape;
};
Gear.prototype._smoothConvexCorners = function (corners) {
// removes single convex corners located between concave corners
return this._smoothCorners(corners, /* removeSingleConvex= */ true);
};
Gear.prototype._smoothConcaveCorners = function (corners) {
// removes single concave corners located between convex corners
return this._smoothCorners(corners, /* removeSingleConvex= */ false);
};
Gear.prototype._smoothCorners = function (corners, removeSingleConvex) {
var isConvex = [];
var previousCorner = corners[corners.length - 1];
var currentCorner = corners[0];
for (var i = 0; i < corners.length; i++) {
var nextCorner = corners[(i + 1) % corners.length];
var v1 = previousCorner.minus(currentCorner);
var v2 = nextCorner.minus(currentCorner);
var crossProduct = v1.cross(v2);
isConvex.push(crossProduct < 0);
previousCorner = currentCorner;
currentCorner = nextCorner;
}
// we want to remove any concave corners that are located between two convex corners
var cleanedUpCorners = [];
var previousIndex = corners.length - 1;
var currentIndex = 0;
for (var i = 0; i < corners.length; i++) {
var corner = corners[currentIndex];
var nextIndex = (i + 1) % corners.length;
var isSingleConcave = (!isConvex[currentIndex] && isConvex[previousIndex] && isConvex[nextIndex]);
var isSingleConvex = (isConvex[currentIndex] && !isConvex[previousIndex] && !isConvex[nextIndex]);
previousIndex = currentIndex;
currentIndex = nextIndex;
if (removeSingleConvex && isSingleConvex) {
//OpenJsCad.log("skipping single convex: " + currentIndex);
continue;
}
if (!removeSingleConvex && isSingleConcave) {
//OpenJsCad.log("skipping single concave: " + currentIndex);
continue;
}
cleanedUpCorners.push(corner);
}
return cleanedUpCorners;
};
Gear.prototype._createInternalToothCutter = function () {
// To cut the internal gear teeth, the actual pinion comes close but we need to enlarge it so properly cater for clearance and backlash
var pinion = this.connectedGear;
var enlargedPinion = new Gear({
circularPitch: pinion.circularPitch,
pressureAngle: pinion.pressureAngle,
clearance: -pinion.clearance,
backlash: -pinion.backlash,
toothCount: pinion.toothCount,
centerHoleDiameter: 0,
profileShift: pinion.profileShift,
qualitySettings: pinion.qualitySettings
});
var tooth = enlargedPinion._createSingleTooth();
return tooth.rotateZ(90 + 180 / enlargedPinion.toothCount); // we need a tooth pointing to the left
};
Gear.prototype._createRackShape = function () {
var rack = new CAG();
var protoTooth = this._createRackTooth();
// we draw one tooth in the middle and then five on either side
var toothCount = 41.0;
for (var i = 0; i < toothCount; i++) {
var tooth = protoTooth.translate([0, (0.5 + -toothCount / 2 + i) * this.circularPitch]);
rack = rack.union(tooth);
}
// creating the bar backing the teeth
var rightX = -(this.addendum + this.clearance);
var width = 4 * this.addendum;
var halfHeight = toothCount * this.circularPitch / 2;
var bar = CAG.rectangle({
corner1: [rightX - width, -halfHeight],
corner2: [rightX, halfHeight]
});
rack = rack.union(bar);
rack = rack.translate([this.addendum * this.profileShift, 0]);
return rack;
};
Gear.prototype._createRackTooth = function () {
var toothWidth = this.circularPitch / 2;
var toothDepth = this.addendum + this.clearance;
var sinPressureAngle = Math.sin(this.pressureAngle * Math.PI / 180);
var cosPressureAngle = Math.cos(this.pressureAngle * Math.PI / 180);
// if a positive backlash is defined then we widen the trapezoid accordingly.
// Each side of the tooth needs to widened by a fourth of the backlash (vertical to cutter faces).
var dx = this.backlash / 4 / cosPressureAngle;
//OpenJsCad.log("backlash: " + this.backlash);
//OpenJsCad.log("dx: " + dx);
var leftDepth = this.addendum + this.clearance;
var upperLeftCorner = [-leftDepth, toothWidth / 2 - dx + (this.addendum + this.clearance) * sinPressureAngle];
var upperRightCorner = [this.addendum, toothWidth / 2 - dx - this.addendum * sinPressureAngle];
var lowerRightCorner = [upperRightCorner[0], -upperRightCorner[1]];
var lowerLeftCorner = [upperLeftCorner[0], -upperLeftCorner[1]];
return CAG.fromPoints([upperLeftCorner, upperRightCorner, lowerRightCorner, lowerLeftCorner]);
};
Gear.prototype.pointsToString = function (points) {
var result = '[';
points.map(function (point) {
result += '[' + point.x + ',' + point.y + '],';
});
return result + ']';
};
return Gear;
})();
// GearSet class
var GearSet = (function () {
function GearSet(gear1, gear2, showOption) {
this.gear1 = gear1;
gear1.connectedGear = gear2;
this.gear2 = gear2;
gear2.connectedGear = gear1;
// in order for the two gears to mesh we need to turn the second one by 'half a tooth'
//this.gear1.setAngle(0);
this.gearRatio = this.gear1.toothCount / this.gear1.toothCount;
var relativePitchRadius1 = (this.gear1.gearType == GearType.Internal) ? -this.gear1.pitchRadius : this.gear1.pitchRadius;
var relativePitchRadius2 = (this.gear2.gearType == GearType.Internal) ? -this.gear2.pitchRadius : this.gear2.pitchRadius;
this.gearsDistance = relativePitchRadius1 + relativePitchRadius2;
this.showOption = showOption;
}
GearSet.prototype.createShape = function () {
var shape = new CAG();
if ((this.showOption & 1) > 0) {
// show gear 1
var gear1Shape = this.gear1.getZeroedShape();
//var gear1Shape = this.gear1.createCutoutDemo();
shape = shape.union(gear1Shape);
}
if ((this.showOption & 2) > 0) {
// show gear 2
var gear2Shape = this.gear2.getZeroedShape();
if (this.gear2.gearType == GearType.Regular) {
// we need an angle offset of half a tooth for the two gears to mesh
var angle = 180 + 180 / this.gear2.toothCount;
// apply gear rotation
gear2Shape = gear2Shape.rotateZ(angle);
} else if (this.gear2.gearType == GearType.Internal) {
// we need an angle offset of half a tooth for the two gears to mesh
var angle = 180; // + 180 / this.gear2.toothCount;
// apply gear rotation
gear2Shape = gear2Shape.rotateZ(angle);
} else if (this.gear2.gearType == GearType.Rack) {
gear2Shape = gear2Shape.rotateZ(180);
gear2Shape = gear2Shape.translate([0, this.gear2.circularPitch / 2]);
}
// move to correct center
gear2Shape = gear2Shape.translate([this.gearsDistance, 0]);
//var gear2Shape = this.gear2.createCutoutDemo();
shape = shape.union(gear2Shape);
}
return shape;
};
return GearSet;
})();
//// The following code is added by OpenJsCad:
var _csg_baselibraries = ['OpenJsCad/csg.js', 'OpenJsCad/openjscad.js'];
var _csg_libraries = [];
var _csg_baseurl = 'http://hessmer.org/gears/InvoluteSpurGearBuilder.html';
var _csg_openjscadurl = 'http://hessmer.org/gears/InvoluteSpurGearBuilder.html';
var _csg_makeAbsoluteURL = function (url, baseurl) {
if (!url.match(/^[a-z]+\:/i)) {
var basecomps = baseurl.split('/');
if (basecomps.length > 0) {
basecomps.splice(basecomps.length - 1, 1);
}
var urlcomps = url.split('/');
var comps = basecomps.concat(urlcomps);
var comps2 = [];
comps.map(function (c) {
if (c == '..') {
if (comps2.length > 0) {
comps2.splice(comps2.length - 1, 1);
}
} else {
comps2.push(c);
}
});
url = '';
for (var i = 0; i < comps2.length; i++) {
if (i > 0) url += '/';
url += comps2[i];
}
}
return url;
};
_csg_baselibraries = _csg_baselibraries.map(function (l) {
return _csg_makeAbsoluteURL(l, _csg_openjscadurl);
});
_csg_libraries = _csg_libraries.map(function (l) {
return _csg_makeAbsoluteURL(l, _csg_baseurl);
});
_csg_baselibraries.map(function (l) {
importScripts(l);
});
_csg_libraries.map(function (l) {
importScripts(l);
});
self.addEventListener('message', function (e) {
if (e.data && e.data.cmd == 'render') {
OpenJsCad.runMainInWorker({
'circularPitch': 8,
'pressureAngle': 20,
'clearance': 0.05,
'backlash': 0.05,
'profileShift': 0,
'wheel1ToothCount': 30,
'wheel1CenterHoleDiamater': 4,
'wheel2ToothCount': 8,
'wheel2CenterHoleDiamater': 4,
'showOption': '3',
'stepsPerToothAngle': 3,
'resolution': 30
});
}
}, false);