not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

1451 lines
51 KiB

// Generated by CoffeeScript 1.6.3
(function() {
"use strict";
var ArcSegment, Board, Chain, Gear, LineSegment, Point, Util,
__hasProp = {}.hasOwnProperty,
__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; };
Point = window.gearsketch.Point;
ArcSegment = window.gearsketch.ArcSegment;
LineSegment = window.gearsketch.LineSegment;
Util = window.gearsketch.Util;
window.gearsketch.model = {};
Gear = (function() {
function Gear(location, rotation, numberOfTeeth, id, momentum, group, level, connections, hue) {
this.location = location;
this.rotation = rotation;
this.numberOfTeeth = numberOfTeeth;
this.id = id;
this.momentum = momentum != null ? momentum : 0;
this.group = group != null ? group : 0;
this.level = level != null ? level : 0;
this.connections = connections != null ? connections : {};
if (this.id == null) {
this.id = Util.createUUID();
}
this.pitchRadius = Util.MODULE * (0.5 * this.numberOfTeeth);
this.innerRadius = Util.MODULE * (0.5 * this.numberOfTeeth - 1.25);
this.outerRadius = Util.MODULE * (0.5 * this.numberOfTeeth + 1);
this.hue = hue != null? hue : Math.floor(Math.random()*360);
}
Gear.prototype.getCircumference = function() {
return 2 * Math.PI * this.pitchRadius;
};
Gear.prototype.distanceToPoint = function(point) {
return Math.max(0, this.location.distance(point) - this.pitchRadius);
};
Gear.prototype.edgeDistance = function(gear) {
var axisDistance;
axisDistance = this.location.distance(gear.location);
return Math.abs(axisDistance - this.pitchRadius - gear.pitchRadius);
};
Gear.prototype.restore = function(gear) {
this.location = gear.location;
this.rotation = gear.rotation;
this.momentum = gear.momentum;
this.group = gear.group;
this.level = gear.level;
return this.connections = gear.connections;
};
Gear.prototype.clone = function() {
return new Gear(this.location.clone(), this.rotation, this.numberOfTeeth, this.id, this.momentum, this.group, this.level, Util.clone(this.connections), this.hue);
};
Gear.fromObject = function(obj) {
return new Gear(Point.fromObject(obj.location), obj.rotation, obj.numberOfTeeth, obj.id, obj.momentum, obj.group, obj.level, obj.connections, obj.hue);
};
return Gear;
})();
window.gearsketch.model.Gear = Gear;
Chain = (function() {
Chain.WIDTH = 8;
Chain.prototype.points = [];
Chain.prototype.segments = [];
function Chain(stroke, id, group, level, connections) {
this.id = id;
this.group = group != null ? group : 0;
this.level = level != null ? level : 0;
this.connections = connections != null ? connections : {};
if (this.id == null) {
this.id = Util.createUUID();
}
this.points = Util.clone(stroke);
this.rotation = 0;
}
Chain.prototype.getLength = function() {
return this.segments.reduce((function(total, segment) {
return total + segment.getLength();
}), 0);
};
Chain.prototype.getCircumference = function() {
return this.getLength();
};
Chain.prototype.getStartingPoint = function() {
if (this.direction === Util.Direction.CLOCKWISE) {
return this.rotation / (2 * Math.PI) * this.getLength();
} else {
return -this.rotation / (2 * Math.PI) * this.getLength();
}
};
Chain.prototype.findPointOnChain = function(distance) {
var distanceToGo, length, segment, segmentLength, _i, _len, _ref;
length = this.getLength();
distanceToGo = Util.mod(distance + this.getStartingPoint(), length);
_ref = this.segments;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
segment = _ref[_i];
segmentLength = segment.getLength();
if (distanceToGo < segmentLength) {
return segment.findPoint(distanceToGo);
} else {
distanceToGo -= segmentLength;
}
}
return null;
};
Chain.prototype.findPointsOnChain = function(numberOfPoints) {
var delta, p, _i, _results;
delta = this.getLength() / numberOfPoints;
_results = [];
for (p = _i = 0; 0 <= numberOfPoints ? _i < numberOfPoints : _i > numberOfPoints; p = 0 <= numberOfPoints ? ++_i : --_i) {
_results.push(this.findPointOnChain(p * delta));
}
return _results;
};
Chain.prototype.distanceToPoint = function(point) {
var segment;
return Math.min.apply(null, (function() {
var _i, _len, _ref, _results;
_ref = this.segments;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
segment = _ref[_i];
_results.push(segment.distanceToPoint(point));
}
return _results;
}).call(this));
};
Chain.prototype.distanceToSegment = function(s) {
var segment;
return Math.min.apply(null, (function() {
var _i, _len, _ref, _results;
_ref = this.segments;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
segment = _ref[_i];
_results.push(segment.distanceToSegment(s));
}
return _results;
}).call(this));
};
Chain.prototype.distanceToChain = function(chain) {
var segment;
return Math.min.apply(null, (function() {
var _i, _len, _ref, _results;
_ref = this.segments;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
segment = _ref[_i];
_results.push(chain.distanceToSegment(segment));
}
return _results;
}).call(this));
};
Chain.prototype.intersectsPath = function(path) {
var i, j, _i, _ref;
for (i = _i = 0, _ref = path.length - 1; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
j = i + 1;
if (this.distanceToSegment(new LineSegment(path[i], path[j])) === 0) {
return true;
}
}
return false;
};
Chain.prototype.crossesNonSupportingGears = function(board) {
var gear, id, _ref;
_ref = board.getGears();
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
gear = _ref[id];
if (!(__indexOf.call(this.supportingGearIds, id) >= 0) && !(id in this.ignoredGearIds)) {
if (this.distanceToPoint(gear.location) < gear.pitchRadius + Util.EPSILON) {
return true;
}
}
}
return false;
};
Chain.prototype.findPointOnSupportingGear = function(gearIndex, incoming) {
if (incoming) {
return this.points[Util.mod(2 * gearIndex - 1, this.points.length)];
} else {
return this.points[2 * gearIndex];
}
};
Chain.prototype.removeGear = function(gear, board) {
var acknowledgedGears, afterIndex, beforeGear, beforeIndex, g, gears, index, numberOfGears, path, replacementGears;
while ((index = this.supportingGearIds.indexOf(gear.id)) !== -1) {
gears = board.getGearsWithIds(this.supportingGearIds);
numberOfGears = gears.length;
beforeIndex = Util.mod(index - 1, numberOfGears);
beforeGear = gears[beforeIndex];
afterIndex = Util.mod(index + 1, numberOfGears);
acknowledgedGears = board.getAcknowledgedGears(this.ignoredGearIds);
path = [this.findPointOnSupportingGear(index, true), this.findPointOnSupportingGear(index, false), this.findPointOnSupportingGear(afterIndex, true)];
replacementGears = this.findSupportingGearsOnPath(acknowledgedGears, beforeGear, path, 0, false);
gears.splice.apply(gears, [index, 1].concat(replacementGears));
this.removeRepeatedGears(gears);
this.supportingGearIds = (function() {
var _i, _len, _results;
_results = [];
for (_i = 0, _len = gears.length; _i < _len; _i++) {
g = gears[_i];
_results.push(g.id);
}
return _results;
})();
}
return this.update(board);
};
Chain.prototype.findChainTangentSide = function(gear) {
if ((this.direction === Util.Direction.CLOCKWISE) === (gear.id in this.innerGearIds)) {
return Util.Side.LEFT;
} else {
return Util.Side.RIGHT;
}
};
Chain.prototype.findReverseChainTangentSide = function(gear) {
if (this.findChainTangentSide(gear) === Util.Side.LEFT) {
return Util.Side.RIGHT;
} else {
return Util.Side.LEFT;
}
};
Chain.prototype.findFirstSupportingGearOnPath = function(path, gears) {
var a, b, d, pathLength, stepSize, supportingGear;
stepSize = 10;
pathLength = Util.getLength(path);
supportingGear = null;
a = path[0];
d = 0;
while (d < pathLength && (supportingGear == null)) {
d += stepSize;
b = Util.findPointOnPath(path, d);
supportingGear = Util.findNearestIntersectingGear(gears, new LineSegment(a, b));
}
return [supportingGear, d];
};
Chain.prototype.findSupportingGearsOnPath = function(gears, firstSupportingGear, path, startDistance, isClosed) {
var a, b, d, lineSegment, nextSupportingGear, pathLength, stepSize, supportingGear, supportingGears, tangentPoint, tangentSide;
if (startDistance == null) {
startDistance = 0;
}
if (isClosed == null) {
isClosed = true;
}
stepSize = 10;
pathLength = Util.getLength(path, isClosed);
supportingGear = firstSupportingGear;
supportingGears = [];
a = firstSupportingGear.location;
d = startDistance;
while (d < pathLength) {
d += stepSize;
b = Util.findPointOnPath(path, d);
tangentSide = this.findReverseChainTangentSide(supportingGear);
tangentPoint = Util.findGearTangentPoints(b, supportingGear)[tangentSide];
if (tangentPoint != null) {
a = tangentPoint;
}
lineSegment = new LineSegment(a, b);
nextSupportingGear = Util.findNearestIntersectingGear(gears, lineSegment, Util.makeSet(supportingGear.id));
if (nextSupportingGear != null) {
supportingGear = nextSupportingGear;
supportingGears.push(supportingGear);
}
}
return supportingGears;
};
Chain.prototype.removeRepeatedGears = function(gearsList) {
var g1, g2, i, j, numberOfGears, numberOfNoops;
numberOfNoops = 0;
i = 0;
while (numberOfNoops < (numberOfGears = gearsList.length)) {
j = (i + 1) % numberOfGears;
g1 = gearsList[i];
g2 = gearsList[j];
if (g1 === g2) {
gearsList.splice(j, 1);
numberOfNoops = 0;
} else {
numberOfNoops++;
i = (i + 1) % numberOfGears;
}
}
return gearsList;
};
Chain.prototype.containsSuccessiveOverlappingGears = function(gearsList) {
var g1, g2, i, j, numberOfGears, _i;
numberOfGears = gearsList.length;
for (i = _i = 0; 0 <= numberOfGears ? _i < numberOfGears : _i > numberOfGears; i = 0 <= numberOfGears ? ++_i : --_i) {
j = (i + 1) % numberOfGears;
g1 = gearsList[i];
g2 = gearsList[j];
if (g1.location.distance(g2.location) < (g1.outerRadius + g2.outerRadius)) {
return true;
}
}
return false;
};
Chain.prototype.findSupportingGearIds = function(gears) {
var finalSegment, firstSupportingGear, gear, lastSupportingGear, nextSupportingGears, startDistance, supportingGears, tangentPoint, tangentSide, _i, _len, _ref, _ref1, _results;
_ref = this.findFirstSupportingGearOnPath(this.points, gears), firstSupportingGear = _ref[0], startDistance = _ref[1];
supportingGears = [firstSupportingGear];
nextSupportingGears = this.findSupportingGearsOnPath(gears, firstSupportingGear, this.points, startDistance);
supportingGears = supportingGears.concat(nextSupportingGears);
tangentSide = this.findChainTangentSide(firstSupportingGear);
tangentPoint = Util.findGearTangentPoints(this.points[0], firstSupportingGear)[tangentSide];
if (tangentPoint != null) {
finalSegment = [this.points[0], tangentPoint];
lastSupportingGear = supportingGears[supportingGears.length - 1];
nextSupportingGears = this.findSupportingGearsOnPath(gears, lastSupportingGear, finalSegment, 0, false);
supportingGears = supportingGears.concat(nextSupportingGears);
}
_ref1 = this.removeRepeatedGears(supportingGears);
_results = [];
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
gear = _ref1[_i];
_results.push(gear.id);
}
return _results;
};
Chain.prototype.findIgnoredGearIds = function(board) {
var acknowledgedLevels, currentDistance, currentLevel, d, distance, gear, gears, group, id, ignoredGearIds, level, levels, minDistances, _ref;
gears = board.getGears();
minDistances = {};
for (id in gears) {
if (!__hasProp.call(gears, id)) continue;
gear = gears[id];
group = gear.group;
level = gear.level;
d = Util.pointPathDistance(gear.location, this.points) - gear.pitchRadius;
if ((((_ref = minDistances[group]) != null ? _ref[level] : void 0) == null) || d < minDistances[group][level]) {
if (minDistances[group] == null) {
minDistances[group] = {};
}
minDistances[group][level] = d;
}
}
acknowledgedLevels = {};
for (group in minDistances) {
if (!__hasProp.call(minDistances, group)) continue;
levels = minDistances[group];
for (level in levels) {
if (!__hasProp.call(levels, level)) continue;
distance = levels[level];
currentLevel = acknowledgedLevels[group];
if (currentLevel == null) {
acknowledgedLevels[group] = parseInt(level, 10);
} else if (distance > 0) {
currentDistance = minDistances[group][currentLevel];
if (currentDistance < 0 || distance < currentDistance) {
acknowledgedLevels[group] = parseInt(level, 10);
}
}
}
}
ignoredGearIds = {};
for (id in gears) {
if (!__hasProp.call(gears, id)) continue;
gear = gears[id];
if (acknowledgedLevels[gear.group] !== gear.level) {
ignoredGearIds[id] = true;
}
}
return ignoredGearIds;
};
Chain.prototype.findIgnoredGearIdsInTightenedChain = function(board) {
var gear, gearId, group, groups, id, level, updatedIgnoredGearIds, _i, _len, _ref, _ref1;
groups = {};
_ref = this.supportingGearIds;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
gearId = _ref[_i];
gear = board.getGearWithId(gearId);
group = gear.group;
level = gear.level;
if (groups[group] == null) {
groups[group] = {};
}
groups[group][level] = true;
}
updatedIgnoredGearIds = {};
_ref1 = board.getGears();
for (id in _ref1) {
if (!__hasProp.call(_ref1, id)) continue;
gear = _ref1[id];
group = gear.group;
level = gear.level;
if ((groups[group] != null) && (groups[group][level] == null)) {
updatedIgnoredGearIds[id] = true;
}
}
return this.ignoredGearIds = updatedIgnoredGearIds;
};
Chain.prototype.toPolygon = function(segments) {
var polygon, segment, _i, _len;
if (segments == null) {
segments = this.segments;
}
polygon = [];
for (_i = 0, _len = segments.length; _i < _len; _i++) {
segment = segments[_i];
if (segment instanceof LineSegment) {
polygon.push(segment.start);
} else {
polygon.push(segment.findPoint(0));
polygon.push(segment.findPoint(0.5 * segment.getLength()));
}
}
return polygon;
};
Chain.prototype.update = function(board, gears) {
var acknowledgedGears, arcEnd, arcSegment, arcStart, chainPolygon, direction, g1, g2, g3, gear, gearId, i, id, intersection, j, k, lineSegment, lineSegment1, lineSegment2, middleSegment, numberOfGears, numberOfSegments, p0, p1, p2, path, replacementGears, s1, s2, tangentLine, tangentPointG1, tangentPointG3, tangentSideG1, tangentSideG3, updatedAcknowledgedGears, updatedIgnoredGearIds, updatedInnerGearIds, updatedPoints, updatedSegments, _i, _j, _k, _l, _ref, _ref1, _ref2;
if (gears == null) {
gears = board.getGearsWithIds(this.supportingGearIds);
}
if (gears.length < 2) {
return false;
}
if (this.containsSuccessiveOverlappingGears(gears)) {
return false;
}
updatedIgnoredGearIds = this.findIgnoredGearIdsInTightenedChain(board);
acknowledgedGears = board.getAcknowledgedGears(updatedIgnoredGearIds);
i = 0;
while (i < (numberOfGears = gears.length)) {
j = (i + 1) % numberOfGears;
k = (i + 2) % numberOfGears;
g1 = gears[i];
g2 = gears[j];
g3 = gears[k];
lineSegment1 = Util.findTangentLine(g1, g2, this.innerGearIds, this.direction);
lineSegment2 = Util.findTangentLine(g2, g3, this.innerGearIds, this.direction);
intersection = lineSegment1.findIntersection(lineSegment2);
if (intersection != null) {
tangentSideG1 = this.findReverseChainTangentSide(g1);
tangentPointG1 = Util.findGearTangentPoints(intersection, g1)[tangentSideG1];
tangentSideG3 = this.findChainTangentSide(g3);
tangentPointG3 = Util.findGearTangentPoints(intersection, g3)[tangentSideG3];
path = [tangentPointG1, intersection, tangentPointG3];
replacementGears = this.findSupportingGearsOnPath(acknowledgedGears, g1, path, 0, false);
if (__indexOf.call(replacementGears, g2) >= 0) {
return false;
}
gears.splice.apply(gears, [j, 1].concat(replacementGears));
this.removeRepeatedGears(gears);
return this.update(board, gears);
}
gear = Util.findNearestIntersectingGear(acknowledgedGears, lineSegment1, Util.makeSet(g1.id, g2.id));
if (gear != null) {
gears.splice(j, 0, gear);
if (this.containsSuccessiveOverlappingGears(gears)) {
return false;
}
}
i++;
}
updatedPoints = [];
for (i = _i = 0; 0 <= numberOfGears ? _i < numberOfGears : _i > numberOfGears; i = 0 <= numberOfGears ? ++_i : --_i) {
j = (i + 1) % numberOfGears;
g1 = gears[i];
g2 = gears[j];
tangentLine = Util.findTangentLine(g1, g2, this.innerGearIds, this.direction);
updatedPoints.push(tangentLine.start, tangentLine.end);
}
updatedSegments = [];
for (i = _j = 0; 0 <= numberOfGears ? _j < numberOfGears : _j > numberOfGears; i = 0 <= numberOfGears ? ++_j : --_j) {
p0 = updatedPoints[2 * i];
p1 = updatedPoints[2 * i + 1];
p2 = updatedPoints[2 * ((i + 1) % numberOfGears)];
gear = gears[(i + 1) % numberOfGears];
lineSegment = new LineSegment(p0, p1);
arcStart = Math.atan2(p1.y - gear.location.y, p1.x - gear.location.x);
arcEnd = Math.atan2(p2.y - gear.location.y, p2.x - gear.location.x);
direction = (this.direction === Util.Direction.CLOCKWISE) === (gear.id in this.innerGearIds) ? Util.Direction.CLOCKWISE : Util.Direction.COUNTER_CLOCKWISE;
arcSegment = new ArcSegment(gear.location, gear.pitchRadius, arcStart, arcEnd, direction);
updatedSegments.push(lineSegment, arcSegment);
}
numberOfSegments = updatedSegments.length;
for (i = _k = 0, _ref = numberOfSegments - 2; 0 <= _ref ? _k < _ref : _k > _ref; i = 0 <= _ref ? ++_k : --_k) {
for (j = _l = _ref1 = i + 2; _ref1 <= numberOfSegments ? _l < numberOfSegments : _l > numberOfSegments; j = _ref1 <= numberOfSegments ? ++_l : --_l) {
if (i !== 0 || j !== numberOfSegments - 1) {
s1 = updatedSegments[i];
s2 = updatedSegments[j];
if (s1.distanceToSegment(s2) < Chain.WIDTH) {
if ((i + 2) === j) {
middleSegment = updatedSegments[i + 1];
if ((middleSegment instanceof ArcSegment) && (middleSegment.getLength() < 2 * Chain.WIDTH)) {
continue;
}
}
if (((j + 2) % numberOfSegments) === i) {
middleSegment = updatedSegments[(j + 1) % numberOfSegments];
if ((middleSegment instanceof ArcSegment) && (middleSegment.getLength() < 2 * Chain.WIDTH)) {
continue;
}
}
return false;
}
}
}
}
updatedIgnoredGearIds = this.findIgnoredGearIdsInTightenedChain(board);
updatedAcknowledgedGears = board.getAcknowledgedGears(updatedIgnoredGearIds);
chainPolygon = this.toPolygon(updatedSegments);
updatedInnerGearIds = {};
for (id in updatedAcknowledgedGears) {
if (!__hasProp.call(updatedAcknowledgedGears, id)) continue;
gear = updatedAcknowledgedGears[id];
if (Util.isPointInsidePolygon(gear.location, chainPolygon)) {
updatedInnerGearIds[id] = true;
}
}
_ref2 = this.innerGearIds;
for (gearId in _ref2) {
if (!__hasProp.call(_ref2, gearId)) continue;
if (!(gearId in updatedInnerGearIds) && (__indexOf.call(this.supportingGearIds, gearId) >= 0)) {
return false;
}
}
this.points = updatedPoints;
this.segments = updatedSegments;
this.ignoredGearIds = updatedIgnoredGearIds;
this.innerGearIds = updatedInnerGearIds;
this.supportingGearIds = (function() {
var _len, _m, _results;
_results = [];
for (_m = 0, _len = gears.length; _m < _len; _m++) {
gear = gears[_m];
_results.push(gear.id);
}
return _results;
})();
return true;
};
Chain.prototype.tighten = function(board) {
var acknowledgedGears, gear;
this.ignoredGearIds = this.findIgnoredGearIds(board);
acknowledgedGears = board.getAcknowledgedGears(this.ignoredGearIds);
this.innerGearIds = Util.makeSetFromList((function() {
var _i, _len, _ref, _results;
_ref = Util.findGearsInsidePolygon(this.points, acknowledgedGears);
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
gear = _ref[_i];
_results.push(gear.id);
}
return _results;
}).call(this));
if (Object.keys(this.innerGearIds).length < 2) {
return false;
}
this.direction = Util.findDirection(this.points);
this.supportingGearIds = this.findSupportingGearIds(acknowledgedGears);
return this.update(board);
};
Chain.prototype.clone = function() {
var copy;
copy = new Chain(this.points, this.id, this.group, this.level, Util.clone(this.connections));
copy.segments = Util.clone(this.segments);
copy.ignoredGearIds = Util.clone(this.ignoredGearIds);
copy.innerGearIds = Util.clone(this.innerGearIds);
copy.direction = this.direction;
copy.supportingGearIds = Util.clone(this.supportingGearIds);
return copy;
};
Chain.fromObject = function(obj) {
var chain, createSegment, p, points, segment;
createSegment = function(obj) {
if (obj.center != null) {
return ArcSegment.fromObject(obj);
} else {
return LineSegment.fromObject(obj);
}
};
points = (function() {
var _i, _len, _ref, _results;
_ref = obj.points;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
p = _ref[_i];
_results.push(new Point(p.x, p.y));
}
return _results;
})();
chain = new Chain(points, obj.id, obj.group, obj.level, obj.connections);
chain.segments = (function() {
var _i, _len, _ref, _results;
_ref = obj.segments;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
segment = _ref[_i];
_results.push(createSegment(segment));
}
return _results;
})();
chain.ignoredGearIds = obj.ignoredGearIds;
chain.innerGearIds = obj.innerGearIds;
chain.direction = obj.direction;
chain.supportingGearIds = obj.supportingGearIds;
return chain;
};
return Chain;
})();
window.gearsketch.model.Chain = Chain;
Board = (function() {
var AXIS_RADIUS, ConnectionType, EPSILON, MIN_STACKED_GEARS_TEETH_DIFFERENCE, MODULE, SNAPPING_DISTANCE;
MODULE = Util.MODULE;
AXIS_RADIUS = Util.AXIS_RADIUS;
MIN_STACKED_GEARS_TEETH_DIFFERENCE = Util.MIN_STACKED_GEARS_TEETH_DIFFERENCE;
SNAPPING_DISTANCE = Util.SNAPPING_DISTANCE;
EPSILON = Util.EPSILON;
ConnectionType = {
ANY: "any",
MESHING: "meshing",
AXIS: "axis",
CHAIN_INSIDE: "chain_inside",
CHAIN_OUTSIDE: "chain_outside"
};
function Board(gears, chains) {
this.gears = gears != null ? gears : {};
this.chains = chains != null ? chains : {};
}
Board.prototype.restore = function(board) {
var gear, id, _ref;
_ref = this.gears;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
gear = _ref[id];
gear.restore(board.gears[id]);
}
return this.chains = board.chains;
};
Board.prototype.restoreAfterDemo = function(board) {
this.gears = board.gears;
return this.chains = board.chains;
};
Board.prototype.clear = function() {
this.gears = {};
return this.chains = {};
};
Board.prototype.getNextGroup = function() {
var gear, id, nextGroup, _ref;
nextGroup = 0;
_ref = this.gears;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
gear = _ref[id];
nextGroup = Math.max(nextGroup, gear.group + 1);
}
return nextGroup;
};
Board.prototype.getGears = function() {
return this.gears;
};
Board.prototype.getGearList = function() {
var gear, id, _ref, _results;
_ref = this.gears;
_results = [];
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
gear = _ref[id];
_results.push(gear);
}
return _results;
};
Board.prototype.getAcknowledgedGears = function(ignoredGearIds) {
var acknowledgedGears, gear, id, _ref;
acknowledgedGears = {};
_ref = this.gears;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
gear = _ref[id];
if (!(id in ignoredGearIds)) {
acknowledgedGears[id] = gear;
}
}
return acknowledgedGears;
};
Board.prototype.getLevelScore = function(gear) {
return 1000 * gear.group + gear.level;
};
Board.prototype.getGearsSortedByGroupAndLevel = function(gears) {
var _this = this;
if (gears == null) {
gears = this.getGearList();
}
return gears.sort(function(g1, g2) {
return _this.getLevelScore(g1) - _this.getLevelScore(g2);
});
};
Board.prototype.removeConnection = function(turningObject1, turningObject2) {
delete turningObject1.connections[turningObject2.id];
return delete turningObject2.connections[turningObject1.id];
};
Board.prototype.removeAllConnections = function(turningObject) {
var neighbor, neighborId, _ref;
_ref = turningObject.connections;
for (neighborId in _ref) {
if (!__hasProp.call(_ref, neighborId)) continue;
neighbor = this.getTurningObjects()[neighborId];
this.removeConnection(turningObject, neighbor);
}
return this.updateGroupsAndLevels();
};
Board.prototype.findNearestAxis = function(gear) {
var candidate, distance, id, nearestAxis, shortestDistance, _ref;
nearestAxis = null;
shortestDistance = Number.MAX_VALUE;
_ref = this.gears;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
candidate = _ref[id];
if (candidate !== gear) {
distance = gear.location.distance(candidate.location);
if (!nearestAxis || distance < (shortestDistance - EPSILON) || (distance < (shortestDistance + EPSILON) && candidate.numberOfTeeth < nearestAxis.numberOfTeeth)) {
nearestAxis = candidate;
shortestDistance = distance;
}
}
}
return nearestAxis;
};
Board.prototype.updateGroupsAndLevelsFrom = function(turningObjectId, group, level, updatedGroups, updatedLevels) {
var connectionType, connections, gear, neighbor, neighborId, sameLevelConnectionTypes, turningObject, _results;
turningObject = this.getTurningObjects()[turningObjectId];
updatedGroups[turningObjectId] = group;
updatedLevels[turningObjectId] = level;
connections = turningObject.connections;
sameLevelConnectionTypes = [ConnectionType.MESHING, ConnectionType.CHAIN_INSIDE, ConnectionType.CHAIN_OUTSIDE];
_results = [];
for (neighborId in connections) {
if (!__hasProp.call(connections, neighborId)) continue;
connectionType = connections[neighborId];
if (!(neighborId in updatedGroups)) {
if (__indexOf.call(sameLevelConnectionTypes, connectionType) >= 0) {
_results.push(this.updateGroupsAndLevelsFrom(neighborId, group, level, updatedGroups, updatedLevels));
} else {
gear = this.gears[turningObjectId];
neighbor = this.gears[neighborId];
if (gear.numberOfTeeth > neighbor.numberOfTeeth) {
_results.push(this.updateGroupsAndLevelsFrom(neighborId, group, level + 1, updatedGroups, updatedLevels));
} else {
_results.push(this.updateGroupsAndLevelsFrom(neighborId, group, level - 1, updatedGroups, updatedLevels));
}
}
} else {
_results.push(void 0);
}
}
return _results;
};
Board.prototype.updateGroupsAndLevels = function() {
var group, id, turningObject, updatedGroups, updatedLevels, _ref, _ref1;
updatedGroups = {};
updatedLevels = {};
group = 0;
_ref = this.gears;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
if (!(id in updatedGroups)) {
this.updateGroupsAndLevelsFrom(id, group, 0, updatedGroups, updatedLevels);
group++;
}
}
_ref1 = this.getTurningObjects();
for (id in _ref1) {
if (!__hasProp.call(_ref1, id)) continue;
turningObject = _ref1[id];
turningObject.group = updatedGroups[id];
turningObject.level = updatedLevels[id];
}
return null;
};
Board.prototype.addConnection = function(turningObject1, turningObject2, connectionType) {
turningObject1.connections[turningObject2.id] = connectionType;
turningObject2.connections[turningObject1.id] = connectionType;
return this.updateGroupsAndLevels();
};
Board.prototype.findMeshingNeighbors = function(gear) {
var candidate, candidateId, meshingNeighbors, _ref;
meshingNeighbors = [];
_ref = this.gears;
for (candidateId in _ref) {
if (!__hasProp.call(_ref, candidateId)) continue;
candidate = _ref[candidateId];
if (candidate !== gear && gear.edgeDistance(candidate) < EPSILON) {
if ((candidate.group !== gear.group) || (candidate.level === gear.level)) {
meshingNeighbors.push(candidate);
}
}
}
return meshingNeighbors;
};
Board.prototype.findRelativeAlignment = function(gear1, gear2) {
var angle1, angle2, p1, p2, phase1, phase2, phaseSum, r1, r2, shift1, shift2, toothAngle1, toothAngle2;
p1 = gear1.location;
r1 = gear1.rotation;
p2 = gear2.location;
r2 = gear2.rotation;
angle1 = Math.atan2(p2.y - p1.y, p2.x - p1.x);
angle2 = angle1 + Math.PI;
shift1 = Util.mod(angle1 - r1, 2 * Math.PI);
shift2 = Util.mod(angle2 - r2, 2 * Math.PI);
toothAngle1 = (2 * Math.PI) / gear1.numberOfTeeth;
toothAngle2 = (2 * Math.PI) / gear2.numberOfTeeth;
phase1 = (shift1 % toothAngle1) / toothAngle1;
phase2 = (shift2 % toothAngle2) / toothAngle2;
phaseSum = (phase1 + phase2) % 1;
return (phaseSum - 0.25) * toothAngle1;
};
Board.prototype.alignGearTeeth = function(rotatingGear, meshingGear) {
return rotatingGear.rotation += this.findRelativeAlignment(rotatingGear, meshingGear);
};
Board.prototype.areMeshingGearsAligned = function(gear1, gear2) {
return Math.abs(this.findRelativeAlignment(gear1, gear2)) < EPSILON;
};
Board.prototype.rotateTurningObjectsFrom = function(turningObject, angle, rotatedTurningObjectIds) {
var connectionType, neighbor, neighborId, ratio, _ref, _results;
if (!(turningObject.id in rotatedTurningObjectIds)) {
turningObject.rotation = Util.mod(turningObject.rotation + angle, 2 * Math.PI);
rotatedTurningObjectIds[turningObject.id] = true;
}
_ref = turningObject.connections;
_results = [];
for (neighborId in _ref) {
if (!__hasProp.call(_ref, neighborId)) continue;
connectionType = _ref[neighborId];
neighbor = this.getTurningObjects()[neighborId];
if (!(neighborId in rotatedTurningObjectIds)) {
ratio = this.calculateRatio(turningObject, neighbor, connectionType);
_results.push(this.rotateTurningObjectsFrom(neighbor, angle * ratio, rotatedTurningObjectIds));
} else {
_results.push(void 0);
}
}
return _results;
};
Board.prototype.alignMeshingGears = function(gear) {
var angle, neighbor, neighbors, r, rotatedGearIds, _i, _len, _results;
rotatedGearIds = {};
rotatedGearIds[gear.id] = true;
neighbors = this.findMeshingNeighbors(gear);
_results = [];
for (_i = 0, _len = neighbors.length; _i < _len; _i++) {
neighbor = neighbors[_i];
this.addConnection(gear, neighbor, ConnectionType.MESHING);
r = neighbor.rotation;
this.alignGearTeeth(neighbor, gear);
angle = neighbor.rotation - r;
rotatedGearIds[neighbor.id] = true;
_results.push(this.rotateTurningObjectsFrom(neighbor, angle, rotatedGearIds));
}
return _results;
};
Board.prototype.connectToAxis = function(upperGear, lowerGear) {
this.addConnection(upperGear, lowerGear, ConnectionType.AXIS);
upperGear.location = lowerGear.location.clone();
upperGear.rotation = lowerGear.rotation;
return this.alignMeshingGears(upperGear);
};
Board.prototype.findNearestNeighbor = function(gear, gearIdsToIgnore) {
var edgeDistance, nearestNeighbor, neighbor, neighborId, shortestEdgeDistance, _ref;
if (gearIdsToIgnore == null) {
gearIdsToIgnore = {};
}
nearestNeighbor = null;
shortestEdgeDistance = Number.MAX_VALUE;
_ref = this.gears;
for (neighborId in _ref) {
if (!__hasProp.call(_ref, neighborId)) continue;
neighbor = _ref[neighborId];
if (neighbor !== gear && !(neighborId in gearIdsToIgnore)) {
edgeDistance = gear.edgeDistance(neighbor);
if (edgeDistance < shortestEdgeDistance) {
nearestNeighbor = neighbor;
shortestEdgeDistance = edgeDistance;
}
}
}
return nearestNeighbor;
};
Board.prototype.connectToOneMeshingGear = function(gear, meshingGear) {
var angle, delta;
delta = gear.location.minus(meshingGear.location);
angle = Math.atan2(delta.y, delta.x);
gear.location = meshingGear.location.plus(Point.polar(angle, gear.pitchRadius + meshingGear.pitchRadius));
this.alignGearTeeth(gear, meshingGear);
return this.addConnection(gear, meshingGear, ConnectionType.MESHING);
};
Board.prototype.connectToTwoMeshingGears = function(gear, meshingGear1, meshingGear2) {
var a, d, h, p0, p1, p2, p3_1, p3_2, p3x1, p3x2, p3y1, p3y2, r0, r1;
p0 = meshingGear1.location;
p1 = meshingGear2.location;
r0 = meshingGear1.pitchRadius + gear.pitchRadius;
r1 = meshingGear2.pitchRadius + gear.pitchRadius;
d = p0.distance(p1);
if (r0 + r1 < d || p0.distance(p1) < EPSILON) {
if (gear.edgeDistance(meshingGear1) < gear.edgeDistance(meshingGear2)) {
this.connectToOneMeshingGear(gear, meshingGear1);
return;
} else {
this.connectToOneMeshingGear(gear, meshingGear2);
return;
}
}
a = (r0 * r0 - r1 * r1 + d * d) / (2 * d);
h = Math.sqrt(r0 * r0 - a * a);
p2 = p0.plus(p1.minus(p0).times(a / d));
p3x1 = p2.x + h * (p1.y - p0.y) / d;
p3y1 = p2.y - h * (p1.x - p0.x) / d;
p3x2 = p2.x - h * (p1.y - p0.y) / d;
p3y2 = p2.y + h * (p1.x - p0.x) / d;
p3_1 = new Point(p3x1, p3y1);
p3_2 = new Point(p3x2, p3y2);
if (gear.location.distance(p3_1) < gear.location.distance(p3_2)) {
gear.location = p3_1;
} else {
gear.location = p3_2;
}
return this.alignMeshingGears(gear);
};
Board.prototype.doChainsCrossNonSupportingGears = function() {
var chain, id, _ref;
_ref = this.chains;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
chain = _ref[id];
if (chain.crossesNonSupportingGears(this)) {
return true;
}
}
return false;
};
Board.prototype.doChainsCrossEachOther = function(c1, c2) {
if ((c1.group !== c2.group) || (c1.level === c2.level)) {
if (c1.distanceToChain(c2) < Chain.WIDTH) {
return true;
}
}
return false;
};
Board.prototype.doesChainCrossAnyOtherChain = function(chain) {
var chain2, id2, _ref;
_ref = this.chains;
for (id2 in _ref) {
if (!__hasProp.call(_ref, id2)) continue;
chain2 = _ref[id2];
if (chain !== chain2) {
if (this.doChainsCrossEachOther(chain, chain2)) {
return true;
}
}
}
return false;
};
Board.prototype.doAnyChainsCrossEachOther = function() {
var c1, c2, chain, chainList, i, id, j, numberOfChains, _i, _j, _ref, _ref1;
chainList = (function() {
var _ref, _results;
_ref = this.chains;
_results = [];
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
chain = _ref[id];
_results.push(chain);
}
return _results;
}).call(this);
numberOfChains = chainList.length;
if (numberOfChains < 2) {
return false;
}
for (i = _i = 0, _ref = numberOfChains - 1; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
for (j = _j = _ref1 = i + 1; _ref1 <= numberOfChains ? _j < numberOfChains : _j > numberOfChains; j = _ref1 <= numberOfChains ? ++_j : --_j) {
c1 = chainList[i];
c2 = chainList[j];
if (this.doChainsCrossEachOther(c1, c2)) {
return true;
}
}
}
return false;
};
Board.prototype.areAllMeshingGearsAligned = function() {
var g1, g2, gears, i, j, numberOfGears, _i, _j, _ref, _ref1;
gears = this.getGearList();
numberOfGears = gears.length;
if (numberOfGears < 2) {
return true;
}
for (i = _i = 0, _ref = numberOfGears - 1; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
for (j = _j = _ref1 = i + 1; _ref1 <= numberOfGears ? _j < numberOfGears : _j > numberOfGears; j = _ref1 <= numberOfGears ? ++_j : --_j) {
g1 = gears[i];
g2 = gears[j];
if (g1.connections[g2.id] === ConnectionType.MESHING) {
if (!this.areMeshingGearsAligned(g1, g2)) {
return false;
}
}
}
}
return true;
};
Board.prototype.calculateRatio = function(turningObject1, turningObject2, connectionType) {
if (connectionType === ConnectionType.AXIS) {
return 1;
} else if ((connectionType === ConnectionType.MESHING) || (connectionType === ConnectionType.CHAIN_OUTSIDE)) {
return -turningObject1.getCircumference() / turningObject2.getCircumference();
} else {
return turningObject1.getCircumference() / turningObject2.getCircumference();
}
};
Board.prototype.calculatePathRatio = function(path) {
var connectionType, i, pathLength, ratio, turningObject1, turningObject2, _i, _ref;
ratio = 1;
pathLength = path.length;
for (i = _i = 0, _ref = pathLength - 1; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
turningObject1 = path[i];
turningObject2 = path[i + 1];
connectionType = turningObject1.connections[turningObject2.id];
ratio *= this.calculateRatio(turningObject1, turningObject2, connectionType);
}
return ratio;
};
Board.prototype.areConnectionRatiosConsistent = function() {
var path, pathName, paths, ratio, ratios, _i, _len;
ratios = {};
paths = Util.findAllSimplePathsBetweenNeighbors(this.getTurningObjects());
for (_i = 0, _len = paths.length; _i < _len; _i++) {
path = paths[_i];
pathName = "" + path[0].id + "-" + path[path.length - 1].id;
ratio = this.calculatePathRatio(path);
if (ratios[pathName] == null) {
ratios[pathName] = ratio;
} else {
if (Math.abs(ratios[pathName] - ratio) > EPSILON) {
return false;
}
}
}
return true;
};
Board.prototype.isBoardValid = function() {
var axisDistance, combinedOuterRadius, gear1, gear2, group1, group2, id1, id2, level1, level2, maxOuterRadius, _ref, _ref1;
_ref = this.gears;
for (id1 in _ref) {
if (!__hasProp.call(_ref, id1)) continue;
gear1 = _ref[id1];
group1 = gear1.group;
level1 = gear1.level;
_ref1 = this.gears;
for (id2 in _ref1) {
if (!__hasProp.call(_ref1, id2)) continue;
gear2 = _ref1[id2];
if (gear1 !== gear2) {
group2 = gear2.group;
level2 = gear2.level;
axisDistance = gear1.location.distance(gear2.location);
maxOuterRadius = Math.max(gear1.outerRadius, gear2.outerRadius);
combinedOuterRadius = gear1.outerRadius + gear2.outerRadius;
if (axisDistance < EPSILON) {
if ((group1 !== group2) || (level1 === level2)) {
return false;
}
} else if (group1 === group2 && level1 === level2 && (gear1.connections[gear2.id] == null)) {
if (axisDistance < combinedOuterRadius) {
return false;
}
} else if (axisDistance < maxOuterRadius + AXIS_RADIUS) {
return false;
}
}
}
}
return !this.doChainsCrossNonSupportingGears() && !this.doAnyChainsCrossEachOther() && this.areAllMeshingGearsAligned() && this.areConnectionRatiosConsistent();
};
Board.prototype.placeGear = function(gear, location) {
var chain, id, nearestAxis, neighbor1, neighbor2, oldBoard, _ref;
oldBoard = this.clone();
this.removeAllConnections(gear);
gear.location = location.clone();
nearestAxis = this.findNearestAxis(gear);
if (nearestAxis && gear.location.distance(nearestAxis.location) < SNAPPING_DISTANCE && nearestAxis.numberOfTeeth - gear.numberOfTeeth > MIN_STACKED_GEARS_TEETH_DIFFERENCE) {
this.connectToAxis(gear, nearestAxis);
} else {
neighbor1 = this.findNearestNeighbor(gear);
if (neighbor1 && gear.edgeDistance(neighbor1) < SNAPPING_DISTANCE) {
neighbor2 = this.findNearestNeighbor(gear, Util.makeSet(neighbor1.id));
if (neighbor2 && gear.edgeDistance(neighbor2) < SNAPPING_DISTANCE) {
this.connectToTwoMeshingGears(gear, neighbor1, neighbor2);
} else {
this.connectToOneMeshingGear(gear, neighbor1);
}
}
}
_ref = this.chains;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
chain = _ref[id];
if (chain.update(this)) {
this.updateChainConnections(chain);
} else {
this.restore(oldBoard);
return false;
}
}
if (this.isBoardValid()) {
return true;
} else {
this.restore(oldBoard);
return false;
}
};
Board.prototype.addGearToChains = function(gear) {
var chain, id, _ref, _results;
_ref = this.chains;
_results = [];
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
chain = _ref[id];
if (Util.isPointInsidePolygon(gear.location, chain.toPolygon())) {
_results.push(chain.innerGearIds[gear.id] = true);
} else {
_results.push(void 0);
}
}
return _results;
};
Board.prototype.removeGearFromChains = function(gear) {
var chain, id, _ref, _results;
_ref = this.chains;
_results = [];
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
chain = _ref[id];
if (!chain.removeGear(gear, this) || this.doesChainCrossAnyOtherChain(chain)) {
_results.push(this.removeChain(chain));
} else {
_results.push(this.updateChainConnections(chain));
}
}
return _results;
};
Board.prototype.addGear = function(gear) {
var oldBoard;
oldBoard = this.clone();
gear.group = this.getNextGroup();
this.gears[gear.id] = gear;
this.addGearToChains(gear);
if (!this.placeGear(gear, gear.location)) {
this.removeGear(gear);
this.restore(oldBoard);
return false;
} else {
return true;
}
};
Board.prototype.removeGear = function(gear) {
this.removeAllConnections(gear);
delete this.gears[gear.id];
return this.removeGearFromChains(gear);
};
Board.prototype.getGearAt = function(location, candidates) {
var candidate, distance, gear, id;
if (candidates == null) {
candidates = this.gears;
}
gear = null;
for (id in candidates) {
if (!__hasProp.call(candidates, id)) continue;
candidate = candidates[id];
distance = location.distance(candidate.location);
if (distance < candidate.outerRadius) {
if (!gear || candidate.numberOfTeeth < gear.numberOfTeeth) {
gear = candidate;
}
}
}
return gear;
};
Board.prototype.isTopLevelGear = function(gear) {
var connectionType, id, _ref;
_ref = gear.connections;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
connectionType = _ref[id];
if (connectionType === ConnectionType.AXIS && this.gears[id].level > gear.level) {
return false;
}
}
return true;
};
Board.prototype.getTopLevelGears = function() {
var gear, id, topLevelGears, _ref;
topLevelGears = {};
_ref = this.gears;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
gear = _ref[id];
if (this.isTopLevelGear(gear)) {
topLevelGears[id] = gear;
}
}
return topLevelGears;
};
Board.prototype.getTopLevelGearAt = function(location) {
return this.getGearAt(location, this.getTopLevelGears());
};
Board.prototype.getGearWithId = function(id) {
return this.gears[id];
};
Board.prototype.getGearsWithIds = function(ids) {
var id, _i, _len, _results;
_results = [];
for (_i = 0, _len = ids.length; _i < _len; _i++) {
id = ids[_i];
_results.push(this.gears[id]);
}
return _results;
};
Board.prototype.rotateAllTurningObjects = function(delta) {
var angle, gear, id, _ref, _results;
_ref = this.gears;
_results = [];
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
gear = _ref[id];
if (gear.momentum) {
angle = gear.momentum * (delta / 1000);
_results.push(this.rotateTurningObjectsFrom(gear, angle, {}));
} else {
_results.push(void 0);
}
}
return _results;
};
Board.prototype.addChainConnections = function(chain) {
var gearId, _i, _len, _ref, _results;
_ref = chain.supportingGearIds;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
gearId = _ref[_i];
if (gearId in chain.innerGearIds) {
_results.push(this.addConnection(chain, this.getGearWithId(gearId), ConnectionType.CHAIN_INSIDE));
} else {
_results.push(this.addConnection(chain, this.getGearWithId(gearId), ConnectionType.CHAIN_OUTSIDE));
}
}
return _results;
};
Board.prototype.updateChainConnections = function(chain) {
this.removeAllConnections(chain);
return this.addChainConnections(chain);
};
Board.prototype.addChain = function(chain) {
var oldBoard;
oldBoard = this.clone();
this.chains[chain.id] = chain;
if (chain.tighten(this)) {
this.chains[chain.id] = chain;
this.addChainConnections(chain);
} else {
this.restore(oldBoard);
return false;
}
if (this.isBoardValid()) {
return true;
} else {
this.restore(oldBoard);
return false;
}
};
Board.prototype.removeChain = function(chain) {
this.removeAllConnections(chain);
return delete this.chains[chain.id];
};
Board.prototype.getChains = function() {
return this.chains;
};
Board.prototype.getChainsInGroupOnLevel = function(group, level) {
var chain, id, _ref, _results;
_ref = this.chains;
_results = [];
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
chain = _ref[id];
if ((chain.group === group) && (chain.level === level)) {
_results.push(chain);
}
}
return _results;
};
Board.prototype.getTurningObjects = function() {
var chain, gear, id, turningObjects, _ref, _ref1;
turningObjects = {};
_ref = this.gears;
for (id in _ref) {
if (!__hasProp.call(_ref, id)) continue;
gear = _ref[id];
turningObjects[id] = gear;
}
_ref1 = this.chains;
for (id in _ref1) {
if (!__hasProp.call(_ref1, id)) continue;
chain = _ref1[id];
turningObjects[id] = chain;
}
return turningObjects;
};
Board.prototype.clone = function() {
return {
gears: Util.clone(this.gears),
chains: Util.clone(this.chains)
};
};
Board.fromObject = function(obj) {
var board, chain, gear, id, _ref, _ref1;
board = new Board();
_ref = obj.gears;
for (id in _ref) {
gear = _ref[id];
board.gears[id] = Gear.fromObject(gear);
}
_ref1 = obj.chains;
for (id in _ref1) {
chain = _ref1[id];
board.chains[id] = Chain.fromObject(chain);
}
return board;
};
return Board;
})();
window.gearsketch.model.Board = Board;
}).call(this);
/*
//@ sourceMappingURL=gearsketch_model.map
*/