// Generated by CoffeeScript 1.6.3
|
|
(function() {
|
|
"use strict";
|
|
var ArcSegment, LineSegment, Point, Util,
|
|
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
|
|
__hasProp = {}.hasOwnProperty,
|
|
__slice = [].slice;
|
|
|
|
window.gearsketch = {};
|
|
|
|
Point = (function() {
|
|
function Point(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
Point.prototype.plus = function(p) {
|
|
return new Point(this.x + p.x, this.y + p.y);
|
|
};
|
|
|
|
Point.prototype.minus = function(p) {
|
|
return new Point(this.x - p.x, this.y - p.y);
|
|
};
|
|
|
|
Point.prototype.times = function(n) {
|
|
return new Point(n * this.x, n * this.y);
|
|
};
|
|
|
|
Point.prototype.distance = function(p) {
|
|
return Math.sqrt(Math.pow(this.x - p.x, 2) + Math.pow(this.y - p.y, 2));
|
|
};
|
|
|
|
Point.prototype.cross = function(p) {
|
|
return this.x * p.y - this.y * p.x;
|
|
};
|
|
|
|
Point.prototype.clone = function() {
|
|
return new Point(this.x, this.y);
|
|
};
|
|
|
|
Point.polar = function(theta, r) {
|
|
return new Point(r * Math.cos(theta), r * Math.sin(theta));
|
|
};
|
|
|
|
Point.fromObject = function(obj) {
|
|
return new Point(obj.x, obj.y);
|
|
};
|
|
|
|
return Point;
|
|
|
|
})();
|
|
|
|
window.gearsketch.Point = Point;
|
|
|
|
ArcSegment = (function() {
|
|
function ArcSegment(center, radius, startAngle, endAngle, direction) {
|
|
this.center = center;
|
|
this.radius = radius;
|
|
this.startAngle = startAngle;
|
|
this.endAngle = endAngle;
|
|
this.direction = direction;
|
|
this.start = this.pointOnCircle(startAngle);
|
|
this.end = this.pointOnCircle(endAngle);
|
|
}
|
|
|
|
ArcSegment.prototype.getLength = function() {
|
|
var angle;
|
|
angle = this.direction === Util.Direction.CLOCKWISE ? Util.mod(this.endAngle - this.startAngle, 2 * Math.PI) : Util.mod(this.startAngle - this.endAngle, 2 * Math.PI);
|
|
return angle * this.radius;
|
|
};
|
|
|
|
ArcSegment.prototype.findPoint = function(distanceToGo) {
|
|
var angle, angleToGo;
|
|
angleToGo = distanceToGo / this.radius;
|
|
angle = this.startAngle + (this.direction === Util.Direction.CLOCKWISE ? angleToGo : -angleToGo);
|
|
return this.center.plus(Point.polar(angle, this.radius));
|
|
};
|
|
|
|
ArcSegment.prototype.pointOnCircle = function(angle) {
|
|
return this.center.plus(Point.polar(angle, this.radius));
|
|
};
|
|
|
|
ArcSegment.prototype.containsAngle = function(angle) {
|
|
if (this.direction === Util.Direction.CLOCKWISE) {
|
|
return Util.mod(this.endAngle - this.startAngle, 2 * Math.PI) > Util.mod(angle - this.startAngle, 2 * Math.PI);
|
|
} else {
|
|
return Util.mod(this.startAngle - this.endAngle, 2 * Math.PI) > Util.mod(this.startAngle - angle, 2 * Math.PI);
|
|
}
|
|
};
|
|
|
|
ArcSegment.prototype.distanceToPoint = function(point) {
|
|
var angle;
|
|
angle = Math.atan2(point.y - this.center.y, point.x - this.center.x);
|
|
if (this.containsAngle(angle)) {
|
|
return Math.abs(point.distance(this.center) - this.radius);
|
|
} else {
|
|
return Math.min(point.distance(this.start), point.distance(this.end));
|
|
}
|
|
};
|
|
|
|
ArcSegment.prototype.intersectsLineSegment = function(lineSegment) {
|
|
var d, discriminant, dr, dx, dy, i1, i1x, i1y, i2, i2x, i2y, p1, p2;
|
|
p1 = lineSegment.start.minus(this.center);
|
|
p2 = lineSegment.end.minus(this.center);
|
|
dx = p2.x - p1.x;
|
|
dy = p2.y - p1.y;
|
|
dr = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
|
|
if (dr === 0) {
|
|
return false;
|
|
}
|
|
d = p1.x * p2.y - p2.x * p1.y;
|
|
discriminant = Math.pow(this.radius, 2) * Math.pow(dr, 2) - Math.pow(d, 2);
|
|
if (discriminant < 0) {
|
|
return false;
|
|
} else {
|
|
i1x = (d * dy + Util.sign(dy) * dx * Math.sqrt(discriminant)) / Math.pow(dr, 2);
|
|
i1y = (-d * dx + Math.abs(dy) * Math.sqrt(discriminant)) / Math.pow(dr, 2);
|
|
i1 = new Point(i1x, i1y).plus(this.center);
|
|
if (lineSegment.distanceToPoint(i1) < Util.EPSILON && this.distanceToPoint(i1) < Util.EPSILON) {
|
|
return true;
|
|
}
|
|
i2x = (d * dy - Util.sign(dy) * dx * Math.sqrt(discriminant)) / Math.pow(dr, 2);
|
|
i2y = (-d * dx - Math.abs(dy) * Math.sqrt(discriminant)) / Math.pow(dr, 2);
|
|
i2 = new Point(i2x, i2y).plus(this.center);
|
|
if (lineSegment.distanceToPoint(i2) < Util.EPSILON && this.distanceToPoint(i2) < Util.EPSILON) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
ArcSegment.prototype.distanceToSegment = function(segment) {
|
|
var angle1, angle2, centerDistance, pointNearestToCenter;
|
|
if (segment instanceof ArcSegment) {
|
|
if (this.center.distance(segment.center) > Util.EPSILON) {
|
|
angle1 = Math.atan2(segment.center.y - this.center.y, segment.center.x - this.center.x);
|
|
angle2 = Util.mod(angle1 + Math.PI, 2 * Math.PI);
|
|
if (this.containsAngle(angle1) && segment.containsAngle(angle2)) {
|
|
centerDistance = this.center.distance(segment.center);
|
|
return Math.max(0, centerDistance - this.radius - segment.radius);
|
|
}
|
|
}
|
|
return Math.min(this.distanceToPoint(segment.start), this.distanceToPoint(segment.end), segment.distanceToPoint(this.start), segment.distanceToPoint(this.end));
|
|
} else {
|
|
if (this.intersectsLineSegment(segment)) {
|
|
return 0;
|
|
} else {
|
|
pointNearestToCenter = segment.findNearestPoint(this.center);
|
|
return Math.min(this.distanceToPoint(pointNearestToCenter), this.distanceToPoint(segment.start), this.distanceToPoint(segment.end), segment.distanceToPoint(this.start), segment.distanceToPoint(this.end));
|
|
}
|
|
}
|
|
};
|
|
|
|
ArcSegment.prototype.clone = function() {
|
|
return new ArcSegment(this.center.clone(), this.radius, this.startAngle, this.endAngle, this.direction);
|
|
};
|
|
|
|
ArcSegment.fromObject = function(obj) {
|
|
return new ArcSegment(Point.fromObject(obj.center), obj.radius, obj.startAngle, obj.endAngle, obj.direction);
|
|
};
|
|
|
|
return ArcSegment;
|
|
|
|
})();
|
|
|
|
window.gearsketch.ArcSegment = ArcSegment;
|
|
|
|
LineSegment = (function() {
|
|
function LineSegment(start, end) {
|
|
this.start = start;
|
|
this.end = end;
|
|
}
|
|
|
|
LineSegment.prototype.getLength = function() {
|
|
return this.start.distance(this.end);
|
|
};
|
|
|
|
LineSegment.prototype.findPoint = function(distanceToGo) {
|
|
var fraction;
|
|
fraction = distanceToGo / this.start.distance(this.end);
|
|
return this.start.plus(this.end.minus(this.start).times(fraction));
|
|
};
|
|
|
|
LineSegment.prototype.findNearestPoint = function(p) {
|
|
var segmentLength, t;
|
|
segmentLength = this.getLength();
|
|
if (segmentLength === 0) {
|
|
return this.start;
|
|
} else {
|
|
t = ((p.x - this.start.x) * (this.end.x - this.start.x) + (p.y - this.start.y) * (this.end.y - this.start.y)) / Math.pow(segmentLength, 2);
|
|
if (t < 0) {
|
|
return this.start;
|
|
} else if (t > 1) {
|
|
return this.end;
|
|
} else {
|
|
return this.start.plus(this.end.minus(this.start).times(t));
|
|
}
|
|
}
|
|
};
|
|
|
|
LineSegment.prototype.distanceToPoint = function(point) {
|
|
return point.distance(this.findNearestPoint(point));
|
|
};
|
|
|
|
LineSegment.prototype.findIntersection = function(lineSegment) {
|
|
var crossRS, p, q, r, s, t, u;
|
|
p = this.start;
|
|
r = this.end.minus(p);
|
|
q = lineSegment.start;
|
|
s = lineSegment.end.minus(q);
|
|
crossRS = r.cross(s);
|
|
t = q.minus(p).cross(s) / crossRS;
|
|
u = q.minus(p).cross(r) / crossRS;
|
|
if (Math.abs(crossRS) > Util.EPSILON && 0 <= t && t <= 1 && 0 <= u && u <= 1) {
|
|
return p.plus(r.times(t));
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
LineSegment.prototype.distanceToSegment = function(segment) {
|
|
if (segment instanceof LineSegment) {
|
|
if (this.findIntersection(segment)) {
|
|
return 0;
|
|
} else {
|
|
return Math.min(this.distanceToPoint(segment.start), this.distanceToPoint(segment.end), segment.distanceToPoint(this.start), segment.distanceToPoint(this.end));
|
|
}
|
|
} else {
|
|
return segment.distanceToSegment(this);
|
|
}
|
|
};
|
|
|
|
LineSegment.prototype.clone = function() {
|
|
return new LineSegment(this.start.clone(), this.end.clone());
|
|
};
|
|
|
|
LineSegment.fromObject = function(obj) {
|
|
return new LineSegment(Point.fromObject(obj.start), Point.fromObject(obj.end));
|
|
};
|
|
|
|
return LineSegment;
|
|
|
|
})();
|
|
|
|
window.gearsketch.LineSegment = LineSegment;
|
|
|
|
Util = (function() {
|
|
function Util() {}
|
|
|
|
Point = window.gearsketch.Point;
|
|
|
|
Util.MODULE = 6;
|
|
|
|
Util.AXIS_RADIUS = 1.5 * Util.MODULE;
|
|
|
|
Util.MIN_STACKED_GEARS_TEETH_DIFFERENCE = 4;
|
|
|
|
Util.SNAPPING_DISTANCE = 2 * Util.MODULE;
|
|
|
|
Util.EPSILON = 0.000001;
|
|
|
|
Util.Direction = {
|
|
CLOCKWISE: "clockwise",
|
|
COUNTER_CLOCKWISE: "counterclockwise"
|
|
};
|
|
|
|
Util.Side = {
|
|
LEFT: "left",
|
|
RIGHT: "right"
|
|
};
|
|
|
|
Util.clone = function(obj) {
|
|
var copy, i, key, knownClasses, _i, _ref, _ref1;
|
|
if ((obj == null) || (typeof obj !== "object")) {
|
|
return obj;
|
|
}
|
|
knownClasses = ["Point", "Gear", "ArcSegment", "LineSegment", "Chain"];
|
|
if (_ref = obj.constructor.name, __indexOf.call(knownClasses, _ref) >= 0) {
|
|
return obj.clone();
|
|
}
|
|
if (obj instanceof Array) {
|
|
copy = [];
|
|
for (i = _i = 0, _ref1 = obj.length; 0 <= _ref1 ? _i < _ref1 : _i > _ref1; i = 0 <= _ref1 ? ++_i : --_i) {
|
|
copy[i] = this.clone(obj[i]);
|
|
}
|
|
return copy;
|
|
}
|
|
if (obj instanceof Object) {
|
|
copy = {};
|
|
for (key in obj) {
|
|
if (!__hasProp.call(obj, key)) continue;
|
|
copy[key] = this.clone(obj[key]);
|
|
}
|
|
return copy;
|
|
}
|
|
throw new Error("Unable to clone object. Its type is not supported.");
|
|
};
|
|
|
|
Util.createUUID = function() {
|
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
var r, v;
|
|
r = Math.random() * 16 | 0;
|
|
v = c === "x" ? r : r & 0x3 | 0x8;
|
|
return v.toString(16);
|
|
});
|
|
};
|
|
|
|
Util.mod = function(a, b) {
|
|
return (a % b + b) % b;
|
|
};
|
|
|
|
Util.sign = function(x) {
|
|
if (x < 0) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
Util.addAll = function(set, elements) {
|
|
var element, _i, _len;
|
|
for (_i = 0, _len = elements.length; _i < _len; _i++) {
|
|
element = elements[_i];
|
|
set[element] = true;
|
|
}
|
|
return set;
|
|
};
|
|
|
|
Util.makeSetFromList = function(elements) {
|
|
return this.addAll({}, elements);
|
|
};
|
|
|
|
Util.makeSet = function() {
|
|
var elements;
|
|
elements = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
|
|
return this.makeSetFromList(elements);
|
|
};
|
|
|
|
Util.findPointOnPath = function(path, distance) {
|
|
var distanceToGo, i, j, numberOfPoints, segment, segmentLength;
|
|
distanceToGo = distance;
|
|
i = 0;
|
|
numberOfPoints = path.length;
|
|
while (distanceToGo > 0) {
|
|
j = (i + 1) % numberOfPoints;
|
|
segment = new LineSegment(path[i], path[j]);
|
|
segmentLength = segment.getLength();
|
|
if (distanceToGo <= segmentLength) {
|
|
return segment.findPoint(distanceToGo);
|
|
} else {
|
|
i = j;
|
|
distanceToGo -= segmentLength;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
Util.getLength = function(path, isPathClosed) {
|
|
var finalIndex, i, j, length, numberOfPoints, _i;
|
|
if (isPathClosed == null) {
|
|
isPathClosed = true;
|
|
}
|
|
length = 0;
|
|
numberOfPoints = path.length;
|
|
finalIndex = numberOfPoints - (isPathClosed ? 0 : 1);
|
|
for (i = _i = 0; 0 <= finalIndex ? _i < finalIndex : _i > finalIndex; i = 0 <= finalIndex ? ++_i : --_i) {
|
|
j = (i + 1) % numberOfPoints;
|
|
length += path[i].distance(path[j]);
|
|
}
|
|
return length;
|
|
};
|
|
|
|
Util.isPointInsidePolygon = function(point, polygon) {
|
|
var i, isPointInPolygon, j, numberOfVertices, pix, piy, pjx, pjy, x, y, _i;
|
|
isPointInPolygon = false;
|
|
x = point.x;
|
|
y = point.y;
|
|
numberOfVertices = polygon.length;
|
|
j = numberOfVertices - 1;
|
|
for (i = _i = 0; 0 <= numberOfVertices ? _i < numberOfVertices : _i > numberOfVertices; i = 0 <= numberOfVertices ? ++_i : --_i) {
|
|
pix = polygon[i].x;
|
|
piy = polygon[i].y;
|
|
pjx = polygon[j].x;
|
|
pjy = polygon[j].y;
|
|
if (((piy > y) !== (pjy > y)) && (x < ((pjx - pix) * (y - piy) / (pjy - piy) + pix))) {
|
|
isPointInPolygon = !isPointInPolygon;
|
|
}
|
|
j = i;
|
|
}
|
|
return isPointInPolygon;
|
|
};
|
|
|
|
Util.isGearInsidePolygon = function(gear, polygon) {
|
|
var edgePointAtAngle, edgePoints, i,
|
|
_this = this;
|
|
edgePointAtAngle = function(angle) {
|
|
return gear.location.plus(Point.polar(angle, gear.innerRadius));
|
|
};
|
|
edgePoints = (function() {
|
|
var _i, _results;
|
|
_results = [];
|
|
for (i = _i = 0; _i < 8; i = ++_i) {
|
|
_results.push(edgePointAtAngle(0.25 * Math.PI * i));
|
|
}
|
|
return _results;
|
|
})();
|
|
return edgePoints.every(function(p) {
|
|
return _this.isPointInsidePolygon(p, polygon);
|
|
});
|
|
};
|
|
|
|
Util.findGearsInsidePolygon = function(polygon, gears) {
|
|
var gear, id, _results;
|
|
_results = [];
|
|
for (id in gears) {
|
|
if (!__hasProp.call(gears, id)) continue;
|
|
gear = gears[id];
|
|
if (this.isGearInsidePolygon(gear, polygon)) {
|
|
_results.push(gear);
|
|
}
|
|
}
|
|
return _results;
|
|
};
|
|
|
|
Util.doesGearIntersectLineSegment = function(gear, segment) {
|
|
return segment.distanceToPoint(gear.location) < (gear.pitchRadius + Util.EPSILON);
|
|
};
|
|
|
|
Util.findGearsIntersectingSegment = function(gears, segment) {
|
|
var gear, id, _results;
|
|
_results = [];
|
|
for (id in gears) {
|
|
if (!__hasProp.call(gears, id)) continue;
|
|
gear = gears[id];
|
|
if (this.doesGearIntersectLineSegment(gear, segment)) {
|
|
_results.push(gear);
|
|
}
|
|
}
|
|
return _results;
|
|
};
|
|
|
|
Util.pointPathDistance = function(point, path, isPathClosed) {
|
|
var d, distance, finalIndex, i, j, numberOfPoints, segment, _i;
|
|
if (isPathClosed == null) {
|
|
isPathClosed = true;
|
|
}
|
|
distance = Number.MAX_VALUE;
|
|
numberOfPoints = path.length;
|
|
finalIndex = numberOfPoints - (isPathClosed ? 0 : 1);
|
|
for (i = _i = 0; 0 <= finalIndex ? _i < finalIndex : _i > finalIndex; i = 0 <= finalIndex ? ++_i : --_i) {
|
|
j = (i + 1) % numberOfPoints;
|
|
segment = new LineSegment(path[i], path[j]);
|
|
d = Math.max(0, segment.distanceToPoint(point));
|
|
distance = Math.min(distance, d);
|
|
}
|
|
return distance;
|
|
};
|
|
|
|
Util.findNearestIntersectingGear = function(gears, lineSegment, ignoredGearIds) {
|
|
var intersectingGear, intersectingGears, _i, _len,
|
|
_this = this;
|
|
if (ignoredGearIds == null) {
|
|
ignoredGearIds = {};
|
|
}
|
|
intersectingGears = this.findGearsIntersectingSegment(gears, lineSegment);
|
|
intersectingGears.sort(function(g1, g2) {
|
|
return g1.distanceToPoint(lineSegment.start) - g2.distanceToPoint(lineSegment.start);
|
|
});
|
|
for (_i = 0, _len = intersectingGears.length; _i < _len; _i++) {
|
|
intersectingGear = intersectingGears[_i];
|
|
if (!(intersectingGear.id in ignoredGearIds)) {
|
|
return intersectingGear;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
Util.findDirection = function(polygon) {
|
|
var doubleArea, i, j, numberOfPoints, _i;
|
|
numberOfPoints = polygon.length;
|
|
doubleArea = 0;
|
|
for (i = _i = 0; 0 <= numberOfPoints ? _i < numberOfPoints : _i > numberOfPoints; i = 0 <= numberOfPoints ? ++_i : --_i) {
|
|
j = (i + 1) % numberOfPoints;
|
|
doubleArea += polygon[i].x * polygon[j].y;
|
|
doubleArea -= polygon[i].y * polygon[j].x;
|
|
}
|
|
if (doubleArea > 0) {
|
|
return this.Direction.CLOCKWISE;
|
|
} else {
|
|
return this.Direction.COUNTER_CLOCKWISE;
|
|
}
|
|
};
|
|
|
|
Util.findTangentPoints = function(p, c, r) {
|
|
var alpha, beta, d, l, tangentPoints;
|
|
tangentPoints = {};
|
|
d = p.distance(c);
|
|
if (Math.abs(d - r) < Util.EPSILON) {
|
|
tangentPoints[this.Side.RIGHT] = p.clone();
|
|
tangentPoints[this.Side.LEFT] = p.clone();
|
|
} else {
|
|
l = Math.sqrt(d * d - r * r);
|
|
alpha = Math.atan2(c.y - p.y, c.x - p.x);
|
|
beta = Math.asin(r / d);
|
|
tangentPoints[this.Side.RIGHT] = p.plus(Point.polar(alpha + beta, l));
|
|
tangentPoints[this.Side.LEFT] = p.plus(Point.polar(alpha - beta, l));
|
|
}
|
|
return tangentPoints;
|
|
};
|
|
|
|
Util.findGearTangentPoints = function(p, gear) {
|
|
return this.findTangentPoints(p, gear.location, gear.pitchRadius);
|
|
};
|
|
|
|
Util.findExternalTangents = function(centers, radii) {
|
|
var angle, largest, o1, o2, offset1, offset2, r1, r2, r3, ratio, tangentLine1, tangentLine2, tangentLines, tangentPoints, tpl, tpr;
|
|
largest = radii[0] >= radii[1] ? 0 : 1;
|
|
o1 = centers[largest];
|
|
o2 = centers[1 - largest];
|
|
r1 = radii[largest];
|
|
r2 = radii[1 - largest];
|
|
r3 = r1 - r2;
|
|
if (r3 === 0) {
|
|
tangentPoints = {};
|
|
tangentPoints[this.Side.LEFT] = o1;
|
|
tangentPoints[this.Side.RIGHT] = o1;
|
|
angle = Math.atan2(o2.y - o1.y, o2.x - o1.x);
|
|
offset1 = Point.polar(angle + 0.5 * Math.PI, r1);
|
|
offset2 = Point.polar(angle - 0.5 * Math.PI, r1);
|
|
} else {
|
|
tangentPoints = this.findTangentPoints(o2, o1, r3);
|
|
ratio = r2 / r3;
|
|
tpl = tangentPoints[this.Side.LEFT];
|
|
tpr = tangentPoints[this.Side.RIGHT];
|
|
offset1 = tpl.minus(o1).times(ratio);
|
|
offset2 = tpr.minus(o1).times(ratio);
|
|
}
|
|
tangentLine1 = [tangentPoints[this.Side.LEFT].plus(offset1), o2.plus(offset1)];
|
|
tangentLine2 = [tangentPoints[this.Side.RIGHT].plus(offset2), o2.plus(offset2)];
|
|
tangentLines = {};
|
|
if (o1 === centers[0]) {
|
|
tangentLines[this.Side.RIGHT] = new LineSegment(tangentLine1[0], tangentLine1[1]);
|
|
tangentLines[this.Side.LEFT] = new LineSegment(tangentLine2[0], tangentLine2[1]);
|
|
} else {
|
|
tangentLines[this.Side.RIGHT] = new LineSegment(tangentLine2[1], tangentLine2[0]);
|
|
tangentLines[this.Side.LEFT] = new LineSegment(tangentLine1[1], tangentLine1[0]);
|
|
}
|
|
return tangentLines;
|
|
};
|
|
|
|
Util.findInternalTangents = function(centers, radii) {
|
|
var largest, o1, o2, offset1, offset2, r1, r2, r3, ratio, tangentLine1, tangentLine2, tangentLines, tangentPoints, tpl, tpr;
|
|
largest = radii[0] >= radii[1] ? 0 : 1;
|
|
o1 = centers[largest];
|
|
o2 = centers[1 - largest];
|
|
r1 = radii[largest];
|
|
r2 = radii[1 - largest];
|
|
r3 = r1 + r2;
|
|
tangentPoints = this.findTangentPoints(o2, o1, r3);
|
|
ratio = r2 / r3;
|
|
tpl = tangentPoints[this.Side.LEFT];
|
|
tpr = tangentPoints[this.Side.RIGHT];
|
|
offset1 = o1.minus(tpl).times(ratio);
|
|
offset2 = o1.minus(tpr).times(ratio);
|
|
tangentLine1 = [tpl.plus(offset1), o2.plus(offset1)];
|
|
tangentLine2 = [tpr.plus(offset2), o2.plus(offset2)];
|
|
tangentLines = {};
|
|
if (o1 === centers[0]) {
|
|
tangentLines[this.Side.RIGHT] = new LineSegment(tangentLine1[0], tangentLine1[1]);
|
|
tangentLines[this.Side.LEFT] = new LineSegment(tangentLine2[0], tangentLine2[1]);
|
|
} else {
|
|
tangentLines[this.Side.RIGHT] = new LineSegment(tangentLine1[1], tangentLine1[0]);
|
|
tangentLines[this.Side.LEFT] = new LineSegment(tangentLine2[1], tangentLine2[0]);
|
|
}
|
|
return tangentLines;
|
|
};
|
|
|
|
Util.findExternalTangentsOfGears = function(gear1, gear2) {
|
|
return this.findExternalTangents([gear1.location, gear2.location], [gear1.pitchRadius, gear2.pitchRadius]);
|
|
};
|
|
|
|
Util.findInternalTangentsOfGears = function(gear1, gear2) {
|
|
return this.findInternalTangents([gear1.location, gear2.location], [gear1.pitchRadius, gear2.pitchRadius]);
|
|
};
|
|
|
|
Util.findTangentLine = function(gear1, gear2, innerGearIds, direction) {
|
|
var gear1isInnerGear, side;
|
|
gear1isInnerGear = gear1.id in innerGearIds;
|
|
if (gear1isInnerGear === (direction === this.Direction.CLOCKWISE)) {
|
|
side = this.Side.LEFT;
|
|
} else {
|
|
side = this.Side.RIGHT;
|
|
}
|
|
if (gear1isInnerGear === (gear2.id in innerGearIds)) {
|
|
return this.findExternalTangentsOfGears(gear1, gear2)[side];
|
|
} else {
|
|
return this.findInternalTangentsOfGears(gear1, gear2)[side];
|
|
}
|
|
};
|
|
|
|
Util.findAllSimplePathsForNodes = function(turningObjects, goalNode, nodesVisited) {
|
|
var currentNode, neighbor, neighborId, paths, updatedNodesVisited, _ref;
|
|
paths = [];
|
|
currentNode = nodesVisited[nodesVisited.length - 1];
|
|
_ref = currentNode.connections;
|
|
for (neighborId in _ref) {
|
|
if (!__hasProp.call(_ref, neighborId)) continue;
|
|
neighbor = turningObjects[neighborId];
|
|
if (__indexOf.call(nodesVisited, neighbor) < 0) {
|
|
updatedNodesVisited = nodesVisited.slice(0);
|
|
updatedNodesVisited.push(neighbor);
|
|
if (neighbor === goalNode) {
|
|
paths.push(updatedNodesVisited);
|
|
} else {
|
|
paths = paths.concat(this.findAllSimplePathsForNodes(turningObjects, goalNode, updatedNodesVisited));
|
|
}
|
|
}
|
|
}
|
|
return paths;
|
|
};
|
|
|
|
Util.findAllSimplePathsBetweenNeighbors = function(turningObjects) {
|
|
var i, id, j, nodes, paths, turningObject, _i, _j, _k, _ref, _ref1, _ref2, _ref3;
|
|
paths = [];
|
|
nodes = (function() {
|
|
var _results;
|
|
_results = [];
|
|
for (id in turningObjects) {
|
|
if (!__hasProp.call(turningObjects, id)) continue;
|
|
turningObject = turningObjects[id];
|
|
_results.push(turningObject);
|
|
}
|
|
return _results;
|
|
})();
|
|
if (nodes.length < 2) {
|
|
return [];
|
|
}
|
|
for (i = _i = 0, _ref = nodes.length - 1; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
|
|
for (j = _j = _ref1 = i + 1, _ref2 = nodes.length; _ref1 <= _ref2 ? _j < _ref2 : _j > _ref2; j = _ref1 <= _ref2 ? ++_j : --_j) {
|
|
if (nodes[i].connections[nodes[j].id] != null) {
|
|
paths = paths.concat(this.findAllSimplePathsForNodes(turningObjects, nodes[j], [nodes[i]]));
|
|
}
|
|
}
|
|
}
|
|
for (i = _k = 0, _ref3 = paths.length; 0 <= _ref3 ? _k < _ref3 : _k > _ref3; i = 0 <= _ref3 ? ++_k : --_k) {
|
|
paths.push(paths[i].slice(0).reverse());
|
|
}
|
|
return paths;
|
|
};
|
|
|
|
Util.sendGetRequest = function(url) {
|
|
var request;
|
|
request = new XMLHttpRequest();
|
|
request.open("GET", url, false);
|
|
request.send(null);
|
|
return request.responseText;
|
|
};
|
|
|
|
Util.sendPostRequest = function(data, url, callback) {
|
|
var request;
|
|
request = new XMLHttpRequest();
|
|
request.open("POST", url, true);
|
|
request.setRequestHeader("Content-type", "application/json; charset=UTF-8");
|
|
request.onload = callback;
|
|
return request.send(data);
|
|
};
|
|
|
|
return Util;
|
|
|
|
})();
|
|
|
|
window.gearsketch.Util = Util;
|
|
|
|
(function() {
|
|
var lastTime, vendor, vendors, _i, _len;
|
|
lastTime = 0;
|
|
vendors = ["ms", "moz", "webkit", "o"];
|
|
for (_i = 0, _len = vendors.length; _i < _len; _i++) {
|
|
vendor = vendors[_i];
|
|
if (!(!window.requestAnimationFrame)) {
|
|
continue;
|
|
}
|
|
window.requestAnimationFrame = window[vendor + "RequestAnimationFrame"];
|
|
window.cancelAnimationFrame = window[vendor + "CancelAnimationFrame"] || window[vendor + "CancelRequestAnimationFrame"];
|
|
}
|
|
if (!window.requestAnimationFrame) {
|
|
window.requestAnimationFrame = function(callback) {
|
|
var currTime, id, timeToCall;
|
|
currTime = new Date().getTime();
|
|
timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
|
id = window.setTimeout((function() {
|
|
return callback(currTime + timeToCall);
|
|
}), timeToCall);
|
|
lastTime = currTime + timeToCall;
|
|
return id;
|
|
};
|
|
}
|
|
if (!window.cancelAnimationFrame) {
|
|
return window.cancelAnimationFrame = function(id) {
|
|
return clearTimeout(id);
|
|
};
|
|
}
|
|
})();
|
|
|
|
(function() {
|
|
if ((Function.prototype.name == null) && (Object.defineProperty != null)) {
|
|
return Object.defineProperty(Function.prototype, "name", {
|
|
get: function() {
|
|
var funcNameRegex, results;
|
|
funcNameRegex = /function\s([^(]{1,})\(/;
|
|
results = funcNameRegex.exec(this.toString());
|
|
if ((results != null) && results.length > 1) {
|
|
return results[1].trim();
|
|
} else {
|
|
return "";
|
|
}
|
|
},
|
|
set: function(value) {}
|
|
});
|
|
}
|
|
})();
|
|
|
|
}).call(this);
|
|
|
|
/*
|
|
//@ sourceMappingURL=gearsketch_util.map
|
|
*/
|