UNPKG

makerjs

Version:

Maker.js, a Microsoft Garage project, is a JavaScript library for creating and sharing modular line drawings for CNC and laser cutters.

1,286 lines (1,281 loc) 433 kB
/* __________________________________________________________________________________________________________________________________________ __________________________________________________________________________________________________________________________________________ ________/\\\\____________/\\\\_____/\\\\\\\\\_____/\\\________/\\\__/\\\\\\\\\\\\\\\__/\\\\\\\\\\\________________________________________ _______\/\\\\\\________/\\\\\\___/\\\\/////\\\\__\/\\\_____/\\\//__\/\\\///////////__\/\\\///////\\\_______________/\\\___________________ _______\/\\\//\\\____/\\\//\\\__/\\\/____\///\\\_\/\\\__/\\\//_____\/\\\_____________\/\\\_____\/\\\______________\///____________________ _______\/\\\\///\\\/\\\/_\/\\\_\/\\\_______\/\\\_\/\\\\\\//\\\_____\/\\\\\\\\\\\_____\/\\\\\\\\\\\/________________/\\\__/\\\\\\\\\\______ _______\/\\\__\///\\\/___\/\\\_\/\\\\\\\\\\\\\\\_\/\\\//_\//\\\____\/\\\///////______\/\\\//////\\\_______________\/\\\_\/\\\//////_______ _______\/\\\____\///_____\/\\\_\/\\\/////////\\\_\/\\\____\//\\\___\/\\\_____________\/\\\____\//\\\______________\/\\\_\/\\\\\\\\\\______ _______\/\\\_____________\/\\\_\/\\\_______\/\\\_\/\\\_____\//\\\__\/\\\_____________\/\\\_____\//\\\_________/\\_\/\\\_\////////\\\______ _______\/\\\_____________\/\\\_\/\\\_______\/\\\_\/\\\______\//\\\_\/\\\\\\\\\\\\\\\_\/\\\______\//\\\__/\\\_\//\\\\\\___/\\\\\\\\\\______ _______\///______________\///__\///________\///__\///________\///__\///////////////__\///________\///__\///___\//////___\//////////_______ __________________________________________________________________________________________________________________________________________ __________________________________________________________________________________________________________________________________________ Maker.js https://github.com/Microsoft/maker.js Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. */ /** * Root module for Maker.js. * * Example: get a reference to Maker.js * ``` * var makerjs = require('makerjs'); * ``` * */ var MakerJs; (function (MakerJs) { /** * Version info */ MakerJs.version = 'debug'; /** * Enumeration of environment types. */ MakerJs.environmentTypes = { BrowserUI: 'browser', NodeJs: 'node', WebWorker: 'worker', Unknown: 'unknown' }; /** * @private */ function tryEval(name) { try { var value = eval(name); return value; } catch (e) { } return; } /** * @private */ function detectEnvironment() { if (tryEval('WorkerGlobalScope') && tryEval('self')) { return MakerJs.environmentTypes.WebWorker; } if (tryEval('window') && tryEval('document')) { return MakerJs.environmentTypes.BrowserUI; } //put node last since packagers usually add shims for it if (tryEval('global') && tryEval('process')) { return MakerJs.environmentTypes.NodeJs; } return MakerJs.environmentTypes.Unknown; } /** * Current execution environment type, should be one of environmentTypes. */ MakerJs.environment = detectEnvironment(); //units /** * String-based enumeration of unit types: imperial, metric or otherwise. * A model may specify the unit system it is using, if any. When importing a model, it may have different units. * Unit conversion function is makerjs.units.conversionScale(). * Important: If you add to this, you must also add a corresponding conversion ratio in the unit.ts file! */ MakerJs.unitType = { Centimeter: 'cm', Foot: 'foot', Inch: 'inch', Meter: 'm', Millimeter: 'mm' }; /** * @private */ function split(s, char) { var p = s.indexOf(char); if (p < 0) { return [s]; } else if (p > 0) { return [s.substr(0, p), s.substr(p + 1)]; } else { return ['', s]; } } /** * Split a decimal into its whole and fractional parts as strings. * * Example: get whole and fractional parts of 42.056 * ``` * makerjs.splitDecimal(42.056); //returns ["42", "056"] * ``` * * @param n The number to split. * @returns Array of 2 strings when n contains a decimal point, or an array of one string when n is an integer. */ function splitDecimal(n) { var s = n.toString(); if (s.indexOf('e') > 0) { //max digits is 20 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed s = n.toFixed(20).match(/.*[^(0+$)]/)[0]; //regex trims trailing zeros } return split(s, '.'); } MakerJs.splitDecimal = splitDecimal; /** * Numeric rounding * * Example: round to 3 decimal places * ``` * makerjs.round(3.14159, .001); //returns 3.142 * ``` * * @param n The number to round off. * @param accuracy Optional exemplar of number of decimal places. * @returns Rounded number. */ function round(n, accuracy) { if (accuracy === void 0) { accuracy = .0000001; } //optimize for early exit for integers if (n % 1 === 0) return n; var exp = 1 - String(Math.ceil(1 / accuracy)).length; //Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round // If the exp is undefined or zero... if (typeof exp === 'undefined' || +exp === 0) { return Math.round(n); } n = +n; exp = +exp; // If the value is not a number or the exp is not an integer... if (isNaN(n) || !(typeof exp === 'number' && exp % 1 === 0)) { return NaN; } // If the value is negative... if (n < 0) { return -round(-n, accuracy); } // Shift var a = split(n.toString(), 'e'); n = Math.round(+(a[0] + 'e' + (a[1] ? (+a[1] - exp) : -exp))); // Shift back a = split(n.toString(), 'e'); return +(a[0] + 'e' + (a[1] ? (+a[1] + exp) : exp)); } MakerJs.round = round; /** * Create a string representation of a route array. * * @param route Array of strings which are segments of a route. * @returns String of the flattened array. */ function createRouteKey(route) { var converted = []; for (var i = 0; i < route.length; i++) { var element = route[i]; var newElement; if (i % 2 === 0) { newElement = (i > 0 ? '.' : '') + element; } else { newElement = JSON.stringify([element]); } converted.push(newElement); } return converted.join(''); } MakerJs.createRouteKey = createRouteKey; /** * Travel along a route inside of a model to extract a specific node in its tree. * * @param modelContext Model to travel within. * @param route String of a flattened route, or a string array of route segments. * @returns Model or Path object within the modelContext tree. */ function travel(modelContext, route) { if (!modelContext || !route) return null; var routeArray; if (Array.isArray(route)) { routeArray = route; } else { routeArray = JSON.parse(route); } var props = routeArray.slice(); var ref = modelContext; var origin = modelContext.origin || [0, 0]; while (props.length) { var prop = props.shift(); ref = ref[prop]; if (!ref) return null; if (ref.origin && props.length) { origin = MakerJs.point.add(origin, ref.origin); } } return { result: ref, offset: origin }; } MakerJs.travel = travel; /** * @private */ var clone = require('clone'); /** * Clone an object. * * @param objectToClone The object to clone. * @returns A new clone of the original object. */ function cloneObject(objectToClone) { return clone(objectToClone); } MakerJs.cloneObject = cloneObject; /** * Copy the properties from one object to another object. * * Example: * ``` * makerjs.extendObject({ abc: 123 }, { xyz: 789 }); //returns { abc: 123, xyz: 789 } * ``` * * @param target The object to extend. It will receive the new properties. * @param other An object containing properties to merge in. * @returns The original object after merging. */ function extendObject(target, other) { if (target && other) { for (var key in other) { if (typeof other[key] !== 'undefined') { target[key] = other[key]; } } } return target; } MakerJs.extendObject = extendObject; /** * Test to see if a variable is a function. * * @param value The object to test. * @returns True if the object is a function type. */ function isFunction(value) { return typeof value === 'function'; } MakerJs.isFunction = isFunction; /** * Test to see if a variable is a number. * * @param value The object to test. * @returns True if the object is a number type. */ function isNumber(value) { return typeof value === 'number'; } MakerJs.isNumber = isNumber; /** * Test to see if a variable is an object. * * @param value The object to test. * @returns True if the object is an object type. */ function isObject(value) { return typeof value === 'object'; } MakerJs.isObject = isObject; //points /** * Test to see if an object implements the required properties of a point. * * @param item The item to test. */ function isPoint(item) { return item && Array.isArray(item) && item.length == 2 && isNumber(item[0]) && isNumber(item[1]); } MakerJs.isPoint = isPoint; /** * Test to see if an object implements the required properties of a path. * * @param item The item to test. */ function isPath(item) { return item && item.type && isPoint(item.origin); } MakerJs.isPath = isPath; /** * Test to see if an object implements the required properties of a line. * * @param item The item to test. */ function isPathLine(item) { return isPath(item) && item.type == MakerJs.pathType.Line && isPoint(item.end); } MakerJs.isPathLine = isPathLine; /** * Test to see if an object implements the required properties of a circle. * * @param item The item to test. */ function isPathCircle(item) { return isPath(item) && item.type == MakerJs.pathType.Circle && isNumber(item.radius); } MakerJs.isPathCircle = isPathCircle; /** * Test to see if an object implements the required properties of an arc. * * @param item The item to test. */ function isPathArc(item) { return isPath(item) && item.type == MakerJs.pathType.Arc && isNumber(item.radius) && isNumber(item.startAngle) && isNumber(item.endAngle); } MakerJs.isPathArc = isPathArc; /** * Test to see if an object implements the required properties of an arc in a bezier curve. * * @param item The item to test. */ function isPathArcInBezierCurve(item) { return isPathArc(item) && isObject(item.bezierData) && isNumber(item.bezierData.startT) && isNumber(item.bezierData.endT); } MakerJs.isPathArcInBezierCurve = isPathArcInBezierCurve; /** * String-based enumeration of all paths types. * * Examples: use pathType instead of string literal when creating a circle. * ``` * var circle: IPathCircle = { type: pathType.Circle, origin: [0, 0], radius: 7 }; //typescript * var circle = { type: pathType.Circle, origin: [0, 0], radius: 7 }; //javascript * ``` */ MakerJs.pathType = { Line: "line", Circle: "circle", Arc: "arc", BezierSeed: "bezier-seed" }; /** * Test to see if an object implements the required properties of a model. */ function isModel(item) { return item && (item.paths || item.models); } MakerJs.isModel = isModel; /** * Test to see if an object implements the required properties of a chain. * * @param item The item to test. */ function isChain(item) { var x = item; return x && x.links && Array.isArray(x.links) && isNumber(x.pathLength); } MakerJs.isChain = isChain; /** * @private */ var Cascade = /** @class */ (function () { function Cascade(_module, $initial) { this._module = _module; this.$initial = $initial; for (var methodName in this._module) this._shadow(methodName); this.$result = $initial; } Cascade.prototype._shadow = function (methodName) { var _this = this; this[methodName] = function () { return _this._apply(_this._module[methodName], arguments); }; }; Cascade.prototype._apply = function (fn, carriedArguments) { var args = [].slice.call(carriedArguments); args.unshift(this.$result); this.$result = fn.apply(undefined, args); return this; }; Cascade.prototype.$reset = function () { this.$result = this.$initial; return this; }; return Cascade; }()); function $(context) { if (isModel(context)) { return new Cascade(MakerJs.model, context); } else if (isPath(context)) { return new Cascade(MakerJs.path, context); } else if (isPoint(context)) { return new Cascade(MakerJs.point, context); } } MakerJs.$ = $; })(MakerJs || (MakerJs = {})); //CommonJs module.exports = MakerJs; //This file is generated by ./target/cascadable.js var MakerJs; (function (MakerJs) { var angle; (function (angle) { /** * @private */ function getFractionalPart(n) { return MakerJs.splitDecimal(n)[1]; } /** * @private */ function setFractionalPart(n, fractionalPart) { if (fractionalPart) { return +(MakerJs.splitDecimal(n)[0] + '.' + fractionalPart); } else { return n; } } /** * @private */ function copyFractionalPart(src, dest) { if ((src < 0 && dest < 0) || (src > 0 && dest > 0)) { return setFractionalPart(dest, getFractionalPart(src)); } return dest; } /** * Ensures an angle is not greater than 360 * * @param angleInDegrees Angle in degrees. * @returns Same polar angle but not greater than 360 degrees. */ function noRevolutions(angleInDegrees) { var revolutions = Math.floor(angleInDegrees / 360); if (revolutions === 0) return angleInDegrees; var a = angleInDegrees - (360 * revolutions); return copyFractionalPart(angleInDegrees, a); } angle.noRevolutions = noRevolutions; /** * Convert an angle from degrees to radians. * * @param angleInDegrees Angle in degrees. * @returns Angle in radians. */ function toRadians(angleInDegrees) { return noRevolutions(angleInDegrees) * Math.PI / 180.0; } angle.toRadians = toRadians; /** * Convert an angle from radians to degrees. * * @param angleInRadians Angle in radians. * @returns Angle in degrees. */ function toDegrees(angleInRadians) { return angleInRadians * 180.0 / Math.PI; } angle.toDegrees = toDegrees; /** * Get an arc's end angle, ensured to be greater than its start angle. * * @param arc An arc path object. * @returns End angle of arc. */ function ofArcEnd(arc) { //compensate for values past zero. This allows easy compute of total angle size. //for example 0 = 360 if (arc.endAngle < arc.startAngle) { var revolutions = Math.ceil((arc.startAngle - arc.endAngle) / 360); var a = revolutions * 360 + arc.endAngle; return copyFractionalPart(arc.endAngle, a); } return arc.endAngle; } angle.ofArcEnd = ofArcEnd; /** * Get the angle in the middle of an arc's start and end angles. * * @param arc An arc path object. * @param ratio Optional number between 0 and 1 specifying percentage between start and end angles. Default is .5 * @returns Middle angle of arc. */ function ofArcMiddle(arc, ratio) { if (ratio === void 0) { ratio = .5; } return arc.startAngle + ofArcSpan(arc) * ratio; } angle.ofArcMiddle = ofArcMiddle; /** * Total angle of an arc between its start and end angles. * * @param arc The arc to measure. * @returns Angle of arc. */ function ofArcSpan(arc) { var endAngle = angle.ofArcEnd(arc); var a = endAngle - arc.startAngle; if (MakerJs.round(a) > 360) { return noRevolutions(a); } else { return a; } } angle.ofArcSpan = ofArcSpan; /** * Angle of a line path. * * @param line The line path to find the angle of. * @returns Angle of the line path, in degrees. */ function ofLineInDegrees(line) { return noRevolutions(toDegrees(ofPointInRadians(line.origin, line.end))); } angle.ofLineInDegrees = ofLineInDegrees; /** * Angle of a line through a point, in degrees. * * @param pointToFindAngle The point to find the angle. * @param origin Point of origin of the angle. * @returns Angle of the line throught the point, in degrees. */ function ofPointInDegrees(origin, pointToFindAngle) { return toDegrees(ofPointInRadians(origin, pointToFindAngle)); } angle.ofPointInDegrees = ofPointInDegrees; /** * Angle of a line through a point, in radians. * * @param pointToFindAngle The point to find the angle. * @param origin Point of origin of the angle. * @returns Angle of the line throught the point, in radians. */ function ofPointInRadians(origin, pointToFindAngle) { var d = MakerJs.point.subtract(pointToFindAngle, origin); var x = d[0]; var y = d[1]; return Math.atan2(-y, -x) + Math.PI; } angle.ofPointInRadians = ofPointInRadians; /** * Mirror an angle on either or both x and y axes. * * @param angleInDegrees The angle to mirror. * @param mirrorX Boolean to mirror on the x axis. * @param mirrorY Boolean to mirror on the y axis. * @returns Mirrored angle. */ function mirror(angleInDegrees, mirrorX, mirrorY) { if (mirrorY) { angleInDegrees = 360 - angleInDegrees; } if (mirrorX) { angleInDegrees = (angleInDegrees < 180 ? 180 : 540) - angleInDegrees; } return angleInDegrees; } angle.mirror = mirror; /** * @private */ var linkLineMap = {}; linkLineMap[MakerJs.pathType.Arc] = function (arc, first, reversed) { var fromEnd = first != reversed; var angleToRotate = fromEnd ? arc.endAngle - 90 : arc.startAngle + 90; var origin = MakerJs.point.fromArc(arc)[fromEnd ? 1 : 0]; var end = MakerJs.point.rotate(MakerJs.point.add(origin, [arc.radius, 0]), angleToRotate, origin); return new MakerJs.paths.Line(first ? [end, origin] : [origin, end]); }; linkLineMap[MakerJs.pathType.Line] = function (line, first, reversed) { return reversed ? new MakerJs.paths.Line(line.end, line.origin) : line; }; /** * @private */ function getLinkLine(chainLink, first) { if (chainLink) { var p = chainLink.walkedPath.pathContext; var fn = linkLineMap[p.type]; if (fn) { return fn(p, first, chainLink.reversed); } } } /** * Get the angle of a joint between 2 chain links. * * @param linkA First chain link. * @param linkB Second chain link. * @returns Angle between chain links. */ function ofChainLinkJoint(linkA, linkB) { if (arguments.length < 2) return null; var linkLines = [linkA, linkB].map(function (link, i) { return getLinkLine(link, i === 0); }); var result = noRevolutions(ofLineInDegrees(linkLines[1]) - ofLineInDegrees(linkLines[0])); if (result > 180) result -= 360; return result; } angle.ofChainLinkJoint = ofChainLinkJoint; })(angle = MakerJs.angle || (MakerJs.angle = {})); })(MakerJs || (MakerJs = {})); var MakerJs; (function (MakerJs) { var point; (function (point) { /** * Add two points together and return the result as a new point object. * * @param a First point. * @param b Second point. * @param subtract Optional boolean to subtract instead of add. * @returns A new point object. */ function add(a, b, subtract) { var newPoint = clone(a); if (!b) return newPoint; for (var i = 2; i--;) { if (subtract) { newPoint[i] -= b[i]; } else { newPoint[i] += b[i]; } } return newPoint; } point.add = add; /** * Get the average of two points. * * @param a First point. * @param b Second point. * @returns New point object which is the average of a and b. */ function average(a, b) { function avg(i) { return (a[i] + b[i]) / 2; } return [avg(0), avg(1)]; } point.average = average; /** * Clone a point into a new point. * * @param pointToClone The point to clone. * @returns A new point with same values as the original. */ function clone(pointToClone) { if (!pointToClone) return point.zero(); return [pointToClone[0], pointToClone[1]]; } point.clone = clone; /** * From an array of points, find the closest point to a given reference point. * * @param referencePoint The reference point. * @param pointOptions Array of points to choose from. * @returns The first closest point from the pointOptions. */ function closest(referencePoint, pointOptions) { var smallest = { index: 0, distance: -1 }; for (var i = 0; i < pointOptions.length; i++) { var distance = MakerJs.measure.pointDistance(referencePoint, pointOptions[i]); if (smallest.distance == -1 || distance < smallest.distance) { smallest.distance = distance; smallest.index = i; } } return pointOptions[smallest.index]; } point.closest = closest; /** * @private */ var zero_cos = {}; zero_cos[Math.PI / 2] = true; zero_cos[3 * Math.PI / 2] = true; /** * @private */ var zero_sin = {}; zero_sin[Math.PI] = true; zero_sin[2 * Math.PI] = true; /** * Get a point from its polar coordinates. * * @param angleInRadians The angle of the polar coordinate, in radians. * @param radius The radius of the polar coordinate. * @returns A new point object. */ function fromPolar(angleInRadians, radius) { return [ (angleInRadians in zero_cos) ? 0 : MakerJs.round(radius * Math.cos(angleInRadians)), (angleInRadians in zero_sin) ? 0 : MakerJs.round(radius * Math.sin(angleInRadians)) ]; } point.fromPolar = fromPolar; /** * Get a point on a circle or arc path, at a given angle. * @param angleInDegrees The angle at which you want to find the point, in degrees. * @param circle A circle or arc. * @returns A new point object. */ function fromAngleOnCircle(angleInDegrees, circle) { return add(circle.origin, fromPolar(MakerJs.angle.toRadians(angleInDegrees), circle.radius)); } point.fromAngleOnCircle = fromAngleOnCircle; /** * Get the two end points of an arc path. * * @param arc The arc path object. * @returns Array with 2 elements: [0] is the point object corresponding to the start angle, [1] is the point object corresponding to the end angle. */ function fromArc(arc) { return [fromAngleOnCircle(arc.startAngle, arc), fromAngleOnCircle(arc.endAngle, arc)]; } point.fromArc = fromArc; /** * @private */ var pathEndsMap = {}; pathEndsMap[MakerJs.pathType.Arc] = function (arc) { return point.fromArc(arc); }; pathEndsMap[MakerJs.pathType.Line] = function (line) { return [line.origin, line.end]; }; pathEndsMap[MakerJs.pathType.BezierSeed] = pathEndsMap[MakerJs.pathType.Line]; /** * Get the two end points of a path. * * @param pathContext The path object. * @returns Array with 2 elements: [0] is the point object corresponding to the origin, [1] is the point object corresponding to the end. */ function fromPathEnds(pathContext, pathOffset) { var result = null; var fn = pathEndsMap[pathContext.type]; if (fn) { result = fn(pathContext); if (pathOffset) { result = result.map(function (p) { return add(p, pathOffset); }); } } return result; } point.fromPathEnds = fromPathEnds; /** * @private */ function verticalIntersectionPoint(verticalLine, nonVerticalSlope) { var x = verticalLine.origin[0]; var y = nonVerticalSlope.slope * x + nonVerticalSlope.yIntercept; return [x, y]; } /** * Calculates the intersection of slopes of two lines. * * @param lineA First line to use for slope. * @param lineB Second line to use for slope. * @param options Optional IPathIntersectionOptions. * @returns point of intersection of the two slopes, or null if the slopes did not intersect. */ function fromSlopeIntersection(lineA, lineB, options) { if (options === void 0) { options = {}; } var slopeA = MakerJs.measure.lineSlope(lineA); var slopeB = MakerJs.measure.lineSlope(lineB); //see if slope are parallel if (MakerJs.measure.isSlopeParallel(slopeA, slopeB)) { if (MakerJs.measure.isSlopeEqual(slopeA, slopeB)) { //check for overlap options.out_AreOverlapped = MakerJs.measure.isLineOverlapping(lineA, lineB, options.excludeTangents); } return null; } var pointOfIntersection; if (!slopeA.hasSlope) { pointOfIntersection = verticalIntersectionPoint(lineA, slopeB); } else if (!slopeB.hasSlope) { pointOfIntersection = verticalIntersectionPoint(lineB, slopeA); } else { // find intersection by line equation var x = (slopeB.yIntercept - slopeA.yIntercept) / (slopeA.slope - slopeB.slope); var y = slopeA.slope * x + slopeA.yIntercept; pointOfIntersection = [x, y]; } return pointOfIntersection; } point.fromSlopeIntersection = fromSlopeIntersection; /** * @private */ function midCircle(circle, midAngle) { return point.add(circle.origin, point.fromPolar(MakerJs.angle.toRadians(midAngle), circle.radius)); } /** * @private */ var middleMap = {}; middleMap[MakerJs.pathType.Arc] = function (arc, ratio) { var midAngle = MakerJs.angle.ofArcMiddle(arc, ratio); return midCircle(arc, midAngle); }; middleMap[MakerJs.pathType.Circle] = function (circle, ratio) { return midCircle(circle, 360 * ratio); }; middleMap[MakerJs.pathType.Line] = function (line, ratio) { function ration(a, b) { return a + (b - a) * ratio; } ; return [ ration(line.origin[0], line.end[0]), ration(line.origin[1], line.end[1]) ]; }; middleMap[MakerJs.pathType.BezierSeed] = function (seed, ratio) { return MakerJs.models.BezierCurve.computePoint(seed, ratio); }; /** * Get the middle point of a path. * * @param pathContext The path object. * @param ratio Optional ratio (between 0 and 1) of point along the path. Default is .5 for middle. * @returns Point on the path, in the middle of the path. */ function middle(pathContext, ratio) { if (ratio === void 0) { ratio = .5; } var midPoint = null; var fn = middleMap[pathContext.type]; if (fn) { midPoint = fn(pathContext, ratio); } return midPoint; } point.middle = middle; /** * Create a clone of a point, mirrored on either or both x and y axes. * * @param pointToMirror The point to mirror. * @param mirrorX Boolean to mirror on the x axis. * @param mirrorY Boolean to mirror on the y axis. * @returns Mirrored point. */ function mirror(pointToMirror, mirrorX, mirrorY) { var p = clone(pointToMirror); if (mirrorX) { p[0] = -p[0]; } if (mirrorY) { p[1] = -p[1]; } return p; } point.mirror = mirror; /** * Round the values of a point. * * @param pointContext The point to serialize. * @param accuracy Optional exemplar number of decimal places. * @returns A new point with the values rounded. */ function rounded(pointContext, accuracy) { return [MakerJs.round(pointContext[0], accuracy), MakerJs.round(pointContext[1], accuracy)]; } point.rounded = rounded; /** * Rotate a point. * * @param pointToRotate The point to rotate. * @param angleInDegrees The amount of rotation, in degrees. * @param rotationOrigin The center point of rotation. * @returns A new point. */ function rotate(pointToRotate, angleInDegrees, rotationOrigin) { if (rotationOrigin === void 0) { rotationOrigin = [0, 0]; } var pointAngleInRadians = MakerJs.angle.ofPointInRadians(rotationOrigin, pointToRotate); var d = MakerJs.measure.pointDistance(rotationOrigin, pointToRotate); var rotatedPoint = fromPolar(pointAngleInRadians + MakerJs.angle.toRadians(angleInDegrees), d); return add(rotationOrigin, rotatedPoint); } point.rotate = rotate; /** * Scale a point's coordinates. * * @param pointToScale The point to scale. * @param scaleValue The amount of scaling. * @returns A new point. */ function scale(pointToScale, scaleValue) { var p = clone(pointToScale); for (var i = 2; i--;) { p[i] *= scaleValue; } return p; } point.scale = scale; /** * Distort a point's coordinates. * * @param pointToDistort The point to distort. * @param scaleX The amount of x scaling. * @param scaleY The amount of y scaling. * @returns A new point. */ function distort(pointToDistort, scaleX, scaleY) { return [pointToDistort[0] * scaleX, pointToDistort[1] * scaleY]; } point.distort = distort; /** * Subtract a point from another point, and return the result as a new point. Shortcut to Add(a, b, subtract = true). * * @param a First point. * @param b Second point. * @returns A new point object. */ function subtract(a, b) { return add(a, b, true); } point.subtract = subtract; /** * A point at 0,0 coordinates. * NOTE: It is important to call this as a method, with the empty parentheses. * * @returns A new point. */ function zero() { return [0, 0]; } point.zero = zero; })(point = MakerJs.point || (MakerJs.point = {})); })(MakerJs || (MakerJs = {})); var MakerJs; (function (MakerJs) { var path; (function (path) { /** * Add a path to a model. This is basically equivalent to: * ``` * parentModel.paths[pathId] = childPath; * ``` * with additional checks to make it safe for cascading. * * @param childPath The path to add. * @param parentModel The model to add to. * @param pathId The id of the path. * @param overwrite Optional flag to overwrite any path referenced by pathId. Default is false, which will create an id similar to pathId. * @returns The original path (for cascading). */ function addTo(childPath, parentModel, pathId, overwrite) { if (overwrite === void 0) { overwrite = false; } MakerJs.model.addPath(parentModel, childPath, pathId, overwrite); return childPath; } path.addTo = addTo; /** * @private */ function copyLayer(pathA, pathB) { if (pathA && pathB && typeof pathA.layer !== 'undefined') { pathB.layer = pathA.layer; } //carry extra props if this is an IPathArcInBezierCurve if (pathA && pathB && ('bezierData' in pathA)) { pathB.bezierData = pathA.bezierData; } } /** * @private */ var copyPropsMap = {}; copyPropsMap[MakerJs.pathType.Circle] = function (srcCircle, destCircle, offset) { destCircle.radius = srcCircle.radius; }; copyPropsMap[MakerJs.pathType.Arc] = function (srcArc, destArc, offset) { copyPropsMap[MakerJs.pathType.Circle](srcArc, destArc, offset); destArc.startAngle = srcArc.startAngle; destArc.endAngle = srcArc.endAngle; }; copyPropsMap[MakerJs.pathType.Line] = function (srcLine, destLine, offset) { destLine.end = MakerJs.point.add(srcLine.end, offset); }; copyPropsMap[MakerJs.pathType.BezierSeed] = function (srcSeed, destSeed, offset) { copyPropsMap[MakerJs.pathType.Line](srcSeed, destSeed, offset); destSeed.controls = srcSeed.controls.map(function (p) { return MakerJs.point.add(p, offset); }); }; /** * Create a clone of a path. This is faster than cloneObject. * * @param pathToClone The path to clone. * @param offset Optional point to move path a relative distance. * @returns Cloned path. */ function clone(pathToClone, offset) { var result = { type: pathToClone.type, origin: MakerJs.point.add(pathToClone.origin, offset) }; var fn = copyPropsMap[pathToClone.type]; if (fn) { fn(pathToClone, result, offset); } copyLayer(pathToClone, result); return result; } path.clone = clone; /** * Copy the schema properties of one path to another. * * @param srcPath The source path to copy property values from. * @param destPath The destination path to copy property values to. * @returns The source path. */ function copyProps(srcPath, destPath) { var fn = copyPropsMap[srcPath.type]; if (fn) { destPath.origin = MakerJs.point.clone(srcPath.origin); fn(srcPath, destPath); } copyLayer(srcPath, destPath); return srcPath; } path.copyProps = copyProps; /** * @private */ var mirrorMap = {}; mirrorMap[MakerJs.pathType.Line] = function (line, origin, mirrorX, mirrorY) { return new MakerJs.paths.Line(origin, MakerJs.point.mirror(line.end, mirrorX, mirrorY)); }; mirrorMap[MakerJs.pathType.Circle] = function (circle, origin, mirrorX, mirrorY) { return new MakerJs.paths.Circle(origin, circle.radius); }; mirrorMap[MakerJs.pathType.Arc] = function (arc, origin, mirrorX, mirrorY) { var startAngle = MakerJs.angle.mirror(arc.startAngle, mirrorX, mirrorY); var endAngle = MakerJs.angle.mirror(MakerJs.angle.ofArcEnd(arc), mirrorX, mirrorY); var xor = mirrorX != mirrorY; return new MakerJs.paths.Arc(origin, arc.radius, xor ? endAngle : startAngle, xor ? startAngle : endAngle); }; mirrorMap[MakerJs.pathType.BezierSeed] = function (seed, origin, mirrorX, mirrorY) { var mirrored = mirrorMap[MakerJs.pathType.Line](seed, origin, mirrorX, mirrorY); mirrored.type = MakerJs.pathType.BezierSeed; mirrored.controls = seed.controls.map(function (c) { return MakerJs.point.mirror(c, mirrorX, mirrorY); }); return mirrored; }; /** * Set the layer of a path. This is equivalent to: * ``` * pathContext.layer = layer; * ``` * * @param pathContext The path to set the layer. * @param layer The layer name. * @returns The original path (for cascading). */ function layer(pathContext, layer) { pathContext.layer = layer; return pathContext; } path.layer = layer; /** * Create a clone of a path, mirrored on either or both x and y axes. * * @param pathToMirror The path to mirror. * @param mirrorX Boolean to mirror on the x axis. * @param mirrorY Boolean to mirror on the y axis. * @returns Mirrored path. */ function mirror(pathToMirror, mirrorX, mirrorY) { var newPath = null; if (pathToMirror) { var origin = MakerJs.point.mirror(pathToMirror.origin, mirrorX, mirrorY); var fn = mirrorMap[pathToMirror.type]; if (fn) { newPath = fn(pathToMirror, origin, mirrorX, mirrorY); } } copyLayer(pathToMirror, newPath); return newPath; } path.mirror = mirror; /** * @private */ var moveMap = {}; moveMap[MakerJs.pathType.Line] = function (line, origin) { var delta = MakerJs.point.subtract(line.end, line.origin); line.end = MakerJs.point.add(origin, delta); }; /** * Move a path to an absolute point. * * @param pathToMove The path to move. * @param origin The new origin for the path. * @returns The original path (for cascading). */ function move(pathToMove, origin) { if (pathToMove) { var fn = moveMap[pathToMove.type]; if (fn) { fn(pathToMove, origin); } pathToMove.origin = origin; } return pathToMove; } path.move = move; /** * @private */ var moveRelativeMap = {}; moveRelativeMap[MakerJs.pathType.Line] = function (line, delta, subtract) { line.end = MakerJs.point.add(line.end, delta, subtract); }; moveRelativeMap[MakerJs.pathType.BezierSeed] = function (seed, delta, subtract) { moveRelativeMap[MakerJs.pathType.Line](seed, delta, subtract); seed.controls = seed.controls.map(function (c) { return MakerJs.point.add(c, delta, subtract); }); }; /** * Move a path's origin by a relative amount. * * @param pathToMove The path to move. * @param delta The x & y adjustments as a point object. * @param subtract Optional boolean to subtract instead of add. * @returns The original path (for cascading). */ function moveRelative(pathToMove, delta, subtract) { if (pathToMove && delta) { pathToMove.origin = MakerJs.point.add(pathToMove.origin, delta, subtract); var fn = moveRelativeMap[pathToMove.type]; if (fn) { fn(pathToMove, delta, subtract); } } return pathToMove; } path.moveRelative = moveRelative; /** * Move some paths relatively during a task execution, then unmove them. * * @param pathsToMove The paths to move. * @param deltas The x & y adjustments as a point object array. * @param task The function to call while the paths are temporarily moved. */ function moveTemporary(pathsToMove, deltas, task) { var subtract = false; function move(pathToOffset, i) { if (deltas[i]) { moveRelative(pathToOffset, deltas[i], subtract); } } pathsToMove.map(move); task(); subtract = true; pathsToMove.map(move); } path.moveTemporary = moveTemporary; /** * @private */ var rotateMap = {}; rotateMap[MakerJs.pathType.Line] = function (line, angleInDegrees, rotationOrigin) { line.end = MakerJs.point.rotate(line.end, angleInDegrees, rotationOrigin); }; rotateMap[MakerJs.pathType.Arc] = function (arc, angleInDegrees, rotationOrigin) { arc.startAngle = MakerJs.angle.noRevolutions(arc.startAngle + angleInDegrees); arc.endAngle = MakerJs.angle.noRevolutions(arc.endAngle + angleInDegrees); }; rotateMap[MakerJs.pathType.BezierSeed] = function (seed, angleInDegrees, rotationOrigin) { rotateMap[MakerJs.pathType.Line](seed, angleInDegrees, rotationOrigin); seed.controls = seed.controls.map(function (c) { return MakerJs.point.rotate(c, angleInDegrees, rotationOrigin); }); }; /** * Rotate a path. * * @param pathToRotate The path to rotate. * @param angleInDegrees The amount of rotation, in degrees. * @param rotationOrigin The center point of rotation. * @returns The original path (for cascading). */ function rotate(pathToRotate, angleInDegrees, rotationOrigin) { if (rotationOrigin === void 0) { rotationOrigin = [0, 0]; } if (!pathToRotate || !angleInDegrees) return pathToRotate; pathToRotate.origin = MakerJs.point.rotate(pathToRotate.origin, angleInDegrees, rotationOrigin); var fn = rotateMap[pathToRotate.type]; if (fn) { fn(pathToRotate, angleInDegrees, rotationOrigin); } return pathToRotate; } path.rotate = rotate; /** * @private */ var scaleMap = {}; scaleMap[MakerJs.pathType.Line] = function (line, scaleValue) { line.end = MakerJs.point.scale(line.end, scaleValue); }; scaleMap[MakerJs.pathType.BezierSeed] = function (seed, scaleValue) { scaleMap[MakerJs.pathType.Line](seed, scaleValue); seed.controls = seed.controls.map(function (c) { return MakerJs.point.scale(c, scaleValue); }); }; scaleMap[MakerJs.pathType.Circle] = function (circle, scaleValue) { circle.radius *= scaleValue; }; scaleMap[MakerJs.pathType.Arc] = scaleMap[MakerJs.pathType.Circle]; /** * Scale a path. * * @param pathToScale The path to scale. * @param scaleValue The amount of scaling. * @returns The original path (for cascading). */ function scale(pathToScale, scaleValue) { if (!pathToScale || scaleValue === 1 || !scaleValue) return pathToScale; pathToScale.origin = MakerJs.point.scale(pathToScale.origin, scaleValue); var fn = scaleMap[pathToScale.type]; if (fn) { fn(pathToScale, scaleValue); } return pathToScale; } path.scale = scale; /** * @private */ var distortMap = {}; distortMap[MakerJs.pathType.Arc] = function (arc, scaleX, scaleY) { return new MakerJs.models.EllipticArc(arc, scaleX, scaleY); }; distortMap[MakerJs.pathType.Circle] = function (circle, scaleX, scaleY) { var ellipse = new MakerJs.models.Ellipse(circle.radius * scaleX, circle.radius * scaleY); ellipse.origin = MakerJs.point.distort(circle.origin, scaleX, scaleY); return ellipse; }; distortMap[MakerJs.pathType.Line] = function (line, scaleX, scaleY) { return new MakerJs.paths.Line([line.origin, line.end].map(function (p) { return MakerJs.point.distort(p, scaleX, scaleY); })); }; distortMap[MakerJs.pathType.BezierSeed] = function (seed, scaleX, scaleY) { var d = MakerJs.point.distort; r