UNPKG

437 kBJavaScriptView Raw
1/*
2__________________________________________________________________________________________________________________________________________
3 __________________________________________________________________________________________________________________________________________
4 ________/\\\\____________/\\\\_____/\\\\\\\\\_____/\\\________/\\\__/\\\\\\\\\\\\\\\__/\\\\\\\\\\\________________________________________
5 _______\/\\\\\\________/\\\\\\___/\\\\/////\\\\__\/\\\_____/\\\//__\/\\\///////////__\/\\\///////\\\_______________/\\\___________________
6 _______\/\\\//\\\____/\\\//\\\__/\\\/____\///\\\_\/\\\__/\\\//_____\/\\\_____________\/\\\_____\/\\\______________\///____________________
7 _______\/\\\\///\\\/\\\/_\/\\\_\/\\\_______\/\\\_\/\\\\\\//\\\_____\/\\\\\\\\\\\_____\/\\\\\\\\\\\/________________/\\\__/\\\\\\\\\\______
8 _______\/\\\__\///\\\/___\/\\\_\/\\\\\\\\\\\\\\\_\/\\\//_\//\\\____\/\\\///////______\/\\\//////\\\_______________\/\\\_\/\\\//////_______
9 _______\/\\\____\///_____\/\\\_\/\\\/////////\\\_\/\\\____\//\\\___\/\\\_____________\/\\\____\//\\\______________\/\\\_\/\\\\\\\\\\______
10 _______\/\\\_____________\/\\\_\/\\\_______\/\\\_\/\\\_____\//\\\__\/\\\_____________\/\\\_____\//\\\_________/\\_\/\\\_\////////\\\______
11 _______\/\\\_____________\/\\\_\/\\\_______\/\\\_\/\\\______\//\\\_\/\\\\\\\\\\\\\\\_\/\\\______\//\\\__/\\\_\//\\\\\\___/\\\\\\\\\\______
12 _______\///______________\///__\///________\///__\///________\///__\///////////////__\///________\///__\///___\//////___\//////////_______
13 __________________________________________________________________________________________________________________________________________
14 __________________________________________________________________________________________________________________________________________
15
16Maker.js
17https://github.com/Microsoft/maker.js
18
19Copyright (c) Microsoft Corporation. All rights reserved.
20Licensed under the Apache License, Version 2.0 (the "License"); you may not use
21this file except in compliance with the License. You may obtain a copy of the
22License at http://www.apache.org/licenses/LICENSE-2.0
23
24THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
25KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
26WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
27MERCHANTABLITY OR NON-INFRINGEMENT.
28
29See the Apache Version 2.0 License for specific language governing permissions
30and limitations under the License.
31
32*/
33/**
34 * Root module for Maker.js.
35 *
36 * Example: get a reference to Maker.js
37 * ```
38 * var makerjs = require('makerjs');
39 * ```
40 *
41 */
42var MakerJs;
43(function (MakerJs) {
44 /**
45 * Version info
46 */
47 MakerJs.version = 'debug';
48 /**
49 * Enumeration of environment types.
50 */
51 MakerJs.environmentTypes = {
52 BrowserUI: 'browser',
53 NodeJs: 'node',
54 WebWorker: 'worker',
55 Unknown: 'unknown'
56 };
57 /**
58 * @private
59 */
60 function tryEval(name) {
61 try {
62 var value = eval(name);
63 return value;
64 }
65 catch (e) { }
66 return;
67 }
68 /**
69 * @private
70 */
71 function detectEnvironment() {
72 if (tryEval('WorkerGlobalScope') && tryEval('self')) {
73 return MakerJs.environmentTypes.WebWorker;
74 }
75 if (tryEval('window') && tryEval('document')) {
76 return MakerJs.environmentTypes.BrowserUI;
77 }
78 //put node last since packagers usually add shims for it
79 if (tryEval('global') && tryEval('process')) {
80 return MakerJs.environmentTypes.NodeJs;
81 }
82 return MakerJs.environmentTypes.Unknown;
83 }
84 /**
85 * Current execution environment type, should be one of environmentTypes.
86 */
87 MakerJs.environment = detectEnvironment();
88 //units
89 /**
90 * String-based enumeration of unit types: imperial, metric or otherwise.
91 * A model may specify the unit system it is using, if any. When importing a model, it may have different units.
92 * Unit conversion function is makerjs.units.conversionScale().
93 * Important: If you add to this, you must also add a corresponding conversion ratio in the unit.ts file!
94 */
95 MakerJs.unitType = {
96 Centimeter: 'cm',
97 Foot: 'foot',
98 Inch: 'inch',
99 Meter: 'm',
100 Millimeter: 'mm'
101 };
102 /**
103 * @private
104 */
105 function split(s, char) {
106 var p = s.indexOf(char);
107 if (p < 0) {
108 return [s];
109 }
110 else if (p > 0) {
111 return [s.substr(0, p), s.substr(p + 1)];
112 }
113 else {
114 return ['', s];
115 }
116 }
117 /**
118 * Split a decimal into its whole and fractional parts as strings.
119 *
120 * Example: get whole and fractional parts of 42.056
121 * ```
122 * makerjs.splitDecimal(42.056); //returns ["42", "056"]
123 * ```
124 *
125 * @param n The number to split.
126 * @returns Array of 2 strings when n contains a decimal point, or an array of one string when n is an integer.
127 */
128 function splitDecimal(n) {
129 var s = n.toString();
130 if (s.indexOf('e') > 0) {
131 //max digits is 20 - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
132 s = n.toFixed(20).match(/.*[^(0+$)]/)[0]; //regex trims trailing zeros
133 }
134 return split(s, '.');
135 }
136 MakerJs.splitDecimal = splitDecimal;
137 /**
138 * Numeric rounding
139 *
140 * Example: round to 3 decimal places
141 * ```
142 * makerjs.round(3.14159, .001); //returns 3.142
143 * ```
144 *
145 * @param n The number to round off.
146 * @param accuracy Optional exemplar of number of decimal places.
147 * @returns Rounded number.
148 */
149 function round(n, accuracy) {
150 if (accuracy === void 0) { accuracy = .0000001; }
151 //optimize for early exit for integers
152 if (n % 1 === 0)
153 return n;
154 var exp = 1 - String(Math.ceil(1 / accuracy)).length;
155 //Adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
156 // If the exp is undefined or zero...
157 if (typeof exp === 'undefined' || +exp === 0) {
158 return Math.round(n);
159 }
160 n = +n;
161 exp = +exp;
162 // If the value is not a number or the exp is not an integer...
163 if (isNaN(n) || !(typeof exp === 'number' && exp % 1 === 0)) {
164 return NaN;
165 }
166 // If the value is negative...
167 if (n < 0) {
168 return -round(-n, accuracy);
169 }
170 // Shift
171 var a = split(n.toString(), 'e');
172 n = Math.round(+(a[0] + 'e' + (a[1] ? (+a[1] - exp) : -exp)));
173 // Shift back
174 a = split(n.toString(), 'e');
175 return +(a[0] + 'e' + (a[1] ? (+a[1] + exp) : exp));
176 }
177 MakerJs.round = round;
178 /**
179 * Create a string representation of a route array.
180 *
181 * @param route Array of strings which are segments of a route.
182 * @returns String of the flattened array.
183 */
184 function createRouteKey(route) {
185 var converted = [];
186 for (var i = 0; i < route.length; i++) {
187 var element = route[i];
188 var newElement;
189 if (i % 2 === 0) {
190 newElement = (i > 0 ? '.' : '') + element;
191 }
192 else {
193 newElement = JSON.stringify([element]);
194 }
195 converted.push(newElement);
196 }
197 return converted.join('');
198 }
199 MakerJs.createRouteKey = createRouteKey;
200 /**
201 * Travel along a route inside of a model to extract a specific node in its tree.
202 *
203 * @param modelContext Model to travel within.
204 * @param route String of a flattened route, or a string array of route segments.
205 * @returns Model or Path object within the modelContext tree.
206 */
207 function travel(modelContext, route) {
208 if (!modelContext || !route)
209 return null;
210 var routeArray;
211 if (Array.isArray(route)) {
212 routeArray = route;
213 }
214 else {
215 routeArray = JSON.parse(route);
216 }
217 var props = routeArray.slice();
218 var ref = modelContext;
219 var origin = modelContext.origin || [0, 0];
220 while (props.length) {
221 var prop = props.shift();
222 ref = ref[prop];
223 if (!ref)
224 return null;
225 if (ref.origin && props.length) {
226 origin = MakerJs.point.add(origin, ref.origin);
227 }
228 }
229 return {
230 result: ref,
231 offset: origin
232 };
233 }
234 MakerJs.travel = travel;
235 /**
236 * @private
237 */
238 var clone = require('clone');
239 /**
240 * Clone an object.
241 *
242 * @param objectToClone The object to clone.
243 * @returns A new clone of the original object.
244 */
245 function cloneObject(objectToClone) {
246 return clone(objectToClone);
247 }
248 MakerJs.cloneObject = cloneObject;
249 /**
250 * Copy the properties from one object to another object.
251 *
252 * Example:
253 * ```
254 * makerjs.extendObject({ abc: 123 }, { xyz: 789 }); //returns { abc: 123, xyz: 789 }
255 * ```
256 *
257 * @param target The object to extend. It will receive the new properties.
258 * @param other An object containing properties to merge in.
259 * @returns The original object after merging.
260 */
261 function extendObject(target, other) {
262 if (target && other) {
263 for (var key in other) {
264 if (typeof other[key] !== 'undefined') {
265 target[key] = other[key];
266 }
267 }
268 }
269 return target;
270 }
271 MakerJs.extendObject = extendObject;
272 /**
273 * Test to see if a variable is a function.
274 *
275 * @param value The object to test.
276 * @returns True if the object is a function type.
277 */
278 function isFunction(value) {
279 return typeof value === 'function';
280 }
281 MakerJs.isFunction = isFunction;
282 /**
283 * Test to see if a variable is a number.
284 *
285 * @param value The object to test.
286 * @returns True if the object is a number type.
287 */
288 function isNumber(value) {
289 return typeof value === 'number';
290 }
291 MakerJs.isNumber = isNumber;
292 /**
293 * Test to see if a variable is an object.
294 *
295 * @param value The object to test.
296 * @returns True if the object is an object type.
297 */
298 function isObject(value) {
299 return typeof value === 'object';
300 }
301 MakerJs.isObject = isObject;
302 //points
303 /**
304 * Test to see if an object implements the required properties of a point.
305 *
306 * @param item The item to test.
307 */
308 function isPoint(item) {
309 return item && Array.isArray(item) && item.length == 2 && isNumber(item[0]) && isNumber(item[1]);
310 }
311 MakerJs.isPoint = isPoint;
312 /**
313 * Test to see if an object implements the required properties of a path.
314 *
315 * @param item The item to test.
316 */
317 function isPath(item) {
318 return item && item.type && isPoint(item.origin);
319 }
320 MakerJs.isPath = isPath;
321 /**
322 * Test to see if an object implements the required properties of a line.
323 *
324 * @param item The item to test.
325 */
326 function isPathLine(item) {
327 return isPath(item) && item.type == MakerJs.pathType.Line && isPoint(item.end);
328 }
329 MakerJs.isPathLine = isPathLine;
330 /**
331 * Test to see if an object implements the required properties of a circle.
332 *
333 * @param item The item to test.
334 */
335 function isPathCircle(item) {
336 return isPath(item) && item.type == MakerJs.pathType.Circle && isNumber(item.radius);
337 }
338 MakerJs.isPathCircle = isPathCircle;
339 /**
340 * Test to see if an object implements the required properties of an arc.
341 *
342 * @param item The item to test.
343 */
344 function isPathArc(item) {
345 return isPath(item) && item.type == MakerJs.pathType.Arc && isNumber(item.radius) && isNumber(item.startAngle) && isNumber(item.endAngle);
346 }
347 MakerJs.isPathArc = isPathArc;
348 /**
349 * Test to see if an object implements the required properties of an arc in a bezier curve.
350 *
351 * @param item The item to test.
352 */
353 function isPathArcInBezierCurve(item) {
354 return isPathArc(item) && isObject(item.bezierData) && isNumber(item.bezierData.startT) && isNumber(item.bezierData.endT);
355 }
356 MakerJs.isPathArcInBezierCurve = isPathArcInBezierCurve;
357 /**
358 * String-based enumeration of all paths types.
359 *
360 * Examples: use pathType instead of string literal when creating a circle.
361 * ```
362 * var circle: IPathCircle = { type: pathType.Circle, origin: [0, 0], radius: 7 }; //typescript
363 * var circle = { type: pathType.Circle, origin: [0, 0], radius: 7 }; //javascript
364 * ```
365 */
366 MakerJs.pathType = {
367 Line: "line",
368 Circle: "circle",
369 Arc: "arc",
370 BezierSeed: "bezier-seed"
371 };
372 /**
373 * Test to see if an object implements the required properties of a model.
374 */
375 function isModel(item) {
376 return item && (item.paths || item.models);
377 }
378 MakerJs.isModel = isModel;
379 /**
380 * Test to see if an object implements the required properties of a chain.
381 *
382 * @param item The item to test.
383 */
384 function isChain(item) {
385 var x = item;
386 return x && x.links && Array.isArray(x.links) && isNumber(x.pathLength);
387 }
388 MakerJs.isChain = isChain;
389 /**
390 * @private
391 */
392 var Cascade = /** @class */ (function () {
393 function Cascade(_module, $initial) {
394 this._module = _module;
395 this.$initial = $initial;
396 for (var methodName in this._module)
397 this._shadow(methodName);
398 this.$result = $initial;
399 }
400 Cascade.prototype._shadow = function (methodName) {
401 var _this = this;
402 this[methodName] = function () {
403 return _this._apply(_this._module[methodName], arguments);
404 };
405 };
406 Cascade.prototype._apply = function (fn, carriedArguments) {
407 var args = [].slice.call(carriedArguments);
408 args.unshift(this.$result);
409 this.$result = fn.apply(undefined, args);
410 return this;
411 };
412 Cascade.prototype.$reset = function () {
413 this.$result = this.$initial;
414 return this;
415 };
416 return Cascade;
417 }());
418 function $(context) {
419 if (isModel(context)) {
420 return new Cascade(MakerJs.model, context);
421 }
422 else if (isPath(context)) {
423 return new Cascade(MakerJs.path, context);
424 }
425 else if (isPoint(context)) {
426 return new Cascade(MakerJs.point, context);
427 }
428 }
429 MakerJs.$ = $;
430})(MakerJs || (MakerJs = {}));
431//CommonJs
432module.exports = MakerJs;
433//This file is generated by ./target/cascadable.js
434var MakerJs;
435(function (MakerJs) {
436 var angle;
437 (function (angle) {
438 /**
439 * @private
440 */
441 function getFractionalPart(n) {
442 return MakerJs.splitDecimal(n)[1];
443 }
444 /**
445 * @private
446 */
447 function setFractionalPart(n, fractionalPart) {
448 if (fractionalPart) {
449 return +(MakerJs.splitDecimal(n)[0] + '.' + fractionalPart);
450 }
451 else {
452 return n;
453 }
454 }
455 /**
456 * @private
457 */
458 function copyFractionalPart(src, dest) {
459 if ((src < 0 && dest < 0) || (src > 0 && dest > 0)) {
460 return setFractionalPart(dest, getFractionalPart(src));
461 }
462 return dest;
463 }
464 /**
465 * Ensures an angle is not greater than 360
466 *
467 * @param angleInDegrees Angle in degrees.
468 * @returns Same polar angle but not greater than 360 degrees.
469 */
470 function noRevolutions(angleInDegrees) {
471 var revolutions = Math.floor(angleInDegrees / 360);
472 if (revolutions === 0)
473 return angleInDegrees;
474 var a = angleInDegrees - (360 * revolutions);
475 return copyFractionalPart(angleInDegrees, a);
476 }
477 angle.noRevolutions = noRevolutions;
478 /**
479 * Convert an angle from degrees to radians.
480 *
481 * @param angleInDegrees Angle in degrees.
482 * @returns Angle in radians.
483 */
484 function toRadians(angleInDegrees) {
485 return noRevolutions(angleInDegrees) * Math.PI / 180.0;
486 }
487 angle.toRadians = toRadians;
488 /**
489 * Convert an angle from radians to degrees.
490 *
491 * @param angleInRadians Angle in radians.
492 * @returns Angle in degrees.
493 */
494 function toDegrees(angleInRadians) {
495 return angleInRadians * 180.0 / Math.PI;
496 }
497 angle.toDegrees = toDegrees;
498 /**
499 * Get an arc's end angle, ensured to be greater than its start angle.
500 *
501 * @param arc An arc path object.
502 * @returns End angle of arc.
503 */
504 function ofArcEnd(arc) {
505 //compensate for values past zero. This allows easy compute of total angle size.
506 //for example 0 = 360
507 if (arc.endAngle < arc.startAngle) {
508 var revolutions = Math.ceil((arc.startAngle - arc.endAngle) / 360);
509 var a = revolutions * 360 + arc.endAngle;
510 return copyFractionalPart(arc.endAngle, a);
511 }
512 return arc.endAngle;
513 }
514 angle.ofArcEnd = ofArcEnd;
515 /**
516 * Get the angle in the middle of an arc's start and end angles.
517 *
518 * @param arc An arc path object.
519 * @param ratio Optional number between 0 and 1 specifying percentage between start and end angles. Default is .5
520 * @returns Middle angle of arc.
521 */
522 function ofArcMiddle(arc, ratio) {
523 if (ratio === void 0) { ratio = .5; }
524 return arc.startAngle + ofArcSpan(arc) * ratio;
525 }
526 angle.ofArcMiddle = ofArcMiddle;
527 /**
528 * Total angle of an arc between its start and end angles.
529 *
530 * @param arc The arc to measure.
531 * @returns Angle of arc.
532 */
533 function ofArcSpan(arc) {
534 var endAngle = angle.ofArcEnd(arc);
535 var a = endAngle - arc.startAngle;
536 if (MakerJs.round(a) > 360) {
537 return noRevolutions(a);
538 }
539 else {
540 return a;
541 }
542 }
543 angle.ofArcSpan = ofArcSpan;
544 /**
545 * Angle of a line path.
546 *
547 * @param line The line path to find the angle of.
548 * @returns Angle of the line path, in degrees.
549 */
550 function ofLineInDegrees(line) {
551 return noRevolutions(toDegrees(ofPointInRadians(line.origin, line.end)));
552 }
553 angle.ofLineInDegrees = ofLineInDegrees;
554 /**
555 * Angle of a line through a point, in degrees.
556 *
557 * @param pointToFindAngle The point to find the angle.
558 * @param origin Point of origin of the angle.
559 * @returns Angle of the line throught the point, in degrees.
560 */
561 function ofPointInDegrees(origin, pointToFindAngle) {
562 return toDegrees(ofPointInRadians(origin, pointToFindAngle));
563 }
564 angle.ofPointInDegrees = ofPointInDegrees;
565 /**
566 * Angle of a line through a point, in radians.
567 *
568 * @param pointToFindAngle The point to find the angle.
569 * @param origin Point of origin of the angle.
570 * @returns Angle of the line throught the point, in radians.
571 */
572 function ofPointInRadians(origin, pointToFindAngle) {
573 var d = MakerJs.point.subtract(pointToFindAngle, origin);
574 var x = d[0];
575 var y = d[1];
576 return Math.atan2(-y, -x) + Math.PI;
577 }
578 angle.ofPointInRadians = ofPointInRadians;
579 /**
580 * Mirror an angle on either or both x and y axes.
581 *
582 * @param angleInDegrees The angle to mirror.
583 * @param mirrorX Boolean to mirror on the x axis.
584 * @param mirrorY Boolean to mirror on the y axis.
585 * @returns Mirrored angle.
586 */
587 function mirror(angleInDegrees, mirrorX, mirrorY) {
588 if (mirrorY) {
589 angleInDegrees = 360 - angleInDegrees;
590 }
591 if (mirrorX) {
592 angleInDegrees = (angleInDegrees < 180 ? 180 : 540) - angleInDegrees;
593 }
594 return angleInDegrees;
595 }
596 angle.mirror = mirror;
597 /**
598 * @private
599 */
600 var linkLineMap = {};
601 linkLineMap[MakerJs.pathType.Arc] = function (arc, first, reversed) {
602 var fromEnd = first != reversed;
603 var angleToRotate = fromEnd ? arc.endAngle - 90 : arc.startAngle + 90;
604 var origin = MakerJs.point.fromArc(arc)[fromEnd ? 1 : 0];
605 var end = MakerJs.point.rotate(MakerJs.point.add(origin, [arc.radius, 0]), angleToRotate, origin);
606 return new MakerJs.paths.Line(first ? [end, origin] : [origin, end]);
607 };
608 linkLineMap[MakerJs.pathType.Line] = function (line, first, reversed) {
609 return reversed ? new MakerJs.paths.Line(line.end, line.origin) : line;
610 };
611 /**
612 * @private
613 */
614 function getLinkLine(chainLink, first) {
615 if (chainLink) {
616 var p = chainLink.walkedPath.pathContext;
617 var fn = linkLineMap[p.type];
618 if (fn) {
619 return fn(p, first, chainLink.reversed);
620 }
621 }
622 }
623 /**
624 * Get the angle of a joint between 2 chain links.
625 *
626 * @param linkA First chain link.
627 * @param linkB Second chain link.
628 * @returns Angle between chain links.
629 */
630 function ofChainLinkJoint(linkA, linkB) {
631 if (arguments.length < 2)
632 return null;
633 var linkLines = [linkA, linkB].map(function (link, i) { return getLinkLine(link, i === 0); });
634 var result = noRevolutions(ofLineInDegrees(linkLines[1]) - ofLineInDegrees(linkLines[0]));
635 if (result > 180)
636 result -= 360;
637 return result;
638 }
639 angle.ofChainLinkJoint = ofChainLinkJoint;
640 })(angle = MakerJs.angle || (MakerJs.angle = {}));
641})(MakerJs || (MakerJs = {}));
642var MakerJs;
643(function (MakerJs) {
644 var point;
645 (function (point) {
646 /**
647 * Add two points together and return the result as a new point object.
648 *
649 * @param a First point.
650 * @param b Second point.
651 * @param subtract Optional boolean to subtract instead of add.
652 * @returns A new point object.
653 */
654 function add(a, b, subtract) {
655 var newPoint = clone(a);
656 if (!b)
657 return newPoint;
658 for (var i = 2; i--;) {
659 if (subtract) {
660 newPoint[i] -= b[i];
661 }
662 else {
663 newPoint[i] += b[i];
664 }
665 }
666 return newPoint;
667 }
668 point.add = add;
669 /**
670 * Get the average of two points.
671 *
672 * @param a First point.
673 * @param b Second point.
674 * @returns New point object which is the average of a and b.
675 */
676 function average(a, b) {
677 function avg(i) {
678 return (a[i] + b[i]) / 2;
679 }
680 return [avg(0), avg(1)];
681 }
682 point.average = average;
683 /**
684 * Clone a point into a new point.
685 *
686 * @param pointToClone The point to clone.
687 * @returns A new point with same values as the original.
688 */
689 function clone(pointToClone) {
690 if (!pointToClone)
691 return point.zero();
692 return [pointToClone[0], pointToClone[1]];
693 }
694 point.clone = clone;
695 /**
696 * From an array of points, find the closest point to a given reference point.
697 *
698 * @param referencePoint The reference point.
699 * @param pointOptions Array of points to choose from.
700 * @returns The first closest point from the pointOptions.
701 */
702 function closest(referencePoint, pointOptions) {
703 var smallest = {
704 index: 0,
705 distance: -1
706 };
707 for (var i = 0; i < pointOptions.length; i++) {
708 var distance = MakerJs.measure.pointDistance(referencePoint, pointOptions[i]);
709 if (smallest.distance == -1 || distance < smallest.distance) {
710 smallest.distance = distance;
711 smallest.index = i;
712 }
713 }
714 return pointOptions[smallest.index];
715 }
716 point.closest = closest;
717 /**
718 * @private
719 */
720 var zero_cos = {};
721 zero_cos[Math.PI / 2] = true;
722 zero_cos[3 * Math.PI / 2] = true;
723 /**
724 * @private
725 */
726 var zero_sin = {};
727 zero_sin[Math.PI] = true;
728 zero_sin[2 * Math.PI] = true;
729 /**
730 * Get a point from its polar coordinates.
731 *
732 * @param angleInRadians The angle of the polar coordinate, in radians.
733 * @param radius The radius of the polar coordinate.
734 * @returns A new point object.
735 */
736 function fromPolar(angleInRadians, radius) {
737 return [
738 (angleInRadians in zero_cos) ? 0 : MakerJs.round(radius * Math.cos(angleInRadians)),
739 (angleInRadians in zero_sin) ? 0 : MakerJs.round(radius * Math.sin(angleInRadians))
740 ];
741 }
742 point.fromPolar = fromPolar;
743 /**
744 * Get a point on a circle or arc path, at a given angle.
745 * @param angleInDegrees The angle at which you want to find the point, in degrees.
746 * @param circle A circle or arc.
747 * @returns A new point object.
748 */
749 function fromAngleOnCircle(angleInDegrees, circle) {
750 return add(circle.origin, fromPolar(MakerJs.angle.toRadians(angleInDegrees), circle.radius));
751 }
752 point.fromAngleOnCircle = fromAngleOnCircle;
753 /**
754 * Get the two end points of an arc path.
755 *
756 * @param arc The arc path object.
757 * @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.
758 */
759 function fromArc(arc) {
760 return [fromAngleOnCircle(arc.startAngle, arc), fromAngleOnCircle(arc.endAngle, arc)];
761 }
762 point.fromArc = fromArc;
763 /**
764 * @private
765 */
766 var pathEndsMap = {};
767 pathEndsMap[MakerJs.pathType.Arc] = function (arc) {
768 return point.fromArc(arc);
769 };
770 pathEndsMap[MakerJs.pathType.Line] = function (line) {
771 return [line.origin, line.end];
772 };
773 pathEndsMap[MakerJs.pathType.BezierSeed] = pathEndsMap[MakerJs.pathType.Line];
774 /**
775 * Get the two end points of a path.
776 *
777 * @param pathContext The path object.
778 * @returns Array with 2 elements: [0] is the point object corresponding to the origin, [1] is the point object corresponding to the end.
779 */
780 function fromPathEnds(pathContext, pathOffset) {
781 var result = null;
782 var fn = pathEndsMap[pathContext.type];
783 if (fn) {
784 result = fn(pathContext);
785 if (pathOffset) {
786 result = result.map(function (p) { return add(p, pathOffset); });
787 }
788 }
789 return result;
790 }
791 point.fromPathEnds = fromPathEnds;
792 /**
793 * @private
794 */
795 function verticalIntersectionPoint(verticalLine, nonVerticalSlope) {
796 var x = verticalLine.origin[0];
797 var y = nonVerticalSlope.slope * x + nonVerticalSlope.yIntercept;
798 return [x, y];
799 }
800 /**
801 * Calculates the intersection of slopes of two lines.
802 *
803 * @param lineA First line to use for slope.
804 * @param lineB Second line to use for slope.
805 * @param options Optional IPathIntersectionOptions.
806 * @returns point of intersection of the two slopes, or null if the slopes did not intersect.
807 */
808 function fromSlopeIntersection(lineA, lineB, options) {
809 if (options === void 0) { options = {}; }
810 var slopeA = MakerJs.measure.lineSlope(lineA);
811 var slopeB = MakerJs.measure.lineSlope(lineB);
812 //see if slope are parallel
813 if (MakerJs.measure.isSlopeParallel(slopeA, slopeB)) {
814 if (MakerJs.measure.isSlopeEqual(slopeA, slopeB)) {
815 //check for overlap
816 options.out_AreOverlapped = MakerJs.measure.isLineOverlapping(lineA, lineB, options.excludeTangents);
817 }
818 return null;
819 }
820 var pointOfIntersection;
821 if (!slopeA.hasSlope) {
822 pointOfIntersection = verticalIntersectionPoint(lineA, slopeB);
823 }
824 else if (!slopeB.hasSlope) {
825 pointOfIntersection = verticalIntersectionPoint(lineB, slopeA);
826 }
827 else {
828 // find intersection by line equation
829 var x = (slopeB.yIntercept - slopeA.yIntercept) / (slopeA.slope - slopeB.slope);
830 var y = slopeA.slope * x + slopeA.yIntercept;
831 pointOfIntersection = [x, y];
832 }
833 return pointOfIntersection;
834 }
835 point.fromSlopeIntersection = fromSlopeIntersection;
836 /**
837 * @private
838 */
839 function midCircle(circle, midAngle) {
840 return point.add(circle.origin, point.fromPolar(MakerJs.angle.toRadians(midAngle), circle.radius));
841 }
842 /**
843 * @private
844 */
845 var middleMap = {};
846 middleMap[MakerJs.pathType.Arc] = function (arc, ratio) {
847 var midAngle = MakerJs.angle.ofArcMiddle(arc, ratio);
848 return midCircle(arc, midAngle);
849 };
850 middleMap[MakerJs.pathType.Circle] = function (circle, ratio) {
851 return midCircle(circle, 360 * ratio);
852 };
853 middleMap[MakerJs.pathType.Line] = function (line, ratio) {
854 function ration(a, b) {
855 return a + (b - a) * ratio;
856 }
857 ;
858 return [
859 ration(line.origin[0], line.end[0]),
860 ration(line.origin[1], line.end[1])
861 ];
862 };
863 middleMap[MakerJs.pathType.BezierSeed] = function (seed, ratio) {
864 return MakerJs.models.BezierCurve.computePoint(seed, ratio);
865 };
866 /**
867 * Get the middle point of a path.
868 *
869 * @param pathContext The path object.
870 * @param ratio Optional ratio (between 0 and 1) of point along the path. Default is .5 for middle.
871 * @returns Point on the path, in the middle of the path.
872 */
873 function middle(pathContext, ratio) {
874 if (ratio === void 0) { ratio = .5; }
875 var midPoint = null;
876 var fn = middleMap[pathContext.type];
877 if (fn) {
878 midPoint = fn(pathContext, ratio);
879 }
880 return midPoint;
881 }
882 point.middle = middle;
883 /**
884 * Create a clone of a point, mirrored on either or both x and y axes.
885 *
886 * @param pointToMirror The point to mirror.
887 * @param mirrorX Boolean to mirror on the x axis.
888 * @param mirrorY Boolean to mirror on the y axis.
889 * @returns Mirrored point.
890 */
891 function mirror(pointToMirror, mirrorX, mirrorY) {
892 var p = clone(pointToMirror);
893 if (mirrorX) {
894 p[0] = -p[0];
895 }
896 if (mirrorY) {
897 p[1] = -p[1];
898 }
899 return p;
900 }
901 point.mirror = mirror;
902 /**
903 * Round the values of a point.
904 *
905 * @param pointContext The point to serialize.
906 * @param accuracy Optional exemplar number of decimal places.
907 * @returns A new point with the values rounded.
908 */
909 function rounded(pointContext, accuracy) {
910 return [MakerJs.round(pointContext[0], accuracy), MakerJs.round(pointContext[1], accuracy)];
911 }
912 point.rounded = rounded;
913 /**
914 * Rotate a point.
915 *
916 * @param pointToRotate The point to rotate.
917 * @param angleInDegrees The amount of rotation, in degrees.
918 * @param rotationOrigin The center point of rotation.
919 * @returns A new point.
920 */
921 function rotate(pointToRotate, angleInDegrees, rotationOrigin) {
922 if (rotationOrigin === void 0) { rotationOrigin = [0, 0]; }
923 var pointAngleInRadians = MakerJs.angle.ofPointInRadians(rotationOrigin, pointToRotate);
924 var d = MakerJs.measure.pointDistance(rotationOrigin, pointToRotate);
925 var rotatedPoint = fromPolar(pointAngleInRadians + MakerJs.angle.toRadians(angleInDegrees), d);
926 return add(rotationOrigin, rotatedPoint);
927 }
928 point.rotate = rotate;
929 /**
930 * Scale a point's coordinates.
931 *
932 * @param pointToScale The point to scale.
933 * @param scaleValue The amount of scaling.
934 * @returns A new point.
935 */
936 function scale(pointToScale, scaleValue) {
937 var p = clone(pointToScale);
938 for (var i = 2; i--;) {
939 p[i] *= scaleValue;
940 }
941 return p;
942 }
943 point.scale = scale;
944 /**
945 * Distort a point's coordinates.
946 *
947 * @param pointToDistort The point to distort.
948 * @param scaleX The amount of x scaling.
949 * @param scaleY The amount of y scaling.
950 * @returns A new point.
951 */
952 function distort(pointToDistort, scaleX, scaleY) {
953 return [pointToDistort[0] * scaleX, pointToDistort[1] * scaleY];
954 }
955 point.distort = distort;
956 /**
957 * Subtract a point from another point, and return the result as a new point. Shortcut to Add(a, b, subtract = true).
958 *
959 * @param a First point.
960 * @param b Second point.
961 * @returns A new point object.
962 */
963 function subtract(a, b) {
964 return add(a, b, true);
965 }
966 point.subtract = subtract;
967 /**
968 * A point at 0,0 coordinates.
969 * NOTE: It is important to call this as a method, with the empty parentheses.
970 *
971 * @returns A new point.
972 */
973 function zero() {
974 return [0, 0];
975 }
976 point.zero = zero;
977 })(point = MakerJs.point || (MakerJs.point = {}));
978})(MakerJs || (MakerJs = {}));
979var MakerJs;
980(function (MakerJs) {
981 var path;
982 (function (path) {
983 /**
984 * Add a path to a model. This is basically equivalent to:
985 * ```
986 * parentModel.paths[pathId] = childPath;
987 * ```
988 * with additional checks to make it safe for cascading.
989 *
990 * @param childPath The path to add.
991 * @param parentModel The model to add to.
992 * @param pathId The id of the path.
993 * @param overwrite Optional flag to overwrite any path referenced by pathId. Default is false, which will create an id similar to pathId.
994 * @returns The original path (for cascading).
995 */
996 function addTo(childPath, parentModel, pathId, overwrite) {
997 if (overwrite === void 0) { overwrite = false; }
998 MakerJs.model.addPath(parentModel, childPath, pathId, overwrite);
999 return childPath;
1000 }
1001 path.addTo = addTo;
1002 /**
1003 * @private
1004 */
1005 function copyLayer(pathA, pathB) {
1006 if (pathA && pathB && typeof pathA.layer !== 'undefined') {
1007 pathB.layer = pathA.layer;
1008 }
1009 //carry extra props if this is an IPathArcInBezierCurve
1010 if (pathA && pathB && ('bezierData' in pathA)) {
1011 pathB.bezierData = pathA.bezierData;
1012 }
1013 }
1014 /**
1015 * @private
1016 */
1017 var copyPropsMap = {};
1018 copyPropsMap[MakerJs.pathType.Circle] = function (srcCircle, destCircle, offset) {
1019 destCircle.radius = srcCircle.radius;
1020 };
1021 copyPropsMap[MakerJs.pathType.Arc] = function (srcArc, destArc, offset) {
1022 copyPropsMap[MakerJs.pathType.Circle](srcArc, destArc, offset);
1023 destArc.startAngle = srcArc.startAngle;
1024 destArc.endAngle = srcArc.endAngle;
1025 };
1026 copyPropsMap[MakerJs.pathType.Line] = function (srcLine, destLine, offset) {
1027 destLine.end = MakerJs.point.add(srcLine.end, offset);
1028 };
1029 copyPropsMap[MakerJs.pathType.BezierSeed] = function (srcSeed, destSeed, offset) {
1030 copyPropsMap[MakerJs.pathType.Line](srcSeed, destSeed, offset);
1031 destSeed.controls = srcSeed.controls.map(function (p) { return MakerJs.point.add(p, offset); });
1032 };
1033 /**
1034 * Create a clone of a path. This is faster than cloneObject.
1035 *
1036 * @param pathToClone The path to clone.
1037 * @param offset Optional point to move path a relative distance.
1038 * @returns Cloned path.
1039 */
1040 function clone(pathToClone, offset) {
1041 var result = { type: pathToClone.type, origin: MakerJs.point.add(pathToClone.origin, offset) };
1042 var fn = copyPropsMap[pathToClone.type];
1043 if (fn) {
1044 fn(pathToClone, result, offset);
1045 }
1046 copyLayer(pathToClone, result);
1047 return result;
1048 }
1049 path.clone = clone;
1050 /**
1051 * Copy the schema properties of one path to another.
1052 *
1053 * @param srcPath The source path to copy property values from.
1054 * @param destPath The destination path to copy property values to.
1055 * @returns The source path.
1056 */
1057 function copyProps(srcPath, destPath) {
1058 var fn = copyPropsMap[srcPath.type];
1059 if (fn) {
1060 destPath.origin = MakerJs.point.clone(srcPath.origin);
1061 fn(srcPath, destPath);
1062 }
1063 copyLayer(srcPath, destPath);
1064 return srcPath;
1065 }
1066 path.copyProps = copyProps;
1067 /**
1068 * @private
1069 */
1070 var mirrorMap = {};
1071 mirrorMap[MakerJs.pathType.Line] = function (line, origin, mirrorX, mirrorY) {
1072 return new MakerJs.paths.Line(origin, MakerJs.point.mirror(line.end, mirrorX, mirrorY));
1073 };
1074 mirrorMap[MakerJs.pathType.Circle] = function (circle, origin, mirrorX, mirrorY) {
1075 return new MakerJs.paths.Circle(origin, circle.radius);
1076 };
1077 mirrorMap[MakerJs.pathType.Arc] = function (arc, origin, mirrorX, mirrorY) {
1078 var startAngle = MakerJs.angle.mirror(arc.startAngle, mirrorX, mirrorY);
1079 var endAngle = MakerJs.angle.mirror(MakerJs.angle.ofArcEnd(arc), mirrorX, mirrorY);
1080 var xor = mirrorX != mirrorY;
1081 return new MakerJs.paths.Arc(origin, arc.radius, xor ? endAngle : startAngle, xor ? startAngle : endAngle);
1082 };
1083 mirrorMap[MakerJs.pathType.BezierSeed] = function (seed, origin, mirrorX, mirrorY) {
1084 var mirrored = mirrorMap[MakerJs.pathType.Line](seed, origin, mirrorX, mirrorY);
1085 mirrored.type = MakerJs.pathType.BezierSeed;
1086 mirrored.controls = seed.controls.map(function (c) { return MakerJs.point.mirror(c, mirrorX, mirrorY); });
1087 return mirrored;
1088 };
1089 /**
1090 * Set the layer of a path. This is equivalent to:
1091 * ```
1092 * pathContext.layer = layer;
1093 * ```
1094 *
1095 * @param pathContext The path to set the layer.
1096 * @param layer The layer name.
1097 * @returns The original path (for cascading).
1098 */
1099 function layer(pathContext, layer) {
1100 pathContext.layer = layer;
1101 return pathContext;
1102 }
1103 path.layer = layer;
1104 /**
1105 * Create a clone of a path, mirrored on either or both x and y axes.
1106 *
1107 * @param pathToMirror The path to mirror.
1108 * @param mirrorX Boolean to mirror on the x axis.
1109 * @param mirrorY Boolean to mirror on the y axis.
1110 * @returns Mirrored path.
1111 */
1112 function mirror(pathToMirror, mirrorX, mirrorY) {
1113 var newPath = null;
1114 if (pathToMirror) {
1115 var origin = MakerJs.point.mirror(pathToMirror.origin, mirrorX, mirrorY);
1116 var fn = mirrorMap[pathToMirror.type];
1117 if (fn) {
1118 newPath = fn(pathToMirror, origin, mirrorX, mirrorY);
1119 }
1120 }
1121 copyLayer(pathToMirror, newPath);
1122 return newPath;
1123 }
1124 path.mirror = mirror;
1125 /**
1126 * @private
1127 */
1128 var moveMap = {};
1129 moveMap[MakerJs.pathType.Line] = function (line, origin) {
1130 var delta = MakerJs.point.subtract(line.end, line.origin);
1131 line.end = MakerJs.point.add(origin, delta);
1132 };
1133 /**
1134 * Move a path to an absolute point.
1135 *
1136 * @param pathToMove The path to move.
1137 * @param origin The new origin for the path.
1138 * @returns The original path (for cascading).
1139 */
1140 function move(pathToMove, origin) {
1141 if (pathToMove) {
1142 var fn = moveMap[pathToMove.type];
1143 if (fn) {
1144 fn(pathToMove, origin);
1145 }
1146 pathToMove.origin = origin;
1147 }
1148 return pathToMove;
1149 }
1150 path.move = move;
1151 /**
1152 * @private
1153 */
1154 var moveRelativeMap = {};
1155 moveRelativeMap[MakerJs.pathType.Line] = function (line, delta, subtract) {
1156 line.end = MakerJs.point.add(line.end, delta, subtract);
1157 };
1158 moveRelativeMap[MakerJs.pathType.BezierSeed] = function (seed, delta, subtract) {
1159 moveRelativeMap[MakerJs.pathType.Line](seed, delta, subtract);
1160 seed.controls = seed.controls.map(function (c) { return MakerJs.point.add(c, delta, subtract); });
1161 };
1162 /**
1163 * Move a path's origin by a relative amount.
1164 *
1165 * @param pathToMove The path to move.
1166 * @param delta The x & y adjustments as a point object.
1167 * @param subtract Optional boolean to subtract instead of add.
1168 * @returns The original path (for cascading).
1169 */
1170 function moveRelative(pathToMove, delta, subtract) {
1171 if (pathToMove && delta) {
1172 pathToMove.origin = MakerJs.point.add(pathToMove.origin, delta, subtract);
1173 var fn = moveRelativeMap[pathToMove.type];
1174 if (fn) {
1175 fn(pathToMove, delta, subtract);
1176 }
1177 }
1178 return pathToMove;
1179 }
1180 path.moveRelative = moveRelative;
1181 /**
1182 * Move some paths relatively during a task execution, then unmove them.
1183 *
1184 * @param pathsToMove The paths to move.
1185 * @param deltas The x & y adjustments as a point object array.
1186 * @param task The function to call while the paths are temporarily moved.
1187 */
1188 function moveTemporary(pathsToMove, deltas, task) {
1189 var subtract = false;
1190 function move(pathToOffset, i) {
1191 if (deltas[i]) {
1192 moveRelative(pathToOffset, deltas[i], subtract);
1193 }
1194 }
1195 pathsToMove.map(move);
1196 task();
1197 subtract = true;
1198 pathsToMove.map(move);
1199 }
1200 path.moveTemporary = moveTemporary;
1201 /**
1202 * @private
1203 */
1204 var rotateMap = {};
1205 rotateMap[MakerJs.pathType.Line] = function (line, angleInDegrees, rotationOrigin) {
1206 line.end = MakerJs.point.rotate(line.end, angleInDegrees, rotationOrigin);
1207 };
1208 rotateMap[MakerJs.pathType.Arc] = function (arc, angleInDegrees, rotationOrigin) {
1209 arc.startAngle = MakerJs.angle.noRevolutions(arc.startAngle + angleInDegrees);
1210 arc.endAngle = MakerJs.angle.noRevolutions(arc.endAngle + angleInDegrees);
1211 };
1212 rotateMap[MakerJs.pathType.BezierSeed] = function (seed, angleInDegrees, rotationOrigin) {
1213 rotateMap[MakerJs.pathType.Line](seed, angleInDegrees, rotationOrigin);
1214 seed.controls = seed.controls.map(function (c) { return MakerJs.point.rotate(c, angleInDegrees, rotationOrigin); });
1215 };
1216 /**
1217 * Rotate a path.
1218 *
1219 * @param pathToRotate The path to rotate.
1220 * @param angleInDegrees The amount of rotation, in degrees.
1221 * @param rotationOrigin The center point of rotation.
1222 * @returns The original path (for cascading).
1223 */
1224 function rotate(pathToRotate, angleInDegrees, rotationOrigin) {
1225 if (rotationOrigin === void 0) { rotationOrigin = [0, 0]; }
1226 if (!pathToRotate || !angleInDegrees)
1227 return pathToRotate;
1228 pathToRotate.origin = MakerJs.point.rotate(pathToRotate.origin, angleInDegrees, rotationOrigin);
1229 var fn = rotateMap[pathToRotate.type];
1230 if (fn) {
1231 fn(pathToRotate, angleInDegrees, rotationOrigin);
1232 }
1233 return pathToRotate;
1234 }
1235 path.rotate = rotate;
1236 /**
1237 * @private
1238 */
1239 var scaleMap = {};
1240 scaleMap[MakerJs.pathType.Line] = function (line, scaleValue) {
1241 line.end = MakerJs.point.scale(line.end, scaleValue);
1242 };
1243 scaleMap[MakerJs.pathType.BezierSeed] = function (seed, scaleValue) {
1244 scaleMap[MakerJs.pathType.Line](seed, scaleValue);
1245 seed.controls = seed.controls.map(function (c) { return MakerJs.point.scale(c, scaleValue); });
1246 };
1247 scaleMap[MakerJs.pathType.Circle] = function (circle, scaleValue) {
1248 circle.radius *= scaleValue;
1249 };
1250 scaleMap[MakerJs.pathType.Arc] = scaleMap[MakerJs.pathType.Circle];
1251 /**
1252 * Scale a path.
1253 *
1254 * @param pathToScale The path to scale.
1255 * @param scaleValue The amount of scaling.
1256 * @returns The original path (for cascading).
1257 */
1258 function scale(pathToScale, scaleValue) {
1259 if (!pathToScale || scaleValue === 1 || !scaleValue)
1260 return pathToScale;
1261 pathToScale.origin = MakerJs.point.scale(pathToScale.origin, scaleValue);
1262 var fn = scaleMap[pathToScale.type];
1263 if (fn) {
1264 fn(pathToScale, scaleValue);
1265 }
1266 return pathToScale;
1267 }
1268 path.scale = scale;
1269 /**
1270 * @private
1271 */
1272 var distortMap = {};
1273 distortMap[MakerJs.pathType.Arc] = function (arc, scaleX, scaleY) {
1274 return new MakerJs.models.EllipticArc(arc, scaleX, scaleY);
1275 };
1276 distortMap[MakerJs.pathType.Circle] = function (circle, scaleX, scaleY) {
1277 var ellipse = new MakerJs.models.Ellipse(circle.radius * scaleX, circle.radius * scaleY);
1278 ellipse.origin = MakerJs.point.distort(circle.origin, scaleX, scaleY);
1279 return ellipse;
1280 };
1281 distortMap[MakerJs.pathType.Line] = function (line, scaleX, scaleY) {
1282 return new MakerJs.paths.Line([line.origin, line.end].map(function (p) { return MakerJs.point.distort(p, scaleX, scaleY); }));
1283 };
1284 distortMap[MakerJs.pathType.BezierSeed] = function (seed, scaleX, scaleY) {
1285 var d = MakerJs.point.distort;
1286 return {
1287 type: MakerJs.pathType.BezierSeed,
1288 origin: d(seed.origin, scaleX, scaleY),
1289 controls: seed.controls.map(function (c) { return d(c, scaleX, scaleY); }),
1290 end: d(seed.end, scaleX, scaleY)
1291 };
1292 };
1293 /**
1294 * Distort a path - scale x and y individually.
1295 *
1296 * @param pathToDistort The path to distort.
1297 * @param scaleX The amount of x scaling.
1298 * @param scaleY The amount of y scaling.
1299 * @returns A new IModel (for circles and arcs) or IPath (for lines and bezier seeds).
1300 */
1301 function distort(pathToDistort, scaleX, scaleY) {
1302 if (!pathToDistort || !scaleX || !scaleY)
1303 return null;
1304 var fn = distortMap[pathToDistort.type];
1305 if (fn) {
1306 var distorted = fn(pathToDistort, scaleX, scaleY);
1307 if (typeof pathToDistort.layer !== 'undefined') {
1308 distorted.layer = pathToDistort.layer;
1309 }
1310 return distorted;
1311 }
1312 return null;
1313 }
1314 path.distort = distort;
1315 /**
1316 * Connect 2 lines at their slope intersection point.
1317 *
1318 * @param lineA First line to converge.
1319 * @param lineB Second line to converge.
1320 * @param useOriginA Optional flag to converge the origin point of lineA instead of the end point.
1321 * @param useOriginB Optional flag to converge the origin point of lineB instead of the end point.
1322 * @returns point of convergence.
1323 */
1324 function converge(lineA, lineB, useOriginA, useOriginB) {
1325 var p = MakerJs.point.fromSlopeIntersection(lineA, lineB);
1326 if (p) {
1327 var lines = [lineA, lineB];
1328 var useOrigin = [useOriginA, useOriginB];
1329 if (arguments.length === 2) {
1330 //converge to closest
1331 lines.forEach(function (line, i) {
1332 useOrigin[i] = (MakerJs.point.closest(p, [line.origin, line.end]) === line.origin);
1333 });
1334 }
1335 function setPoint(line, useOrigin) {
1336 var setP;
1337 if (useOrigin) {
1338 setP = line.origin;
1339 }
1340 else {
1341 setP = line.end;
1342 }
1343 setP[0] = p[0];
1344 setP[1] = p[1];
1345 }
1346 lines.forEach(function (line, i) {
1347 setPoint(line, useOrigin[i]);
1348 });
1349 }
1350 return p;
1351 }
1352 path.converge = converge;
1353 /**
1354 * @private
1355 */
1356 var alterMap = {};
1357 alterMap[MakerJs.pathType.Arc] = function (arc, pathLength, distance, useOrigin) {
1358 var span = MakerJs.angle.ofArcSpan(arc);
1359 var delta = ((pathLength + distance) * span / pathLength) - span;
1360 if (useOrigin) {
1361 arc.startAngle -= delta;
1362 }
1363 else {
1364 arc.endAngle += delta;
1365 }
1366 };
1367 alterMap[MakerJs.pathType.Circle] = function (circle, pathLength, distance, useOrigin) {
1368 circle.radius *= (pathLength + distance) / pathLength;
1369 };
1370 alterMap[MakerJs.pathType.Line] = function (line, pathLength, distance, useOrigin) {
1371 var delta = MakerJs.point.scale(MakerJs.point.subtract(line.end, line.origin), distance / pathLength);
1372 if (useOrigin) {
1373 line.origin = MakerJs.point.subtract(line.origin, delta);
1374 }
1375 else {
1376 line.end = MakerJs.point.add(line.end, delta);
1377 }
1378 };
1379 /**
1380 * Alter a path by lengthening or shortening it.
1381 *
1382 * @param pathToAlter Path to alter.
1383 * @param distance Numeric amount of length to add or remove from the path. Use a positive number to lengthen, negative to shorten. When shortening: this function will not alter the path and will return null if the resulting path length is less than or equal to zero.
1384 * @param useOrigin Optional flag to alter from the origin instead of the end of the path.
1385 * @returns The original path (for cascading), or null if the path could not be altered.
1386 */
1387 function alterLength(pathToAlter, distance, useOrigin) {
1388 if (useOrigin === void 0) { useOrigin = false; }
1389 if (!pathToAlter || !distance)
1390 return null;
1391 var fn = alterMap[pathToAlter.type];
1392 if (fn) {
1393 var pathLength = MakerJs.measure.pathLength(pathToAlter);
1394 if (!pathLength || -distance >= pathLength)
1395 return null;
1396 fn(pathToAlter, pathLength, distance, useOrigin);
1397 return pathToAlter;
1398 }
1399 return null;
1400 }
1401 path.alterLength = alterLength;
1402 /**
1403 * Get points along a path.
1404 *
1405 * @param pathContext Path to get points from.
1406 * @param numberOfPoints Number of points to divide the path.
1407 * @returns Array of points which are on the path spread at a uniform interval.
1408 */
1409 function toPoints(pathContext, numberOfPoints) {
1410 //avoid division by zero when there is only one point
1411 if (numberOfPoints == 1) {
1412 return [MakerJs.point.middle(pathContext)];
1413 }
1414 var points = [];
1415 var base = numberOfPoints;
1416 if (pathContext.type != MakerJs.pathType.Circle)
1417 base--;
1418 for (var i = 0; i < numberOfPoints; i++) {
1419 points.push(MakerJs.point.middle(pathContext, i / base));
1420 }
1421 return points;
1422 }
1423 path.toPoints = toPoints;
1424 /**
1425 * @private
1426 */
1427 var numberOfKeyPointsMap = {};
1428 numberOfKeyPointsMap[MakerJs.pathType.Line] = function (line) {
1429 return 2;
1430 };
1431 numberOfKeyPointsMap[MakerJs.pathType.Circle] = function (circle, maxPointDistance) {
1432 var len = MakerJs.measure.pathLength(circle);
1433 if (!len)
1434 return 0;
1435 maxPointDistance = maxPointDistance || len;
1436 return Math.max(8, Math.ceil(len / (maxPointDistance || len)));
1437 };
1438 numberOfKeyPointsMap[MakerJs.pathType.Arc] = function (arc, maxPointDistance) {
1439 var len = MakerJs.measure.pathLength(arc);
1440 if (!len)
1441 return 0;
1442 var minPoints = Math.ceil(MakerJs.angle.ofArcSpan(arc) / 45) + 1;
1443 return Math.max(minPoints, Math.ceil(len / (maxPointDistance || len)));
1444 };
1445 /**
1446 * Get key points (a minimal a number of points) along a path.
1447 *
1448 * @param pathContext Path to get points from.
1449 * @param maxArcFacet Optional maximum length between points on an arc or circle.
1450 * @returns Array of points which are on the path.
1451 */
1452 function toKeyPoints(pathContext, maxArcFacet) {
1453 if (pathContext.type == MakerJs.pathType.BezierSeed) {
1454 var curve = new MakerJs.models.BezierCurve(pathContext);
1455 var curveKeyPoints;
1456 MakerJs.model.findChains(curve, function (chains, loose, layer) {
1457 if (chains.length == 1) {
1458 var c = chains[0];
1459 switch (c.links[0].walkedPath.pathId) {
1460 case 'arc_0':
1461 case 'line_0':
1462 break;
1463 default:
1464 MakerJs.chain.reverse(c);
1465 }
1466 curveKeyPoints = MakerJs.chain.toKeyPoints(c);
1467 }
1468 else if (loose.length === 1) {
1469 curveKeyPoints = toKeyPoints(loose[0].pathContext);
1470 }
1471 });
1472 return curveKeyPoints;
1473 }
1474 else {
1475 var fn = numberOfKeyPointsMap[pathContext.type];
1476 if (fn) {
1477 var numberOfKeyPoints = fn(pathContext, maxArcFacet);
1478 if (numberOfKeyPoints) {
1479 return toPoints(pathContext, numberOfKeyPoints);
1480 }
1481 }
1482 }
1483 return [];
1484 }
1485 path.toKeyPoints = toKeyPoints;
1486 /**
1487 * Center a path at [0, 0].
1488 *
1489 * @param pathToCenter The path to center.
1490 * @returns The original path (for cascading).
1491 */
1492 function center(pathToCenter) {
1493 var m = MakerJs.measure.pathExtents(pathToCenter);
1494 var c = MakerJs.point.average(m.high, m.low);
1495 var o = MakerJs.point.subtract(pathToCenter.origin || [0, 0], c);
1496 move(pathToCenter, o);
1497 return pathToCenter;
1498 }
1499 path.center = center;
1500 /**
1501 * Move a path so its bounding box begins at [0, 0].
1502 *
1503 * @param pathToZero The path to zero.
1504 * @returns The original path (for cascading).
1505 */
1506 function zero(pathToZero) {
1507 var m = MakerJs.measure.pathExtents(pathToZero);
1508 var z = MakerJs.point.subtract(pathToZero.origin || [0, 0], m.low);
1509 move(pathToZero, z);
1510 return pathToZero;
1511 }
1512 path.zero = zero;
1513 })(path = MakerJs.path || (MakerJs.path = {}));
1514})(MakerJs || (MakerJs = {}));
1515var MakerJs;
1516(function (MakerJs) {
1517 var path;
1518 (function (path_1) {
1519 /**
1520 * @private
1521 */
1522 var breakPathFunctionMap = {};
1523 breakPathFunctionMap[MakerJs.pathType.Arc] = function (arc, pointOfBreak) {
1524 var angleAtBreakPoint = MakerJs.angle.ofPointInDegrees(arc.origin, pointOfBreak);
1525 if (MakerJs.measure.isAngleEqual(angleAtBreakPoint, arc.startAngle) || MakerJs.measure.isAngleEqual(angleAtBreakPoint, arc.endAngle)) {
1526 return null;
1527 }
1528 function getAngleStrictlyBetweenArcAngles() {
1529 var startAngle = MakerJs.angle.noRevolutions(arc.startAngle);
1530 var endAngle = startAngle + MakerJs.angle.ofArcEnd(arc) - arc.startAngle;
1531 var tries = [0, 1, -1];
1532 for (var i = 0; i < tries.length; i++) {
1533 var add = +360 * tries[i];
1534 if (MakerJs.measure.isBetween(angleAtBreakPoint + add, startAngle, endAngle, true)) {
1535 return arc.startAngle + angleAtBreakPoint + add - startAngle;
1536 }
1537 }
1538 return null;
1539 }
1540 var angleAtBreakPointBetween = getAngleStrictlyBetweenArcAngles();
1541 if (angleAtBreakPointBetween == null) {
1542 return null;
1543 }
1544 var savedEndAngle = arc.endAngle;
1545 arc.endAngle = angleAtBreakPointBetween;
1546 //clone the original to carry other properties
1547 var copy = MakerJs.cloneObject(arc);
1548 copy.startAngle = angleAtBreakPointBetween;
1549 copy.endAngle = savedEndAngle;
1550 return copy;
1551 };
1552 breakPathFunctionMap[MakerJs.pathType.Circle] = function (circle, pointOfBreak) {
1553 //breaking a circle turns it into an arc
1554 circle.type = MakerJs.pathType.Arc;
1555 var arc = circle;
1556 var angleAtBreakPoint = MakerJs.angle.ofPointInDegrees(circle.origin, pointOfBreak);
1557 arc.startAngle = angleAtBreakPoint;
1558 arc.endAngle = angleAtBreakPoint + 360;
1559 return null;
1560 };
1561 breakPathFunctionMap[MakerJs.pathType.Line] = function (line, pointOfBreak) {
1562 if (!MakerJs.measure.isBetweenPoints(pointOfBreak, line, true)) {
1563 return null;
1564 }
1565 var savedEndPoint = line.end;
1566 line.end = pointOfBreak;
1567 //clone the original to carry other properties
1568 var copy = MakerJs.cloneObject(line);
1569 copy.origin = pointOfBreak;
1570 copy.end = savedEndPoint;
1571 return copy;
1572 };
1573 /**
1574 * Breaks a path in two. The supplied path will end at the supplied pointOfBreak,
1575 * a new path is returned which begins at the pointOfBreak and ends at the supplied path's initial end point.
1576 * For Circle, the original path will be converted in place to an Arc, and null is returned.
1577 *
1578 * @param pathToBreak The path to break.
1579 * @param pointOfBreak The point at which to break the path.
1580 * @returns A new path of the same type, when path type is line or arc. Returns null for circle.
1581 */
1582 function breakAtPoint(pathToBreak, pointOfBreak) {
1583 if (pathToBreak && pointOfBreak) {
1584 var fn = breakPathFunctionMap[pathToBreak.type];
1585 if (fn) {
1586 var result = fn(pathToBreak, pointOfBreak);
1587 if (result && ('layer' in pathToBreak)) {
1588 result.layer = pathToBreak.layer;
1589 }
1590 return result;
1591 }
1592 }
1593 return null;
1594 }
1595 path_1.breakAtPoint = breakAtPoint;
1596 })(path = MakerJs.path || (MakerJs.path = {}));
1597})(MakerJs || (MakerJs = {}));
1598var MakerJs;
1599(function (MakerJs) {
1600 var paths;
1601 (function (paths) {
1602 /**
1603 * Class for arc path.
1604 */
1605 var Arc = /** @class */ (function () {
1606 function Arc() {
1607 var args = [];
1608 for (var _i = 0; _i < arguments.length; _i++) {
1609 args[_i] = arguments[_i];
1610 }
1611 function getSpan(origin) {
1612 var startAngle = MakerJs.angle.ofPointInDegrees(origin, args[clockwise ? 1 : 0]);
1613 var endAngle = MakerJs.angle.ofPointInDegrees(origin, args[clockwise ? 0 : 1]);
1614 if (endAngle < startAngle) {
1615 endAngle += 360;
1616 }
1617 return {
1618 origin: origin,
1619 startAngle: startAngle,
1620 endAngle: endAngle,
1621 size: endAngle - startAngle
1622 };
1623 }
1624 switch (args.length) {
1625 case 5:
1626 //SVG style arc designation
1627 var pointA = args[0];
1628 var pointB = args[1];
1629 this.radius = args[2];
1630 var largeArc = args[3];
1631 var clockwise = args[4];
1632 var span;
1633 //make sure arc can reach. if not, scale up.
1634 var smallestRadius = MakerJs.measure.pointDistance(pointA, pointB) / 2;
1635 if (MakerJs.round(this.radius - smallestRadius) <= 0) {
1636 this.radius = smallestRadius;
1637 span = getSpan(MakerJs.point.average(pointA, pointB));
1638 }
1639 else {
1640 //find the 2 potential origins
1641 var origins = MakerJs.path.intersection(new Circle(pointA, this.radius), new Circle(pointB, this.radius));
1642 var spans = [];
1643 for (var i = origins.intersectionPoints.length; i--;) {
1644 span = getSpan(origins.intersectionPoints[i]);
1645 //insert sorted by size ascending
1646 if (spans.length == 0 || span.size > spans[0].size) {
1647 spans.push(span);
1648 }
1649 else {
1650 spans.unshift(span);
1651 }
1652 }
1653 var index = largeArc ? 1 : 0;
1654 span = spans[index];
1655 }
1656 this.origin = span.origin;
1657 this.startAngle = span.startAngle;
1658 this.endAngle = span.endAngle;
1659 break;
1660 case 4:
1661 this.origin = args[0];
1662 this.radius = args[1];
1663 this.startAngle = args[2];
1664 this.endAngle = args[3];
1665 break;
1666 case 3:
1667 if (MakerJs.isPoint(args[2])) {
1668 //from 3 points
1669 Circle.apply(this, args);
1670 var angles = [];
1671 for (var i = 0; i < 3; i++) {
1672 angles.push(MakerJs.angle.ofPointInDegrees(this.origin, args[i]));
1673 }
1674 this.startAngle = angles[0];
1675 this.endAngle = angles[2];
1676 //swap start and end angles if this arc does not contain the midpoint
1677 if (!MakerJs.measure.isBetweenArcAngles(angles[1], this, false)) {
1678 this.startAngle = angles[2];
1679 this.endAngle = angles[0];
1680 }
1681 //do not fall through if this was 3 points
1682 break;
1683 }
1684 //fall through to below if 2 points
1685 case 2:
1686 //from 2 points (and optional clockwise flag)
1687 var clockwise = args[2];
1688 Circle.call(this, args[0], args[1]);
1689 this.startAngle = MakerJs.angle.ofPointInDegrees(this.origin, args[clockwise ? 1 : 0]);
1690 this.endAngle = MakerJs.angle.ofPointInDegrees(this.origin, args[clockwise ? 0 : 1]);
1691 break;
1692 }
1693 //do this after Circle.apply / Circle.call to make sure this is an arc
1694 this.type = MakerJs.pathType.Arc;
1695 }
1696 return Arc;
1697 }());
1698 paths.Arc = Arc;
1699 /**
1700 * Class for circle path.
1701 */
1702 var Circle = /** @class */ (function () {
1703 function Circle() {
1704 var args = [];
1705 for (var _i = 0; _i < arguments.length; _i++) {
1706 args[_i] = arguments[_i];
1707 }
1708 this.type = MakerJs.pathType.Circle;
1709 switch (args.length) {
1710 case 1:
1711 this.origin = [0, 0];
1712 this.radius = args[0];
1713 break;
1714 case 2:
1715 if (MakerJs.isNumber(args[1])) {
1716 this.origin = args[0];
1717 this.radius = args[1];
1718 }
1719 else {
1720 //Circle from 2 points
1721 this.origin = MakerJs.point.average(args[0], args[1]);
1722 this.radius = MakerJs.measure.pointDistance(this.origin, args[0]);
1723 }
1724 break;
1725 default:
1726 //Circle from 3 points
1727 //create 2 lines with 2nd point in common
1728 var lines = [
1729 new Line(args[0], args[1]),
1730 new Line(args[1], args[2])
1731 ];
1732 //create perpendicular lines
1733 var perpendiculars = [];
1734 for (var i = 2; i--;) {
1735 var midpoint = MakerJs.point.middle(lines[i]);
1736 perpendiculars.push(MakerJs.path.rotate(lines[i], 90, midpoint));
1737 }
1738 //find intersection of slopes of perpendiculars
1739 var origin = MakerJs.point.fromSlopeIntersection(perpendiculars[0], perpendiculars[1]);
1740 if (origin) {
1741 this.origin = origin;
1742 //radius is distance to any of the 3 points
1743 this.radius = MakerJs.measure.pointDistance(this.origin, args[0]);
1744 }
1745 else {
1746 throw 'invalid parameters - attempted to construct a circle from 3 points on a line: ' + JSON.stringify(args);
1747 }
1748 break;
1749 }
1750 }
1751 return Circle;
1752 }());
1753 paths.Circle = Circle;
1754 /**
1755 * Class for line path.
1756 */
1757 var Line = /** @class */ (function () {
1758 function Line() {
1759 var args = [];
1760 for (var _i = 0; _i < arguments.length; _i++) {
1761 args[_i] = arguments[_i];
1762 }
1763 this.type = MakerJs.pathType.Line;
1764 switch (args.length) {
1765 case 1:
1766 var points = args[0];
1767 this.origin = points[0];
1768 this.end = points[1];
1769 break;
1770 case 2:
1771 this.origin = args[0];
1772 this.end = args[1];
1773 break;
1774 }
1775 }
1776 return Line;
1777 }());
1778 paths.Line = Line;
1779 /**
1780 * Class for chord, which is simply a line path that connects the endpoints of an arc.
1781 *
1782 * @param arc Arc to use as the basic for the chord.
1783 */
1784 var Chord = /** @class */ (function () {
1785 function Chord(arc) {
1786 var arcPoints = MakerJs.point.fromArc(arc);
1787 this.type = MakerJs.pathType.Line;
1788 this.origin = arcPoints[0];
1789 this.end = arcPoints[1];
1790 }
1791 return Chord;
1792 }());
1793 paths.Chord = Chord;
1794 /**
1795 * Class for a parallel line path.
1796 *
1797 * @param toLine A line to be parallel to.
1798 * @param distance Distance between parallel and original line.
1799 * @param nearPoint Any point to determine which side of the line to place the parallel.
1800 */
1801 var Parallel = /** @class */ (function () {
1802 function Parallel(toLine, distance, nearPoint) {
1803 this.type = MakerJs.pathType.Line;
1804 this.origin = MakerJs.point.clone(toLine.origin);
1805 this.end = MakerJs.point.clone(toLine.end);
1806 var angleOfLine = MakerJs.angle.ofLineInDegrees(this);
1807 function getNewOrigin(offsetAngle) {
1808 var origin = MakerJs.point.add(toLine.origin, MakerJs.point.fromPolar(MakerJs.angle.toRadians(angleOfLine + offsetAngle), distance));
1809 return {
1810 origin: origin,
1811 nearness: MakerJs.measure.pointDistance(origin, nearPoint)
1812 };
1813 }
1814 var newOrigins = [getNewOrigin(-90), getNewOrigin(90)];
1815 var newOrigin = (newOrigins[0].nearness < newOrigins[1].nearness) ? newOrigins[0].origin : newOrigins[1].origin;
1816 MakerJs.path.move(this, newOrigin);
1817 }
1818 return Parallel;
1819 }());
1820 paths.Parallel = Parallel;
1821 })(paths = MakerJs.paths || (MakerJs.paths = {}));
1822})(MakerJs || (MakerJs = {}));
1823var MakerJs;
1824(function (MakerJs) {
1825 var model;
1826 (function (model) {
1827 /**
1828 * Add a Caption object to a model.
1829 * @param modelContext The model to add to.
1830 * @param text Text to add.
1831 * @param leftAnchorPoint Optional Point on left side middle of text.
1832 * @param rightAnchorPoint Optional Point on right side middle of text.
1833 * @returns The original model (for cascading).
1834 */
1835 function addCaption(modelContext, text, leftAnchorPoint, rightAnchorPoint) {
1836 if (!leftAnchorPoint) {
1837 leftAnchorPoint = MakerJs.point.zero();
1838 }
1839 if (!rightAnchorPoint) {
1840 rightAnchorPoint = MakerJs.point.clone(leftAnchorPoint);
1841 }
1842 modelContext.caption = { text: text, anchor: new MakerJs.paths.Line(leftAnchorPoint, rightAnchorPoint) };
1843 return modelContext;
1844 }
1845 model.addCaption = addCaption;
1846 /**
1847 * Add a path as a child. This is basically equivalent to:
1848 * ```
1849 * parentModel.paths[childPathId] = childPath;
1850 * ```
1851 * with additional checks to make it safe for cascading.
1852 *
1853 * @param modelContext The model to add to.
1854 * @param pathContext The path to add.
1855 * @param pathId The id of the path.
1856 * @param overWrite Optional flag to overwrite any path referenced by pathId. Default is false, which will create an id similar to pathId.
1857 * @returns The original model (for cascading).
1858 */
1859 function addPath(modelContext, pathContext, pathId, overWrite) {
1860 if (overWrite === void 0) { overWrite = false; }
1861 var id = overWrite ? pathId : getSimilarPathId(modelContext, pathId);
1862 modelContext.paths = modelContext.paths || {};
1863 modelContext.paths[id] = pathContext;
1864 return modelContext;
1865 }
1866 model.addPath = addPath;
1867 /**
1868 * Add a model as a child. This is basically equivalent to:
1869 * ```
1870 * parentModel.models[childModelId] = childModel;
1871 * ```
1872 * with additional checks to make it safe for cascading.
1873 *
1874 * @param parentModel The model to add to.
1875 * @param childModel The model to add.
1876 * @param childModelId The id of the child model.
1877 * @param overWrite Optional flag to overwrite any model referenced by childModelId. Default is false, which will create an id similar to childModelId.
1878 * @returns The original model (for cascading).
1879 */
1880 function addModel(parentModel, childModel, childModelId, overWrite) {
1881 if (overWrite === void 0) { overWrite = false; }
1882 var id = overWrite ? childModelId : getSimilarModelId(parentModel, childModelId);
1883 parentModel.models = parentModel.models || {};
1884 parentModel.models[id] = childModel;
1885 return parentModel;
1886 }
1887 model.addModel = addModel;
1888 /**
1889 * Add a model as a child of another model. This is basically equivalent to:
1890 * ```
1891 * parentModel.models[childModelId] = childModel;
1892 * ```
1893 * with additional checks to make it safe for cascading.
1894 *
1895 * @param childModel The model to add.
1896 * @param parentModel The model to add to.
1897 * @param childModelId The id of the child model.
1898 * @param overWrite Optional flag to overwrite any model referenced by childModelId. Default is false, which will create an id similar to childModelId.
1899 * @returns The original model (for cascading).
1900 */
1901 function addTo(childModel, parentModel, childModelId, overWrite) {
1902 if (overWrite === void 0) { overWrite = false; }
1903 addModel(parentModel, childModel, childModelId, overWrite);
1904 return childModel;
1905 }
1906 model.addTo = addTo;
1907 /**
1908 * Clone a model. Alias of makerjs.cloneObject(modelToClone)
1909 *
1910 * @param modelToClone The model to clone.
1911 * @returns A clone of the model you passed.
1912 */
1913 function clone(modelToClone) {
1914 return MakerJs.cloneObject(modelToClone);
1915 }
1916 model.clone = clone;
1917 /**
1918 * Count the number of child models within a given model.
1919 *
1920 * @param modelContext The model containing other models.
1921 * @returns Number of child models.
1922 */
1923 function countChildModels(modelContext) {
1924 var count = 0;
1925 if (modelContext.models) {
1926 for (var id in modelContext.models) {
1927 count++;
1928 }
1929 }
1930 return count;
1931 }
1932 model.countChildModels = countChildModels;
1933 /**
1934 * Gets all Caption objects, in absolute position, in this model and its children.
1935 * @param modelContext The model to search for Caption objects.
1936 * @returns Array of Caption objects.
1937 */
1938 function getAllCaptionsOffset(modelContext) {
1939 var captions = [];
1940 function tryAddCaption(m, offset) {
1941 if (m.caption) {
1942 captions.push({ text: m.caption.text, anchor: MakerJs.path.clone(m.caption.anchor, offset) });
1943 }
1944 }
1945 tryAddCaption(modelContext, modelContext.origin);
1946 model.walk(modelContext, {
1947 afterChildWalk: function (wm) { return tryAddCaption(wm.childModel, wm.offset); }
1948 });
1949 return captions;
1950 }
1951 model.getAllCaptionsOffset = getAllCaptionsOffset;
1952 /**
1953 * @private
1954 */
1955 function getSimilarId(map, id) {
1956 if (!map)
1957 return id;
1958 var i = 0;
1959 var newId = id;
1960 while (newId in map) {
1961 i++;
1962 newId = [id, i].join('_');
1963 }
1964 return newId;
1965 }
1966 /**
1967 * Get an unused id in the models map with the same prefix.
1968 *
1969 * @param modelContext The model containing the models map.
1970 * @param modelId The id to use directly (if unused), or as a prefix.
1971 */
1972 function getSimilarModelId(modelContext, modelId) {
1973 return getSimilarId(modelContext.models, modelId);
1974 }
1975 model.getSimilarModelId = getSimilarModelId;
1976 /**
1977 * Get an unused id in the paths map with the same prefix.
1978 *
1979 * @param modelContext The model containing the paths map.
1980 * @param pathId The id to use directly (if unused), or as a prefix.
1981 */
1982 function getSimilarPathId(modelContext, pathId) {
1983 return getSimilarId(modelContext.paths, pathId);
1984 }
1985 model.getSimilarPathId = getSimilarPathId;
1986 /**
1987 * Set the layer of a model. This is equivalent to:
1988 * ```
1989 * modelContext.layer = layer;
1990 * ```
1991 *
1992 * @param modelContext The model to set the layer.
1993 * @param layer The layer name.
1994 * @returns The original model (for cascading).
1995 */
1996 function layer(modelContext, layer) {
1997 modelContext.layer = layer;
1998 return modelContext;
1999 }
2000 model.layer = layer;
2001 /**
2002 * Moves all of a model's children (models and paths, recursively) in reference to a single common origin. Useful when points between children need to connect to each other.
2003 *
2004 * @param modelToOriginate The model to originate.
2005 * @param origin Optional offset reference point.
2006 * @returns The original model (for cascading).
2007 */
2008 function originate(modelToOriginate, origin) {
2009 function innerOriginate(m, o) {
2010 if (!m)
2011 return;
2012 var newOrigin = MakerJs.point.add(m.origin, o);
2013 if (m.type === MakerJs.models.BezierCurve.typeName) {
2014 MakerJs.path.moveRelative(m.seed, newOrigin);
2015 }
2016 if (m.paths) {
2017 for (var id in m.paths) {
2018 MakerJs.path.moveRelative(m.paths[id], newOrigin);
2019 }
2020 }
2021 if (m.models) {
2022 for (var id in m.models) {
2023 innerOriginate(m.models[id], newOrigin);
2024 }
2025 }
2026 if (m.caption) {
2027 MakerJs.path.moveRelative(m.caption.anchor, newOrigin);
2028 }
2029 m.origin = MakerJs.point.zero();
2030 }
2031 innerOriginate(modelToOriginate, origin ? MakerJs.point.subtract([0, 0], origin) : [0, 0]);
2032 if (origin) {
2033 modelToOriginate.origin = origin;
2034 }
2035 return modelToOriginate;
2036 }
2037 model.originate = originate;
2038 /**
2039 * Center a model at [0, 0].
2040 *
2041 * @param modelToCenter The model to center.
2042 * @param centerX Boolean to center on the x axis. Default is true.
2043 * @param centerY Boolean to center on the y axis. Default is true.
2044 * @returns The original model (for cascading).
2045 */
2046 function center(modelToCenter, centerX, centerY) {
2047 if (centerX === void 0) { centerX = true; }
2048 if (centerY === void 0) { centerY = true; }
2049 var m = MakerJs.measure.modelExtents(modelToCenter);
2050 var o = modelToCenter.origin || [0, 0];
2051 if (centerX)
2052 o[0] -= m.center[0];
2053 if (centerY)
2054 o[1] -= m.center[1];
2055 modelToCenter.origin = o;
2056 return modelToCenter;
2057 }
2058 model.center = center;
2059 /**
2060 * Create a clone of a model, mirrored on either or both x and y axes.
2061 *
2062 * @param modelToMirror The model to mirror.
2063 * @param mirrorX Boolean to mirror on the x axis.
2064 * @param mirrorY Boolean to mirror on the y axis.
2065 * @returns Mirrored model.
2066 */
2067 function mirror(modelToMirror, mirrorX, mirrorY) {
2068 var newModel = {};
2069 if (!modelToMirror)
2070 return null;
2071 if (modelToMirror.origin) {
2072 newModel.origin = MakerJs.point.mirror(modelToMirror.origin, mirrorX, mirrorY);
2073 }
2074 if (modelToMirror.type) {
2075 newModel.type = modelToMirror.type;
2076 }
2077 if ('layer' in modelToMirror) {
2078 newModel.layer = modelToMirror.layer;
2079 }
2080 if (modelToMirror.units) {
2081 newModel.units = modelToMirror.units;
2082 }
2083 if (modelToMirror.type === MakerJs.models.BezierCurve.typeName) {
2084 newModel.type = MakerJs.models.BezierCurve.typeName;
2085 newModel.seed = MakerJs.path.mirror(modelToMirror.seed, mirrorX, mirrorY);
2086 }
2087 if (modelToMirror.paths) {
2088 newModel.paths = {};
2089 for (var id in modelToMirror.paths) {
2090 var pathToMirror = modelToMirror.paths[id];
2091 if (!pathToMirror)
2092 continue;
2093 var pathMirrored = MakerJs.path.mirror(pathToMirror, mirrorX, mirrorY);
2094 if (!pathMirrored)
2095 continue;
2096 newModel.paths[id] = pathMirrored;
2097 }
2098 }
2099 if (modelToMirror.models) {
2100 newModel.models = {};
2101 for (var id in modelToMirror.models) {
2102 var childModelToMirror = modelToMirror.models[id];
2103 if (!childModelToMirror)
2104 continue;
2105 var childModelMirrored = mirror(childModelToMirror, mirrorX, mirrorY);
2106 if (!childModelMirrored)
2107 continue;
2108 newModel.models[id] = childModelMirrored;
2109 }
2110 }
2111 if (modelToMirror.caption) {
2112 newModel.caption = MakerJs.cloneObject(modelToMirror.caption);
2113 newModel.caption.anchor = MakerJs.path.mirror(modelToMirror.caption.anchor, mirrorX, mirrorY);
2114 }
2115 return newModel;
2116 }
2117 model.mirror = mirror;
2118 /**
2119 * Move a model to an absolute point. Note that this is also accomplished by directly setting the origin property. This function exists for cascading.
2120 *
2121 * @param modelToMove The model to move.
2122 * @param origin The new position of the model.
2123 * @returns The original model (for cascading).
2124 */
2125 function move(modelToMove, origin) {
2126 modelToMove.origin = MakerJs.point.clone(origin);
2127 return modelToMove;
2128 }
2129 model.move = move;
2130 /**
2131 * Move a model's origin by a relative amount.
2132 *
2133 * @param modelToMove The model to move.
2134 * @param delta The x & y adjustments as a point object.
2135 * @returns The original model (for cascading).
2136 */
2137 function moveRelative(modelToMove, delta) {
2138 if (modelToMove) {
2139 modelToMove.origin = MakerJs.point.add(modelToMove.origin || MakerJs.point.zero(), delta);
2140 }
2141 return modelToMove;
2142 }
2143 model.moveRelative = moveRelative;
2144 /**
2145 * Prefix the ids of paths in a model.
2146 *
2147 * @param modelToPrefix The model to prefix.
2148 * @param prefix The prefix to prepend on paths ids.
2149 * @returns The original model (for cascading).
2150 */
2151 function prefixPathIds(modelToPrefix, prefix) {
2152 var walkedPaths = [];
2153 //first collect the paths because we don't want to modify keys during an iteration on keys
2154 walk(modelToPrefix, {
2155 onPath: function (walkedPath) {
2156 walkedPaths.push(walkedPath);
2157 }
2158 });
2159 //now modify the ids in our own iteration
2160 for (var i = 0; i < walkedPaths.length; i++) {
2161 var walkedPath = walkedPaths[i];
2162 delete walkedPath.modelContext.paths[walkedPath.pathId];
2163 walkedPath.modelContext.paths[prefix + walkedPath.pathId] = walkedPath.pathContext;
2164 }
2165 return modelToPrefix;
2166 }
2167 model.prefixPathIds = prefixPathIds;
2168 /**
2169 * Rotate a model.
2170 *
2171 * @param modelToRotate The model to rotate.
2172 * @param angleInDegrees The amount of rotation, in degrees.
2173 * @param rotationOrigin The center point of rotation.
2174 * @returns The original model (for cascading).
2175 */
2176 function rotate(modelToRotate, angleInDegrees, rotationOrigin) {
2177 if (rotationOrigin === void 0) { rotationOrigin = [0, 0]; }
2178 if (!modelToRotate || !angleInDegrees)
2179 return modelToRotate;
2180 var offsetOrigin = MakerJs.point.subtract(rotationOrigin, modelToRotate.origin);
2181 if (modelToRotate.type === MakerJs.models.BezierCurve.typeName) {
2182 MakerJs.path.rotate(modelToRotate.seed, angleInDegrees, offsetOrigin);
2183 }
2184 if (modelToRotate.paths) {
2185 for (var id in modelToRotate.paths) {
2186 MakerJs.path.rotate(modelToRotate.paths[id], angleInDegrees, offsetOrigin);
2187 }
2188 }
2189 if (modelToRotate.models) {
2190 for (var id in modelToRotate.models) {
2191 rotate(modelToRotate.models[id], angleInDegrees, offsetOrigin);
2192 }
2193 }
2194 if (modelToRotate.caption) {
2195 MakerJs.path.rotate(modelToRotate.caption.anchor, angleInDegrees, offsetOrigin);
2196 }
2197 return modelToRotate;
2198 }
2199 model.rotate = rotate;
2200 /**
2201 * Scale a model.
2202 *
2203 * @param modelToScale The model to scale.
2204 * @param scaleValue The amount of scaling.
2205 * @param scaleOrigin Optional boolean to scale the origin point. Typically false for the root model.
2206 * @returns The original model (for cascading).
2207 */
2208 function scale(modelToScale, scaleValue, scaleOrigin) {
2209 if (scaleOrigin === void 0) { scaleOrigin = false; }
2210 if (scaleOrigin && modelToScale.origin) {
2211 modelToScale.origin = MakerJs.point.scale(modelToScale.origin, scaleValue);
2212 }
2213 if (modelToScale.type === MakerJs.models.BezierCurve.typeName) {
2214 MakerJs.path.scale(modelToScale.seed, scaleValue);
2215 }
2216 if (modelToScale.paths) {
2217 for (var id in modelToScale.paths) {
2218 MakerJs.path.scale(modelToScale.paths[id], scaleValue);
2219 }
2220 }
2221 if (modelToScale.models) {
2222 for (var id in modelToScale.models) {
2223 scale(modelToScale.models[id], scaleValue, true);
2224 }
2225 }
2226 if (modelToScale.caption) {
2227 MakerJs.path.scale(modelToScale.caption.anchor, scaleValue);
2228 }
2229 return modelToScale;
2230 }
2231 model.scale = scale;
2232 /**
2233 * @private
2234 */
2235 function addDistortedPath(parentModel, pathToDistort, pathId, layer, scaleX, scaleY, bezierAccuracy) {
2236 var distortedPath = MakerJs.path.distort(pathToDistort, scaleX, scaleY);
2237 layer = layer || pathToDistort.layer;
2238 if (layer) {
2239 distortedPath.layer = layer;
2240 }
2241 if (MakerJs.isPath(distortedPath)) {
2242 if (distortedPath.type === MakerJs.pathType.BezierSeed) {
2243 var curve = new MakerJs.models.BezierCurve(distortedPath, bezierAccuracy);
2244 addModel(parentModel, curve, pathId);
2245 }
2246 else {
2247 addPath(parentModel, distortedPath, pathId);
2248 }
2249 }
2250 else {
2251 addModel(parentModel, distortedPath, pathId);
2252 }
2253 }
2254 /**
2255 * Create a distorted copy of a model - scale x and y individually.
2256 *
2257 * @param modelToDistort The model to distort.
2258 * @param scaleX The amount of x scaling.
2259 * @param scaleY The amount of y scaling.
2260 * @param scaleOrigin Optional boolean to scale the origin point. Typically false for the root model.
2261 * @param bezierAccuracy Optional accuracy of Bezier curves.
2262 * @returns New model (for cascading).
2263 */
2264 function distort(modelToDistort, scaleX, scaleY, scaleOrigin, bezierAccuracy) {
2265 if (scaleOrigin === void 0) { scaleOrigin = false; }
2266 var distorted = {};
2267 if (modelToDistort.layer) {
2268 distorted.layer = modelToDistort.layer;
2269 }
2270 if (scaleOrigin && modelToDistort.origin) {
2271 distorted.origin = MakerJs.point.distort(modelToDistort.origin, scaleX, scaleY);
2272 }
2273 if (modelToDistort.type === MakerJs.models.BezierCurve.typeName) {
2274 var b = modelToDistort;
2275 var bezierPartsByLayer = MakerJs.models.BezierCurve.getBezierSeeds(b, { byLayers: true });
2276 var _loop_1 = function (layer_1) {
2277 var pathArray = bezierPartsByLayer[layer_1];
2278 pathArray.forEach(function (p, i) {
2279 addDistortedPath(distorted, p, i.toString(), layer_1, scaleX, scaleY, bezierAccuracy);
2280 });
2281 };
2282 for (var layer_1 in bezierPartsByLayer) {
2283 _loop_1(layer_1);
2284 }
2285 }
2286 else if (modelToDistort.paths) {
2287 for (var pathId in modelToDistort.paths) {
2288 var pathToDistort = modelToDistort.paths[pathId];
2289 addDistortedPath(distorted, pathToDistort, pathId, null, scaleX, scaleY, bezierAccuracy);
2290 }
2291 }
2292 if (modelToDistort.models) {
2293 for (var childId in modelToDistort.models) {
2294 var childModel = modelToDistort.models[childId];
2295 var distortedChild = distort(childModel, scaleX, scaleY, true, bezierAccuracy);
2296 addModel(distorted, distortedChild, childId);
2297 }
2298 }
2299 if (modelToDistort.caption) {
2300 distorted.caption = MakerJs.cloneObject(modelToDistort.caption);
2301 distorted.caption.anchor = MakerJs.path.distort(modelToDistort.caption.anchor, scaleX, scaleY);
2302 }
2303 return distorted;
2304 }
2305 model.distort = distort;
2306 /**
2307 * Convert a model to match a different unit system.
2308 *
2309 * @param modeltoConvert The model to convert.
2310 * @param destUnitType The unit system.
2311 * @returns The scaled model (for cascading).
2312 */
2313 function convertUnits(modeltoConvert, destUnitType) {
2314 if (modeltoConvert.units && MakerJs.units.isValidUnit(modeltoConvert.units) && MakerJs.units.isValidUnit(destUnitType)) {
2315 var ratio = MakerJs.units.conversionScale(modeltoConvert.units, destUnitType);
2316 if (ratio != 1) {
2317 scale(modeltoConvert, ratio);
2318 //update the model with its new unit type
2319 modeltoConvert.units = destUnitType;
2320 }
2321 }
2322 return modeltoConvert;
2323 }
2324 model.convertUnits = convertUnits;
2325 /**
2326 * DEPRECATED - use model.walk instead.
2327 * Recursively walk through all paths for a given model.
2328 *
2329 * @param modelContext The model to walk.
2330 * @param callback Callback for each path.
2331 */
2332 function walkPaths(modelContext, callback) {
2333 if (modelContext.paths) {
2334 for (var pathId in modelContext.paths) {
2335 if (!modelContext.paths[pathId])
2336 continue;
2337 callback(modelContext, pathId, modelContext.paths[pathId]);
2338 }
2339 }
2340 if (modelContext.models) {
2341 for (var id in modelContext.models) {
2342 if (!modelContext.models[id])
2343 continue;
2344 walkPaths(modelContext.models[id], callback);
2345 }
2346 }
2347 }
2348 model.walkPaths = walkPaths;
2349 /**
2350 * Recursively walk through all child models and paths for a given model.
2351 *
2352 * @param modelContext The model to walk.
2353 * @param options Object containing callbacks.
2354 * @returns The original model (for cascading).
2355 */
2356 function walk(modelContext, options) {
2357 if (!modelContext)
2358 return;
2359 function walkRecursive(modelContext, layer, offset, route, routeKey) {
2360 var newOffset = MakerJs.point.add(modelContext.origin, offset);
2361 layer = (layer != undefined) ? layer : '';
2362 if (modelContext.paths) {
2363 for (var pathId in modelContext.paths) {
2364 var pathContext = modelContext.paths[pathId];
2365 if (!pathContext)
2366 continue;
2367 var walkedPath = {
2368 modelContext: modelContext,
2369 layer: (pathContext.layer != undefined) ? pathContext.layer : layer,
2370 offset: newOffset,
2371 pathContext: pathContext,
2372 pathId: pathId,
2373 route: route.concat(['paths', pathId]),
2374 routeKey: routeKey + (routeKey ? '.' : '') + 'paths' + JSON.stringify([pathId])
2375 };
2376 if (options.onPath)
2377 options.onPath(walkedPath);
2378 }
2379 }
2380 if (modelContext.models) {
2381 for (var modelId in modelContext.models) {
2382 var childModel = modelContext.models[modelId];
2383 if (!childModel)
2384 continue;
2385 var walkedModel = {
2386 parentModel: modelContext,
2387 layer: (childModel.layer != undefined) ? childModel.layer : layer,
2388 offset: newOffset,
2389 route: route.concat(['models', modelId]),
2390 routeKey: routeKey + (routeKey ? '.' : '') + 'models' + JSON.stringify([modelId]),
2391 childId: modelId,
2392 childModel: childModel
2393 };
2394 if (options.beforeChildWalk) {
2395 if (!options.beforeChildWalk(walkedModel))
2396 continue;
2397 }
2398 walkRecursive(walkedModel.childModel, walkedModel.layer, newOffset, walkedModel.route, walkedModel.routeKey);
2399 if (options.afterChildWalk) {
2400 options.afterChildWalk(walkedModel);
2401 }
2402 }
2403 }
2404 }
2405 walkRecursive(modelContext, modelContext.layer, [0, 0], [], '');
2406 return modelContext;
2407 }
2408 model.walk = walk;
2409 /**
2410 * Move a model so its bounding box begins at [0, 0].
2411 *
2412 * @param modelToZero The model to zero.
2413 * @param zeroX Boolean to zero on the x axis. Default is true.
2414 * @param zeroY Boolean to zero on the y axis. Default is true.
2415 * @returns The original model (for cascading).
2416 */
2417 function zero(modelToZero, zeroX, zeroY) {
2418 if (zeroX === void 0) { zeroX = true; }
2419 if (zeroY === void 0) { zeroY = true; }
2420 var m = MakerJs.measure.modelExtents(modelToZero);
2421 var z = modelToZero.origin || [0, 0];
2422 if (zeroX)
2423 z[0] -= m.low[0];
2424 if (zeroY)
2425 z[1] -= m.low[1];
2426 modelToZero.origin = z;
2427 return modelToZero;
2428 }
2429 model.zero = zero;
2430 })(model = MakerJs.model || (MakerJs.model = {}));
2431})(MakerJs || (MakerJs = {}));
2432var MakerJs;
2433(function (MakerJs) {
2434 var model;
2435 (function (model) {
2436 /**
2437 * @private
2438 */
2439 function getNonZeroSegments(pathToSegment, breakPoint) {
2440 var segment1 = MakerJs.cloneObject(pathToSegment);
2441 if (!segment1)
2442 return null;
2443 var segment2 = MakerJs.path.breakAtPoint(segment1, breakPoint);
2444 if (segment2) {
2445 var segments = [segment1, segment2];
2446 for (var i = 2; i--;) {
2447 if (MakerJs.round(MakerJs.measure.pathLength(segments[i]), .0001) == 0) {
2448 return null;
2449 }
2450 }
2451 return segments;
2452 }
2453 else if (pathToSegment.type == MakerJs.pathType.Circle) {
2454 return [segment1];
2455 }
2456 return null;
2457 }
2458 /**
2459 * @private
2460 */
2461 function getPointsOnPath(points, onPath, popOptions) {
2462 var endpointsOnPath = [];
2463 points.forEach(function (p) {
2464 if (MakerJs.measure.isPointOnPath(p, onPath, .00001, null, popOptions)) {
2465 endpointsOnPath.push(p);
2466 }
2467 });
2468 return endpointsOnPath;
2469 }
2470 /**
2471 * @private
2472 */
2473 function breakAlongForeignPath(crossedPath, overlappedSegments, foreignWalkedPath) {
2474 var foreignPath = foreignWalkedPath.pathContext;
2475 var segments = crossedPath.segments;
2476 if (MakerJs.measure.isPathEqual(segments[0].absolutePath, foreignPath, .0001, null, foreignWalkedPath.offset)) {
2477 segments[0].overlapped = true;
2478 segments[0].duplicate = true;
2479 overlappedSegments.push(segments[0]);
2480 return;
2481 }
2482 //this will cache the slope, to keep from being recalculated for each segment
2483 var popOptions = {};
2484 var options = { path1Offset: crossedPath.offset, path2Offset: foreignWalkedPath.offset };
2485 var foreignIntersection = MakerJs.path.intersection(crossedPath.pathContext, foreignPath, options);
2486 var intersectionPoints = foreignIntersection ? foreignIntersection.intersectionPoints : null;
2487 var foreignPathEndPoints = MakerJs.point.fromPathEnds(foreignPath, foreignWalkedPath.offset) || [];
2488 for (var i = 0; i < segments.length; i++) {
2489 var pointsOfInterest = intersectionPoints ? foreignPathEndPoints.concat(intersectionPoints) : foreignPathEndPoints;
2490 var pointsToCheck = getPointsOnPath(pointsOfInterest, segments[i].absolutePath, popOptions);
2491 if (options.out_AreOverlapped) {
2492 segments[i].overlapped = true;
2493 overlappedSegments.push(segments[i]);
2494 }
2495 if (pointsToCheck.length > 0) {
2496 //break the path which intersected, and add the shard to the end of the array so it can also be checked in this loop for further sharding.
2497 var subSegments = null;
2498 var p = 0;
2499 while (!subSegments && p < pointsToCheck.length) {
2500 subSegments = getNonZeroSegments(segments[i].absolutePath, pointsToCheck[p]);
2501 p++;
2502 }
2503 if (subSegments) {
2504 crossedPath.broken = true;
2505 segments[i].absolutePath = subSegments[0];
2506 if (subSegments[1]) {
2507 var newSegment = {
2508 absolutePath: subSegments[1],
2509 pathId: segments[0].pathId,
2510 overlapped: segments[i].overlapped,
2511 uniqueForeignIntersectionPoints: []
2512 };
2513 if (segments[i].overlapped) {
2514 overlappedSegments.push(newSegment);
2515 }
2516 segments.push(newSegment);
2517 }
2518 //re-check this segment for another deep intersection
2519 i--;
2520 }
2521 }
2522 }
2523 }
2524 /**
2525 * DEPRECATED - use measure.isPointInsideModel instead.
2526 * Check to see if a path is inside of a model.
2527 *
2528 * @param pathContext The path to check.
2529 * @param modelContext The model to check against.
2530 * @param farPoint Optional point of reference which is outside the bounds of the modelContext.
2531 * @returns Boolean true if the path is inside of the modelContext.
2532 */
2533 function isPathInsideModel(pathContext, modelContext, pathOffset, farPoint, measureAtlas) {
2534 var options = {
2535 farPoint: farPoint,
2536 measureAtlas: measureAtlas
2537 };
2538 var p = MakerJs.point.add(MakerJs.point.middle(pathContext), pathOffset);
2539 return MakerJs.measure.isPointInsideModel(p, modelContext, options);
2540 }
2541 model.isPathInsideModel = isPathInsideModel;
2542 /**
2543 * DEPRECATED
2544 * Break a model's paths everywhere they intersect with another path.
2545 *
2546 * @param modelToBreak The model containing paths to be broken.
2547 * @param modelToIntersect Optional model containing paths to look for intersection, or else the modelToBreak will be used.
2548 * @returns The original model (for cascading).
2549 */
2550 function breakPathsAtIntersections(modelToBreak, modelToIntersect) {
2551 var modelToBreakAtlas = new MakerJs.measure.Atlas(modelToBreak);
2552 modelToBreakAtlas.measureModels();
2553 var modelToIntersectAtlas;
2554 if (!modelToIntersect) {
2555 modelToIntersect = modelToBreak;
2556 modelToIntersectAtlas = modelToBreakAtlas;
2557 }
2558 else {
2559 modelToIntersectAtlas = new MakerJs.measure.Atlas(modelToIntersect);
2560 modelToIntersectAtlas.measureModels();
2561 }
2562 ;
2563 breakAllPathsAtIntersections(modelToBreak, modelToIntersect || modelToBreak, false, modelToBreakAtlas, modelToIntersectAtlas);
2564 return modelToBreak;
2565 }
2566 model.breakPathsAtIntersections = breakPathsAtIntersections;
2567 /**
2568 * @private
2569 */
2570 function breakAllPathsAtIntersections(modelToBreak, modelToIntersect, checkIsInside, modelToBreakAtlas, modelToIntersectAtlas, farPoint) {
2571 var crossedPaths = [];
2572 var overlappedSegments = [];
2573 var walkModelToBreakOptions = {
2574 onPath: function (outerWalkedPath) {
2575 //clone this path and make it the first segment
2576 var segment = {
2577 absolutePath: MakerJs.path.clone(outerWalkedPath.pathContext, outerWalkedPath.offset),
2578 pathId: outerWalkedPath.pathId,
2579 overlapped: false,
2580 uniqueForeignIntersectionPoints: []
2581 };
2582 var thisPath = outerWalkedPath;
2583 thisPath.broken = false;
2584 thisPath.segments = [segment];
2585 var walkModelToIntersectOptions = {
2586 onPath: function (innerWalkedPath) {
2587 if (outerWalkedPath.pathContext !== innerWalkedPath.pathContext && MakerJs.measure.isMeasurementOverlapping(modelToBreakAtlas.pathMap[outerWalkedPath.routeKey], modelToIntersectAtlas.pathMap[innerWalkedPath.routeKey])) {
2588 breakAlongForeignPath(thisPath, overlappedSegments, innerWalkedPath);
2589 }
2590 },
2591 beforeChildWalk: function (innerWalkedModel) {
2592 //see if there is a model measurement. if not, it is because the model does not contain paths.
2593 var innerModelMeasurement = modelToIntersectAtlas.modelMap[innerWalkedModel.routeKey];
2594 return innerModelMeasurement && MakerJs.measure.isMeasurementOverlapping(modelToBreakAtlas.pathMap[outerWalkedPath.routeKey], innerModelMeasurement);
2595 }
2596 };
2597 //keep breaking the segments anywhere they intersect with paths of the other model
2598 model.walk(modelToIntersect, walkModelToIntersectOptions);
2599 if (checkIsInside) {
2600 //check each segment whether it is inside or outside
2601 for (var i = 0; i < thisPath.segments.length; i++) {
2602 var p = MakerJs.point.middle(thisPath.segments[i].absolutePath);
2603 var pointInsideOptions = { measureAtlas: modelToIntersectAtlas, farPoint: farPoint };
2604 thisPath.segments[i].isInside = MakerJs.measure.isPointInsideModel(p, modelToIntersect, pointInsideOptions);
2605 thisPath.segments[i].uniqueForeignIntersectionPoints = pointInsideOptions.out_intersectionPoints;
2606 }
2607 }
2608 crossedPaths.push(thisPath);
2609 }
2610 };
2611 model.walk(modelToBreak, walkModelToBreakOptions);
2612 return { crossedPaths: crossedPaths, overlappedSegments: overlappedSegments };
2613 }
2614 /**
2615 * @private
2616 */
2617 function checkForEqualOverlaps(crossedPathsA, crossedPathsB, pointMatchingDistance) {
2618 function compareSegments(segment1, segment2) {
2619 if (MakerJs.measure.isPathEqual(segment1.absolutePath, segment2.absolutePath, pointMatchingDistance)) {
2620 segment1.duplicate = segment2.duplicate = true;
2621 }
2622 }
2623 function compareAll(segment) {
2624 for (var i = 0; i < crossedPathsB.length; i++) {
2625 compareSegments(crossedPathsB[i], segment);
2626 }
2627 }
2628 for (var i = 0; i < crossedPathsA.length; i++) {
2629 compareAll(crossedPathsA[i]);
2630 }
2631 }
2632 /**
2633 * @private
2634 */
2635 function addOrDeleteSegments(crossedPath, includeInside, includeOutside, keepDuplicates, atlas, trackDeleted) {
2636 function addSegment(modelContext, pathIdBase, segment) {
2637 var id = model.getSimilarPathId(modelContext, pathIdBase);
2638 var newRouteKey = (id == pathIdBase) ? crossedPath.routeKey : MakerJs.createRouteKey(crossedPath.route.slice(0, -1).concat([id]));
2639 segment.addedPath = MakerJs.cloneObject(crossedPath.pathContext);
2640 //circles may have become arcs
2641 segment.addedPath.type = segment.absolutePath.type;
2642 MakerJs.path.copyProps(segment.absolutePath, segment.addedPath);
2643 MakerJs.path.moveRelative(segment.addedPath, crossedPath.offset, true);
2644 modelContext.paths[id] = segment.addedPath;
2645 if (crossedPath.broken) {
2646 //save the new segment's measurement
2647 var measurement = MakerJs.measure.pathExtents(segment.absolutePath);
2648 atlas.pathMap[newRouteKey] = measurement;
2649 atlas.modelsMeasured = false;
2650 }
2651 else {
2652 //keep the original measurement
2653 atlas.pathMap[newRouteKey] = savedMeasurement;
2654 }
2655 }
2656 function checkAddSegment(modelContext, pathIdBase, segment) {
2657 if (segment.isInside && includeInside || !segment.isInside && includeOutside) {
2658 addSegment(modelContext, pathIdBase, segment);
2659 }
2660 else {
2661 atlas.modelsMeasured = false;
2662 trackDeleted(segment.absolutePath, crossedPath.routeKey, 'segment is ' + (segment.isInside ? 'inside' : 'outside') + ' intersectionPoints=' + JSON.stringify(segment.uniqueForeignIntersectionPoints));
2663 }
2664 }
2665 //save the original measurement
2666 var savedMeasurement = atlas.pathMap[crossedPath.routeKey];
2667 //delete the original, its segments will be added
2668 delete crossedPath.modelContext.paths[crossedPath.pathId];
2669 delete atlas.pathMap[crossedPath.routeKey];
2670 for (var i = 0; i < crossedPath.segments.length; i++) {
2671 if (crossedPath.segments[i].duplicate) {
2672 if (keepDuplicates) {
2673 addSegment(crossedPath.modelContext, crossedPath.pathId, crossedPath.segments[i]);
2674 }
2675 else {
2676 trackDeleted(crossedPath.segments[i].absolutePath, crossedPath.routeKey, 'segment is duplicate');
2677 }
2678 }
2679 else {
2680 checkAddSegment(crossedPath.modelContext, crossedPath.pathId, crossedPath.segments[i]);
2681 }
2682 }
2683 }
2684 /**
2685 * Combine 2 models. Each model will be modified accordingly.
2686 *
2687 * @param modelA First model to combine.
2688 * @param modelB Second model to combine.
2689 * @param includeAInsideB Flag to include paths from modelA which are inside of modelB.
2690 * @param includeAOutsideB Flag to include paths from modelA which are outside of modelB.
2691 * @param includeBInsideA Flag to include paths from modelB which are inside of modelA.
2692 * @param includeBOutsideA Flag to include paths from modelB which are outside of modelA.
2693 * @param options Optional ICombineOptions object.
2694 * @returns A new model containing both of the input models as "a" and "b".
2695 */
2696 function combine(modelA, modelB, includeAInsideB, includeAOutsideB, includeBInsideA, includeBOutsideA, options) {
2697 if (includeAInsideB === void 0) { includeAInsideB = false; }
2698 if (includeAOutsideB === void 0) { includeAOutsideB = true; }
2699 if (includeBInsideA === void 0) { includeBInsideA = false; }
2700 if (includeBOutsideA === void 0) { includeBOutsideA = true; }
2701 var opts = {
2702 trimDeadEnds: true,
2703 pointMatchingDistance: .005,
2704 out_deleted: [{ paths: {} }, { paths: {} }]
2705 };
2706 MakerJs.extendObject(opts, options);
2707 opts.measureA = opts.measureA || new MakerJs.measure.Atlas(modelA);
2708 opts.measureB = opts.measureB || new MakerJs.measure.Atlas(modelB);
2709 //make sure model measurements capture all paths
2710 opts.measureA.measureModels();
2711 opts.measureB.measureModels();
2712 if (!opts.farPoint) {
2713 var measureBoth = MakerJs.measure.increase(MakerJs.measure.increase({ high: [null, null], low: [null, null] }, opts.measureA.modelMap['']), opts.measureB.modelMap['']);
2714 opts.farPoint = MakerJs.point.add(measureBoth.high, [1, 1]);
2715 }
2716 var pathsA = breakAllPathsAtIntersections(modelA, modelB, true, opts.measureA, opts.measureB, opts.farPoint);
2717 var pathsB = breakAllPathsAtIntersections(modelB, modelA, true, opts.measureB, opts.measureA, opts.farPoint);
2718 checkForEqualOverlaps(pathsA.overlappedSegments, pathsB.overlappedSegments, opts.pointMatchingDistance);
2719 function trackDeleted(which, deletedPath, routeKey, reason) {
2720 model.addPath(opts.out_deleted[which], deletedPath, 'deleted');
2721 var p = deletedPath;
2722 p.reason = reason;
2723 p.routeKey = routeKey;
2724 }
2725 for (var i = 0; i < pathsA.crossedPaths.length; i++) {
2726 addOrDeleteSegments(pathsA.crossedPaths[i], includeAInsideB, includeAOutsideB, true, opts.measureA, function (p, id, reason) { return trackDeleted(0, p, id, reason); });
2727 }
2728 for (var i = 0; i < pathsB.crossedPaths.length; i++) {
2729 addOrDeleteSegments(pathsB.crossedPaths[i], includeBInsideA, includeBOutsideA, false, opts.measureB, function (p, id, reason) { return trackDeleted(1, p, id, reason); });
2730 }
2731 var result = { models: { a: modelA, b: modelB } };
2732 if (opts.trimDeadEnds) {
2733 var shouldKeep;
2734 //union
2735 if (!includeAInsideB && !includeBInsideA) {
2736 shouldKeep = function (walkedPath) {
2737 //When A and B share an outer contour, the segments marked as duplicate will not pass the "inside" test on either A or B.
2738 //Duplicates were discarded from B but kept in A
2739 for (var i = 0; i < pathsA.overlappedSegments.length; i++) {
2740 if (pathsA.overlappedSegments[i].duplicate && walkedPath.pathContext === pathsA.overlappedSegments[i].addedPath) {
2741 return false;
2742 }
2743 }
2744 //default - keep the path
2745 return true;
2746 };
2747 }
2748 model.removeDeadEnds(result, null, shouldKeep, function (wp, reason) {
2749 var which = wp.route[1] === 'a' ? 0 : 1;
2750 trackDeleted(which, wp.pathContext, wp.routeKey, reason);
2751 });
2752 }
2753 //pass options back to caller
2754 MakerJs.extendObject(options, opts);
2755 return result;
2756 }
2757 model.combine = combine;
2758 /**
2759 * Combine 2 models, resulting in a intersection. Each model will be modified accordingly.
2760 *
2761 * @param modelA First model to combine.
2762 * @param modelB Second model to combine.
2763 * @returns A new model containing both of the input models as "a" and "b".
2764 */
2765 function combineIntersection(modelA, modelB) {
2766 return combine(modelA, modelB, true, false, true, false);
2767 }
2768 model.combineIntersection = combineIntersection;
2769 /**
2770 * Combine 2 models, resulting in a subtraction of B from A. Each model will be modified accordingly.
2771 *
2772 * @param modelA First model to combine.
2773 * @param modelB Second model to combine.
2774 * @returns A new model containing both of the input models as "a" and "b".
2775 */
2776 function combineSubtraction(modelA, modelB) {
2777 return combine(modelA, modelB, false, true, true, false);
2778 }
2779 model.combineSubtraction = combineSubtraction;
2780 /**
2781 * Combine 2 models, resulting in a union. Each model will be modified accordingly.
2782 *
2783 * @param modelA First model to combine.
2784 * @param modelB Second model to combine.
2785 * @returns A new model containing both of the input models as "a" and "b".
2786 */
2787 function combineUnion(modelA, modelB) {
2788 return combine(modelA, modelB, false, true, false, true);
2789 }
2790 model.combineUnion = combineUnion;
2791 })(model = MakerJs.model || (MakerJs.model = {}));
2792})(MakerJs || (MakerJs = {}));
2793var MakerJs;
2794(function (MakerJs) {
2795 /**
2796 * Collects items that share a common key.
2797 */
2798 var Collector = /** @class */ (function () {
2799 function Collector(comparer) {
2800 this.comparer = comparer;
2801 this.collections = [];
2802 }
2803 Collector.prototype.addItemToCollection = function (key, item) {
2804 var found = this.findCollection(key);
2805 if (found) {
2806 found.push(item);
2807 }
2808 else {
2809 var collection = { key: key, items: [item] };
2810 this.collections.push(collection);
2811 }
2812 };
2813 Collector.prototype.findCollection = function (key, action) {
2814 for (var i = 0; i < this.collections.length; i++) {
2815 var collection = this.collections[i];
2816 if (this.comparer(key, collection.key)) {
2817 if (action) {
2818 action(i);
2819 }
2820 return collection.items;
2821 }
2822 }
2823 return null;
2824 };
2825 Collector.prototype.removeCollection = function (key) {
2826 var _this = this;
2827 if (this.findCollection(key, function (index) { _this.collections.splice(index, 1); })) {
2828 return true;
2829 }
2830 return false;
2831 };
2832 Collector.prototype.removeItemFromCollection = function (key, item) {
2833 var collection = this.findCollection(key);
2834 if (!collection)
2835 return;
2836 for (var i = 0; i < collection.length; i++) {
2837 if (collection[i] === item) {
2838 collection.splice(i, 1);
2839 return true;
2840 }
2841 }
2842 return false;
2843 };
2844 Collector.prototype.getCollectionsOfMultiple = function (cb) {
2845 for (var i = 0; i < this.collections.length; i++) {
2846 var collection = this.collections[i];
2847 if (collection.items.length > 1) {
2848 cb(collection.key, collection.items);
2849 }
2850 }
2851 };
2852 return Collector;
2853 }());
2854 MakerJs.Collector = Collector;
2855 /**
2856 * @private
2857 */
2858 var _kdbush = require('kdbush');
2859 /**
2860 * @private
2861 */
2862 var kdbush = (_kdbush["default"] || _kdbush);
2863 /**
2864 * A graph of items which may be located on the same points.
2865 */
2866 var PointGraph = /** @class */ (function () {
2867 function PointGraph() {
2868 this.reset();
2869 }
2870 /**
2871 * Reset the stored points, graphs, lists, to initial state.
2872 */
2873 PointGraph.prototype.reset = function () {
2874 this.insertedCount = 0;
2875 this.graph = {};
2876 this.index = {};
2877 this.merged = {};
2878 this.values = [];
2879 };
2880 /**
2881 * Insert a value.
2882 * @param value Value associated with this point.
2883 * @returns valueId of the inserted value.
2884 */
2885 PointGraph.prototype.insertValue = function (value) {
2886 this.values.push(value);
2887 return this.values.length - 1;
2888 };
2889 /**
2890 * Insert a value at a point.
2891 * @param p Point.
2892 * @param value Value associated with this point.
2893 */
2894 PointGraph.prototype.insertValueIdAtPoint = function (valueId, p) {
2895 var x = p[0], y = p[1];
2896 if (!this.graph[x]) {
2897 this.graph[x] = {};
2898 }
2899 var pgx = this.graph[x];
2900 var existed = (y in pgx);
2901 var el;
2902 var pointId;
2903 if (!existed) {
2904 pgx[y] = pointId = this.insertedCount++;
2905 el = {
2906 pointId: pointId,
2907 point: p,
2908 valueIds: [valueId]
2909 };
2910 this.index[pointId] = el;
2911 }
2912 else {
2913 pointId = pgx[y];
2914 if (pointId in this.merged) {
2915 pointId = this.merged[pointId];
2916 }
2917 el = this.index[pointId];
2918 el.valueIds.push(valueId);
2919 }
2920 return { existed: existed, pointId: pointId };
2921 };
2922 /**
2923 * Merge points within a given distance from each other. Call this after inserting values.
2924 * @param withinDistance Distance to consider points equal.
2925 */
2926 PointGraph.prototype.mergePoints = function (withinDistance) {
2927 var _this = this;
2928 var points = [];
2929 var kEls = [];
2930 for (var pointId in this.index) {
2931 var el = this.index[pointId];
2932 var p = el.point;
2933 el.kdId = points.length;
2934 points.push(p);
2935 kEls.push(el);
2936 }
2937 this.kdbush = kdbush(points);
2938 var _loop_2 = function (pointId) {
2939 if (pointId in this_1.merged)
2940 return "continue";
2941 var el = this_1.index[pointId];
2942 var mergeIds = this_1.kdbush.within(el.point[0], el.point[1], withinDistance);
2943 mergeIds.forEach(function (kdId) {
2944 if (kdId === el.kdId)
2945 return;
2946 _this.mergeIndexElements(el, kEls[kdId]);
2947 });
2948 };
2949 var this_1 = this;
2950 for (var pointId in this.index) {
2951 _loop_2(pointId);
2952 }
2953 };
2954 /**
2955 * Finds all points which have only one value associated. Then, merge to the nearest other point within this set.
2956 * Call this after inserting values.
2957 * @param withinDistance Distance to consider points equal.
2958 */
2959 PointGraph.prototype.mergeNearestSinglePoints = function (withinDistance) {
2960 var _this = this;
2961 var singles = [];
2962 for (var pointId in this.index) {
2963 var el = this.index[pointId];
2964 if (el.valueIds.length === 1) {
2965 singles.push(el);
2966 }
2967 }
2968 this.kdbush = kdbush(singles.map(function (el) { return el.point; }));
2969 singles.forEach(function (el) {
2970 if (el.pointId in _this.merged)
2971 return;
2972 var mergeIds = _this.kdbush.within(el.point[0], el.point[1], withinDistance);
2973 var byDistance = [];
2974 mergeIds.forEach(function (i) {
2975 var other = singles[i];
2976 if (other.pointId === el.pointId)
2977 return;
2978 byDistance.push({ el: other, distance: MakerJs.measure.pointDistance(other.point, el.point) });
2979 });
2980 byDistance.sort(function (a, b) { return a.distance - b.distance; });
2981 for (var i = 0; i < byDistance.length; i++) {
2982 var other = byDistance[i].el;
2983 if (other.pointId in _this.merged)
2984 continue;
2985 if (other.merged && other.merged.length > 0) {
2986 _this.mergeIndexElements(other, el);
2987 }
2988 else {
2989 _this.mergeIndexElements(el, other);
2990 }
2991 return;
2992 }
2993 });
2994 };
2995 PointGraph.prototype.mergeIndexElements = function (keep, remove) {
2996 keep.merged = keep.merged || [];
2997 keep.merged.push(remove.pointId);
2998 this.merged[remove.pointId] = keep.pointId;
2999 keep.valueIds.push.apply(keep.valueIds, remove.valueIds);
3000 delete this.index[remove.pointId];
3001 return keep.pointId;
3002 };
3003 /**
3004 * Iterate over points in the index.
3005 * @param cb Callback for each point in the index.
3006 */
3007 PointGraph.prototype.forEachPoint = function (cb) {
3008 var _this = this;
3009 for (var pointId = 0; pointId < this.insertedCount; pointId++) {
3010 var el = this.index[pointId];
3011 if (!el)
3012 continue;
3013 var length_1 = el.valueIds.length;
3014 if (length_1 > 0) {
3015 cb(el.point, el.valueIds.map(function (i) { return _this.values[i]; }), pointId, el);
3016 }
3017 }
3018 };
3019 /**
3020 * Gets the id of a point, after merging.
3021 * @param p Point to look up id.
3022 */
3023 PointGraph.prototype.getIdOfPoint = function (p) {
3024 var px = this.graph[p[0]];
3025 if (px) {
3026 var pointId = px[p[1]];
3027 if (pointId >= 0) {
3028 if (pointId in this.merged) {
3029 return this.merged[pointId];
3030 }
3031 else {
3032 return pointId;
3033 }
3034 }
3035 }
3036 };
3037 /**
3038 * Get the index element of a point, after merging.
3039 * @param p Point to look up index element.
3040 */
3041 PointGraph.prototype.getElementAtPoint = function (p) {
3042 var pointId = this.getIdOfPoint(p);
3043 if (pointId >= 0) {
3044 return this.index[pointId];
3045 }
3046 };
3047 return PointGraph;
3048 }());
3049 MakerJs.PointGraph = PointGraph;
3050})(MakerJs || (MakerJs = {}));
3051var MakerJs;
3052(function (MakerJs) {
3053 var model;
3054 (function (model) {
3055 /**
3056 * @private
3057 */
3058 function checkForOverlaps(refPaths, isOverlapping, overlapUnion) {
3059 var currIndex = 0;
3060 do {
3061 var root = refPaths[currIndex];
3062 do {
3063 var overlaps = false;
3064 for (var i = currIndex + 1; i < refPaths.length; i++) {
3065 var arcRef = refPaths[i];
3066 overlaps = isOverlapping(root.pathContext, arcRef.pathContext, false);
3067 if (overlaps) {
3068 overlapUnion(root.pathContext, arcRef.pathContext);
3069 delete arcRef.modelContext.paths[arcRef.pathId];
3070 refPaths.splice(i, 1);
3071 break;
3072 }
3073 }
3074 } while (overlaps);
3075 currIndex++;
3076 } while (currIndex < refPaths.length);
3077 }
3078 /**
3079 * Simplify a model's paths by reducing redundancy: combine multiple overlapping paths into a single path. The model must be originated.
3080 *
3081 * @param modelContext The originated model to search for similar paths.
3082 * @param options Optional options object.
3083 * @returns The simplified model (for cascading).
3084 */
3085 function simplify(modelToSimplify, options) {
3086 function compareCircles(circleA, circleB) {
3087 if (Math.abs(circleA.radius - circleB.radius) <= opts.scalarMatchingDistance) {
3088 var distance = MakerJs.measure.pointDistance(circleA.origin, circleB.origin);
3089 return distance <= opts.pointMatchingDistance;
3090 }
3091 return false;
3092 }
3093 var similarArcs = new MakerJs.Collector(compareCircles);
3094 var similarCircles = new MakerJs.Collector(compareCircles);
3095 var similarLines = new MakerJs.Collector(MakerJs.measure.isSlopeEqual);
3096 var map = {};
3097 map[MakerJs.pathType.Arc] = function (arcRef) {
3098 similarArcs.addItemToCollection(arcRef.pathContext, arcRef);
3099 };
3100 map[MakerJs.pathType.Circle] = function (circleRef) {
3101 similarCircles.addItemToCollection(circleRef.pathContext, circleRef);
3102 };
3103 map[MakerJs.pathType.Line] = function (lineRef) {
3104 var slope = MakerJs.measure.lineSlope(lineRef.pathContext);
3105 similarLines.addItemToCollection(slope, lineRef);
3106 };
3107 var opts = {
3108 scalarMatchingDistance: .001,
3109 pointMatchingDistance: .005
3110 };
3111 MakerJs.extendObject(opts, options);
3112 //walk the model and collect: arcs on same center / radius, circles on same center / radius, lines on same y-intercept / slope.
3113 var walkOptions = {
3114 onPath: function (walkedPath) {
3115 var fn = map[walkedPath.pathContext.type];
3116 if (fn) {
3117 fn(walkedPath);
3118 }
3119 }
3120 };
3121 model.walk(modelToSimplify, walkOptions);
3122 //for all arcs that are similar, see if they overlap.
3123 //combine overlapping arcs into the first one and delete the second.
3124 similarArcs.getCollectionsOfMultiple(function (key, arcRefs) {
3125 checkForOverlaps(arcRefs, MakerJs.measure.isArcOverlapping, function (arcA, arcB) {
3126 //find ends within the other
3127 var aEndsInB = MakerJs.measure.isBetweenArcAngles(arcA.endAngle, arcB, false);
3128 var bEndsInA = MakerJs.measure.isBetweenArcAngles(arcB.endAngle, arcA, false);
3129 //check for complete circle
3130 if (aEndsInB && bEndsInA) {
3131 arcA.endAngle = arcA.startAngle + 360;
3132 return;
3133 }
3134 //find the leader, in polar terms
3135 var ordered = aEndsInB ? [arcA, arcB] : [arcB, arcA];
3136 //save in arcA
3137 arcA.startAngle = MakerJs.angle.noRevolutions(ordered[0].startAngle);
3138 arcA.endAngle = ordered[1].endAngle;
3139 });
3140 });
3141 //for all circles that are similar, delete all but the first.
3142 similarCircles.getCollectionsOfMultiple(function (key, circleRefs) {
3143 for (var i = 1; i < circleRefs.length; i++) {
3144 var circleRef = circleRefs[i];
3145 delete circleRef.modelContext.paths[circleRef.pathId];
3146 }
3147 });
3148 //for all lines that are similar, see if they overlap.
3149 //combine overlapping lines into the first one and delete the second.
3150 similarLines.getCollectionsOfMultiple(function (slope, arcRefs) {
3151 checkForOverlaps(arcRefs, MakerJs.measure.isLineOverlapping, function (lineA, lineB) {
3152 var box = { paths: { lineA: lineA, lineB: lineB } };
3153 var m = MakerJs.measure.modelExtents(box);
3154 if (!slope.hasSlope) {
3155 //vertical
3156 lineA.origin[1] = m.low[1];
3157 lineA.end[1] = m.high[1];
3158 }
3159 else {
3160 //non-vertical
3161 if (slope.slope < 0) {
3162 //downward
3163 lineA.origin = [m.low[0], m.high[1]];
3164 lineA.end = [m.high[0], m.low[1]];
3165 }
3166 else if (slope.slope > 0) {
3167 //upward
3168 lineA.origin = m.low;
3169 lineA.end = m.high;
3170 }
3171 else {
3172 //horizontal
3173 lineA.origin[0] = m.low[0];
3174 lineA.end[0] = m.high[0];
3175 }
3176 }
3177 });
3178 });
3179 return modelToSimplify;
3180 }
3181 model.simplify = simplify;
3182 })(model = MakerJs.model || (MakerJs.model = {}));
3183})(MakerJs || (MakerJs = {}));
3184var MakerJs;
3185(function (MakerJs) {
3186 var path;
3187 (function (path) {
3188 /**
3189 * @private
3190 */
3191 var map = {};
3192 map[MakerJs.pathType.Arc] = function (arc, expansion, isolateCaps) {
3193 return new MakerJs.models.OvalArc(arc.startAngle, arc.endAngle, arc.radius, expansion, false, isolateCaps);
3194 };
3195 map[MakerJs.pathType.Circle] = function (circle, expansion, isolateCaps) {
3196 return new MakerJs.models.Ring(circle.radius + expansion, circle.radius - expansion);
3197 };
3198 map[MakerJs.pathType.Line] = function (line, expansion, isolateCaps) {
3199 return new MakerJs.models.Slot(line.origin, line.end, expansion, isolateCaps);
3200 };
3201 /**
3202 * Expand path by creating a model which surrounds it.
3203 *
3204 * @param pathToExpand Path to expand.
3205 * @param expansion Distance to expand.
3206 * @param isolateCaps Optional flag to put the end caps into a separate model named "caps".
3207 * @returns Model which surrounds the path.
3208 */
3209 function expand(pathToExpand, expansion, isolateCaps) {
3210 if (!pathToExpand)
3211 return null;
3212 var result = null;
3213 var fn = map[pathToExpand.type];
3214 if (fn) {
3215 result = fn(pathToExpand, expansion, isolateCaps);
3216 result.origin = pathToExpand.origin;
3217 }
3218 return result;
3219 }
3220 path.expand = expand;
3221 /**
3222 * Represent an arc using straight lines.
3223 *
3224 * @param arc Arc to straighten.
3225 * @param bevel Optional flag to bevel the angle to prevent it from being too sharp.
3226 * @param prefix Optional string prefix to apply to path ids.
3227 * @param close Optional flag to make a closed geometry by connecting the endpoints.
3228 * @returns Model of straight lines with same endpoints as the arc.
3229 */
3230 function straighten(arc, bevel, prefix, close) {
3231 var arcSpan = MakerJs.angle.ofArcSpan(arc);
3232 var joints = 1;
3233 if (arcSpan >= 270) {
3234 joints = 4;
3235 }
3236 else if (arcSpan > 180) {
3237 joints = 3;
3238 }
3239 else if (arcSpan > 150 || bevel) { //30 degrees is the sharpest
3240 joints = 2;
3241 }
3242 var jointAngleInRadians = MakerJs.angle.toRadians(arcSpan / joints);
3243 var circumscribedRadius = MakerJs.models.Polygon.circumscribedRadius(arc.radius, jointAngleInRadians);
3244 var ends = MakerJs.point.fromArc(arc);
3245 var points = [MakerJs.point.subtract(ends[0], arc.origin)];
3246 var a = MakerJs.angle.toRadians(arc.startAngle) + jointAngleInRadians / 2;
3247 for (var i = 0; i < joints; i++) {
3248 points.push(MakerJs.point.fromPolar(a, circumscribedRadius));
3249 a += jointAngleInRadians;
3250 }
3251 points.push(MakerJs.point.subtract(ends[1], arc.origin));
3252 var result = new MakerJs.models.ConnectTheDots(close, points);
3253 result.origin = arc.origin;
3254 if (typeof prefix === 'string' && prefix.length) {
3255 MakerJs.model.prefixPathIds(result, prefix);
3256 }
3257 return result;
3258 }
3259 path.straighten = straighten;
3260 })(path = MakerJs.path || (MakerJs.path = {}));
3261})(MakerJs || (MakerJs = {}));
3262(function (MakerJs) {
3263 var model;
3264 (function (model) {
3265 /**
3266 * Expand all paths in a model, then combine the resulting expansions.
3267 *
3268 * @param modelToExpand Model to expand.
3269 * @param distance Distance to expand.
3270 * @param joints Number of points at a joint between paths. Use 0 for round joints, 1 for pointed joints, 2 for beveled joints.
3271 * @param combineOptions Optional object containing combine options.
3272 * @returns Model which surrounds the paths of the original model.
3273 */
3274 function expandPaths(modelToExpand, distance, joints, combineOptions) {
3275 if (joints === void 0) { joints = 0; }
3276 if (combineOptions === void 0) { combineOptions = {}; }
3277 if (distance <= 0)
3278 return null;
3279 var result = {
3280 models: {
3281 expansions: { models: {} },
3282 caps: { models: {} }
3283 }
3284 };
3285 var first = true;
3286 var lastFarPoint = combineOptions.farPoint;
3287 var walkOptions = {
3288 onPath: function (walkedPath) {
3289 //don't expand paths shorter than the tolerance for combine operations
3290 if (combineOptions.pointMatchingDistance && MakerJs.measure.pathLength(walkedPath.pathContext) < combineOptions.pointMatchingDistance)
3291 return;
3292 var expandedPathModel = MakerJs.path.expand(walkedPath.pathContext, distance, true);
3293 if (expandedPathModel) {
3294 model.moveRelative(expandedPathModel, walkedPath.offset);
3295 var newId = model.getSimilarModelId(result.models['expansions'], walkedPath.pathId);
3296 model.prefixPathIds(expandedPathModel, walkedPath.pathId + '_');
3297 model.originate(expandedPathModel);
3298 if (!first) {
3299 model.combine(result, expandedPathModel, false, true, false, true, combineOptions);
3300 combineOptions.measureA.modelsMeasured = false;
3301 lastFarPoint = combineOptions.farPoint;
3302 delete combineOptions.farPoint;
3303 delete combineOptions.measureB;
3304 }
3305 result.models['expansions'].models[newId] = expandedPathModel;
3306 if (expandedPathModel.models) {
3307 var caps = expandedPathModel.models['Caps'];
3308 if (caps) {
3309 delete expandedPathModel.models['Caps'];
3310 result.models['caps'].models[newId] = caps;
3311 }
3312 }
3313 first = false;
3314 }
3315 }
3316 };
3317 model.walk(modelToExpand, walkOptions);
3318 if (joints) {
3319 var roundCaps = result.models['caps'];
3320 var straightCaps = { models: {} };
3321 result.models['straightcaps'] = straightCaps;
3322 model.simplify(roundCaps);
3323 //straighten each cap, optionally beveling
3324 for (var id in roundCaps.models) {
3325 //add a model container to the straight caps
3326 straightCaps.models[id] = { models: {} };
3327 model.walk(roundCaps.models[id], {
3328 onPath: function (walkedPath) {
3329 var arc = walkedPath.pathContext;
3330 //make a small closed shape using the straightened arc
3331 var straightened = MakerJs.path.straighten(arc, joints == 2, walkedPath.pathId + '_', true);
3332 //union this little pointy shape with the rest of the result
3333 model.combine(result, straightened, false, true, false, true, combineOptions);
3334 combineOptions.measureA.modelsMeasured = false;
3335 lastFarPoint = combineOptions.farPoint;
3336 delete combineOptions.farPoint;
3337 delete combineOptions.measureB;
3338 //replace the rounded path with the straightened model
3339 straightCaps.models[id].models[walkedPath.pathId] = straightened;
3340 //delete this path in the parent model
3341 delete walkedPath.modelContext.paths[walkedPath.pathId];
3342 }
3343 });
3344 }
3345 //delete the round caps
3346 delete result.models['caps'];
3347 }
3348 combineOptions.farPoint = lastFarPoint;
3349 return result;
3350 }
3351 model.expandPaths = expandPaths;
3352 /**
3353 * @private
3354 */
3355 function getEndlessChains(modelContext) {
3356 var endlessChains = [];
3357 model.findChains(modelContext, function (chains, loose, layer) {
3358 endlessChains = chains.filter(function (chain) { return chain.endless; });
3359 });
3360 return endlessChains;
3361 }
3362 /**
3363 * @private
3364 */
3365 function getClosedGeometries(modelContext) {
3366 //get endless chains from the model
3367 var endlessChains = getEndlessChains(modelContext);
3368 if (endlessChains.length == 0)
3369 return null;
3370 //make a new model with only closed geometries
3371 var closed = { models: {} };
3372 endlessChains.forEach(function (c, i) {
3373 closed.models[i] = MakerJs.chain.toNewModel(c);
3374 });
3375 return closed;
3376 }
3377 /**
3378 * Outline a model by a specified distance. Useful for accommodating for kerf.
3379 *
3380 * @param modelToOutline Model to outline.
3381 * @param distance Distance to outline.
3382 * @param joints Number of points at a joint between paths. Use 0 for round joints, 1 for pointed joints, 2 for beveled joints.
3383 * @param inside Optional boolean to draw lines inside the model instead of outside.
3384 * @param options Options to send to combine() function.
3385 * @returns Model which surrounds the paths outside of the original model.
3386 */
3387 function outline(modelToOutline, distance, joints, inside, options) {
3388 if (joints === void 0) { joints = 0; }
3389 if (inside === void 0) { inside = false; }
3390 if (options === void 0) { options = {}; }
3391 var expanded = expandPaths(modelToOutline, distance, joints, options);
3392 if (!expanded)
3393 return null;
3394 //get closed geometries from the model
3395 var closed = getClosedGeometries(modelToOutline);
3396 if (closed) {
3397 var childCount = 0;
3398 var result = { models: {} };
3399 //get closed geometries from the expansion
3400 var chains = getEndlessChains(expanded);
3401 chains.forEach(function (c) {
3402 //sample one link from the chain
3403 var wp = c.links[0].walkedPath;
3404 //see if it is inside the original model
3405 var isInside = MakerJs.measure.isPointInsideModel(MakerJs.point.middle(wp.pathContext), closed, wp.offset);
3406 //save the ones we want
3407 if (inside && isInside || !inside && !isInside) {
3408 result.models[childCount++] = MakerJs.chain.toNewModel(c);
3409 }
3410 ;
3411 });
3412 return result;
3413 }
3414 else {
3415 return expanded;
3416 }
3417 }
3418 model.outline = outline;
3419 })(model = MakerJs.model || (MakerJs.model = {}));
3420})(MakerJs || (MakerJs = {}));
3421var MakerJs;
3422(function (MakerJs) {
3423 var units;
3424 (function (units) {
3425 /**
3426 * The base type is arbitrary. Other conversions are then based off of this.
3427 * @private
3428 */
3429 var base = MakerJs.unitType.Millimeter;
3430 /**
3431 * Initialize all known conversions here.
3432 * @private
3433 */
3434 function init() {
3435 addBaseConversion(MakerJs.unitType.Centimeter, 10);
3436 addBaseConversion(MakerJs.unitType.Meter, 1000);
3437 addBaseConversion(MakerJs.unitType.Inch, 25.4);
3438 addBaseConversion(MakerJs.unitType.Foot, 25.4 * 12);
3439 }
3440 /**
3441 * Table of conversions. Lazy load upon first conversion.
3442 * @private
3443 */
3444 var table;
3445 /**
3446 * Add a conversion, and its inversion.
3447 * @private
3448 */
3449 function addConversion(srcUnitType, destUnitType, value) {
3450 function row(unitType) {
3451 if (!table[unitType]) {
3452 table[unitType] = {};
3453 }
3454 return table[unitType];
3455 }
3456 row(srcUnitType)[destUnitType] = value;
3457 row(destUnitType)[srcUnitType] = 1 / value;
3458 }
3459 /**
3460 * Add a conversion of the base unit.
3461 * @private
3462 */
3463 function addBaseConversion(destUnitType, value) {
3464 addConversion(destUnitType, base, value);
3465 }
3466 /**
3467 * Get a conversion ratio between a source unit and a destination unit.
3468 *
3469 * @param srcUnitType unitType converting from.
3470 * @param destUnitType unitType converting to.
3471 * @returns Numeric ratio of the conversion.
3472 */
3473 function conversionScale(srcUnitType, destUnitType) {
3474 if (srcUnitType == destUnitType) {
3475 return 1;
3476 }
3477 //This will lazy load the table with initial conversions.
3478 if (!table) {
3479 table = {};
3480 init();
3481 }
3482 //look for a cached conversion in the table.
3483 if (!table[srcUnitType][destUnitType]) {
3484 //create a new conversionsand cache it in the table.
3485 addConversion(srcUnitType, destUnitType, table[srcUnitType][base] * table[base][destUnitType]);
3486 }
3487 return table[srcUnitType] && table[srcUnitType][destUnitType];
3488 }
3489 units.conversionScale = conversionScale;
3490 /**
3491 * Check to see if unit type is a valid Maker.js unit.
3492 *
3493 * @param tryUnit unit type to check.
3494 * @returns Boolean true if unit type is valid.
3495 */
3496 function isValidUnit(tryUnit) {
3497 for (var id in MakerJs.unitType) {
3498 if (MakerJs.unitType[id] == tryUnit) {
3499 return true;
3500 }
3501 }
3502 return false;
3503 }
3504 units.isValidUnit = isValidUnit;
3505 })(units = MakerJs.units || (MakerJs.units = {}));
3506})(MakerJs || (MakerJs = {}));
3507var MakerJs;
3508(function (MakerJs) {
3509 var measure;
3510 (function (measure) {
3511 /**
3512 * Find out if two angles are equal.
3513 *
3514 * @param angleA First angle.
3515 * @param angleB Second angle.
3516 * @returns true if angles are the same, false if they are not
3517 */
3518 function isAngleEqual(angleA, angleB, accuracy) {
3519 if (accuracy === void 0) { accuracy = .0001; }
3520 var a = MakerJs.angle.noRevolutions(angleA);
3521 var b = MakerJs.angle.noRevolutions(angleB);
3522 var d = MakerJs.angle.noRevolutions(MakerJs.round(b - a, accuracy));
3523 return d == 0;
3524 }
3525 measure.isAngleEqual = isAngleEqual;
3526 /**
3527 * @private
3528 */
3529 var pathAreEqualMap = {};
3530 pathAreEqualMap[MakerJs.pathType.Line] = function (lineA, lineB, withinPointDistance) {
3531 return (isPointEqual(lineA.origin, lineB.origin, withinPointDistance) && isPointEqual(lineA.end, lineB.end, withinPointDistance))
3532 || (isPointEqual(lineA.origin, lineB.end, withinPointDistance) && isPointEqual(lineA.end, lineB.origin, withinPointDistance));
3533 };
3534 pathAreEqualMap[MakerJs.pathType.Circle] = function (circleA, circleB, withinPointDistance) {
3535 return isPointEqual(circleA.origin, circleB.origin, withinPointDistance) && circleA.radius == circleB.radius;
3536 };
3537 pathAreEqualMap[MakerJs.pathType.Arc] = function (arcA, arcB, withinPointDistance) {
3538 return pathAreEqualMap[MakerJs.pathType.Circle](arcA, arcB, withinPointDistance) && isAngleEqual(arcA.startAngle, arcB.startAngle) && isAngleEqual(arcA.endAngle, arcB.endAngle);
3539 };
3540 /**
3541 * Find out if two paths are equal.
3542 *
3543 * @param pathA First path.
3544 * @param pathB Second path.
3545 * @returns true if paths are the same, false if they are not
3546 */
3547 function isPathEqual(pathA, pathB, withinPointDistance, pathAOffset, pathBOffset) {
3548 var result = false;
3549 if (pathA.type == pathB.type) {
3550 var fn = pathAreEqualMap[pathA.type];
3551 if (fn) {
3552 function getResult() {
3553 result = fn(pathA, pathB, withinPointDistance);
3554 }
3555 if (pathAOffset || pathBOffset) {
3556 MakerJs.path.moveTemporary([pathA, pathB], [pathAOffset, pathBOffset], getResult);
3557 }
3558 else {
3559 getResult();
3560 }
3561 }
3562 }
3563 return result;
3564 }
3565 measure.isPathEqual = isPathEqual;
3566 /**
3567 * Find out if two points are equal.
3568 *
3569 * @param a First point.
3570 * @param b Second point.
3571 * @param withinDistance Optional distance to consider points equal.
3572 * @returns true if points are the same, false if they are not
3573 */
3574 function isPointEqual(a, b, withinDistance) {
3575 if (!withinDistance) {
3576 return MakerJs.round(a[0] - b[0]) == 0 && MakerJs.round(a[1] - b[1]) == 0;
3577 }
3578 else {
3579 if (!a || !b)
3580 return false;
3581 var distance = measure.pointDistance(a, b);
3582 return distance <= withinDistance;
3583 }
3584 }
3585 measure.isPointEqual = isPointEqual;
3586 /**
3587 * Find out if a point is distinct among an array of points.
3588 *
3589 * @param pointToCheck point to check.
3590 * @param pointArray array of points.
3591 * @param withinDistance Optional distance to consider points equal.
3592 * @returns false if point is equal to any point in the array.
3593 */
3594 function isPointDistinct(pointToCheck, pointArray, withinDistance) {
3595 for (var i = 0; i < pointArray.length; i++) {
3596 if (isPointEqual(pointArray[i], pointToCheck, withinDistance)) {
3597 return false;
3598 }
3599 }
3600 return true;
3601 }
3602 measure.isPointDistinct = isPointDistinct;
3603 /**
3604 * Find out if point is on a slope.
3605 *
3606 * @param p Point to check.
3607 * @param b Slope.
3608 * @param withinDistance Optional distance of tolerance.
3609 * @returns true if point is on the slope
3610 */
3611 function isPointOnSlope(p, slope, withinDistance) {
3612 if (withinDistance === void 0) { withinDistance = 0; }
3613 if (slope.hasSlope) {
3614 // y = mx * b
3615 return Math.abs(p[1] - (slope.slope * p[0] + slope.yIntercept)) <= withinDistance;
3616 }
3617 else {
3618 //vertical slope
3619 return Math.abs(p[0] - slope.line.origin[0]) <= withinDistance;
3620 }
3621 }
3622 measure.isPointOnSlope = isPointOnSlope;
3623 /**
3624 * Find out if point is on a circle.
3625 *
3626 * @param p Point to check.
3627 * @param circle Circle.
3628 * @param withinDistance Optional distance of tolerance.
3629 * @returns true if point is on the circle
3630 */
3631 function isPointOnCircle(p, circle, withinDistance) {
3632 if (withinDistance === void 0) { withinDistance = 0; }
3633 var d = Math.abs(measure.pointDistance(p, circle.origin) - circle.radius);
3634 return d <= withinDistance;
3635 }
3636 measure.isPointOnCircle = isPointOnCircle;
3637 /**
3638 * @private
3639 */
3640 var onPathMap = {};
3641 onPathMap[MakerJs.pathType.Circle] = function (p, circle, withinDistance) {
3642 return isPointOnCircle(p, circle, withinDistance);
3643 };
3644 onPathMap[MakerJs.pathType.Arc] = function (p, arc, withinDistance) {
3645 if (onPathMap[MakerJs.pathType.Circle](p, arc, withinDistance)) {
3646 var a = MakerJs.angle.ofPointInDegrees(arc.origin, p);
3647 return measure.isBetweenArcAngles(a, arc, false);
3648 }
3649 return false;
3650 };
3651 onPathMap[MakerJs.pathType.Line] = function (p, line, withinDistance, options) {
3652 var slope = (options && options.cachedLineSlope) || measure.lineSlope(line);
3653 if (options && !options.cachedLineSlope) {
3654 options.cachedLineSlope = slope;
3655 }
3656 return isPointOnSlope(p, slope, withinDistance) && measure.isBetweenPoints(p, line, false);
3657 };
3658 /**
3659 * Find out if a point lies on a path.
3660 * @param pointToCheck point to check.
3661 * @param onPath path to check against.
3662 * @param withinDistance Optional distance to consider point on the path.
3663 * @param pathOffset Optional offset of path from [0, 0].
3664 * @param options Optional IIsPointOnPathOptions to cache computation.
3665 */
3666 function isPointOnPath(pointToCheck, onPath, withinDistance, pathOffset, options) {
3667 if (withinDistance === void 0) { withinDistance = 0; }
3668 var fn = onPathMap[onPath.type];
3669 if (fn) {
3670 var offsetPath = pathOffset ? MakerJs.path.clone(onPath, pathOffset) : onPath;
3671 return fn(pointToCheck, offsetPath, withinDistance, options);
3672 }
3673 return false;
3674 }
3675 measure.isPointOnPath = isPointOnPath;
3676 /**
3677 * Check for slope equality.
3678 *
3679 * @param slopeA The ISlope to test.
3680 * @param slopeB The ISlope to check for equality.
3681 * @returns Boolean true if slopes are equal.
3682 */
3683 function isSlopeEqual(slopeA, slopeB) {
3684 if (!isSlopeParallel(slopeA, slopeB))
3685 return false;
3686 if (!slopeA.hasSlope && !slopeB.hasSlope) {
3687 //lines are both vertical, see if x are the same
3688 return MakerJs.round(slopeA.line.origin[0] - slopeB.line.origin[0]) == 0;
3689 }
3690 //lines are parallel, but not vertical, see if y-intercept is the same
3691 return MakerJs.round(slopeA.yIntercept - slopeB.yIntercept, .00001) == 0;
3692 }
3693 measure.isSlopeEqual = isSlopeEqual;
3694 /**
3695 * Check for parallel slopes.
3696 *
3697 * @param slopeA The ISlope to test.
3698 * @param slopeB The ISlope to check for parallel.
3699 * @returns Boolean true if slopes are parallel.
3700 */
3701 function isSlopeParallel(slopeA, slopeB) {
3702 if (!slopeA.hasSlope && !slopeB.hasSlope) {
3703 return true;
3704 }
3705 if (slopeA.hasSlope && slopeB.hasSlope && (MakerJs.round(slopeA.slope - slopeB.slope, .00001) == 0)) {
3706 //lines are parallel
3707 return true;
3708 }
3709 return false;
3710 }
3711 measure.isSlopeParallel = isSlopeParallel;
3712 })(measure = MakerJs.measure || (MakerJs.measure = {}));
3713})(MakerJs || (MakerJs = {}));
3714var MakerJs;
3715(function (MakerJs) {
3716 var measure;
3717 (function (measure) {
3718 /**
3719 * Increase a measurement by an additional measurement.
3720 *
3721 * @param baseMeasure The measurement to increase.
3722 * @param addMeasure The additional measurement.
3723 * @param augmentBaseMeasure Optional flag to call measure.augment on the measurement.
3724 * @returns The increased original measurement (for cascading).
3725 */
3726 function increase(baseMeasure, addMeasure, augmentBaseMeasure) {
3727 function getExtreme(basePoint, newPoint, fn) {
3728 if (!newPoint)
3729 return;
3730 for (var i = 2; i--;) {
3731 if (newPoint[i] == null)
3732 continue;
3733 if (basePoint[i] == null) {
3734 basePoint[i] = newPoint[i];
3735 }
3736 else {
3737 basePoint[i] = fn(basePoint[i], newPoint[i]);
3738 }
3739 }
3740 }
3741 if (addMeasure) {
3742 getExtreme(baseMeasure.low, addMeasure.low, Math.min);
3743 getExtreme(baseMeasure.high, addMeasure.high, Math.max);
3744 }
3745 if (augmentBaseMeasure) {
3746 augment(baseMeasure);
3747 }
3748 return baseMeasure;
3749 }
3750 measure.increase = increase;
3751 /**
3752 * Check for arc being concave or convex towards a given point.
3753 *
3754 * @param arc The arc to test.
3755 * @param towardsPoint The point to test.
3756 * @returns Boolean true if arc is concave towards point.
3757 */
3758 function isArcConcaveTowardsPoint(arc, towardsPoint) {
3759 if (pointDistance(arc.origin, towardsPoint) <= arc.radius) {
3760 return true;
3761 }
3762 var midPointToNearPoint = new MakerJs.paths.Line(MakerJs.point.middle(arc), towardsPoint);
3763 var options = {};
3764 var intersectionPoint = MakerJs.path.intersection(midPointToNearPoint, new MakerJs.paths.Chord(arc), options);
3765 if (intersectionPoint || options.out_AreOverlapped) {
3766 return true;
3767 }
3768 return false;
3769 }
3770 measure.isArcConcaveTowardsPoint = isArcConcaveTowardsPoint;
3771 /**
3772 * DEPRECATED - use isArcSpanOverlapping() instead.
3773 */
3774 function isArcOverlapping(arcA, arcB, excludeTangents) {
3775 return isArcSpanOverlapping(arcA, arcB, excludeTangents);
3776 }
3777 measure.isArcOverlapping = isArcOverlapping;
3778 /**
3779 * Check for arc overlapping another arc.
3780 *
3781 * @param arcA The arc to test.
3782 * @param arcB The arc to check for overlap.
3783 * @param excludeTangents Boolean to exclude exact endpoints and only look for deep overlaps.
3784 * @returns Boolean true if arcA is overlapped with arcB.
3785 */
3786 function isArcSpanOverlapping(arcA, arcB, excludeTangents) {
3787 var pointsOfIntersection = [];
3788 function checkAngles(a, b) {
3789 function checkAngle(n) {
3790 return isBetweenArcAngles(n, a, excludeTangents);
3791 }
3792 return checkAngle(b.startAngle) || checkAngle(b.endAngle);
3793 }
3794 return checkAngles(arcA, arcB) || checkAngles(arcB, arcA) || (arcA.startAngle == arcB.startAngle && arcA.endAngle == arcB.endAngle);
3795 }
3796 measure.isArcSpanOverlapping = isArcSpanOverlapping;
3797 /**
3798 * Check if a given number is between two given limits.
3799 *
3800 * @param valueInQuestion The number to test.
3801 * @param limitA First limit.
3802 * @param limitB Second limit.
3803 * @param exclusive Flag to exclude equaling the limits.
3804 * @returns Boolean true if value is between (or equal to) the limits.
3805 */
3806 function isBetween(valueInQuestion, limitA, limitB, exclusive) {
3807 if (exclusive) {
3808 return Math.min(limitA, limitB) < valueInQuestion && valueInQuestion < Math.max(limitA, limitB);
3809 }
3810 else {
3811 return Math.min(limitA, limitB) <= valueInQuestion && valueInQuestion <= Math.max(limitA, limitB);
3812 }
3813 }
3814 measure.isBetween = isBetween;
3815 /**
3816 * Check if a given angle is between an arc's start and end angles.
3817 *
3818 * @param angleInQuestion The angle to test.
3819 * @param arc Arc to test against.
3820 * @param exclusive Flag to exclude equaling the start or end angles.
3821 * @returns Boolean true if angle is between (or equal to) the arc's start and end angles.
3822 */
3823 function isBetweenArcAngles(angleInQuestion, arc, exclusive) {
3824 var startAngle = MakerJs.angle.noRevolutions(arc.startAngle);
3825 var span = MakerJs.angle.ofArcSpan(arc);
3826 var endAngle = startAngle + span;
3827 angleInQuestion = MakerJs.angle.noRevolutions(angleInQuestion);
3828 //computed angles will not be negative, but the arc may have specified a negative angle, so check against one revolution forward and backward
3829 return (isBetween(angleInQuestion, startAngle, endAngle, exclusive) || isBetween(angleInQuestion, startAngle + 360, endAngle + 360, exclusive) || isBetween(angleInQuestion, startAngle - 360, endAngle - 360, exclusive));
3830 }
3831 measure.isBetweenArcAngles = isBetweenArcAngles;
3832 /**
3833 * Check if a given point is between a line's end points.
3834 *
3835 * @param pointInQuestion The point to test.
3836 * @param line Line to test against.
3837 * @param exclusive Flag to exclude equaling the origin or end points.
3838 * @returns Boolean true if point is between (or equal to) the line's origin and end points.
3839 */
3840 function isBetweenPoints(pointInQuestion, line, exclusive) {
3841 var oneDimension = false;
3842 for (var i = 2; i--;) {
3843 if (MakerJs.round(line.origin[i] - line.end[i], .000001) == 0) {
3844 if (oneDimension)
3845 return false;
3846 oneDimension = true;
3847 continue;
3848 }
3849 var origin_value = MakerJs.round(line.origin[i]);
3850 var end_value = MakerJs.round(line.end[i]);
3851 if (!isBetween(MakerJs.round(pointInQuestion[i]), origin_value, end_value, exclusive))
3852 return false;
3853 }
3854 return true;
3855 }
3856 measure.isBetweenPoints = isBetweenPoints;
3857 /**
3858 * Check if a given bezier seed has all points on the same slope.
3859 *
3860 * @param seed The bezier seed to test.
3861 * @param exclusive Optional boolean to test only within the boundary of the endpoints.
3862 * @returns Boolean true if bezier seed has control points on the line slope and between the line endpoints.
3863 */
3864 function isBezierSeedLinear(seed, exclusive) {
3865 //create a slope from the endpoints
3866 var slope = lineSlope(seed);
3867 for (var i = 0; i < seed.controls.length; i++) {
3868 if (!(measure.isPointOnSlope(seed.controls[i], slope))) {
3869 if (!exclusive)
3870 return false;
3871 if (isBetweenPoints(seed.controls[i], seed, false))
3872 return false;
3873 }
3874 }
3875 return true;
3876 }
3877 measure.isBezierSeedLinear = isBezierSeedLinear;
3878 /**
3879 * @private
3880 */
3881 var graham_scan = require('graham_scan');
3882 /**
3883 * @private
3884 */
3885 function serializePoint(p) {
3886 return p.join(',');
3887 }
3888 /**
3889 * Check for flow of paths in a chain being clockwise or not.
3890 *
3891 * @param chainContext The chain to test.
3892 * @param out_result Optional output object, if provided, will be populated with convex hull results.
3893 * @returns Boolean true if paths in the chain flow clockwise.
3894 */
3895 function isChainClockwise(chainContext, out_result) {
3896 //cannot do non-endless or circle
3897 if (!chainContext.endless || chainContext.links.length === 1) {
3898 return null;
3899 }
3900 var keyPoints = MakerJs.chain.toKeyPoints(chainContext);
3901 return isPointArrayClockwise(keyPoints, out_result);
3902 }
3903 measure.isChainClockwise = isChainClockwise;
3904 /**
3905 * Check for array of points being clockwise or not.
3906 *
3907 * @param points The array of points to test.
3908 * @param out_result Optional output object, if provided, will be populated with convex hull results.
3909 * @returns Boolean true if points flow clockwise.
3910 */
3911 function isPointArrayClockwise(points, out_result) {
3912 var convexHull = new graham_scan();
3913 var pointsInOrder = [];
3914 function add(endPoint) {
3915 convexHull.addPoint(endPoint[0], endPoint[1]);
3916 pointsInOrder.push(serializePoint(endPoint));
3917 }
3918 points.forEach(add);
3919 //we only need to deal with 3 points
3920 var hull = convexHull.getHull();
3921 var hullPoints = hull.slice(0, 3).map(function (p) { return serializePoint([p.x, p.y]); });
3922 var ordered = [];
3923 pointsInOrder.forEach(function (p) {
3924 if (~hullPoints.indexOf(p))
3925 ordered.push(p);
3926 });
3927 //now make sure endpoints of hull are endpoints of ordered. do this by managing the middle point
3928 switch (ordered.indexOf(hullPoints[1])) {
3929 case 0:
3930 //shift down
3931 ordered.unshift(ordered.pop());
3932 break;
3933 case 2:
3934 //shift up
3935 ordered.push(ordered.shift());
3936 break;
3937 }
3938 if (out_result) {
3939 out_result.hullPoints = hull.map(function (p) { return [p.x, p.y]; });
3940 out_result.keyPoints = points;
3941 }
3942 //the hull is counterclockwise, so the result is clockwise if the first elements do not match
3943 return hullPoints[0] != ordered[0];
3944 }
3945 measure.isPointArrayClockwise = isPointArrayClockwise;
3946 /**
3947 * Check for line overlapping another line.
3948 *
3949 * @param lineA The line to test.
3950 * @param lineB The line to check for overlap.
3951 * @param excludeTangents Boolean to exclude exact endpoints and only look for deep overlaps.
3952 * @returns Boolean true if lineA is overlapped with lineB.
3953 */
3954 function isLineOverlapping(lineA, lineB, excludeTangents) {
3955 var pointsOfIntersection = [];
3956 function checkPoints(index, a, b) {
3957 function checkPoint(p) {
3958 return isBetweenPoints(p, a, excludeTangents);
3959 }
3960 return checkPoint(b.origin) || checkPoint(b.end);
3961 }
3962 return checkPoints(0, lineA, lineB) || checkPoints(1, lineB, lineA);
3963 }
3964 measure.isLineOverlapping = isLineOverlapping;
3965 /**
3966 * Check for measurement overlapping another measurement.
3967 *
3968 * @param measureA The measurement to test.
3969 * @param measureB The measurement to check for overlap.
3970 * @returns Boolean true if measureA is overlapped with measureB.
3971 */
3972 function isMeasurementOverlapping(measureA, measureB) {
3973 for (var i = 2; i--;) {
3974 if (!(MakerJs.round(measureA.low[i] - measureB.high[i]) <= 0 && MakerJs.round(measureA.high[i] - measureB.low[i]) >= 0))
3975 return false;
3976 }
3977 return true;
3978 }
3979 measure.isMeasurementOverlapping = isMeasurementOverlapping;
3980 /**
3981 * Gets the slope of a line.
3982 */
3983 function lineSlope(line) {
3984 var dx = line.end[0] - line.origin[0];
3985 if (MakerJs.round(dx, .000001) == 0) {
3986 return {
3987 line: line,
3988 hasSlope: false
3989 };
3990 }
3991 var dy = line.end[1] - line.origin[1];
3992 var slope = dy / dx;
3993 var yIntercept = line.origin[1] - slope * line.origin[0];
3994 return {
3995 line: line,
3996 hasSlope: true,
3997 slope: slope,
3998 yIntercept: yIntercept
3999 };
4000 }
4001 measure.lineSlope = lineSlope;
4002 /**
4003 * Calculates the distance between two points.
4004 *
4005 * @param a First point.
4006 * @param b Second point.
4007 * @returns Distance between points.
4008 */
4009 function pointDistance(a, b) {
4010 var dx = b[0] - a[0];
4011 var dy = b[1] - a[1];
4012 return Math.sqrt(dx * dx + dy * dy);
4013 }
4014 measure.pointDistance = pointDistance;
4015 /**
4016 * @private
4017 */
4018 function getExtremePoint(a, b, fn) {
4019 return [
4020 fn(a[0], b[0]),
4021 fn(a[1], b[1])
4022 ];
4023 }
4024 /**
4025 * @private
4026 */
4027 var pathExtentsMap = {};
4028 pathExtentsMap[MakerJs.pathType.Line] = function (line) {
4029 return {
4030 low: getExtremePoint(line.origin, line.end, Math.min),
4031 high: getExtremePoint(line.origin, line.end, Math.max)
4032 };
4033 };
4034 pathExtentsMap[MakerJs.pathType.Circle] = function (circle) {
4035 var r = circle.radius;
4036 return {
4037 low: MakerJs.point.add(circle.origin, [-r, -r]),
4038 high: MakerJs.point.add(circle.origin, [r, r])
4039 };
4040 };
4041 pathExtentsMap[MakerJs.pathType.Arc] = function (arc) {
4042 var r = arc.radius;
4043 var arcPoints = MakerJs.point.fromArc(arc);
4044 function extremeAngle(xyAngle, value, fn) {
4045 var extremePoint = getExtremePoint(arcPoints[0], arcPoints[1], fn);
4046 for (var i = 2; i--;) {
4047 if (isBetweenArcAngles(xyAngle[i], arc, false)) {
4048 extremePoint[i] = value + arc.origin[i];
4049 }
4050 }
4051 return extremePoint;
4052 }
4053 return {
4054 low: extremeAngle([180, 270], -r, Math.min),
4055 high: extremeAngle([360, 90], r, Math.max)
4056 };
4057 };
4058 /**
4059 * Calculates the smallest rectangle which contains a path.
4060 *
4061 * @param pathToMeasure The path to measure.
4062 * @returns object with low and high points.
4063 */
4064 function pathExtents(pathToMeasure, addOffset) {
4065 if (pathToMeasure) {
4066 var fn = pathExtentsMap[pathToMeasure.type];
4067 if (fn) {
4068 var m = fn(pathToMeasure);
4069 if (addOffset) {
4070 m.high = MakerJs.point.add(m.high, addOffset);
4071 m.low = MakerJs.point.add(m.low, addOffset);
4072 }
4073 return m;
4074 }
4075 }
4076 return { low: null, high: null };
4077 }
4078 measure.pathExtents = pathExtents;
4079 /**
4080 * @private
4081 */
4082 var pathLengthMap = {};
4083 pathLengthMap[MakerJs.pathType.Line] = function (line) {
4084 return pointDistance(line.origin, line.end);
4085 };
4086 pathLengthMap[MakerJs.pathType.Circle] = function (circle) {
4087 return 2 * Math.PI * circle.radius;
4088 };
4089 pathLengthMap[MakerJs.pathType.Arc] = function (arc) {
4090 var value = pathLengthMap[MakerJs.pathType.Circle](arc);
4091 var pct = MakerJs.angle.ofArcSpan(arc) / 360;
4092 value *= pct;
4093 return value;
4094 };
4095 pathLengthMap[MakerJs.pathType.BezierSeed] = function (seed) {
4096 return MakerJs.models.BezierCurve.computeLength(seed);
4097 };
4098 /**
4099 * Measures the length of a path.
4100 *
4101 * @param pathToMeasure The path to measure.
4102 * @returns Length of the path.
4103 */
4104 function pathLength(pathToMeasure) {
4105 if (pathToMeasure) {
4106 var fn = pathLengthMap[pathToMeasure.type];
4107 if (fn) {
4108 return fn(pathToMeasure);
4109 }
4110 }
4111 return 0;
4112 }
4113 measure.pathLength = pathLength;
4114 /**
4115 * Measures the length of all paths in a model.
4116 *
4117 * @param modelToMeasure The model containing paths to measure.
4118 * @returns Length of all paths in the model.
4119 */
4120 function modelPathLength(modelToMeasure) {
4121 var total = 0;
4122 MakerJs.model.walk(modelToMeasure, {
4123 onPath: function (walkedPath) {
4124 total += pathLength(walkedPath.pathContext);
4125 }
4126 });
4127 return total;
4128 }
4129 measure.modelPathLength = modelPathLength;
4130 /**
4131 * @private
4132 */
4133 function cloneMeasure(measureToclone) {
4134 return {
4135 high: MakerJs.point.clone(measureToclone.high),
4136 low: MakerJs.point.clone(measureToclone.low)
4137 };
4138 }
4139 /**
4140 * Measures the smallest rectangle which contains a model.
4141 *
4142 * @param modelToMeasure The model to measure.
4143 * @param atlas Optional atlas to save measurements.
4144 * @returns object with low and high points.
4145 */
4146 function modelExtents(modelToMeasure, atlas) {
4147 function increaseParentModel(childRoute, childMeasurement) {
4148 if (!childMeasurement)
4149 return;
4150 //to get the parent route, just traverse backwards 2 to remove id and 'paths' / 'models'
4151 var parentRoute = childRoute.slice(0, -2);
4152 var parentRouteKey = MakerJs.createRouteKey(parentRoute);
4153 if (!(parentRouteKey in atlas.modelMap)) {
4154 //just start with the known size
4155 atlas.modelMap[parentRouteKey] = cloneMeasure(childMeasurement);
4156 }
4157 else {
4158 increase(atlas.modelMap[parentRouteKey], childMeasurement);
4159 }
4160 }
4161 if (!atlas)
4162 atlas = new Atlas(modelToMeasure);
4163 var walkOptions = {
4164 onPath: function (walkedPath) {
4165 //trust that the path measurement is good
4166 if (!(walkedPath.routeKey in atlas.pathMap)) {
4167 atlas.pathMap[walkedPath.routeKey] = pathExtents(walkedPath.pathContext, walkedPath.offset);
4168 }
4169 increaseParentModel(walkedPath.route, atlas.pathMap[walkedPath.routeKey]);
4170 },
4171 afterChildWalk: function (walkedModel) {
4172 //model has been updated by all its children, update parent
4173 increaseParentModel(walkedModel.route, atlas.modelMap[walkedModel.routeKey]);
4174 }
4175 };
4176 MakerJs.model.walk(modelToMeasure, walkOptions);
4177 atlas.modelsMeasured = true;
4178 var m = atlas.modelMap[''];
4179 if (m) {
4180 return augment(m);
4181 }
4182 return m;
4183 }
4184 measure.modelExtents = modelExtents;
4185 /**
4186 * Augment a measurement - add more properties such as center point, height and width.
4187 *
4188 * @param measureToAugment The measurement to augment.
4189 * @returns Measurement object with augmented properties.
4190 */
4191 function augment(measureToAugment) {
4192 var m = measureToAugment;
4193 m.center = MakerJs.point.average(m.high, m.low);
4194 m.width = m.high[0] - m.low[0];
4195 m.height = m.high[1] - m.low[1];
4196 return m;
4197 }
4198 measure.augment = augment;
4199 /**
4200 * A list of maps of measurements.
4201 *
4202 * @param modelToMeasure The model to measure.
4203 * @param atlas Optional atlas to save measurements.
4204 * @returns object with low and high points.
4205 */
4206 var Atlas = /** @class */ (function () {
4207 /**
4208 * Constructor.
4209 * @param modelContext The model to measure.
4210 */
4211 function Atlas(modelContext) {
4212 this.modelContext = modelContext;
4213 /**
4214 * Flag that models have been measured.
4215 */
4216 this.modelsMeasured = false;
4217 /**
4218 * Map of model measurements, mapped by routeKey.
4219 */
4220 this.modelMap = {};
4221 /**
4222 * Map of path measurements, mapped by routeKey.
4223 */
4224 this.pathMap = {};
4225 }
4226 Atlas.prototype.measureModels = function () {
4227 if (!this.modelsMeasured) {
4228 modelExtents(this.modelContext, this);
4229 }
4230 };
4231 return Atlas;
4232 }());
4233 measure.Atlas = Atlas;
4234 /**
4235 * @private
4236 */
4237 function loopIndex(base, i) {
4238 if (i >= base)
4239 return i - base;
4240 if (i < 0)
4241 return i + base;
4242 return i;
4243 }
4244 /**
4245 * @private
4246 */
4247 function yAtX(slope, x) {
4248 return slope.slope * x + slope.yIntercept;
4249 }
4250 /**
4251 * @private
4252 */
4253 function pointOnSlopeAtX(line, x) {
4254 var slope = lineSlope(line);
4255 return [x, yAtX(slope, x)];
4256 }
4257 /**
4258 * @private
4259 */
4260 function isCircular(bounds) {
4261 for (var i = 1; i < 3; i++) {
4262 if (!measure.isPointEqual(bounds[0].center, bounds[i].center, .000001) || !(MakerJs.round(bounds[0].width - bounds[i].width) === 0)) {
4263 return false;
4264 }
4265 }
4266 return true;
4267 }
4268 /**
4269 * @private
4270 */
4271 function getAngledBounds(index, modelToMeasure, rotateModel, rotatePaths) {
4272 MakerJs.model.rotate(modelToMeasure, rotateModel);
4273 var m = modelExtents(modelToMeasure);
4274 var result = {
4275 index: index,
4276 rotation: rotatePaths,
4277 center: MakerJs.point.rotate(m.center, rotatePaths),
4278 //model is sideways, so width is based on Y, height is based on X
4279 width: m.height,
4280 height: m.width,
4281 bottom: new MakerJs.paths.Line(m.low, [m.high[0], m.low[1]]),
4282 middle: new MakerJs.paths.Line([m.low[0], m.center[1]], [m.high[0], m.center[1]]),
4283 top: new MakerJs.paths.Line(m.high, [m.low[0], m.high[1]])
4284 };
4285 [result.top, result.middle, result.bottom].forEach(function (line) { return MakerJs.path.rotate(line, rotatePaths); });
4286 return result;
4287 }
4288 /**
4289 * @private
4290 */
4291 function hexSolution(lines, bounds) {
4292 var tip = lines[1].origin;
4293 var tipX = tip[0];
4294 var left = lines[3].origin[0];
4295 var right = lines[0].origin[0];
4296 //see if left edge is in bounds if right edge is on the hex boundary
4297 var altRight = tipX - right;
4298 if ((right - left) > 2 * altRight)
4299 return null;
4300 //see if right edge is in bounds if left edge is on the hex boundary
4301 var altLeft = (tipX - left) / 3;
4302 if (altRight < altLeft)
4303 return null;
4304 var altitudeViaSide = Math.min(altLeft, altRight);
4305 var radiusViaSide = MakerJs.solvers.equilateralSide(altitudeViaSide);
4306 //find peaks, then find highest peak
4307 var peakPoints = [MakerJs.point.fromSlopeIntersection(lines[1], lines[2]), MakerJs.point.fromSlopeIntersection(lines[4], lines[5])];
4308 var peakRadii = peakPoints.map(function (p) { return Math.abs(p[1] - tip[1]); });
4309 var peakNum = (peakRadii[0] > peakRadii[1]) ? 0 : 1; //top = 0, bottom = 1
4310 var radiusViaPeak = peakRadii[peakNum];
4311 if (radiusViaPeak > radiusViaSide) {
4312 var altitudeViaPeak = MakerJs.solvers.equilateralAltitude(radiusViaPeak);
4313 var peakX = tipX - 2 * altitudeViaPeak;
4314 //see if it will contain right side
4315 if (right > peakX + altitudeViaPeak)
4316 return null;
4317 //see if it will contain left side
4318 if (left < peakX - altitudeViaPeak)
4319 return null;
4320 //at this point, [tipX - 2 * altitudeViaPeak, tip[1]] is a solution for origin.
4321 //but we want to best center the result by sliding along the boundary middle, balancing the smallest gap
4322 var leftGap = left - peakX + altitudeViaPeak;
4323 var peakGap = 2 * altitudeViaPeak - bounds[peakNum + 1].width;
4324 var minHalfGap = Math.min(leftGap, peakGap) / 2;
4325 return {
4326 origin: pointOnSlopeAtX(bounds[2 - peakNum].middle, peakX + minHalfGap),
4327 radius: radiusViaPeak,
4328 type: 'peak ' + peakNum
4329 };
4330 }
4331 else {
4332 return {
4333 origin: [tipX - 2 * altitudeViaSide, tip[1]],
4334 radius: radiusViaSide,
4335 type: 'side'
4336 };
4337 }
4338 }
4339 /**
4340 * Measures the minimum bounding hexagon surrounding a model. The hexagon is oriented such that the right and left sides are vertical, and the top and bottom are pointed.
4341 *
4342 * @param modelToMeasure The model to measure.
4343 * @returns IBoundingHex object which is a hexagon model, with an additional radius property.
4344 */
4345 function boundingHexagon(modelToMeasure) {
4346 var clone = MakerJs.cloneObject(modelToMeasure);
4347 MakerJs.model.originate(clone);
4348 var originalMeasure = modelExtents(clone);
4349 var bounds = [];
4350 var scratch = { paths: {} };
4351 MakerJs.model.center(clone);
4352 function result(radius, origin, notes) {
4353 return {
4354 radius: radius,
4355 paths: new MakerJs.models.Polygon(6, radius, 30).paths,
4356 origin: MakerJs.point.add(origin, originalMeasure.center),
4357 //models: { scratch: scratch },
4358 notes: notes
4359 };
4360 }
4361 var boundRotations = [[90, -90], [-60, -30], [-60, 30]];
4362 while (boundRotations.length) {
4363 var rotation = boundRotations.shift();
4364 var bound = getAngledBounds(bounds.length, clone, rotation[0], rotation[1]);
4365 var side = MakerJs.solvers.equilateralSide(bound.width / 2);
4366 if (side >= bound.height) {
4367 return result(side, bound.center, 'solved by bound ' + bounds.length);
4368 }
4369 bounds.push(bound);
4370 }
4371 //model.rotate(clone, 30);
4372 //scratch.models = { clone: clone };
4373 //check for a circular solution
4374 if (isCircular(bounds)) {
4375 return result(MakerJs.solvers.equilateralSide(bounds[0].width / 2), bounds[0].center, 'solved as circular');
4376 }
4377 var perimeters = bounds.map(function (b) { return b.top; }).concat(bounds.map(function (b) { return b.bottom; }));
4378 perimeters.forEach(function (p, i) {
4379 scratch.paths[i] = p;
4380 //converge alternate lines to form two triangles
4381 MakerJs.path.converge(perimeters[loopIndex(6, i + 2)], p, true);
4382 });
4383 bounds.forEach(function (b, i) {
4384 scratch.paths['m' + i] = b.middle;
4385 });
4386 var boundCopy = bounds.slice();
4387 var solution;
4388 //solve a hexagon for every tip, keeping the smallest one
4389 for (var i = 0; i < 6; i++) {
4390 //rotate the scratch area so that we always reference the tip at polar 0
4391 if (i > 0) {
4392 perimeters.push(perimeters.shift());
4393 boundCopy.push(boundCopy.shift());
4394 MakerJs.model.rotate(scratch, -60);
4395 }
4396 var s = hexSolution(perimeters, boundCopy);
4397 if (s) {
4398 if (!solution || s.radius < solution.radius) {
4399 solution = s;
4400 solution.index = i;
4401 }
4402 }
4403 }
4404 var p = MakerJs.point.rotate(solution.origin, solution.index * 60);
4405 return result(solution.radius, p, 'solved by ' + solution.index + ' as ' + solution.type);
4406 }
4407 measure.boundingHexagon = boundingHexagon;
4408 /**
4409 * @private
4410 */
4411 function addUniquePoints(pointArray, pointsToAdd) {
4412 var added = 0;
4413 pointsToAdd.forEach(function (p) {
4414 if (!measure.isPointDistinct(p, pointArray, .00000001))
4415 return;
4416 pointArray.push(p);
4417 added++;
4418 });
4419 return added;
4420 }
4421 /**
4422 * @private
4423 */
4424 function getFarPoint(modelContext, farPoint, measureAtlas) {
4425 if (farPoint)
4426 return farPoint;
4427 var high = modelExtents(modelContext).high;
4428 if (high) {
4429 return MakerJs.point.add(high, [1, 1]);
4430 }
4431 return [7654321, 1234567];
4432 }
4433 /**
4434 * Check to see if a point is inside of a model.
4435 *
4436 * @param pointToCheck The point to check.
4437 * @param modelContext The model to check against.
4438 * @param options Optional IMeasurePointInsideOptions object.
4439 * @returns Boolean true if the path is inside of the modelContext.
4440 */
4441 function isPointInsideModel(pointToCheck, modelContext, options) {
4442 if (options === void 0) { options = {}; }
4443 if (!options.farPoint) {
4444 options.farPoint = getFarPoint(modelContext, options.farPoint, options.measureAtlas);
4445 }
4446 options.out_intersectionPoints = [];
4447 var isInside;
4448 var lineToFarPoint = new MakerJs.paths.Line(pointToCheck, options.farPoint);
4449 var measureFarPoint = pathExtents(lineToFarPoint);
4450 var walkOptions = {
4451 onPath: function (walkedPath) {
4452 if (options.measureAtlas && !isMeasurementOverlapping(measureFarPoint, options.measureAtlas.pathMap[walkedPath.routeKey])) {
4453 return;
4454 }
4455 var intersectOptions = { path2Offset: walkedPath.offset };
4456 var farInt = MakerJs.path.intersection(lineToFarPoint, walkedPath.pathContext, intersectOptions);
4457 if (farInt) {
4458 var added = addUniquePoints(options.out_intersectionPoints, farInt.intersectionPoints);
4459 //if number of intersections is an odd number, flip the flag.
4460 if (added % 2 == 1) {
4461 isInside = !!!isInside;
4462 }
4463 }
4464 },
4465 beforeChildWalk: function (innerWalkedModel) {
4466 if (!options.measureAtlas) {
4467 return true;
4468 }
4469 //see if there is a model measurement. if not, it is because the model does not contain paths.
4470 var innerModelMeasurement = options.measureAtlas.modelMap[innerWalkedModel.routeKey];
4471 return innerModelMeasurement && isMeasurementOverlapping(measureFarPoint, innerModelMeasurement);
4472 }
4473 };
4474 MakerJs.model.walk(modelContext, walkOptions);
4475 return !!isInside;
4476 }
4477 measure.isPointInsideModel = isPointInsideModel;
4478 })(measure = MakerJs.measure || (MakerJs.measure = {}));
4479})(MakerJs || (MakerJs = {}));
4480var MakerJs;
4481(function (MakerJs) {
4482 var exporter;
4483 (function (exporter) {
4484 /**
4485 * Renders an item in JSON.
4486 *
4487 * @param itemToExport Item to render: may be a path, an array of paths, or a model object.
4488 * @param options Rendering options object.
4489 * @param options.accuracy Optional exemplar of number of decimal places.
4490 * @param options.indentation Optional number of characters to indent after a newline.
4491 * @returns String of DXF content.
4492 */
4493 function toJson(itemToExport, options) {
4494 if (options === void 0) { options = {}; }
4495 function replacer(key, value) {
4496 if (MakerJs.isNumber(value)) {
4497 var newValue = MakerJs.round(value, options.accuracy);
4498 return newValue;
4499 }
4500 if (MakerJs.isPoint(value)) {
4501 var newPoint = MakerJs.point.rounded(value, options.accuracy);
4502 return newPoint;
4503 }
4504 return value;
4505 }
4506 return JSON.stringify(itemToExport, options.accuracy && replacer, options.indentation);
4507 }
4508 exporter.toJson = toJson;
4509 /**
4510 * Try to get the unit system from a model
4511 * @private
4512 */
4513 function tryGetModelUnits(itemToExport) {
4514 if (MakerJs.isModel(itemToExport)) {
4515 return itemToExport.units;
4516 }
4517 }
4518 exporter.tryGetModelUnits = tryGetModelUnits;
4519 /**
4520 * Named colors, safe for CSS and DXF
4521 * 17 colors from https://www.w3.org/TR/CSS21/syndata.html#value-def-color mapped to DXF equivalent AutoDesk Color Index
4522 */
4523 exporter.colors = {
4524 black: 0,
4525 red: 1,
4526 yellow: 2,
4527 lime: 3,
4528 aqua: 4,
4529 blue: 5,
4530 fuchsia: 6,
4531 white: 7,
4532 gray: 9,
4533 maroon: 14,
4534 orange: 30,
4535 olive: 58,
4536 green: 94,
4537 teal: 134,
4538 navy: 174,
4539 purple: 214,
4540 silver: 254
4541 };
4542 })(exporter = MakerJs.exporter || (MakerJs.exporter = {}));
4543})(MakerJs || (MakerJs = {}));
4544var MakerJs;
4545(function (MakerJs) {
4546 var importer;
4547 (function (importer) {
4548 /**
4549 * Create a numeric array from a string of numbers. The numbers may be delimited by anything non-numeric.
4550 *
4551 * Example:
4552 * ```
4553 * var n = makerjs.importer.parseNumericList('5, 10, 15.20 25-30-35 4e1 .5');
4554 * ```
4555 *
4556 * @param s The string of numbers.
4557 * @returns Array of numbers.
4558 */
4559 function parseNumericList(s) {
4560 var result = [];
4561 //http://stackoverflow.com/questions/638565/parsing-scientific-notation-sensibly
4562 var re = /[\.-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
4563 var matches;
4564 while ((matches = re.exec(s)) !== null) {
4565 if (matches.index === re.lastIndex) {
4566 re.lastIndex++;
4567 }
4568 result.push(parseFloat(matches[0]));
4569 }
4570 return result;
4571 }
4572 importer.parseNumericList = parseNumericList;
4573 })(importer = MakerJs.importer || (MakerJs.importer = {}));
4574})(MakerJs || (MakerJs = {}));
4575var MakerJs;
4576(function (MakerJs) {
4577 var exporter;
4578 (function (exporter) {
4579 /**
4580 * Renders an item in AutoDesk DFX file format.
4581 *
4582 * @param itemToExport Item to render: may be a path, an array of paths, or a model object.
4583 * @param options Rendering options object.
4584 * @param options.units String of the unit system. May be omitted. See makerjs.unitType for possible values.
4585 * @returns String of DXF content.
4586 */
4587 function toDXF(itemToExport, options) {
4588 //DXF format documentation:
4589 //http://images.autodesk.com/adsk/files/acad_dxf0.pdf
4590 if (options === void 0) { options = {}; }
4591 var opts = {
4592 fontSize: 9
4593 };
4594 var layerIds = [];
4595 var doc = {
4596 entities: [],
4597 header: {},
4598 tables: {}
4599 };
4600 MakerJs.extendObject(opts, options);
4601 if (MakerJs.isModel(itemToExport)) {
4602 var modelToExport = itemToExport;
4603 if (modelToExport.exporterOptions) {
4604 MakerJs.extendObject(opts, modelToExport.exporterOptions['toDXF']);
4605 }
4606 }
4607 function colorLayerOptions(layer) {
4608 if (opts.layerOptions && opts.layerOptions[layer])
4609 return opts.layerOptions[layer];
4610 if (layer in exporter.colors) {
4611 return {
4612 color: exporter.colors[layer]
4613 };
4614 }
4615 }
4616 function defaultLayer(pathContext, parentLayer) {
4617 var layerId = (pathContext && pathContext.layer) || parentLayer || '0';
4618 if (layerIds.indexOf(layerId) < 0) {
4619 layerIds.push(layerId);
4620 }
4621 return layerId;
4622 }
4623 var map = {};
4624 map[MakerJs.pathType.Line] = function (line, offset, layer) {
4625 var lineEntity = {
4626 type: "LINE",
4627 layer: defaultLayer(line, layer),
4628 vertices: [
4629 {
4630 x: MakerJs.round(line.origin[0] + offset[0], opts.accuracy),
4631 y: MakerJs.round(line.origin[1] + offset[1], opts.accuracy)
4632 },
4633 {
4634 x: MakerJs.round(line.end[0] + offset[0], opts.accuracy),
4635 y: MakerJs.round(line.end[1] + offset[1], opts.accuracy)
4636 }
4637 ]
4638 };
4639 return lineEntity;
4640 };
4641 map[MakerJs.pathType.Circle] = function (circle, offset, layer) {
4642 var circleEntity = {
4643 type: "CIRCLE",
4644 layer: defaultLayer(circle, layer),
4645 center: {
4646 x: MakerJs.round(circle.origin[0] + offset[0], opts.accuracy),
4647 y: MakerJs.round(circle.origin[1] + offset[1], opts.accuracy)
4648 },
4649 radius: MakerJs.round(circle.radius, opts.accuracy)
4650 };
4651 return circleEntity;
4652 };
4653 map[MakerJs.pathType.Arc] = function (arc, offset, layer) {
4654 var arcEntity = {
4655 type: "ARC",
4656 layer: defaultLayer(arc, layer),
4657 center: {
4658 x: MakerJs.round(arc.origin[0] + offset[0], opts.accuracy),
4659 y: MakerJs.round(arc.origin[1] + offset[1], opts.accuracy)
4660 },
4661 radius: MakerJs.round(arc.radius, opts.accuracy),
4662 startAngle: MakerJs.round(arc.startAngle, opts.accuracy),
4663 endAngle: MakerJs.round(arc.endAngle, opts.accuracy)
4664 };
4665 return arcEntity;
4666 };
4667 //TODO - handle scenario if any bezier seeds get passed
4668 //map[pathType.BezierSeed]
4669 function appendVertex(v, layer, bulge) {
4670 var vertex = {
4671 type: "VERTEX",
4672 layer: defaultLayer(null, layer),
4673 x: MakerJs.round(v[0], opts.accuracy),
4674 y: MakerJs.round(v[1], opts.accuracy),
4675 bulge: bulge
4676 };
4677 return vertex;
4678 }
4679 function polyline(c) {
4680 var polylineEntity = {
4681 type: "POLYLINE",
4682 layer: defaultLayer(null, c.layer),
4683 shape: c.chain.endless,
4684 vertices: []
4685 };
4686 c.chain.links.forEach(function (link, i) {
4687 var bulge;
4688 if (link.walkedPath.pathContext.type === MakerJs.pathType.Arc) {
4689 var arc = link.walkedPath.pathContext;
4690 bulge = MakerJs.round(Math.tan(MakerJs.angle.toRadians(MakerJs.angle.ofArcSpan(arc)) / 4), opts.accuracy);
4691 if (link.reversed) {
4692 bulge *= -1;
4693 }
4694 }
4695 var vertex = link.endPoints[link.reversed ? 1 : 0];
4696 polylineEntity.vertices.push(appendVertex(vertex, c.layer, bulge));
4697 });
4698 if (!c.chain.endless) {
4699 var lastLink = c.chain.links[c.chain.links.length - 1];
4700 var endPoint = lastLink.endPoints[lastLink.reversed ? 0 : 1];
4701 polylineEntity.vertices.push(appendVertex(endPoint, c.layer));
4702 }
4703 return polylineEntity;
4704 }
4705 function mtext(caption) {
4706 var center = MakerJs.point.middle(caption.anchor);
4707 var mtextEntity = {
4708 type: "MTEXT",
4709 position: {
4710 x: MakerJs.round(center[0], opts.accuracy),
4711 y: MakerJs.round(center[1], opts.accuracy)
4712 },
4713 height: opts.fontSize,
4714 text: caption.text,
4715 attachmentPoint: 5,
4716 drawingDirection: 1,
4717 rotation: MakerJs.angle.ofPointInRadians(caption.anchor.origin, caption.anchor.end)
4718 };
4719 return mtextEntity;
4720 }
4721 function layerOut(layerId, layerColor) {
4722 var layerEntity = {
4723 name: layerId,
4724 color: layerColor
4725 };
4726 return layerEntity;
4727 }
4728 function lineTypesOut() {
4729 var lineStyleTable = {
4730 lineTypes: {
4731 "CONTINUOUS": {
4732 name: "CONTINUOUS",
4733 description: "______",
4734 patternLength: 0
4735 }
4736 }
4737 };
4738 var tableName = 'lineType';
4739 doc.tables[tableName] = lineStyleTable;
4740 }
4741 function layersOut() {
4742 var layerTable = {
4743 layers: {}
4744 };
4745 layerIds.forEach(function (layerId) {
4746 var layerOptions = colorLayerOptions(layerId);
4747 if (layerOptions) {
4748 layerTable.layers[layerId] = layerOut(layerId, layerOptions.color);
4749 }
4750 });
4751 var tableName = 'layer';
4752 doc.tables[tableName] = layerTable;
4753 }
4754 function header() {
4755 if (opts.units) {
4756 var units = dxfUnit[opts.units];
4757 doc.header["$INSUNITS"] = units;
4758 }
4759 }
4760 function entities(walkedPaths, chains, captions) {
4761 var entityArray = doc.entities;
4762 entityArray.push.apply(entityArray, chains.map(polyline));
4763 walkedPaths.forEach(function (walkedPath) {
4764 var fn = map[walkedPath.pathContext.type];
4765 if (fn) {
4766 var entity = fn(walkedPath.pathContext, walkedPath.offset, walkedPath.layer);
4767 entityArray.push(entity);
4768 }
4769 });
4770 entityArray.push.apply(entityArray, captions.map(mtext));
4771 }
4772 //fixup options
4773 if (!opts.units) {
4774 var units = exporter.tryGetModelUnits(itemToExport);
4775 if (units) {
4776 opts.units = units;
4777 }
4778 }
4779 //also pass back to options parameter
4780 MakerJs.extendObject(options, opts);
4781 //begin dxf output
4782 var chainsOnLayers = [];
4783 var walkedPaths = [];
4784 if (opts.usePOLYLINE) {
4785 var cb = function (chains, loose, layer) {
4786 chains.forEach(function (c) {
4787 if (c.endless && c.links.length === 1 && c.links[0].walkedPath.pathContext.type === MakerJs.pathType.Circle) {
4788 //don't treat circles as lwpolylines
4789 walkedPaths.push(c.links[0].walkedPath);
4790 return;
4791 }
4792 var chainOnLayer = { chain: c, layer: layer };
4793 chainsOnLayers.push(chainOnLayer);
4794 });
4795 walkedPaths.push.apply(walkedPaths, loose);
4796 };
4797 MakerJs.model.findChains(modelToExport, cb, { byLayers: true, pointMatchingDistance: opts.pointMatchingDistance });
4798 }
4799 else {
4800 var walkOptions = {
4801 onPath: function (walkedPath) {
4802 walkedPaths.push(walkedPath);
4803 }
4804 };
4805 MakerJs.model.walk(modelToExport, walkOptions);
4806 }
4807 entities(walkedPaths, chainsOnLayers, MakerJs.model.getAllCaptionsOffset(modelToExport));
4808 header();
4809 lineTypesOut();
4810 layersOut();
4811 return outputDocument(doc);
4812 }
4813 exporter.toDXF = toDXF;
4814 /**
4815 * @private
4816 */
4817 function outputDocument(doc) {
4818 var dxf = [];
4819 function append() {
4820 var values = [];
4821 for (var _i = 0; _i < arguments.length; _i++) {
4822 values[_i] = arguments[_i];
4823 }
4824 dxf.push.apply(dxf, values);
4825 }
4826 var map = {};
4827 map["LINE"] = function (line) {
4828 append("0", "LINE", "8", line.layer, "10", line.vertices[0].x, "20", line.vertices[0].y, "11", line.vertices[1].x, "21", line.vertices[1].y);
4829 };
4830 map["CIRCLE"] = function (circle) {
4831 append("0", "CIRCLE", "8", circle.layer, "10", circle.center.x, "20", circle.center.y, "40", circle.radius);
4832 };
4833 map["ARC"] = function (arc) {
4834 append("0", "ARC", "8", arc.layer, "10", arc.center.x, "20", arc.center.y, "40", arc.radius, "50", arc.startAngle, "51", arc.endAngle);
4835 };
4836 //TODO - handle scenario if any bezier seeds get passed
4837 //map[pathType.BezierSeed]
4838 map["VERTEX"] = function (vertex) {
4839 append("0", "VERTEX", "8", vertex.layer, "10", vertex.x, "20", vertex.y);
4840 if (vertex.bulge !== undefined) {
4841 append("42", vertex.bulge);
4842 }
4843 };
4844 map["POLYLINE"] = function (polyline) {
4845 append("0", "POLYLINE", "8", polyline.layer, "66", 1, "70", polyline.shape ? 1 : 0);
4846 polyline.vertices.forEach(function (vertex) { return map["VERTEX"](vertex); });
4847 append("0", "SEQEND");
4848 };
4849 map["MTEXT"] = function (mtext) {
4850 append("0", "MTEXT", "10", mtext.position.x, "20", mtext.position.y, "40", mtext.height, "71", mtext.attachmentPoint, "72", mtext.drawingDirection, "1", mtext.text, //TODO: break into 250 char chunks
4851 "50", mtext.rotation);
4852 };
4853 function section(sectionFn) {
4854 append("0", "SECTION");
4855 sectionFn();
4856 append("0", "ENDSEC");
4857 }
4858 function table(fn) {
4859 append("0", "TABLE");
4860 fn();
4861 append("0", "ENDTAB");
4862 }
4863 function tables() {
4864 append("2", "TABLES");
4865 table(lineTypesOut);
4866 table(layersOut);
4867 }
4868 function layerOut(layer) {
4869 append("0", "LAYER", "2", layer.name, "70", "0", "62", layer.color, "6", "CONTINUOUS");
4870 }
4871 function lineTypeOut(lineType) {
4872 append("0", "LTYPE", "72", //72 Alignment code; value is always 65, the ASCII code for A
4873 "65", "70", "64", "2", lineType.name, "3", lineType.description, "73", "0", "40", lineType.patternLength);
4874 }
4875 function lineTypesOut() {
4876 var lineTypeTableName = 'lineType';
4877 var lineTypeTable = doc.tables[lineTypeTableName];
4878 append("2", "LTYPE");
4879 for (var lineTypeId in lineTypeTable.lineTypes) {
4880 var lineType = lineTypeTable.lineTypes[lineTypeId];
4881 lineTypeOut(lineType);
4882 }
4883 }
4884 function layersOut() {
4885 var layerTableName = 'layer';
4886 var layerTable = doc.tables[layerTableName];
4887 append("2", "LAYER");
4888 for (var layerId in layerTable.layers) {
4889 var layer = layerTable.layers[layerId];
4890 layerOut(layer);
4891 }
4892 }
4893 function header() {
4894 append("2", "HEADER");
4895 for (var key in doc.header) {
4896 var value = doc.header[key];
4897 append("9", key, "70", value);
4898 }
4899 }
4900 function entities(entityArray) {
4901 append("2", "ENTITIES");
4902 entityArray.forEach(function (entity) {
4903 var fn = map[entity.type];
4904 if (fn) {
4905 fn(entity);
4906 }
4907 });
4908 }
4909 //begin dxf output
4910 section(header);
4911 section(tables);
4912 section(function () { return entities(doc.entities); });
4913 append("0", "EOF");
4914 return dxf.join('\n');
4915 }
4916 /**
4917 * @private
4918 */
4919 var dxfUnit = {};
4920 //DXF format documentation:
4921 //http://images.autodesk.com/adsk/files/acad_dxf0.pdf
4922 //Default drawing units for AutoCAD DesignCenter blocks:
4923 //0 = Unitless; 1 = Inches; 2 = Feet; 3 = Miles; 4 = Millimeters; 5 = Centimeters; 6 = Meters; 7 = Kilometers; 8 = Microinches;
4924 dxfUnit[''] = 0;
4925 dxfUnit[MakerJs.unitType.Inch] = 1;
4926 dxfUnit[MakerJs.unitType.Foot] = 2;
4927 dxfUnit[MakerJs.unitType.Millimeter] = 4;
4928 dxfUnit[MakerJs.unitType.Centimeter] = 5;
4929 dxfUnit[MakerJs.unitType.Meter] = 6;
4930 })(exporter = MakerJs.exporter || (MakerJs.exporter = {}));
4931})(MakerJs || (MakerJs = {}));
4932var MakerJs;
4933(function (MakerJs) {
4934 var solvers;
4935 (function (solvers) {
4936 /**
4937 * @private
4938 */
4939 var equilateral = Math.sqrt(3) / 2;
4940 /**
4941 * Solves for the altitude of an equilateral triangle when you know its side length.
4942 *
4943 * @param sideLength Length of a side of the equilateral triangle (all 3 sides are equal).
4944 * @returns Altitude of the equilateral triangle.
4945 */
4946 function equilateralAltitude(sideLength) {
4947 return sideLength * equilateral;
4948 }
4949 solvers.equilateralAltitude = equilateralAltitude;
4950 /**
4951 * Solves for the side length of an equilateral triangle when you know its altitude.
4952 *
4953 * @param altitude Altitude of the equilateral triangle.
4954 * @returns Length of the side of the equilateral triangle (all 3 sides are equal).
4955 */
4956 function equilateralSide(altitude) {
4957 return altitude / equilateral;
4958 }
4959 solvers.equilateralSide = equilateralSide;
4960 /**
4961 * Solves for the angle of a triangle when you know lengths of 3 sides.
4962 *
4963 * @param lengthA Length of side of triangle, opposite of the angle you are trying to find.
4964 * @param lengthB Length of any other side of the triangle.
4965 * @param lengthC Length of the remaining side of the triangle.
4966 * @returns Angle opposite of the side represented by the first parameter.
4967 */
4968 function solveTriangleSSS(lengthA, lengthB, lengthC) {
4969 return MakerJs.angle.toDegrees(Math.acos((lengthB * lengthB + lengthC * lengthC - lengthA * lengthA) / (2 * lengthB * lengthC)));
4970 }
4971 solvers.solveTriangleSSS = solveTriangleSSS;
4972 /**
4973 * Solves for the length of a side of a triangle when you know length of one side and 2 angles.
4974 *
4975 * @param oppositeAngleInDegrees Angle which is opposite of the side you are trying to find.
4976 * @param lengthOfSideBetweenAngles Length of one side of the triangle which is between the provided angles.
4977 * @param otherAngleInDegrees An other angle of the triangle.
4978 * @returns Length of the side of the triangle which is opposite of the first angle parameter.
4979 */
4980 function solveTriangleASA(oppositeAngleInDegrees, lengthOfSideBetweenAngles, otherAngleInDegrees) {
4981 var angleOppositeSide = 180 - oppositeAngleInDegrees - otherAngleInDegrees;
4982 return (lengthOfSideBetweenAngles * Math.sin(MakerJs.angle.toRadians(oppositeAngleInDegrees))) / Math.sin(MakerJs.angle.toRadians(angleOppositeSide));
4983 }
4984 solvers.solveTriangleASA = solveTriangleASA;
4985 /**
4986 * Solves for the angles of the tangent lines between 2 circles.
4987 *
4988 * @param a First circle.
4989 * @param b Second circle.
4990 * @param inner Boolean to use inner tangents instead of outer tangents.
4991 * @returns Array of angles in degrees where 2 lines between the circles will be tangent to both circles.
4992 */
4993 function circleTangentAngles(a, b, inner) {
4994 if (inner === void 0) { inner = false; }
4995 var connect = new MakerJs.paths.Line(a.origin, b.origin);
4996 var distance = MakerJs.measure.pointDistance(a.origin, b.origin);
4997 //no tangents if either circle encompasses the other
4998 if (a.radius >= distance + b.radius || b.radius >= distance + a.radius)
4999 return null;
5000 //no inner tangents when circles touch or overlap
5001 if (inner && (a.radius + b.radius >= distance))
5002 return null;
5003 var tangentAngles;
5004 if (!inner && MakerJs.round(a.radius - b.radius) == 0) {
5005 tangentAngles = [90, 270];
5006 }
5007 else {
5008 //solve for circles on the x axis at the distance
5009 var d2 = distance / 2;
5010 var between = new MakerJs.paths.Circle([d2, 0], d2);
5011 var diff = new MakerJs.paths.Circle(a.radius > b.radius ? [0, 0] : [distance, 0], inner ? (a.radius + b.radius) : Math.abs(a.radius - b.radius));
5012 var int = MakerJs.path.intersection(diff, between);
5013 if (!int || !int.path1Angles)
5014 return null;
5015 tangentAngles = int.path1Angles;
5016 }
5017 var connectAngle = MakerJs.angle.ofLineInDegrees(connect);
5018 //add the line's angle to the result
5019 return tangentAngles.map(function (a) { return MakerJs.angle.noRevolutions(a + connectAngle); });
5020 }
5021 solvers.circleTangentAngles = circleTangentAngles;
5022 })(solvers = MakerJs.solvers || (MakerJs.solvers = {}));
5023})(MakerJs || (MakerJs = {}));
5024var MakerJs;
5025(function (MakerJs) {
5026 var path;
5027 (function (path) {
5028 /**
5029 * @private
5030 */
5031 var map = {};
5032 map[MakerJs.pathType.Arc] = {};
5033 map[MakerJs.pathType.Circle] = {};
5034 map[MakerJs.pathType.Line] = {};
5035 map[MakerJs.pathType.Arc][MakerJs.pathType.Arc] = function (arc1, arc2, options, swapOffsets) {
5036 var result = null;
5037 moveTemp([arc1, arc2], options, swapOffsets, function () {
5038 var angles = circleToCircle(arc1, arc2, options);
5039 if (angles) {
5040 var arc1Angles = getAnglesWithinArc(angles[0], arc1, options);
5041 var arc2Angles = getAnglesWithinArc(angles[1], arc2, options);
5042 if (arc1Angles && arc2Angles) {
5043 //must correspond to the same angle indexes
5044 if (arc1Angles.length === 1 || arc2Angles.length === 1) {
5045 for (var i1 = 0; i1 < arc1Angles.length; i1++) {
5046 for (var i2 = 0; i2 < arc2Angles.length; i2++) {
5047 var p1 = MakerJs.point.fromAngleOnCircle(arc1Angles[i1], arc1);
5048 var p2 = MakerJs.point.fromAngleOnCircle(arc2Angles[i2], arc2);
5049 //if they do not correspond then they don't intersect
5050 if (MakerJs.measure.isPointEqual(p1, p2, .0001)) {
5051 result = {
5052 intersectionPoints: [p1],
5053 path1Angles: [arc1Angles[i1]],
5054 path2Angles: [arc2Angles[i2]]
5055 };
5056 return;
5057 }
5058 }
5059 }
5060 }
5061 else {
5062 result = {
5063 intersectionPoints: pointsFromAnglesOnCircle(arc1Angles, arc1),
5064 path1Angles: arc1Angles,
5065 path2Angles: arc2Angles
5066 };
5067 }
5068 }
5069 }
5070 else {
5071 if (options.out_AreOverlapped) {
5072 //overlapped for circle, reset and see if arcs actually overlap.
5073 options.out_AreOverlapped = MakerJs.measure.isArcOverlapping(arc1, arc2, options.excludeTangents);
5074 }
5075 }
5076 });
5077 return result;
5078 };
5079 map[MakerJs.pathType.Arc][MakerJs.pathType.Circle] = function (arc, circle, options, swapOffsets) {
5080 var result = null;
5081 moveTemp([arc, circle], options, swapOffsets, function () {
5082 var angles = circleToCircle(arc, circle, options);
5083 if (angles) {
5084 var arcAngles = getAnglesWithinArc(angles[0], arc, options);
5085 if (arcAngles) {
5086 var circleAngles;
5087 //if both points are on arc, use both on circle
5088 if (arcAngles.length == 2) {
5089 circleAngles = angles[1];
5090 }
5091 else {
5092 //use the corresponding point on circle
5093 var index = findCorrespondingAngleIndex(angles[0], arcAngles[0]);
5094 circleAngles = [angles[1][index]];
5095 }
5096 result = {
5097 intersectionPoints: pointsFromAnglesOnCircle(arcAngles, arc),
5098 path1Angles: arcAngles,
5099 path2Angles: circleAngles
5100 };
5101 }
5102 }
5103 });
5104 return result;
5105 };
5106 map[MakerJs.pathType.Arc][MakerJs.pathType.Line] = function (arc, line, options, swapOffsets) {
5107 var result = null;
5108 moveTemp([arc, line], options, swapOffsets, function () {
5109 var angles = lineToCircle(line, arc, options);
5110 if (angles) {
5111 var arcAngles = getAnglesWithinArc(angles, arc, options);
5112 if (arcAngles) {
5113 result = {
5114 intersectionPoints: pointsFromAnglesOnCircle(arcAngles, arc),
5115 path1Angles: arcAngles
5116 };
5117 }
5118 }
5119 });
5120 return result;
5121 };
5122 map[MakerJs.pathType.Circle][MakerJs.pathType.Arc] = function (circle, arc, options) {
5123 var result = map[MakerJs.pathType.Arc][MakerJs.pathType.Circle](arc, circle, options, true);
5124 if (result) {
5125 return swapAngles(result);
5126 }
5127 return null;
5128 };
5129 map[MakerJs.pathType.Circle][MakerJs.pathType.Circle] = function (circle1, circle2, options, swapOffsets) {
5130 var result = null;
5131 moveTemp([circle1, circle2], options, swapOffsets, function () {
5132 var angles = circleToCircle(circle1, circle2, options);
5133 if (angles) {
5134 result = {
5135 intersectionPoints: pointsFromAnglesOnCircle(angles[0], circle1),
5136 path1Angles: angles[0],
5137 path2Angles: angles[1]
5138 };
5139 }
5140 });
5141 return result;
5142 };
5143 map[MakerJs.pathType.Circle][MakerJs.pathType.Line] = function (circle, line, options, swapOffsets) {
5144 var result = null;
5145 moveTemp([circle, line], options, swapOffsets, function () {
5146 var angles = lineToCircle(line, circle, options);
5147 if (angles) {
5148 result = {
5149 intersectionPoints: pointsFromAnglesOnCircle(angles, circle),
5150 path1Angles: angles
5151 };
5152 }
5153 });
5154 return result;
5155 };
5156 map[MakerJs.pathType.Line][MakerJs.pathType.Arc] = function (line, arc, options) {
5157 var result = map[MakerJs.pathType.Arc][MakerJs.pathType.Line](arc, line, options, true);
5158 if (result) {
5159 return swapAngles(result);
5160 }
5161 return null;
5162 };
5163 map[MakerJs.pathType.Line][MakerJs.pathType.Circle] = function (line, circle, options) {
5164 var result = map[MakerJs.pathType.Circle][MakerJs.pathType.Line](circle, line, options, true);
5165 if (result) {
5166 return swapAngles(result);
5167 }
5168 return null;
5169 };
5170 map[MakerJs.pathType.Line][MakerJs.pathType.Line] = function (line1, line2, options, swapOffsets) {
5171 var result = null;
5172 moveTemp([line1, line2], options, swapOffsets, function () {
5173 var intersectionPoint = MakerJs.point.fromSlopeIntersection(line1, line2, options);
5174 if (intersectionPoint) {
5175 //we have the point of intersection of endless lines, now check to see if the point is between both segemnts
5176 if (MakerJs.measure.isBetweenPoints(intersectionPoint, line1, options.excludeTangents) && MakerJs.measure.isBetweenPoints(intersectionPoint, line2, options.excludeTangents)) {
5177 result = {
5178 intersectionPoints: [intersectionPoint]
5179 };
5180 }
5181 }
5182 });
5183 return result;
5184 };
5185 /**
5186 * @private
5187 */
5188 function moveTemp(pathsToOffset, options, swapOffsets, task) {
5189 var offsets = swapOffsets ? [options.path2Offset, options.path1Offset] : [options.path1Offset, options.path2Offset];
5190 path.moveTemporary(pathsToOffset, offsets, task);
5191 }
5192 ;
5193 /**
5194 * @private
5195 */
5196 function swapAngles(result) {
5197 var temp = result.path1Angles;
5198 if (result.path2Angles) {
5199 result.path1Angles = result.path2Angles;
5200 }
5201 else {
5202 delete result.path1Angles;
5203 }
5204 if (temp) {
5205 result.path2Angles = temp;
5206 }
5207 return result;
5208 }
5209 /**
5210 * Find the point(s) where 2 paths intersect.
5211 *
5212 * @param path1 First path to find intersection.
5213 * @param path2 Second path to find intersection.
5214 * @param options Optional IPathIntersectionOptions.
5215 * @returns IPathIntersection object, with points(s) of intersection (and angles, when a path is an arc or circle); or null if the paths did not intersect.
5216 */
5217 function intersection(path1, path2, options) {
5218 if (options === void 0) { options = {}; }
5219 if (path1 && path2) {
5220 var fn = map[path1.type][path2.type];
5221 if (fn) {
5222 return fn(path1, path2, options);
5223 }
5224 }
5225 return null;
5226 }
5227 path.intersection = intersection;
5228 /**
5229 * @private
5230 */
5231 function findCorrespondingAngleIndex(circleAngles, arcAngle) {
5232 for (var i = 2; i--;) {
5233 if (circleAngles[i] === arcAngle)
5234 return i;
5235 }
5236 }
5237 /**
5238 * @private
5239 */
5240 function pointsFromAnglesOnCircle(anglesInDegrees, circle) {
5241 var result = [];
5242 for (var i = 0; i < anglesInDegrees.length; i++) {
5243 result.push(MakerJs.point.fromAngleOnCircle(anglesInDegrees[i], circle));
5244 }
5245 return result;
5246 }
5247 /**
5248 * @private
5249 */
5250 function getAnglesWithinArc(angles, arc, options) {
5251 if (!angles)
5252 return null;
5253 var anglesWithinArc = [];
5254 for (var i = 0; i < angles.length; i++) {
5255 if (MakerJs.measure.isBetweenArcAngles(angles[i], arc, options.excludeTangents)) {
5256 anglesWithinArc.push(angles[i]);
5257 }
5258 }
5259 if (anglesWithinArc.length == 0)
5260 return null;
5261 return anglesWithinArc;
5262 }
5263 /**
5264 * @private
5265 */
5266 function lineToCircle(line, circle, options) {
5267 var radius = MakerJs.round(circle.radius);
5268 //no-op for degenerate circle
5269 if (circle.radius <= 0) {
5270 return null;
5271 }
5272 //clone the line
5273 var clonedLine = new MakerJs.paths.Line(MakerJs.point.subtract(line.origin, circle.origin), MakerJs.point.subtract(line.end, circle.origin));
5274 //get angle of line
5275 var lineAngleNormal = MakerJs.angle.ofLineInDegrees(line);
5276 //use the positive horizontal angle
5277 var lineAngle = (lineAngleNormal >= 180) ? lineAngleNormal - 360 : lineAngleNormal;
5278 //rotate the line to horizontal
5279 path.rotate(clonedLine, -lineAngle, MakerJs.point.zero());
5280 //remember how to undo the rotation we just did
5281 function unRotate(resultAngle) {
5282 var unrotated = resultAngle + lineAngle;
5283 return MakerJs.round(MakerJs.angle.noRevolutions(unrotated));
5284 }
5285 //line is horizontal, get the y value from any point
5286 var lineY = MakerJs.round(clonedLine.origin[1]);
5287 var lineYabs = Math.abs(lineY);
5288 //if y is greater than radius, there is no intersection
5289 if (lineYabs > radius) {
5290 return null;
5291 }
5292 var anglesOfIntersection = [];
5293 //if horizontal Y is the same as the radius, we know it's 90 degrees
5294 if (lineYabs == radius) {
5295 if (options.excludeTangents) {
5296 return null;
5297 }
5298 anglesOfIntersection.push(unRotate(lineY > 0 ? 90 : 270));
5299 }
5300 else {
5301 function intersectionBetweenEndpoints(x, angleOfX) {
5302 if (MakerJs.measure.isBetween(MakerJs.round(x), MakerJs.round(clonedLine.origin[0]), MakerJs.round(clonedLine.end[0]), options.excludeTangents)) {
5303 anglesOfIntersection.push(unRotate(angleOfX));
5304 }
5305 }
5306 //find angle where line intersects
5307 var intersectRadians = Math.asin(lineY / radius);
5308 var intersectDegrees = MakerJs.angle.toDegrees(intersectRadians);
5309 //line may intersect in 2 places
5310 var intersectX = Math.cos(intersectRadians) * radius;
5311 intersectionBetweenEndpoints(-intersectX, 180 - intersectDegrees);
5312 intersectionBetweenEndpoints(intersectX, intersectDegrees);
5313 }
5314 if (anglesOfIntersection.length > 0) {
5315 return anglesOfIntersection;
5316 }
5317 return null;
5318 }
5319 /**
5320 * @private
5321 */
5322 function circleToCircle(circle1, circle2, options) {
5323 //no-op if either circle is degenerate
5324 if (circle1.radius <= 0 || circle2.radius <= 0) {
5325 return null;
5326 }
5327 //see if circles are the same
5328 if (circle1.radius == circle2.radius && MakerJs.measure.isPointEqual(circle1.origin, circle2.origin, .0001)) {
5329 options.out_AreOverlapped = true;
5330 return null;
5331 }
5332 //get offset from origin
5333 var offset = MakerJs.point.subtract(MakerJs.point.zero(), circle1.origin);
5334 //clone circle1 and move to origin
5335 var c1 = new MakerJs.paths.Circle(MakerJs.point.zero(), circle1.radius);
5336 //clone circle2 and move relative to circle1
5337 var c2 = new MakerJs.paths.Circle(MakerJs.point.subtract(circle2.origin, circle1.origin), circle2.radius);
5338 //rotate circle2 to horizontal, c2 will be to the right of the origin.
5339 var c2Angle = MakerJs.angle.ofPointInDegrees(MakerJs.point.zero(), c2.origin);
5340 path.rotate(c2, -c2Angle, MakerJs.point.zero());
5341 function unRotate(resultAngle) {
5342 var unrotated = resultAngle + c2Angle;
5343 return MakerJs.angle.noRevolutions(unrotated);
5344 }
5345 //get X of c2 origin
5346 var x = c2.origin[0];
5347 //see if circles are tangent interior on left side
5348 if (MakerJs.round(c2.radius - x - c1.radius) == 0) {
5349 if (options.excludeTangents) {
5350 return null;
5351 }
5352 return [[unRotate(180)], [unRotate(180)]];
5353 }
5354 //see if circles are tangent interior on right side
5355 if (MakerJs.round(c2.radius + x - c1.radius) == 0) {
5356 if (options.excludeTangents) {
5357 return null;
5358 }
5359 return [[unRotate(0)], [unRotate(0)]];
5360 }
5361 //see if circles are tangent exterior
5362 if (MakerJs.round(x - c2.radius - c1.radius) == 0) {
5363 if (options.excludeTangents) {
5364 return null;
5365 }
5366 return [[unRotate(0)], [unRotate(180)]];
5367 }
5368 //see if c2 is outside of c1
5369 if (MakerJs.round(x - c2.radius) > c1.radius) {
5370 return null;
5371 }
5372 //see if c2 is within c1
5373 if (MakerJs.round(x + c2.radius) < c1.radius) {
5374 return null;
5375 }
5376 //see if c1 is within c2
5377 if (MakerJs.round(x - c2.radius) < -c1.radius) {
5378 return null;
5379 }
5380 function bothAngles(oneAngle) {
5381 return [unRotate(oneAngle), unRotate(MakerJs.angle.mirror(oneAngle, false, true))];
5382 }
5383 var c1IntersectionAngle = MakerJs.solvers.solveTriangleSSS(c2.radius, c1.radius, x);
5384 var c2IntersectionAngle = MakerJs.solvers.solveTriangleSSS(c1.radius, x, c2.radius);
5385 return [bothAngles(c1IntersectionAngle), bothAngles(180 - c2IntersectionAngle)];
5386 }
5387 })(path = MakerJs.path || (MakerJs.path = {}));
5388})(MakerJs || (MakerJs = {}));
5389var MakerJs;
5390(function (MakerJs) {
5391 var path;
5392 (function (path) {
5393 /**
5394 * @private
5395 */
5396 var propertyNamesMap = {};
5397 propertyNamesMap[MakerJs.pathType.Arc] = function (arc) {
5398 return ['startAngle', 'endAngle'];
5399 };
5400 propertyNamesMap[MakerJs.pathType.Line] = function (line) {
5401 return ['origin', 'end'];
5402 };
5403 /**
5404 * @private
5405 */
5406 function getPointProperties(pathToInspect) {
5407 var points = MakerJs.point.fromPathEnds(pathToInspect);
5408 if (points) {
5409 function pointProperty(index) {
5410 return { point: points[index], propertyName: propertyNames[index] };
5411 }
5412 var propertyNames = null;
5413 var fn = propertyNamesMap[pathToInspect.type];
5414 if (fn) {
5415 propertyNames = fn(pathToInspect);
5416 return [pointProperty(0), pointProperty(1)];
5417 }
5418 }
5419 return null;
5420 }
5421 /**
5422 * @private
5423 */
5424 function getMatchingPointProperties(pathA, pathB, options) {
5425 var pathAProperties = getPointProperties(pathA);
5426 var pathBProperties = getPointProperties(pathB);
5427 var result = null;
5428 function makeMatch(pathContext, pointProperties, index) {
5429 return {
5430 path: pathContext,
5431 isStart: index == 0,
5432 propertyName: pointProperties[index].propertyName,
5433 point: pointProperties[index].point,
5434 oppositePoint: pointProperties[1 - index].point
5435 };
5436 }
5437 function check(iA, iB) {
5438 if (MakerJs.measure.isPointEqual(pathAProperties[iA].point, pathBProperties[iB].point, .0001)) {
5439 result = [
5440 makeMatch(pathA, pathAProperties, iA),
5441 makeMatch(pathB, pathBProperties, iB)
5442 ];
5443 return true;
5444 }
5445 return false;
5446 }
5447 check(0, 0) || check(0, 1) || check(1, 0) || check(1, 1);
5448 return result;
5449 }
5450 /**
5451 * @private
5452 */
5453 function populateShardPointsFromReferenceCircle(filletRadius, center, properties, options) {
5454 var referenceCircle = new MakerJs.paths.Circle(center, filletRadius);
5455 //get reference circle intersection points
5456 for (var i = 0; i < 2; i++) {
5457 var circleIntersection = path.intersection(referenceCircle, properties[i].path);
5458 if (!circleIntersection) {
5459 return false;
5460 }
5461 properties[i].shardPoint = circleIntersection.intersectionPoints[0];
5462 if (MakerJs.measure.isPointEqual(properties[i].point, circleIntersection.intersectionPoints[0], .0001)) {
5463 if (circleIntersection.intersectionPoints.length > 1) {
5464 properties[i].shardPoint = circleIntersection.intersectionPoints[1];
5465 }
5466 else {
5467 return false;
5468 }
5469 }
5470 }
5471 return true;
5472 }
5473 /**
5474 * @private
5475 */
5476 function cloneAndBreakPath(pathToShard, shardPoint) {
5477 var shardStart = path.clone(pathToShard);
5478 var shardEnd = path.breakAtPoint(shardStart, shardPoint);
5479 return [shardStart, shardEnd];
5480 }
5481 /**
5482 * @private
5483 */
5484 var guidePathMap = {};
5485 guidePathMap[MakerJs.pathType.Arc] = function (arc, filletRadius, nearPoint, shardPoint, isStart) {
5486 var guideRadius = arc.radius;
5487 //see if the guideline should be external or internal to the context arc.
5488 var guideArcShard = cloneAndBreakPath(arc, shardPoint)[isStart ? 0 : 1];
5489 if (guideArcShard) {
5490 if (MakerJs.measure.isArcConcaveTowardsPoint(guideArcShard, nearPoint)) {
5491 guideRadius -= filletRadius;
5492 }
5493 else {
5494 guideRadius += filletRadius;
5495 }
5496 if (MakerJs.round(guideRadius) <= 0)
5497 return null;
5498 return new MakerJs.paths.Arc(arc.origin, guideRadius, arc.startAngle, arc.endAngle);
5499 }
5500 return null;
5501 };
5502 guidePathMap[MakerJs.pathType.Line] = function (line, filletRadius, nearPoint, shardPoint, isStart) {
5503 return new MakerJs.paths.Parallel(line, filletRadius, nearPoint);
5504 };
5505 /**
5506 * @private
5507 */
5508 function getGuidePath(context, filletRadius, nearPoint) {
5509 var result = null;
5510 var fn = guidePathMap[context.path.type];
5511 if (fn) {
5512 result = fn(context.path, filletRadius, nearPoint, context.shardPoint, context.isStart);
5513 }
5514 return result;
5515 }
5516 /**
5517 * @private
5518 */
5519 var filletResultMap = {};
5520 filletResultMap[MakerJs.pathType.Arc] = function (arc, propertyName, filletRadius, filletCenter) {
5521 var guideLine = new MakerJs.paths.Line(arc.origin, filletCenter);
5522 var guideLineAngle = MakerJs.angle.ofLineInDegrees(guideLine);
5523 var filletAngle = guideLineAngle;
5524 //the context is an arc and the fillet is an arc so they will be tangent. If the fillet is external to the arc then the tangent is opposite.
5525 if (!MakerJs.measure.isArcConcaveTowardsPoint(arc, filletCenter)) {
5526 filletAngle += 180;
5527 }
5528 return {
5529 filletAngle: MakerJs.angle.noRevolutions(filletAngle),
5530 clipPath: function () {
5531 arc[propertyName] = guideLineAngle;
5532 }
5533 };
5534 };
5535 filletResultMap[MakerJs.pathType.Line] = function (line, propertyName, filletRadius, filletCenter) {
5536 //make a small vertical line
5537 var guideLine = new MakerJs.paths.Line([0, 0], [0, 1]);
5538 //rotate this vertical line the same angle as the line context. It will be perpendicular.
5539 var lineAngle = MakerJs.angle.ofLineInDegrees(line);
5540 path.rotate(guideLine, lineAngle, [0, 0]);
5541 path.moveRelative(guideLine, filletCenter);
5542 //get the intersection point of the slopes of the context line and the perpendicular line. This is where the fillet meets the line.
5543 var intersectionPoint = MakerJs.point.fromSlopeIntersection(line, guideLine);
5544 if (intersectionPoint) {
5545 return {
5546 filletAngle: MakerJs.angle.ofPointInDegrees(filletCenter, intersectionPoint),
5547 clipPath: function () {
5548 line[propertyName] = intersectionPoint;
5549 }
5550 };
5551 }
5552 return null;
5553 };
5554 /**
5555 * @private
5556 */
5557 function getFilletResult(context, filletRadius, filletCenter) {
5558 var result = null;
5559 var fn = filletResultMap[context.path.type];
5560 if (fn) {
5561 result = fn(context.path, context.propertyName, filletRadius, filletCenter);
5562 }
5563 if (!testFilletResult(context, result)) {
5564 result = null;
5565 }
5566 return result;
5567 }
5568 /**
5569 * @private
5570 */
5571 function getDogboneResult(context, filletCenter) {
5572 var result = {
5573 filletAngle: MakerJs.angle.ofPointInDegrees(filletCenter, context.shardPoint),
5574 clipPath: function () {
5575 context.path[context.propertyName] = context.shardPoint;
5576 }
5577 };
5578 if (!testFilletResult(context, result)) {
5579 result = null;
5580 }
5581 return result;
5582 }
5583 /**
5584 * @private
5585 */
5586 function testFilletResult(context, result) {
5587 var test = false;
5588 if (result) {
5589 //temporarily clip the path.
5590 var originalValue = context.path[context.propertyName];
5591 result.clipPath();
5592 //don't allow a fillet which effectivly eliminates the path.
5593 if (MakerJs.measure.pathLength(context.path) > 0) {
5594 test = true;
5595 }
5596 //revert the clipping we just did.
5597 context.path[context.propertyName] = originalValue;
5598 }
5599 return test;
5600 }
5601 /**
5602 * @private
5603 */
5604 function getLineRatio(lines) {
5605 var totalLength = 0;
5606 var lengths = [];
5607 for (var i = 0; i < lines.length; i++) {
5608 var length = MakerJs.measure.pathLength(lines[i]);
5609 lengths.push(length);
5610 totalLength += length;
5611 }
5612 return lengths[0] / totalLength;
5613 }
5614 /**
5615 * Adds a round corner to the outside angle between 2 lines. The lines must meet at one point.
5616 *
5617 * @param lineA First line to fillet, which will be modified to fit the fillet.
5618 * @param lineB Second line to fillet, which will be modified to fit the fillet.
5619 * @returns Arc path object of the new fillet.
5620 */
5621 function dogbone(lineA, lineB, filletRadius, options) {
5622 //TODO: allow arcs in dogbone
5623 if (MakerJs.isPathLine(lineA) && MakerJs.isPathLine(lineB) && filletRadius && filletRadius > 0) {
5624 var opts = {
5625 pointMatchingDistance: .005
5626 };
5627 MakerJs.extendObject(opts, options);
5628 //first find the common point
5629 var commonProperty = getMatchingPointProperties(lineA, lineB, options);
5630 if (commonProperty) {
5631 //get the ratio comparison of the two lines
5632 var ratio = getLineRatio([lineA, lineB]);
5633 //draw a line between the two endpoints, and get the bisection point at the ratio
5634 var span = new MakerJs.paths.Line(commonProperty[0].oppositePoint, commonProperty[1].oppositePoint);
5635 var midRatioPoint = MakerJs.point.middle(span, ratio);
5636 //use the bisection theorem to get the angle bisecting the lines
5637 var bisectionAngle = MakerJs.angle.ofPointInDegrees(commonProperty[0].point, midRatioPoint);
5638 var center = MakerJs.point.add(commonProperty[0].point, MakerJs.point.fromPolar(MakerJs.angle.toRadians(bisectionAngle), filletRadius));
5639 if (!populateShardPointsFromReferenceCircle(filletRadius, center, commonProperty, opts)) {
5640 return null;
5641 }
5642 //get the angles of the fillet and a function which clips the path to the fillet.
5643 var results = [];
5644 for (var i = 0; i < 2; i++) {
5645 var result = getDogboneResult(commonProperty[i], center);
5646 if (!result) {
5647 return null;
5648 }
5649 results.push(result);
5650 }
5651 var filletArc = new MakerJs.paths.Arc(center, filletRadius, results[0].filletAngle, results[1].filletAngle);
5652 //make sure midpoint of fillet is outside of the angle
5653 if (MakerJs.round(MakerJs.angle.noRevolutions(MakerJs.angle.ofArcMiddle(filletArc))) == MakerJs.round(bisectionAngle)) {
5654 filletArc.startAngle = results[1].filletAngle;
5655 filletArc.endAngle = results[0].filletAngle;
5656 }
5657 //clip the paths and return the fillet arc.
5658 results[0].clipPath();
5659 results[1].clipPath();
5660 return filletArc;
5661 }
5662 }
5663 return null;
5664 }
5665 path.dogbone = dogbone;
5666 /**
5667 * Adds a round corner to the inside angle between 2 paths. The paths must meet at one point.
5668 *
5669 * @param pathA First path to fillet, which will be modified to fit the fillet.
5670 * @param pathB Second path to fillet, which will be modified to fit the fillet.
5671 * @param filletRadius Radius of the fillet.
5672 * @param options Optional IPointMatchOptions object to specify pointMatchingDistance.
5673 * @returns Arc path object of the new fillet.
5674 */
5675 function fillet(pathA, pathB, filletRadius, options) {
5676 if (pathA && pathB && filletRadius && filletRadius > 0) {
5677 var opts = {
5678 pointMatchingDistance: .005
5679 };
5680 MakerJs.extendObject(opts, options);
5681 //first find the common point
5682 var commonProperty = getMatchingPointProperties(pathA, pathB, options);
5683 if (commonProperty) {
5684 //since arcs can curl beyond, we need a local reference point.
5685 //An intersection with a circle of the same radius as the desired fillet should suffice.
5686 if (!populateShardPointsFromReferenceCircle(filletRadius, commonProperty[0].point, commonProperty, opts)) {
5687 return null;
5688 }
5689 //get "parallel" guidelines
5690 var guidePaths = [];
5691 for (var i = 0; i < 2; i++) {
5692 var otherPathShardPoint = commonProperty[1 - i].shardPoint;
5693 if (!otherPathShardPoint) {
5694 return null;
5695 }
5696 var guidePath = getGuidePath(commonProperty[i], filletRadius, otherPathShardPoint);
5697 guidePaths.push(guidePath);
5698 }
5699 //the center of the fillet is the point where the guidelines intersect.
5700 var intersectionPoint = path.intersection(guidePaths[0], guidePaths[1]);
5701 if (intersectionPoint) {
5702 var center;
5703 //if guidelines intersect in more than one place, choose the closest one.
5704 if (intersectionPoint.intersectionPoints.length == 1) {
5705 center = intersectionPoint.intersectionPoints[0];
5706 }
5707 else {
5708 center = MakerJs.point.closest(commonProperty[0].point, intersectionPoint.intersectionPoints);
5709 }
5710 //get the angles of the fillet and a function which clips the path to the fillet.
5711 var results = [];
5712 for (var i = 0; i < 2; i++) {
5713 var result = getFilletResult(commonProperty[i], filletRadius, center);
5714 if (!result) {
5715 return null;
5716 }
5717 results.push(result);
5718 }
5719 //the two paths may actually be on the same line
5720 if (MakerJs.round(results[0].filletAngle - results[1].filletAngle) == 0)
5721 return null;
5722 var filletArc = new MakerJs.paths.Arc(center, filletRadius, results[0].filletAngle, results[1].filletAngle);
5723 var filletSpan = MakerJs.angle.ofArcSpan(filletArc);
5724 //the algorithm is only valid for fillet less than 180 degrees
5725 if (filletSpan == 180) {
5726 return null;
5727 }
5728 if (filletSpan > 180) {
5729 //swap to make smallest angle
5730 filletArc.startAngle = results[1].filletAngle;
5731 filletArc.endAngle = results[0].filletAngle;
5732 }
5733 //clip the paths and return the fillet arc.
5734 results[0].clipPath();
5735 results[1].clipPath();
5736 return filletArc;
5737 }
5738 }
5739 }
5740 return null;
5741 }
5742 path.fillet = fillet;
5743 })(path = MakerJs.path || (MakerJs.path = {}));
5744})(MakerJs || (MakerJs = {}));
5745(function (MakerJs) {
5746 var chain;
5747 (function (chain) {
5748 function dogbone(chainToFillet, filletSpec) {
5749 return chainFillet(false, chainToFillet, filletSpec);
5750 }
5751 chain.dogbone = dogbone;
5752 function fillet(chainToFillet, filletSpec) {
5753 return chainFillet(true, chainToFillet, filletSpec);
5754 }
5755 chain.fillet = fillet;
5756 function chainFillet(traditional, chainToFillet, filletSpec) {
5757 var result = { paths: {} };
5758 var added = 0;
5759 var links = chainToFillet.links;
5760 function add(i1, i2) {
5761 var p1 = links[i1].walkedPath, p2 = links[i2].walkedPath;
5762 if (p1.modelContext === p2.modelContext && p1.modelContext.type == MakerJs.models.BezierCurve.typeName)
5763 return;
5764 MakerJs.path.moveTemporary([p1.pathContext, p2.pathContext], [p1.offset, p2.offset], function () {
5765 var filletRadius;
5766 if (MakerJs.isObject(filletSpec)) {
5767 var a = MakerJs.angle.ofChainLinkJoint(links[i1], links[i2]);
5768 if (MakerJs.round(a) === 0)
5769 return;
5770 filletRadius = (a > 0) ? filletSpec.left : filletSpec.right;
5771 }
5772 else {
5773 filletRadius = filletSpec;
5774 }
5775 if (!filletRadius || filletRadius < 0)
5776 return;
5777 var filletArc;
5778 if (traditional) {
5779 filletArc = MakerJs.path.fillet(p1.pathContext, p2.pathContext, filletRadius);
5780 }
5781 else {
5782 filletArc = MakerJs.path.dogbone(p1.pathContext, p2.pathContext, filletRadius);
5783 }
5784 if (filletArc) {
5785 result.paths['fillet' + added] = filletArc;
5786 added++;
5787 }
5788 });
5789 }
5790 for (var i = 1; i < links.length; i++) {
5791 add(i - 1, i);
5792 }
5793 if (chainToFillet.endless) {
5794 add(i - 1, 0);
5795 }
5796 if (!added)
5797 return null;
5798 return result;
5799 }
5800 })(chain = MakerJs.chain || (MakerJs.chain = {}));
5801})(MakerJs || (MakerJs = {}));
5802var MakerJs;
5803(function (MakerJs) {
5804 var kit;
5805 (function (kit) {
5806 //construct a model
5807 /**
5808 * Helper function to use the JavaScript "apply" function in conjunction with the "new" keyword.
5809 *
5810 * @param ctor The constructor for the class which is an IKit.
5811 * @param args The array of parameters passed to the constructor.
5812 * @returns A new instance of the class, which implements the IModel interface.
5813 */
5814 function construct(ctor, args) {
5815 function F() {
5816 return ctor.apply(this, args);
5817 }
5818 F.prototype = ctor.prototype;
5819 return new F();
5820 }
5821 kit.construct = construct;
5822 /**
5823 * Extract just the initial sample values from a kit.
5824 *
5825 * @param ctor The constructor for the class which is an IKit.
5826 * @returns Array of the inital sample values provided in the metaParameters array.
5827 */
5828 function getParameterValues(ctor) {
5829 var parameters = [];
5830 var metaParams = ctor.metaParameters;
5831 if (metaParams) {
5832 for (var i = 0; i < metaParams.length; i++) {
5833 var value = metaParams[i].value;
5834 if (Array.isArray(value)) {
5835 value = value[0];
5836 }
5837 parameters.push(value);
5838 }
5839 }
5840 return parameters;
5841 }
5842 kit.getParameterValues = getParameterValues;
5843 })(kit = MakerJs.kit || (MakerJs.kit = {}));
5844})(MakerJs || (MakerJs = {}));
5845var MakerJs;
5846(function (MakerJs) {
5847 var model;
5848 (function (model) {
5849 /**
5850 * @private
5851 */
5852 function getOpposedLink(linkedPaths, pathContext) {
5853 if (linkedPaths[0].walkedPath.pathContext === pathContext) {
5854 return linkedPaths[1];
5855 }
5856 return linkedPaths[0];
5857 }
5858 /**
5859 * @private
5860 */
5861 function followLinks(pointGraph, chainFound, chainNotFound) {
5862 function followLink(currLink, chain, firstLink) {
5863 while (currLink) {
5864 chain.links.push(currLink);
5865 chain.pathLength += currLink.pathLength;
5866 var next = currLink.reversed ? 0 : 1;
5867 var nextPoint = currLink.endPoints[next];
5868 var nextEl = pointGraph.getElementAtPoint(nextPoint);
5869 if (!nextEl || nextEl.valueIds.length === 0) {
5870 break;
5871 }
5872 var items = nextEl.valueIds.map(function (valueIndex) { return pointGraph.values[valueIndex]; });
5873 var nextLink = getOpposedLink(items, currLink.walkedPath.pathContext);
5874 //remove the first 2 items, which should be currlink and nextlink
5875 nextEl.valueIds.splice(0, 2);
5876 if (!nextLink) {
5877 break;
5878 }
5879 if (nextLink.walkedPath.pathContext === firstLink.walkedPath.pathContext) {
5880 if (chain.links.length > 1) {
5881 chain.endless = true;
5882 }
5883 break;
5884 }
5885 currLink = nextLink;
5886 }
5887 }
5888 pointGraph.forEachPoint(function (p, values, pointId, el) {
5889 if (el.valueIds.length > 0) {
5890 var chain = {
5891 links: [],
5892 pathLength: 0
5893 };
5894 followLink(values[0], chain, values[0]);
5895 if (chain.endless) {
5896 chainFound(chain, false);
5897 }
5898 else {
5899 //need to go in reverse
5900 chain.links.reverse();
5901 var firstLink = chain.links[0];
5902 chain.links.map(function (link) { link.reversed = !link.reversed; });
5903 //remove the last link, it will be added in the call
5904 chain.pathLength -= chain.links[chain.links.length - 1].pathLength;
5905 var currLink = chain.links.pop();
5906 followLink(currLink, chain, firstLink);
5907 if (chain.links.length > 1) {
5908 chainFound(chain, true);
5909 }
5910 else {
5911 chainNotFound(chain.links[0].walkedPath);
5912 }
5913 }
5914 }
5915 });
5916 }
5917 /**
5918 * Find a single chain within a model, across all layers. Shorthand of findChains; useful when you know there is only one chain to find in your model.
5919 *
5920 * @param modelContext The model to search for a chain.
5921 * @returns A chain object or null if chains were not found.
5922 */
5923 function findSingleChain(modelContext) {
5924 var singleChain = null;
5925 findChains(modelContext, function (chains, loose, layer) {
5926 singleChain = chains[0];
5927 }, { byLayers: false });
5928 return singleChain;
5929 }
5930 model.findSingleChain = findSingleChain;
5931 /**
5932 * @private
5933 */
5934 function linkEndpoint(link, beginning) {
5935 var index = (beginning === link.reversed) ? 1 : 0;
5936 return link.endPoints[index];
5937 }
5938 function findChains(modelContext) {
5939 var args = [];
5940 for (var _i = 1; _i < arguments.length; _i++) {
5941 args[_i - 1] = arguments[_i];
5942 }
5943 var options;
5944 var callback;
5945 switch (args.length) {
5946 case 1:
5947 if (typeof args[0] === 'function') {
5948 callback = args[0];
5949 }
5950 else {
5951 options = args[0];
5952 }
5953 break;
5954 case 2:
5955 callback = args[0];
5956 options = args[1];
5957 break;
5958 }
5959 var opts = {
5960 pointMatchingDistance: .005
5961 };
5962 MakerJs.extendObject(opts, options);
5963 var pointGraphsByLayer = {};
5964 var chainsByLayer = {};
5965 var ignored = {};
5966 var walkOptions = {
5967 onPath: function (walkedPath) {
5968 var layer = opts.byLayers ? walkedPath.layer : '';
5969 if (!pointGraphsByLayer[layer]) {
5970 pointGraphsByLayer[layer] = new MakerJs.PointGraph();
5971 }
5972 var pointGraph = pointGraphsByLayer[layer];
5973 var pathLength = MakerJs.measure.pathLength(walkedPath.pathContext);
5974 //circles are loops by nature
5975 if (walkedPath.pathContext.type === MakerJs.pathType.Circle ||
5976 (walkedPath.pathContext.type === MakerJs.pathType.Arc && MakerJs.round(MakerJs.angle.ofArcSpan(walkedPath.pathContext) - 360) === 0) ||
5977 (walkedPath.pathContext.type === MakerJs.pathType.BezierSeed && MakerJs.measure.isPointEqual(walkedPath.pathContext.origin, walkedPath.pathContext.end, opts.pointMatchingDistance))) {
5978 var chain = {
5979 links: [{
5980 walkedPath: walkedPath,
5981 reversed: null,
5982 endPoints: null,
5983 pathLength: pathLength
5984 }],
5985 endless: true,
5986 pathLength: pathLength
5987 };
5988 //store circles so that layers fire grouped
5989 if (!chainsByLayer[layer]) {
5990 chainsByLayer[layer] = [];
5991 }
5992 chainsByLayer[layer].push(chain);
5993 }
5994 else {
5995 //don't add lines which are 5x shorter than the tolerance
5996 if (pathLength < opts.pointMatchingDistance / 5) {
5997 if (!ignored[layer]) {
5998 ignored[layer] = [];
5999 }
6000 ignored[layer].push(walkedPath);
6001 return;
6002 }
6003 //gather both endpoints from all non-circle segments
6004 var endPoints = MakerJs.point.fromPathEnds(walkedPath.pathContext, walkedPath.offset);
6005 for (var i = 0; i < 2; i++) {
6006 var link = {
6007 walkedPath: walkedPath,
6008 endPoints: endPoints,
6009 reversed: i != 0,
6010 pathLength: pathLength
6011 };
6012 var valueId = pointGraph.insertValue(link);
6013 pointGraph.insertValueIdAtPoint(valueId, endPoints[i]);
6014 }
6015 }
6016 }
6017 };
6018 if (opts.shallow) {
6019 walkOptions.beforeChildWalk = function () { return false; };
6020 }
6021 var beziers;
6022 if (opts.unifyBeziers) {
6023 beziers = getBezierModels(modelContext);
6024 swapBezierPathsWithSeeds(beziers, true);
6025 }
6026 model.walk(modelContext, walkOptions);
6027 var _loop_3 = function (layer_2) {
6028 var pointGraph = pointGraphsByLayer[layer_2];
6029 pointGraph.mergeNearestSinglePoints(opts.pointMatchingDistance);
6030 loose = [];
6031 if (!chainsByLayer[layer_2]) {
6032 chainsByLayer[layer_2] = [];
6033 }
6034 //follow paths to find endless chains
6035 followLinks(pointGraph, function (chain, checkEndless) {
6036 if (checkEndless) {
6037 chain.endless = MakerJs.measure.isPointEqual(linkEndpoint(chain.links[0], true), linkEndpoint(chain.links[chain.links.length - 1], false), opts.pointMatchingDistance);
6038 }
6039 else {
6040 chain.endless = !!chain.endless;
6041 }
6042 chainsByLayer[layer_2].push(chain);
6043 }, function (walkedPath) {
6044 loose.push(walkedPath);
6045 });
6046 //sort to return largest chains first
6047 chainsByLayer[layer_2].sort(function (a, b) { return b.pathLength - a.pathLength; });
6048 if (opts.contain) {
6049 containChainsOptions = MakerJs.isObject(opts.contain) ? opts.contain : { alternateDirection: false };
6050 containedChains = getContainment(chainsByLayer[layer_2], containChainsOptions);
6051 chainsByLayer[layer_2] = containedChains;
6052 }
6053 if (callback)
6054 callback(chainsByLayer[layer_2], loose, layer_2, ignored[layer_2]);
6055 };
6056 var loose, containChainsOptions, containedChains;
6057 for (var layer_2 in pointGraphsByLayer) {
6058 _loop_3(layer_2);
6059 }
6060 if (beziers) {
6061 swapBezierPathsWithSeeds(beziers, false);
6062 }
6063 if (opts.byLayers) {
6064 return chainsByLayer;
6065 }
6066 else {
6067 return chainsByLayer[''];
6068 }
6069 }
6070 model.findChains = findChains;
6071 /**
6072 * @private
6073 */
6074 function getContainment(allChains, opts) {
6075 var chainsAsModels = allChains.map(function (c) { return MakerJs.chain.toNewModel(c); });
6076 var parents = [];
6077 //see which are inside of each other
6078 allChains.forEach(function (chainContext, i1) {
6079 if (!chainContext.endless)
6080 return;
6081 var wp = chainContext.links[0].walkedPath;
6082 var firstPath = MakerJs.path.clone(wp.pathContext, wp.offset);
6083 allChains.forEach(function (otherChain, i2) {
6084 if (chainContext === otherChain)
6085 return;
6086 if (!otherChain.endless)
6087 return;
6088 if (MakerJs.measure.isPointInsideModel(MakerJs.point.middle(firstPath), chainsAsModels[i2])) {
6089 //since chains were sorted by pathLength, the smallest pathLength parent will be the parent if contained in multiple chains.
6090 parents[i1] = otherChain;
6091 }
6092 });
6093 });
6094 //convert parent to children
6095 var result = [];
6096 allChains.forEach(function (chainContext, i) {
6097 var parent = parents[i];
6098 if (!parent) {
6099 result.push(chainContext);
6100 }
6101 else {
6102 if (!parent.contains) {
6103 parent.contains = [];
6104 }
6105 parent.contains.push(chainContext);
6106 }
6107 });
6108 if (opts.alternateDirection) {
6109 function alternate(chains, shouldBeClockwise) {
6110 chains.forEach(function (chainContext, i) {
6111 var isClockwise = MakerJs.measure.isChainClockwise(chainContext);
6112 if (isClockwise !== null) {
6113 if (!isClockwise && shouldBeClockwise || isClockwise && !shouldBeClockwise) {
6114 MakerJs.chain.reverse(chainContext);
6115 }
6116 }
6117 if (chainContext.contains) {
6118 alternate(chainContext.contains, !shouldBeClockwise);
6119 }
6120 });
6121 }
6122 alternate(result, true);
6123 }
6124 return result;
6125 }
6126 /**
6127 * @private
6128 */
6129 function getBezierModels(modelContext) {
6130 var beziers = [];
6131 function checkIsBezier(wm) {
6132 if (wm.childModel.type === MakerJs.models.BezierCurve.typeName) {
6133 beziers.push(wm);
6134 }
6135 }
6136 var options = {
6137 beforeChildWalk: function (walkedModel) {
6138 checkIsBezier(walkedModel);
6139 return true;
6140 }
6141 };
6142 var rootModel = {
6143 childId: '',
6144 childModel: modelContext,
6145 layer: modelContext.layer,
6146 offset: modelContext.origin,
6147 parentModel: null,
6148 route: [],
6149 routeKey: ''
6150 };
6151 checkIsBezier(rootModel);
6152 model.walk(modelContext, options);
6153 return beziers;
6154 }
6155 /**
6156 * @private
6157 */
6158 function swapBezierPathsWithSeeds(beziers, swap) {
6159 var tempKey = 'tempPaths';
6160 var tempLayerKey = 'tempLayer';
6161 beziers.forEach(function (wm) {
6162 var b = wm.childModel;
6163 if (swap) {
6164 //set layer prior to looking for seeds by layer
6165 if (wm.layer != undefined && wm.layer !== '') {
6166 b[tempLayerKey] = b.layer;
6167 b.layer = wm.layer;
6168 }
6169 //use seeds as path, hide the arc paths from findChains()
6170 var bezierPartsByLayer = MakerJs.models.BezierCurve.getBezierSeeds(b, { byLayers: true });
6171 for (var layer in bezierPartsByLayer) {
6172 var bezierSeeds = bezierPartsByLayer[layer];
6173 if (bezierSeeds.length > 0) {
6174 b[tempKey] = b.paths;
6175 var newPaths = {};
6176 bezierSeeds.forEach(function (seed, i) {
6177 seed.layer = layer;
6178 newPaths['seed_' + i] = seed;
6179 });
6180 b.paths = newPaths;
6181 }
6182 }
6183 }
6184 else {
6185 //revert the above
6186 if (tempKey in b) {
6187 b.paths = b[tempKey];
6188 delete b[tempKey];
6189 }
6190 if (tempLayerKey in b) {
6191 if (b[tempLayerKey] == undefined) {
6192 delete b.layer;
6193 }
6194 else {
6195 b.layer = b[tempLayerKey];
6196 }
6197 delete b[tempLayerKey];
6198 }
6199 }
6200 });
6201 }
6202 })(model = MakerJs.model || (MakerJs.model = {}));
6203})(MakerJs || (MakerJs = {}));
6204(function (MakerJs) {
6205 var chain;
6206 (function (chain) {
6207 /**
6208 * Shift the links of an endless chain.
6209 *
6210 * @param chainContext Chain to cycle through. Must be endless.
6211 * @param amount Optional number of links to shift. May be negative to cycle backwards.
6212 * @returns The chainContext for cascading.
6213 */
6214 function cycle(chainContext, amount) {
6215 if (amount === void 0) { amount = 1; }
6216 if (!chainContext.endless)
6217 return;
6218 var n = Math.abs(amount);
6219 for (var i = 0; i < n; i++) {
6220 if (amount < 0) {
6221 //remove from beginning, add to end
6222 chainContext.links.push(chainContext.links.shift());
6223 }
6224 else {
6225 //remove from end, add to beginning
6226 chainContext.links.unshift(chainContext.links.pop());
6227 }
6228 }
6229 return chainContext;
6230 }
6231 chain.cycle = cycle;
6232 /**
6233 * Reverse the links of a chain.
6234 *
6235 * @param chainContext Chain to reverse.
6236 * @returns The chainContext for cascading.
6237 */
6238 function reverse(chainContext) {
6239 chainContext.links.reverse();
6240 chainContext.links.forEach(function (link) { return link.reversed = !link.reversed; });
6241 return chainContext;
6242 }
6243 chain.reverse = reverse;
6244 /**
6245 * Set the beginning of an endless chain to a known routeKey of a path.
6246 *
6247 * @param chainContext Chain to cycle through. Must be endless.
6248 * @param routeKey RouteKey of the desired path to start the chain with.
6249 * @returns The chainContext for cascading.
6250 */
6251 function startAt(chainContext, routeKey) {
6252 if (!chainContext.endless)
6253 return;
6254 var index = -1;
6255 for (var i = 0; i < chainContext.links.length; i++) {
6256 if (chainContext.links[i].walkedPath.routeKey == routeKey) {
6257 index = i;
6258 break;
6259 }
6260 }
6261 if (index > 0) {
6262 cycle(chainContext, index);
6263 }
6264 return chainContext;
6265 }
6266 chain.startAt = startAt;
6267 /**
6268 * Convert a chain to a new model, independent of any model from where the chain was found.
6269 *
6270 * @param chainContext Chain to convert to a model.
6271 * @param detachFromOldModel Flag to remove the chain's paths from their current parent model. If false, each path will be cloned. If true, the original path will be re-parented into the resulting new model. Default is false.
6272 * @returns A new model containing paths from the chain.
6273 */
6274 function toNewModel(chainContext, detachFromOldModel) {
6275 if (detachFromOldModel === void 0) { detachFromOldModel = false; }
6276 var result = { paths: {} };
6277 for (var i = 0; i < chainContext.links.length; i++) {
6278 var wp = chainContext.links[i].walkedPath;
6279 if (wp.pathContext.type === MakerJs.pathType.BezierSeed) {
6280 if (detachFromOldModel) {
6281 delete wp.modelContext.paths[wp.pathId];
6282 }
6283 if (!result.models) {
6284 result.models = {};
6285 }
6286 var modelId = MakerJs.model.getSimilarModelId(result, wp.pathId);
6287 result.models[modelId] = MakerJs.model.moveRelative(new MakerJs.models.BezierCurve(wp.pathContext), wp.offset);
6288 }
6289 else {
6290 var newPath;
6291 if (detachFromOldModel) {
6292 newPath = wp.pathContext;
6293 delete wp.modelContext.paths[wp.pathId];
6294 }
6295 else {
6296 newPath = MakerJs.path.clone(wp.pathContext);
6297 }
6298 var pathId = MakerJs.model.getSimilarPathId(result, wp.pathId);
6299 result.paths[pathId] = MakerJs.path.moveRelative(newPath, wp.offset);
6300 }
6301 }
6302 return result;
6303 }
6304 chain.toNewModel = toNewModel;
6305 /**
6306 * @private
6307 */
6308 function removeDuplicateEnds(endless, points) {
6309 if (!endless || points.length < 2)
6310 return;
6311 if (MakerJs.measure.isPointEqual(points[0], points[points.length - 1], .00001)) {
6312 points.pop();
6313 }
6314 }
6315 /**
6316 * Get points along a chain of paths.
6317 *
6318 * @param chainContext Chain of paths to get points from.
6319 * @param distance Numeric distance along the chain between points, or numeric array of distances along the chain between each point.
6320 * @param maxPoints Maximum number of points to retrieve.
6321 * @returns Array of points which are on the chain spread at a uniform interval.
6322 */
6323 function toPoints(chainContext, distanceOrDistances, maxPoints) {
6324 var result = [];
6325 var di = 0;
6326 var t = 0;
6327 var distanceArray;
6328 if (Array.isArray(distanceOrDistances)) {
6329 distanceArray = distanceOrDistances;
6330 }
6331 for (var i = 0; i < chainContext.links.length; i++) {
6332 var link = chainContext.links[i];
6333 var wp = link.walkedPath;
6334 var len = link.pathLength;
6335 while (MakerJs.round(len - t) > 0) {
6336 var r = t / len;
6337 if (link.reversed) {
6338 r = 1 - r;
6339 }
6340 result.push(MakerJs.point.add(MakerJs.point.middle(wp.pathContext, r), wp.offset));
6341 if (maxPoints && result.length >= maxPoints)
6342 return result;
6343 var distance;
6344 if (distanceArray) {
6345 distance = distanceArray[di];
6346 di++;
6347 if (di > distanceArray.length) {
6348 return result;
6349 }
6350 }
6351 else {
6352 distance = distanceOrDistances;
6353 }
6354 t += distance;
6355 }
6356 t -= len;
6357 }
6358 removeDuplicateEnds(chainContext.endless, result);
6359 return result;
6360 }
6361 chain.toPoints = toPoints;
6362 /**
6363 * Get key points (a minimal a number of points) along a chain of paths.
6364 *
6365 * @param chainContext Chain of paths to get points from.
6366 * @param maxArcFacet The maximum length between points on an arc or circle.
6367 * @returns Array of points which are on the chain.
6368 */
6369 function toKeyPoints(chainContext, maxArcFacet) {
6370 var result = [];
6371 for (var i = 0; i < chainContext.links.length; i++) {
6372 var link = chainContext.links[i];
6373 var wp = link.walkedPath;
6374 var keyPoints = MakerJs.path.toKeyPoints(wp.pathContext, maxArcFacet);
6375 if (keyPoints.length > 0) {
6376 if (link.reversed) {
6377 keyPoints.reverse();
6378 }
6379 if (i > 0) {
6380 keyPoints.shift();
6381 }
6382 var offsetPathPoints = keyPoints.map(function (p) { return MakerJs.point.add(p, wp.offset); });
6383 result.push.apply(result, offsetPathPoints);
6384 }
6385 }
6386 removeDuplicateEnds(chainContext.endless, result);
6387 return result;
6388 }
6389 chain.toKeyPoints = toKeyPoints;
6390 })(chain = MakerJs.chain || (MakerJs.chain = {}));
6391})(MakerJs || (MakerJs = {}));
6392var MakerJs;
6393(function (MakerJs) {
6394 var model;
6395 (function (model) {
6396 /**
6397 * @private
6398 */
6399 var DeadEndFinder = /** @class */ (function () {
6400 function DeadEndFinder(modelContext, options) {
6401 this.modelContext = modelContext;
6402 this.options = options;
6403 this.pointMap = new MakerJs.PointGraph();
6404 this.list = [];
6405 this.removed = [];
6406 this.ordinals = {};
6407 this.load();
6408 }
6409 DeadEndFinder.prototype.load = function () {
6410 var _this = this;
6411 var walkOptions = {
6412 onPath: function (walkedPath) {
6413 var endPoints = MakerJs.point.fromPathEnds(walkedPath.pathContext, walkedPath.offset);
6414 if (!endPoints)
6415 return;
6416 var pathRef = walkedPath;
6417 pathRef.endPoints = endPoints;
6418 var valueId = _this.pointMap.insertValue(pathRef);
6419 for (var i = 2; i--;) {
6420 _this.pointMap.insertValueIdAtPoint(valueId, endPoints[i]);
6421 }
6422 }
6423 };
6424 model.walk(this.modelContext, walkOptions);
6425 if (this.options.pointMatchingDistance) {
6426 this.pointMap.mergePoints(this.options.pointMatchingDistance);
6427 }
6428 };
6429 DeadEndFinder.prototype.findDeadEnds = function () {
6430 var _this = this;
6431 var i = 0;
6432 this.pointMap.forEachPoint(function (p, values, pointId, el) {
6433 _this.ordinals[pointId] = i++;
6434 _this.list.push(el);
6435 });
6436 i = 0;
6437 var _loop_4 = function () {
6438 var el = this_2.list[i];
6439 if (el.valueIds.length === 1) {
6440 this_2.removePath(el, el.valueIds[0], i);
6441 }
6442 else if (this_2.options.keep && el.valueIds.length % 2) {
6443 el.valueIds.forEach(function (valueId) {
6444 var value = _this.pointMap.values[valueId];
6445 if (!_this.options.keep(value)) {
6446 _this.removePath(el, valueId, i);
6447 }
6448 });
6449 }
6450 i++;
6451 };
6452 var this_2 = this;
6453 while (i < this.list.length) {
6454 _loop_4();
6455 }
6456 return this.removed;
6457 };
6458 DeadEndFinder.prototype.removePath = function (el, valueId, current) {
6459 var value = this.pointMap.values[valueId];
6460 var otherPointId = this.getOtherPointId(value.endPoints, el.pointId);
6461 var otherElement = this.pointMap.index[otherPointId];
6462 this.removed.push(value);
6463 this.removeValue(el, valueId);
6464 this.removeValue(otherElement, valueId);
6465 if (otherElement.valueIds.length > 0) {
6466 this.appendQueue(otherElement, current);
6467 }
6468 };
6469 DeadEndFinder.prototype.removeValue = function (el, valueId) {
6470 var pos = el.valueIds.indexOf(valueId);
6471 if (pos >= 0) {
6472 el.valueIds.splice(pos, 1);
6473 }
6474 };
6475 DeadEndFinder.prototype.appendQueue = function (el, current) {
6476 var otherOrdinal = this.ordinals[el.pointId];
6477 if (otherOrdinal < current) {
6478 this.list[otherOrdinal] = null;
6479 this.list.push(el);
6480 this.ordinals[el.pointId] = this.list.length;
6481 }
6482 };
6483 DeadEndFinder.prototype.getOtherPointId = function (endPoints, pointId) {
6484 for (var i = 0; i < endPoints.length; i++) {
6485 var id = this.pointMap.getIdOfPoint(endPoints[i]);
6486 if (pointId !== id) {
6487 return id;
6488 }
6489 }
6490 };
6491 return DeadEndFinder;
6492 }());
6493 /**
6494 * Remove paths from a model which have endpoints that do not connect to other paths.
6495 *
6496 * @param modelContext The model to search for dead ends.
6497 * @param pointMatchingDistance Optional max distance to consider two points as the same.
6498 * @param keep Optional callback function (which should return a boolean) to decide if a dead end path should be kept instead.
6499 * @param trackDeleted Optional callback function which will log discarded paths and the reason they were discarded.
6500 * @returns The input model (for cascading).
6501 */
6502 function removeDeadEnds(modelContext, pointMatchingDistance, keep, trackDeleted) {
6503 var options = {
6504 pointMatchingDistance: pointMatchingDistance || .005,
6505 keep: keep
6506 };
6507 var deadEndFinder = new DeadEndFinder(modelContext, options);
6508 var removed = deadEndFinder.findDeadEnds();
6509 //do not leave an empty model
6510 if (removed.length < deadEndFinder.pointMap.values.length) {
6511 removed.forEach(function (wp) {
6512 trackDeleted(wp, 'dead end');
6513 delete wp.modelContext.paths[wp.pathId];
6514 });
6515 }
6516 return modelContext;
6517 }
6518 model.removeDeadEnds = removeDeadEnds;
6519 })(model = MakerJs.model || (MakerJs.model = {}));
6520})(MakerJs || (MakerJs = {}));
6521var MakerJs;
6522(function (MakerJs) {
6523 var exporter;
6524 (function (exporter) {
6525 /**
6526 * Class for an XML tag.
6527 * @private
6528 */
6529 var XmlTag = /** @class */ (function () {
6530 /**
6531 * @param name Name of the XML tag.
6532 * @param attrs Optional attributes for the tag.
6533 */
6534 function XmlTag(name, attrs) {
6535 this.name = name;
6536 this.attrs = attrs;
6537 /**
6538 * Text between the opening and closing tags.
6539 */
6540 this.innerText = '';
6541 }
6542 /**
6543 * Escapes certain characters within a string so that it can appear in a tag or its attribute.
6544 *
6545 * @returns Escaped string.
6546 */
6547 XmlTag.escapeString = function (value) {
6548 var escape = {
6549 '&': '&amp;',
6550 '<': '&lt;',
6551 '>': '&gt;',
6552 '"': '&quot;'
6553 };
6554 for (var code in escape) {
6555 //.split then .join is a 'replace'
6556 value = value.split(code).join(escape[code]);
6557 }
6558 return value;
6559 };
6560 /**
6561 * Get the opening tag.
6562 *
6563 * @param selfClose Flag to determine if opening tag should be self closing.
6564 */
6565 XmlTag.prototype.getOpeningTag = function (selfClose) {
6566 var attrs = '';
6567 function outputAttr(attrName, attrValue) {
6568 if (attrValue == null || typeof attrValue === 'undefined')
6569 return;
6570 if (Array.isArray(attrValue) || typeof attrValue === 'object') {
6571 attrValue = JSON.stringify(attrValue);
6572 }
6573 if (typeof attrValue === 'string') {
6574 attrValue = XmlTag.escapeString(attrValue);
6575 }
6576 attrs += ' ' + attrName + '="' + attrValue + '"';
6577 }
6578 for (var name in this.attrs) {
6579 outputAttr(name, this.attrs[name]);
6580 }
6581 return '<' + this.name + attrs + (selfClose ? '/' : '') + '>';
6582 };
6583 /**
6584 * Get the inner text.
6585 */
6586 XmlTag.prototype.getInnerText = function () {
6587 if (this.innerTextEscaped) {
6588 return this.innerText;
6589 }
6590 else {
6591 return XmlTag.escapeString(this.innerText);
6592 }
6593 };
6594 /**
6595 * Get the closing tag.
6596 */
6597 XmlTag.prototype.getClosingTag = function () {
6598 return '</' + this.name + '>';
6599 };
6600 /**
6601 * Output the entire tag as a string.
6602 */
6603 XmlTag.prototype.toString = function () {
6604 var selfClose = !this.innerText;
6605 if (selfClose && !this.closingTags) {
6606 return this.getOpeningTag(true);
6607 }
6608 else {
6609 return this.getOpeningTag(false) + this.getInnerText() + this.getClosingTag();
6610 }
6611 };
6612 return XmlTag;
6613 }());
6614 exporter.XmlTag = XmlTag;
6615 })(exporter = MakerJs.exporter || (MakerJs.exporter = {}));
6616})(MakerJs || (MakerJs = {}));
6617var MakerJs;
6618(function (MakerJs) {
6619 var exporter;
6620 (function (exporter) {
6621 /**
6622 * @private
6623 */
6624 function wrap(prefix, content, condition) {
6625 if (condition) {
6626 return prefix + '(' + content + ')';
6627 }
6628 else {
6629 return content;
6630 }
6631 }
6632 /**
6633 * @private
6634 */
6635 function facetSizeToResolution(arcOrCircle, facetSize) {
6636 if (!facetSize)
6637 return;
6638 var circle = new MakerJs.paths.Circle([0, 0], arcOrCircle.radius);
6639 var length = MakerJs.measure.pathLength(circle);
6640 if (!length)
6641 return;
6642 return Math.ceil(length / facetSize);
6643 }
6644 /**
6645 * @private
6646 */
6647 function chainToJscadScript(chainContext, facetSize, accuracy) {
6648 var head = '';
6649 var tail = '';
6650 var first = true;
6651 var exit = false;
6652 var reverseTail = false;
6653 var beginMap = {};
6654 beginMap[MakerJs.pathType.Circle] = function (circle, link) {
6655 var circleOptions = {
6656 center: MakerJs.point.rounded(MakerJs.point.add(circle.origin, link.walkedPath.offset), accuracy),
6657 radius: MakerJs.round(circle.radius, accuracy),
6658 resolution: facetSizeToResolution(circle, facetSize)
6659 };
6660 head = wrap('CAG.circle', JSON.stringify(circleOptions), true);
6661 exit = true;
6662 };
6663 beginMap[MakerJs.pathType.Line] = function (line, link) {
6664 var points = link.endPoints.map(function (p) { return MakerJs.point.rounded(p, accuracy); });
6665 if (link.reversed) {
6666 points.reverse();
6667 }
6668 head = wrap('new CSG.Path2D', JSON.stringify(points), true);
6669 };
6670 beginMap[MakerJs.pathType.Arc] = function (arc, link) {
6671 var endAngle = MakerJs.angle.ofArcEnd(arc);
6672 if (link.reversed) {
6673 reverseTail = true;
6674 }
6675 var arcOptions = {
6676 center: MakerJs.point.rounded(MakerJs.point.add(arc.origin, link.walkedPath.offset), accuracy),
6677 radius: MakerJs.round(arc.radius, accuracy),
6678 startangle: MakerJs.round(arc.startAngle, accuracy),
6679 endangle: MakerJs.round(endAngle, accuracy),
6680 resolution: facetSizeToResolution(arc, facetSize)
6681 };
6682 head = wrap('new CSG.Path2D.arc', JSON.stringify(arcOptions), true);
6683 };
6684 var appendMap = {};
6685 appendMap[MakerJs.pathType.Line] = function (line, link) {
6686 var reverse = (reverseTail != link.reversed);
6687 var endPoint = MakerJs.point.rounded(link.endPoints[reverse ? 0 : 1], accuracy);
6688 append(wrap('.appendPoint', JSON.stringify(endPoint), true));
6689 };
6690 appendMap[MakerJs.pathType.Arc] = function (arc, link) {
6691 var reverse = (reverseTail != link.reversed);
6692 var endAngle = MakerJs.angle.ofArcEnd(arc);
6693 var arcOptions = {
6694 radius: MakerJs.round(arc.radius, accuracy),
6695 clockwise: reverse,
6696 large: Math.abs(endAngle - arc.startAngle) > 180,
6697 resolution: facetSizeToResolution(arc, facetSize)
6698 };
6699 var endPoint = MakerJs.point.rounded(link.endPoints[reverse ? 0 : 1], accuracy);
6700 append(wrap('.appendArc', JSON.stringify(endPoint) + ',' + JSON.stringify(arcOptions), true));
6701 };
6702 function append(s) {
6703 if (reverseTail) {
6704 tail = s + tail;
6705 }
6706 else {
6707 tail += s;
6708 }
6709 }
6710 for (var i = 0; i < chainContext.links.length; i++) {
6711 var link = chainContext.links[i];
6712 var pathContext = link.walkedPath.pathContext;
6713 var fn = first ? beginMap[pathContext.type] : appendMap[pathContext.type];
6714 if (fn) {
6715 fn(pathContext, link);
6716 }
6717 if (exit) {
6718 return head;
6719 }
6720 first = false;
6721 }
6722 return head + tail + '.close().innerToCAG()';
6723 }
6724 /**
6725 * @private
6726 */
6727 function makePhasedCallback(originalCb, phaseStart, phaseSpan) {
6728 return function statusCallback(status) {
6729 originalCb && originalCb({ progress: phaseStart + status.progress * phaseSpan / 100 });
6730 };
6731 }
6732 /**
6733 * Converts a model to a @jscad/csg CAG object - 2D to 2D. See https://en.wikibooks.org/wiki/OpenJSCAD_User_Guide#2D_Paths
6734 *
6735 * Example:
6736 * ```
6737 * //First, use npm install @jscad/csg from the command line in your jscad project
6738 * //Create a CAG instance from a model.
6739 * var { CAG } = require('@jscad/csg');
6740 * var model = new makerjs.models.Ellipse(70, 40);
6741 * var cag = makerjs.exporter.toJscadCAG(CAG, model, {maxArcFacet: 1});
6742 * ```
6743 *
6744 * @param jscadCAG @jscad/csg CAG engine, see https://www.npmjs.com/package/@jscad/csg
6745 * @param modelToExport Model object to export.
6746 * @param options Optional options object.
6747 * @param options.byLayers Optional flag to separate chains by layers.
6748 * @param options.pointMatchingDistance Optional max distance to consider two points as the same.
6749 * @param options.maxArcFacet The maximum length between points on an arc or circle.
6750 * @param options.statusCallback Optional callback function to get the percentage complete.
6751 * @returns jscad CAG object in 2D, or a map (keyed by layer id) of jscad CAG objects - if options.byLayers is true.
6752 */
6753 function toJscadCAG(jscadCAG, modelToExport, jsCadCagOptions) {
6754 function chainToJscadCag(c, maxArcFacet) {
6755 var keyPoints = MakerJs.chain.toKeyPoints(c, maxArcFacet);
6756 keyPoints.push(keyPoints[0]);
6757 return jscadCAG.fromPoints(keyPoints);
6758 }
6759 function jscadCagUnion(augend, addend) {
6760 return augend.union(addend);
6761 }
6762 function jscadCagSubtraction(minuend, subtrahend) {
6763 return minuend.subtract(subtrahend);
6764 }
6765 return convertChainsTo2D(chainToJscadCag, jscadCagUnion, jscadCagSubtraction, modelToExport, jsCadCagOptions);
6766 }
6767 exporter.toJscadCAG = toJscadCAG;
6768 /**
6769 * @private
6770 */
6771 function convertChainsTo2D(convertToT, union, subtraction, modelToExport, jsCadCagOptions) {
6772 if (jsCadCagOptions === void 0) { jsCadCagOptions = {}; }
6773 var adds = {};
6774 var status = { total: 0, complete: 0 };
6775 function unionize(phaseStart, phaseSpan, arr) {
6776 var result = arr.shift();
6777 arr.forEach(function (el) { return result = union(result, el); });
6778 status.complete++;
6779 jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: phaseStart + phaseSpan * status.complete / status.total });
6780 return result;
6781 }
6782 function subtractChains(layerId, cs) {
6783 var subtracts = [];
6784 cs.forEach(function (c) {
6785 if (!c.endless)
6786 return;
6787 if (c.contains) {
6788 addChains(layerId, c.contains);
6789 }
6790 status.total++;
6791 subtracts.unshift(convertToT(c, jsCadCagOptions.maxArcFacet));
6792 });
6793 return subtracts;
6794 }
6795 function addChains(layerId, cs) {
6796 cs.forEach(function (c) {
6797 if (!c.endless)
6798 return;
6799 var add = { cag: convertToT(c, jsCadCagOptions.maxArcFacet), subtracts: [] };
6800 if (c.contains) {
6801 var subtracts = subtractChains(layerId, c.contains);
6802 if (subtracts.length > 0) {
6803 add.subtracts.push(subtracts);
6804 }
6805 }
6806 status.total++;
6807 if (!(layerId in adds)) {
6808 adds[layerId] = [];
6809 }
6810 adds[layerId].unshift(add);
6811 });
6812 }
6813 var options = {
6814 pointMatchingDistance: jsCadCagOptions.pointMatchingDistance,
6815 byLayers: jsCadCagOptions.byLayers,
6816 contain: true
6817 };
6818 jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: 25 });
6819 var chainsResult = MakerJs.model.findChains(modelToExport, options);
6820 if (Array.isArray(chainsResult)) {
6821 addChains('', chainsResult);
6822 }
6823 else {
6824 for (var layerId in chainsResult) {
6825 addChains(layerId, chainsResult[layerId]);
6826 }
6827 }
6828 jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: 50 });
6829 var closedCount = 0;
6830 for (var layerId in adds) {
6831 closedCount += adds[layerId].length;
6832 }
6833 if (closedCount === 0) {
6834 jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: 100 });
6835 throw ('No closed geometries found.');
6836 }
6837 var resultMap = {};
6838 for (var layerId in adds) {
6839 var flatAdds = adds[layerId].map(function (add) {
6840 var result = add.cag;
6841 add.subtracts.forEach(function (subtract) {
6842 var union = unionize(50, 50, subtract);
6843 result = subtraction(result, union);
6844 });
6845 return result;
6846 });
6847 resultMap[layerId] = unionize(50, 50, flatAdds);
6848 }
6849 jsCadCagOptions.statusCallback && jsCadCagOptions.statusCallback({ progress: 100 });
6850 return options.byLayers ? resultMap : resultMap[''];
6851 }
6852 /**
6853 * Converts a model to a @jscad/csg CSG object - 2D to 3D.
6854 *
6855 * Example:
6856 * ```
6857 * //First, use npm install @jscad/csg from the command line in your jscad project
6858 * //Create a CSG instance from a model.
6859 * var { CAG } = require('@jscad/csg');
6860 * var model = new makerjs.models.Ellipse(70, 40);
6861 * var csg = makerjs.exporter.toJscadCSG(CAG, model, {maxArcFacet: 1, extrude: 10});
6862 * ```
6863 *
6864 * @param jscadCAG @jscad/csg CAG engine, see https://www.npmjs.com/package/@jscad/csg
6865 * @param modelToExport Model object to export.
6866 * @param options Optional options object.
6867 * @param options.byLayers Optional flag to separate chains by layers.
6868 * @param options.pointMatchingDistance Optional max distance to consider two points as the same.
6869 * @param options.maxArcFacet The maximum length between points on an arc or circle.
6870 * @param options.statusCallback Optional callback function to get the percentage complete.
6871 * @param options.extrude Optional default extrusion distance.
6872 * @param options.layerOptions Optional object map of options per layer, keyed by layer name. Each value for a key is an object with 'extrude' and 'z' properties.
6873 * @returns jscad CAG object in 2D, or a map (keyed by layer id) of jscad CAG objects - if options.byLayers is true.
6874 */
6875 function toJscadCSG(jscadCAG, modelToExport, options) {
6876 function to2D(opts) {
6877 return toJscadCAG(jscadCAG, modelToExport, opts);
6878 }
6879 function to3D(cag, extrude, z) {
6880 var csg = cag.extrude({ offset: [0, 0, extrude] });
6881 if (z) {
6882 csg = csg.translate([0, 0, z]);
6883 }
6884 return csg;
6885 }
6886 function union3D(augend, addend) {
6887 return augend.union(addend);
6888 }
6889 return convert2Dto3D(to2D, to3D, union3D, modelToExport, options);
6890 }
6891 exporter.toJscadCSG = toJscadCSG;
6892 /**
6893 * @private
6894 */
6895 function convert2Dto3D(to2D, to3D, union3D, modelToExport, options) {
6896 if (options === void 0) { options = {}; }
6897 var originalCb = options.statusCallback;
6898 function getDefinedNumber(a, b) {
6899 if (MakerJs.isNumber(a))
6900 return a;
6901 return b;
6902 }
6903 if (modelToExport.exporterOptions) {
6904 MakerJs.extendObject(options, modelToExport.exporterOptions['toJscadCSG']);
6905 }
6906 options.byLayers = options.byLayers || (options.layerOptions && true);
6907 options.statusCallback = makePhasedCallback(originalCb, 0, 50);
6908 var result2D = to2D(options);
6909 var csgs = [];
6910 if (options.byLayers) {
6911 for (var layerId in result2D) {
6912 var layerOptions = options.layerOptions[layerId];
6913 var csg = to3D(result2D[layerId], layerOptions.extrude || options.extrude, getDefinedNumber(layerOptions.z, options.z));
6914 csgs.push(csg);
6915 }
6916 }
6917 else {
6918 var csg = to3D(result2D, options.extrude, options.z);
6919 csgs.push(csg);
6920 }
6921 options.statusCallback = makePhasedCallback(originalCb, 50, 100);
6922 var status = { total: csgs.length - 1, complete: 0 };
6923 var result = csgs.shift();
6924 csgs.forEach(function (el, i) {
6925 result = union3D(result, el);
6926 status.complete++;
6927 options.statusCallback({ progress: status.complete / status.total });
6928 });
6929 return result;
6930 }
6931 /**
6932 * Creates a string of JavaScript code for execution with a Jscad environment.
6933 *
6934 * @param modelToExport Model object to export.
6935 * @param options Export options object.
6936 * @param options.byLayers Optional flag to separate chains by layers.
6937 * @param options.pointMatchingDistance Optional max distance to consider two points as the same.
6938 * @param options.maxArcFacet The maximum length between points on an arc or circle.
6939 * @param options.statusCallback Optional callback function to get the percentage complete.
6940 * @param options.extrude Optional default extrusion distance.
6941 * @param options.layerOptions Optional object map of options per layer, keyed by layer name. Each value for a key is an object with 'extrude' and 'z' properties.
6942 * @returns String of JavaScript containing a main() function for Jscad.
6943 */
6944 function toJscadScript(modelToExport, options) {
6945 if (options === void 0) { options = {}; }
6946 function _chainToJscadScript(c, maxArcFacet) {
6947 return wrap(chainToJscadScript(c, maxArcFacet, options.accuracy));
6948 }
6949 function scriptUnion(augend, addend) {
6950 return augend + (".union(" + addend + ")");
6951 }
6952 function scriptSubtraction(minuend, subtrahend) {
6953 return minuend + (".subtract(" + subtrahend + ")");
6954 }
6955 function to2D(opts) {
6956 return convertChainsTo2D(_chainToJscadScript, scriptUnion, scriptSubtraction, modelToExport, options);
6957 }
6958 function to3D(cag, extrude, z) {
6959 var csg = cag + (".extrude({ offset: [0, 0, " + extrude + "] })");
6960 if (z) {
6961 csg = csg + (".translate([0, 0, " + z + "])");
6962 }
6963 return csg;
6964 }
6965 function wrap(s) {
6966 return "" + nl + indent + s + nl;
6967 }
6968 var indent = new Array((options.indent || 0) + 1).join(' ');
6969 var nl = options.indent ? '\n' : '';
6970 var result = convert2Dto3D(to2D, to3D, scriptUnion, modelToExport, options).trim();
6971 return "function " + (options.functionName || 'main') + "(){" + wrap("return " + result + ";") + "}" + nl;
6972 }
6973 exporter.toJscadScript = toJscadScript;
6974 /**
6975 * Exports a model in STL format - 2D to 3D.
6976 *
6977 * @param jscadCAG @jscad/csg CAG engine, see https://www.npmjs.com/package/@jscad/csg
6978 * @param stlSerializer @jscad/stl-serializer, see https://www.npmjs.com/package/@jscad/stl-serializer
6979 * @param modelToExport Model object to export.
6980 * @param options Optional options object.
6981 * @param options.byLayers Optional flag to separate chains by layers.
6982 * @param options.pointMatchingDistance Optional max distance to consider two points as the same.
6983 * @param options.maxArcFacet The maximum length between points on an arc or circle.
6984 * @param options.statusCallback Optional callback function to get the percentage complete.
6985 * @param options.extrude Optional default extrusion distance.
6986 * @param options.layerOptions Optional object map of options per layer, keyed by layer name. Each value for a key is an object with 'extrude' and 'z' properties.
6987 * @returns String in STL ASCII format.
6988 */
6989 function toJscadSTL(CAG, stlSerializer, modelToExport, options) {
6990 var originalCb = options.statusCallback;
6991 options.statusCallback = makePhasedCallback(originalCb, 0, 50);
6992 var csg = toJscadCSG(CAG, modelToExport, options);
6993 return stlSerializer.serialize(csg, { binary: false, statusCallback: makePhasedCallback(originalCb, 50, 50) });
6994 }
6995 exporter.toJscadSTL = toJscadSTL;
6996 })(exporter = MakerJs.exporter || (MakerJs.exporter = {}));
6997})(MakerJs || (MakerJs = {}));
6998var MakerJs;
6999(function (MakerJs) {
7000 var exporter;
7001 (function (exporter) {
7002 /**
7003 * Injects drawing into a PDFKit document.
7004 *
7005 * @param doc PDFKit.PDFDocument object. See https://pdfkit.org/
7006 * @param modelToExport Model object to export.
7007 * @param options Export options object.
7008 * @returns String of PDF file contents.
7009 */
7010 function toPDF(doc, modelToExport, options) {
7011 if (!modelToExport)
7012 return;
7013 //fixup options
7014 var opts = {
7015 fontName: 'Courier',
7016 fontSize: 9,
7017 origin: [0, 0],
7018 stroke: "#000"
7019 };
7020 MakerJs.extendObject(opts, options);
7021 //try to get the unit system from the itemToExport
7022 var scale = 1;
7023 var exportUnits = opts.units || modelToExport.units;
7024 if (exportUnits) {
7025 //convert to inch
7026 scale = MakerJs.units.conversionScale(exportUnits, MakerJs.unitType.Inch);
7027 }
7028 else {
7029 //assume pixels, convert to inch
7030 scale = 1 / 100;
7031 }
7032 //from inch to PDF PPI
7033 scale *= 72;
7034 //TODO scale each element without a whole clone
7035 var scaledModel = MakerJs.model.scale(MakerJs.cloneObject(modelToExport), scale);
7036 var size = MakerJs.measure.modelExtents(scaledModel);
7037 var left = -size.low[0];
7038 var offset = [left, size.high[1]];
7039 offset = MakerJs.point.add(offset, options.origin);
7040 MakerJs.model.findChains(scaledModel, function (chains, loose, layer) {
7041 function single(walkedPath) {
7042 var pathData = exporter.pathToSVGPathData(walkedPath.pathContext, walkedPath.offset, offset);
7043 doc.path(pathData).stroke(opts.stroke);
7044 }
7045 chains.map(function (chain) {
7046 if (chain.links.length > 1) {
7047 var pathData = exporter.chainToSVGPathData(chain, offset);
7048 doc.path(pathData).stroke(opts.stroke);
7049 }
7050 else {
7051 var walkedPath = chain.links[0].walkedPath;
7052 if (walkedPath.pathContext.type === MakerJs.pathType.Circle) {
7053 var fixedPath;
7054 MakerJs.path.moveTemporary([walkedPath.pathContext], [walkedPath.offset], function () {
7055 fixedPath = MakerJs.path.mirror(walkedPath.pathContext, false, true);
7056 });
7057 MakerJs.path.moveRelative(fixedPath, offset);
7058 //TODO use only chainToSVGPathData instead of circle, so that we can use fill
7059 doc.circle(fixedPath.origin[0], fixedPath.origin[1], walkedPath.pathContext.radius).stroke(opts.stroke);
7060 }
7061 else {
7062 single(walkedPath);
7063 }
7064 }
7065 });
7066 loose.map(single);
7067 }, { byLayers: false });
7068 doc.font(opts.fontName).fontSize(opts.fontSize);
7069 MakerJs.model.getAllCaptionsOffset(scaledModel).forEach(function (caption) {
7070 //measure the angle of the line, prior to mirroring
7071 var a = MakerJs.angle.ofLineInDegrees(caption.anchor);
7072 //mirror into pdf y coords
7073 var anchor = MakerJs.path.mirror(caption.anchor, false, true);
7074 //move mirrored line by document offset
7075 MakerJs.path.moveRelative(anchor, offset);
7076 //measure center point of text
7077 var text = caption.text;
7078 var textCenter = [doc.widthOfString(text) / 2, doc.heightOfString(text) / 2];
7079 //get center point on line
7080 var center = MakerJs.point.middle(anchor);
7081 var textOffset = MakerJs.point.subtract(center, textCenter);
7082 doc.rotate(-a, { origin: center });
7083 doc.text(text, textOffset[0], textOffset[1]);
7084 doc.rotate(a, { origin: center });
7085 });
7086 }
7087 exporter.toPDF = toPDF;
7088 })(exporter = MakerJs.exporter || (MakerJs.exporter = {}));
7089})(MakerJs || (MakerJs = {}));
7090var MakerJs;
7091(function (MakerJs) {
7092 var exporter;
7093 (function (exporter) {
7094 /**
7095 * @private
7096 */
7097 var chainLinkToPathDataMap = {};
7098 chainLinkToPathDataMap[MakerJs.pathType.Arc] = function (arc, endPoint, reversed, d, accuracy) {
7099 d.push('A');
7100 svgArcData(d, arc.radius, endPoint, accuracy, MakerJs.angle.ofArcSpan(arc) > 180, reversed ? (arc.startAngle > arc.endAngle) : (arc.startAngle < arc.endAngle));
7101 };
7102 chainLinkToPathDataMap[MakerJs.pathType.Line] = function (line, endPoint, reversed, d, accuracy) {
7103 d.push('L', MakerJs.round(endPoint[0], accuracy), MakerJs.round(endPoint[1], accuracy));
7104 };
7105 chainLinkToPathDataMap[MakerJs.pathType.BezierSeed] = function (seed, endPoint, reversed, d, accuracy) {
7106 svgBezierData(d, seed, accuracy, reversed);
7107 };
7108 /**
7109 * @private
7110 */
7111 function svgCoords(p) {
7112 return MakerJs.point.mirror(p, false, true);
7113 }
7114 /**
7115 * @private
7116 */
7117 function correctArc(arc) {
7118 var arcSpan = MakerJs.angle.ofArcSpan(arc);
7119 arc.startAngle = MakerJs.angle.noRevolutions(arc.startAngle);
7120 arc.endAngle = arc.startAngle + arcSpan;
7121 }
7122 /**
7123 * Convert a chain to SVG path data.
7124 *
7125 * @param chain Chain to convert.
7126 * @param offset IPoint relative offset point.
7127 * @param accuracy Optional accuracy of SVG path data.
7128 * @returns String of SVG path data.
7129 */
7130 function chainToSVGPathData(chain, offset, accuracy) {
7131 function offsetPoint(p) {
7132 return MakerJs.point.add(p, offset);
7133 }
7134 var first = chain.links[0];
7135 var firstPoint = offsetPoint(svgCoords(first.endPoints[first.reversed ? 1 : 0]));
7136 var d = ['M', MakerJs.round(firstPoint[0], accuracy), MakerJs.round(firstPoint[1], accuracy)];
7137 for (var i = 0; i < chain.links.length; i++) {
7138 var link = chain.links[i];
7139 var pathContext = link.walkedPath.pathContext;
7140 var fn = chainLinkToPathDataMap[pathContext.type];
7141 if (fn) {
7142 var fixedPath;
7143 MakerJs.path.moveTemporary([pathContext], [link.walkedPath.offset], function () {
7144 fixedPath = MakerJs.path.mirror(pathContext, false, true);
7145 });
7146 MakerJs.path.moveRelative(fixedPath, offset);
7147 fn(fixedPath, offsetPoint(svgCoords(link.endPoints[link.reversed ? 0 : 1])), link.reversed, d, accuracy);
7148 }
7149 }
7150 if (chain.endless) {
7151 d.push('Z');
7152 }
7153 return d.join(' ');
7154 }
7155 exporter.chainToSVGPathData = chainToSVGPathData;
7156 /**
7157 * @private
7158 */
7159 function startSvgPathData(start, d, accuracy) {
7160 return ["M", MakerJs.round(start[0], accuracy), MakerJs.round(start[1], accuracy)].concat(d);
7161 }
7162 /**
7163 * @private
7164 */
7165 var svgPathDataMap = {};
7166 svgPathDataMap[MakerJs.pathType.Line] = function (line, accuracy) {
7167 return startSvgPathData(line.origin, MakerJs.point.rounded(line.end, accuracy), accuracy);
7168 };
7169 svgPathDataMap[MakerJs.pathType.Circle] = function (circle, accuracy, clockwiseCircle) {
7170 return startSvgPathData(circle.origin, svgCircleData(circle.radius, accuracy, clockwiseCircle), accuracy);
7171 };
7172 svgPathDataMap[MakerJs.pathType.Arc] = function (arc, accuracy) {
7173 correctArc(arc);
7174 var arcPoints = MakerJs.point.fromArc(arc);
7175 if (MakerJs.measure.isPointEqual(arcPoints[0], arcPoints[1])) {
7176 return svgPathDataMap[MakerJs.pathType.Circle](arc, accuracy);
7177 }
7178 else {
7179 var d = ['A'];
7180 svgArcData(d, arc.radius, arcPoints[1], accuracy, MakerJs.angle.ofArcSpan(arc) > 180, arc.startAngle > arc.endAngle);
7181 return startSvgPathData(arcPoints[0], d, accuracy);
7182 }
7183 };
7184 svgPathDataMap[MakerJs.pathType.BezierSeed] = function (seed, accuracy) {
7185 var d = [];
7186 svgBezierData(d, seed, accuracy);
7187 return startSvgPathData(seed.origin, d, accuracy);
7188 };
7189 /**
7190 * Export a path to SVG path data.
7191 *
7192 * @param pathToExport IPath to export.
7193 * @param pathOffset IPoint relative offset of the path object.
7194 * @param exportOffset IPoint relative offset point of the export.
7195 * @param accuracy Optional accuracy of SVG path data.
7196 * @param clockwiseCircle Optional flag to use clockwise winding for circles.
7197 * @returns String of SVG path data.
7198 */
7199 function pathToSVGPathData(pathToExport, pathOffset, exportOffset, accuracy, clockwiseCircle) {
7200 var fn = svgPathDataMap[pathToExport.type];
7201 if (fn) {
7202 var fixedPath;
7203 MakerJs.path.moveTemporary([pathToExport], [pathOffset], function () {
7204 fixedPath = MakerJs.path.mirror(pathToExport, false, true);
7205 });
7206 MakerJs.path.moveRelative(fixedPath, exportOffset);
7207 var d = fn(fixedPath, accuracy, clockwiseCircle);
7208 return d.join(' ');
7209 }
7210 return '';
7211 }
7212 exporter.pathToSVGPathData = pathToSVGPathData;
7213 /**
7214 * @private
7215 */
7216 function getPathDataByLayer(modelToExport, offset, options, accuracy) {
7217 var pathDataByLayer = {};
7218 options.unifyBeziers = true;
7219 MakerJs.model.findChains(modelToExport, function (chains, loose, layer) {
7220 function single(walkedPath, clockwise) {
7221 var pathData = pathToSVGPathData(walkedPath.pathContext, walkedPath.offset, offset, accuracy, clockwise);
7222 pathDataByLayer[layer].push(pathData);
7223 }
7224 pathDataByLayer[layer] = [];
7225 function doChains(cs, clockwise) {
7226 cs.forEach(function (chain) {
7227 if (chain.links.length > 1) {
7228 var pathData = chainToSVGPathData(chain, offset, accuracy);
7229 pathDataByLayer[layer].push(pathData);
7230 }
7231 else {
7232 single(chain.links[0].walkedPath, clockwise);
7233 }
7234 if (chain.contains) {
7235 doChains(chain.contains, !clockwise);
7236 }
7237 });
7238 }
7239 doChains(chains, true);
7240 loose.forEach(function (wp) { return single(wp); });
7241 }, options);
7242 return pathDataByLayer;
7243 }
7244 function toSVGPathData(modelToExport) {
7245 var args = [];
7246 for (var _i = 1; _i < arguments.length; _i++) {
7247 args[_i - 1] = arguments[_i];
7248 }
7249 var options = {
7250 fillRule: 'evenodd'
7251 };
7252 if (typeof args[0] === 'boolean') {
7253 options.byLayers = args[0];
7254 options.origin = args[1];
7255 options.accuracy = args[2];
7256 }
7257 else if (MakerJs.isObject(args[0])) {
7258 MakerJs.extendObject(options, args[0]);
7259 }
7260 var findChainsOptions = {
7261 byLayers: options.byLayers,
7262 contain: false
7263 };
7264 if (options.fillRule === 'nonzero') {
7265 findChainsOptions.contain = {
7266 alternateDirection: true
7267 };
7268 }
7269 var size = MakerJs.measure.modelExtents(modelToExport);
7270 if (!options.origin) {
7271 options.origin = [-size.low[0], size.high[1]];
7272 }
7273 var pathDataArrayByLayer = getPathDataByLayer(modelToExport, options.origin, findChainsOptions, options.accuracy);
7274 var pathDataStringByLayer = {};
7275 for (var layer in pathDataArrayByLayer) {
7276 pathDataStringByLayer[layer] = pathDataArrayByLayer[layer].join(' ');
7277 }
7278 return findChainsOptions.byLayers ? pathDataStringByLayer : pathDataStringByLayer[''];
7279 }
7280 exporter.toSVGPathData = toSVGPathData;
7281 /**
7282 * Renders an item in SVG markup.
7283 *
7284 * @param itemToExport Item to render: may be a path, an array of paths, or a model object.
7285 * @param options Rendering options object.
7286 * @param options.annotate Boolean to indicate that the id's of paths should be rendered as SVG text elements.
7287 * @param options.origin point object for the rendered reference origin.
7288 * @param options.scale Number to scale the SVG rendering.
7289 * @param options.stroke String color of the rendered paths.
7290 * @param options.strokeWidth String numeric width and optional units of the rendered paths.
7291 * @param options.units String of the unit system. May be omitted. See makerjs.unitType for possible values.
7292 * @param options.useSvgPathOnly Boolean to use SVG path elements instead of line, circle etc.
7293 * @returns String of XML / SVG content.
7294 */
7295 function toSVG(itemToExport, options) {
7296 function append(value, layer, forcePush) {
7297 if (forcePush === void 0) { forcePush = false; }
7298 if (!forcePush && typeof layer == "string" && layer.length > 0) {
7299 if (!(layer in layers)) {
7300 layers[layer] = [];
7301 }
7302 layers[layer].push(value);
7303 }
7304 else {
7305 elements.push(value);
7306 }
7307 }
7308 function cssStyle(elOpts) {
7309 var a = [];
7310 function push(name, val) {
7311 if (val === undefined)
7312 return;
7313 a.push(name + ':' + val);
7314 }
7315 push('stroke', elOpts.stroke);
7316 push('stroke-width', elOpts.strokeWidth);
7317 push('fill', elOpts.fill);
7318 return a.join(';');
7319 }
7320 function addSvgAttrs(attrs, elOpts) {
7321 if (!elOpts)
7322 return;
7323 MakerJs.extendObject(attrs, {
7324 "stroke": elOpts.stroke,
7325 "stroke-width": elOpts.strokeWidth,
7326 "fill": elOpts.fill,
7327 "style": elOpts.cssStyle || cssStyle(elOpts)
7328 });
7329 }
7330 function colorLayerOptions(layer) {
7331 if (opts.layerOptions && opts.layerOptions[layer])
7332 return opts.layerOptions[layer];
7333 if (layer in exporter.colors) {
7334 return {
7335 stroke: layer
7336 };
7337 }
7338 }
7339 function createElement(tagname, attrs, layer, innerText, forcePush) {
7340 if (innerText === void 0) { innerText = null; }
7341 if (forcePush === void 0) { forcePush = false; }
7342 if (tagname !== 'text') {
7343 addSvgAttrs(attrs, colorLayerOptions(layer));
7344 }
7345 if (!opts.scalingStroke) {
7346 attrs['vector-effect'] = 'non-scaling-stroke';
7347 }
7348 var tag = new exporter.XmlTag(tagname, attrs);
7349 tag.closingTags = opts.closingTags;
7350 if (innerText) {
7351 tag.innerText = innerText;
7352 }
7353 append(tag.toString(), layer, forcePush);
7354 }
7355 function fixPoint(pointToFix) {
7356 //in DXF Y increases upward. in SVG, Y increases downward
7357 var pointMirroredY = svgCoords(pointToFix);
7358 return MakerJs.point.scale(pointMirroredY, opts.scale);
7359 }
7360 function fixPath(pathToFix, origin) {
7361 //mirror creates a copy, so we don't modify the original
7362 var mirrorY = MakerJs.path.mirror(pathToFix, false, true);
7363 return MakerJs.path.moveRelative(MakerJs.path.scale(mirrorY, opts.scale), origin);
7364 }
7365 //fixup options
7366 var opts = {
7367 accuracy: .001,
7368 annotate: false,
7369 origin: null,
7370 scale: 1,
7371 stroke: "#000",
7372 strokeLineCap: "round",
7373 strokeWidth: '0.25mm',
7374 fill: "none",
7375 fillRule: "evenodd",
7376 fontSize: '9pt',
7377 useSvgPathOnly: true,
7378 viewBox: true
7379 };
7380 MakerJs.extendObject(opts, options);
7381 var modelToExport;
7382 var itemToExportIsModel = MakerJs.isModel(itemToExport);
7383 if (itemToExportIsModel) {
7384 modelToExport = itemToExport;
7385 if (modelToExport.exporterOptions) {
7386 MakerJs.extendObject(opts, modelToExport.exporterOptions['toSVG']);
7387 }
7388 }
7389 var elements = [];
7390 var layers = {};
7391 //measure the item to move it into svg area
7392 if (itemToExportIsModel) {
7393 modelToExport = itemToExport;
7394 }
7395 else if (Array.isArray(itemToExport)) {
7396 //issue: this won't handle an array of models
7397 var pathMap = {};
7398 itemToExport.forEach(function (p, i) { pathMap[i] = p; });
7399 modelToExport = { paths: pathMap };
7400 }
7401 else if (MakerJs.isPath(itemToExport)) {
7402 modelToExport = { paths: { modelToMeasure: itemToExport } };
7403 }
7404 var size = MakerJs.measure.modelExtents(modelToExport);
7405 //increase size to fit caption text
7406 var captions = MakerJs.model.getAllCaptionsOffset(modelToExport);
7407 captions.forEach(function (caption) {
7408 MakerJs.measure.increase(size, MakerJs.measure.pathExtents(caption.anchor), true);
7409 });
7410 //try to get the unit system from the itemToExport
7411 if (!opts.units) {
7412 var unitSystem = exporter.tryGetModelUnits(itemToExport);
7413 if (unitSystem) {
7414 opts.units = unitSystem;
7415 }
7416 }
7417 //convert unit system (if it exists) into SVG's units. scale if necessary.
7418 var useSvgUnit = exporter.svgUnit[opts.units];
7419 if (useSvgUnit && opts.viewBox) {
7420 opts.scale *= useSvgUnit.scaleConversion;
7421 }
7422 if (size && !opts.origin) {
7423 var left = -size.low[0] * opts.scale;
7424 opts.origin = [left, size.high[1] * opts.scale];
7425 }
7426 //also pass back to options parameter
7427 MakerJs.extendObject(options, opts);
7428 //begin svg output
7429 var svgAttrs;
7430 if (size && opts.viewBox) {
7431 var width = MakerJs.round(size.width * opts.scale, opts.accuracy);
7432 var height = MakerJs.round(size.height * opts.scale, opts.accuracy);
7433 var viewBox = [0, 0, width, height];
7434 var unit = useSvgUnit ? useSvgUnit.svgUnitType : '';
7435 svgAttrs = {
7436 width: width + unit,
7437 height: height + unit,
7438 viewBox: viewBox.join(' ')
7439 };
7440 }
7441 var svgTag = new exporter.XmlTag('svg', MakerJs.extendObject(svgAttrs || {}, opts.svgAttrs));
7442 append(svgTag.getOpeningTag(false));
7443 var groupAttrs = {
7444 id: 'svgGroup',
7445 "stroke-linecap": opts.strokeLineCap,
7446 "fill-rule": opts.fillRule,
7447 "font-size": opts.fontSize
7448 };
7449 addSvgAttrs(groupAttrs, opts);
7450 var svgGroup = new exporter.XmlTag('g', groupAttrs);
7451 append(svgGroup.getOpeningTag(false));
7452 if (opts.useSvgPathOnly) {
7453 var findChainsOptions = {
7454 byLayers: true
7455 };
7456 if (opts.fillRule === 'nonzero') {
7457 findChainsOptions.contain = {
7458 alternateDirection: true
7459 };
7460 }
7461 var pathDataByLayer = getPathDataByLayer(modelToExport, opts.origin, findChainsOptions, opts.accuracy);
7462 for (var layerId1 in pathDataByLayer) {
7463 var pathData = pathDataByLayer[layerId1].join(' ');
7464 var attrs = { "d": pathData };
7465 if (layerId1.length > 0) {
7466 attrs["id"] = layerId1;
7467 }
7468 createElement("path", attrs, layerId1, null, true);
7469 }
7470 }
7471 else {
7472 function drawText(id, textPoint, layer) {
7473 createElement("text", {
7474 "id": id + "_text",
7475 "x": MakerJs.round(textPoint[0], opts.accuracy),
7476 "y": MakerJs.round(textPoint[1], opts.accuracy)
7477 }, layer, id);
7478 }
7479 function drawPath(id, x, y, d, layer, route, textPoint, annotate, flow) {
7480 createElement("path", {
7481 "id": id,
7482 "data-route": route,
7483 "d": ["M", MakerJs.round(x, opts.accuracy), MakerJs.round(y, opts.accuracy)].concat(d).join(" ")
7484 }, layer);
7485 if (annotate) {
7486 drawText(id, textPoint, layer);
7487 }
7488 }
7489 function circleInPaths(id, center, radius, layer, route, annotate, flow) {
7490 var d = svgCircleData(radius, opts.accuracy);
7491 drawPath(id, center[0], center[1], d, layer, route, center, annotate, flow);
7492 }
7493 var map = {};
7494 map[MakerJs.pathType.Line] = function (id, line, layer, className, route, annotate, flow) {
7495 var start = line.origin;
7496 var end = line.end;
7497 createElement("line", {
7498 "id": id,
7499 "class": className,
7500 "data-route": route,
7501 "x1": MakerJs.round(start[0], opts.accuracy),
7502 "y1": MakerJs.round(start[1], opts.accuracy),
7503 "x2": MakerJs.round(end[0], opts.accuracy),
7504 "y2": MakerJs.round(end[1], opts.accuracy)
7505 }, layer);
7506 if (annotate) {
7507 drawText(id, MakerJs.point.middle(line), layer);
7508 }
7509 if (flow) {
7510 addFlowMarks(flow, layer, line.origin, line.end, MakerJs.angle.ofLineInDegrees(line));
7511 }
7512 };
7513 map[MakerJs.pathType.Circle] = function (id, circle, layer, className, route, annotate, flow) {
7514 var center = circle.origin;
7515 createElement("circle", {
7516 "id": id,
7517 "class": className,
7518 "data-route": route,
7519 "r": circle.radius,
7520 "cx": MakerJs.round(center[0], opts.accuracy),
7521 "cy": MakerJs.round(center[1], opts.accuracy)
7522 }, layer);
7523 if (annotate) {
7524 drawText(id, center, layer);
7525 }
7526 };
7527 map[MakerJs.pathType.Arc] = function (id, arc, layer, className, route, annotate, flow) {
7528 correctArc(arc);
7529 var arcPoints = MakerJs.point.fromArc(arc);
7530 if (MakerJs.measure.isPointEqual(arcPoints[0], arcPoints[1])) {
7531 circleInPaths(id, arc.origin, arc.radius, layer, route, annotate, flow);
7532 }
7533 else {
7534 var d = ['A'];
7535 svgArcData(d, arc.radius, arcPoints[1], opts.accuracy, MakerJs.angle.ofArcSpan(arc) > 180, arc.startAngle > arc.endAngle);
7536 drawPath(id, arcPoints[0][0], arcPoints[0][1], d, layer, route, MakerJs.point.middle(arc), annotate, flow);
7537 if (flow) {
7538 addFlowMarks(flow, layer, arcPoints[1], arcPoints[0], MakerJs.angle.noRevolutions(arc.startAngle - 90));
7539 }
7540 }
7541 };
7542 map[MakerJs.pathType.BezierSeed] = function (id, seed, layer, className, route, annotate, flow) {
7543 var d = [];
7544 svgBezierData(d, seed, opts.accuracy);
7545 drawPath(id, seed.origin[0], seed.origin[1], d, layer, route, MakerJs.point.middle(seed), annotate, flow);
7546 };
7547 function addFlowMarks(flow, layer, origin, end, endAngle) {
7548 var className = 'flow';
7549 //origin: add a circle
7550 map[MakerJs.pathType.Circle]('', new MakerJs.paths.Circle(origin, flow.size / 2), layer, className, null, false, null);
7551 //end: add an arrow
7552 var arrowEnd = [-1 * flow.size, flow.size / 2];
7553 var arrowLines = [arrowEnd, MakerJs.point.mirror(arrowEnd, false, true)].map(function (p) { return new MakerJs.paths.Line(MakerJs.point.add(MakerJs.point.rotate(p, endAngle), end), end); });
7554 arrowLines.forEach(function (a) { return map[MakerJs.pathType.Line]('', a, layer, className, null, false, null); });
7555 }
7556 function beginModel(id, modelContext) {
7557 modelGroup.attrs = { id: id };
7558 append(modelGroup.getOpeningTag(false), modelContext.layer);
7559 }
7560 function endModel(modelContext) {
7561 append(modelGroup.getClosingTag(), modelContext.layer);
7562 }
7563 var modelGroup = new exporter.XmlTag('g');
7564 var walkOptions = {
7565 beforeChildWalk: function (walkedModel) {
7566 beginModel(walkedModel.childId, walkedModel.childModel);
7567 return true;
7568 },
7569 onPath: function (walkedPath) {
7570 var fn = map[walkedPath.pathContext.type];
7571 if (fn) {
7572 var offset = MakerJs.point.add(fixPoint(walkedPath.offset), opts.origin);
7573 fn(walkedPath.pathId, fixPath(walkedPath.pathContext, offset), walkedPath.layer, null, walkedPath.route, opts.annotate, opts.flow);
7574 }
7575 },
7576 afterChildWalk: function (walkedModel) {
7577 endModel(walkedModel.childModel);
7578 }
7579 };
7580 beginModel('0', modelToExport);
7581 MakerJs.model.walk(modelToExport, walkOptions);
7582 //export layers as groups
7583 for (var layerId2 in layers) {
7584 var layerGroup = new exporter.XmlTag('g', { id: layerId2 });
7585 addSvgAttrs(layerGroup.attrs, colorLayerOptions(layerId2));
7586 for (var i = 0; i < layers[layerId2].length; i++) {
7587 layerGroup.innerText += layers[layerId2][i];
7588 }
7589 layerGroup.innerTextEscaped = true;
7590 append(layerGroup.toString());
7591 }
7592 }
7593 var captionTags = captions.map(function (caption) {
7594 var anchor = fixPath(caption.anchor, opts.origin);
7595 var center = MakerJs.point.rounded(MakerJs.point.middle(anchor), opts.accuracy);
7596 var tag = new exporter.XmlTag('text', {
7597 "alignment-baseline": "middle",
7598 "text-anchor": "middle",
7599 "transform": "rotate(" + MakerJs.angle.ofLineInDegrees(anchor) + "," + center[0] + "," + center[1] + ")",
7600 "x": center[0],
7601 "y": center[1]
7602 });
7603 tag.innerText = caption.text;
7604 return tag.toString();
7605 });
7606 if (captionTags.length) {
7607 var captionGroup = new exporter.XmlTag('g', { "id": "captions" });
7608 captionGroup.innerText = captionTags.join('');
7609 captionGroup.innerTextEscaped = true;
7610 append(captionGroup.toString());
7611 }
7612 append(svgGroup.getClosingTag());
7613 append(svgTag.getClosingTag());
7614 return elements.join('');
7615 }
7616 exporter.toSVG = toSVG;
7617 /**
7618 * @private
7619 */
7620 function svgCircleData(radius, accuracy, clockwiseCircle) {
7621 var r = MakerJs.round(radius, accuracy);
7622 var d = ['m', -r, 0];
7623 function halfCircle(sign) {
7624 d.push('a');
7625 svgArcData(d, r, [2 * r * sign, 0], accuracy, false, !clockwiseCircle);
7626 }
7627 halfCircle(1);
7628 halfCircle(-1);
7629 d.push('z');
7630 return d;
7631 }
7632 /**
7633 * @private
7634 */
7635 function svgBezierData(d, seed, accuracy, reversed) {
7636 if (seed.controls.length === 1) {
7637 d.push('Q', MakerJs.round(seed.controls[0][0], accuracy), MakerJs.round(seed.controls[0][1], accuracy));
7638 }
7639 else {
7640 var controls = reversed ? [seed.controls[1], seed.controls[0]] : seed.controls;
7641 d.push('C', MakerJs.round(controls[0][0], accuracy), MakerJs.round(controls[0][1], accuracy), MakerJs.round(controls[1][0], accuracy), MakerJs.round(controls[1][1], accuracy));
7642 }
7643 var final = reversed ? seed.origin : seed.end;
7644 d.push(MakerJs.round(final[0], accuracy), MakerJs.round(final[1], accuracy));
7645 }
7646 /**
7647 * @private
7648 */
7649 function svgArcData(d, radius, endPoint, accuracy, largeArc, increasing) {
7650 var r = MakerJs.round(radius, accuracy);
7651 var end = endPoint;
7652 d.push(r, r);
7653 d.push(0); //0 = x-axis rotation
7654 d.push(largeArc ? 1 : 0); //large arc=1, small arc=0
7655 d.push(increasing ? 0 : 1); //sweep-flag 0=increasing, 1=decreasing
7656 d.push(MakerJs.round(end[0], accuracy), MakerJs.round(end[1], accuracy));
7657 }
7658 /**
7659 * Map of MakerJs unit system to SVG unit system
7660 */
7661 exporter.svgUnit = {};
7662 //SVG Coordinate Systems, Transformations and Units documentation:
7663 //http://www.w3.org/TR/SVG/coords.html
7664 //The supported length unit identifiers are: em, ex, px, pt, pc, cm, mm, in, and percentages.
7665 exporter.svgUnit[MakerJs.unitType.Inch] = { svgUnitType: "in", scaleConversion: 1 };
7666 exporter.svgUnit[MakerJs.unitType.Millimeter] = { svgUnitType: "mm", scaleConversion: 1 };
7667 exporter.svgUnit[MakerJs.unitType.Centimeter] = { svgUnitType: "cm", scaleConversion: 1 };
7668 //Add conversions for all unitTypes
7669 exporter.svgUnit[MakerJs.unitType.Foot] = { svgUnitType: "in", scaleConversion: 12 };
7670 exporter.svgUnit[MakerJs.unitType.Meter] = { svgUnitType: "cm", scaleConversion: 100 };
7671 })(exporter = MakerJs.exporter || (MakerJs.exporter = {}));
7672})(MakerJs || (MakerJs = {}));
7673(function (MakerJs) {
7674 var importer;
7675 (function (importer) {
7676 /**
7677 * Create a model from SVG path data.
7678 *
7679 * @param pathData SVG path data.
7680 * @param options ISVGImportOptions object.
7681 * @param options.bezierAccuracy Optional accuracy of Bezier curves.
7682 * @returns An IModel object.
7683 */
7684 function fromSVGPathData(pathData, options) {
7685 if (options === void 0) { options = {}; }
7686 var result = {};
7687 function addPath(p) {
7688 if (!result.paths) {
7689 result.paths = {};
7690 }
7691 result.paths['p_' + ++pathCount] = p;
7692 }
7693 function addModel(m) {
7694 if (!result.models) {
7695 result.models = {};
7696 }
7697 result.models['p_' + ++pathCount] = m;
7698 }
7699 function getPoint(cmd, offset) {
7700 if (offset === void 0) { offset = 0; }
7701 var p = MakerJs.point.mirror([cmd.data[0 + offset], cmd.data[1 + offset]], false, true);
7702 if (cmd.absolute) {
7703 return p;
7704 }
7705 else {
7706 return MakerJs.point.add(p, cmd.from);
7707 }
7708 }
7709 function lineTo(cmd, end) {
7710 if (!MakerJs.measure.isPointEqual(cmd.from, end)) {
7711 addPath(new MakerJs.paths.Line(cmd.from, end));
7712 }
7713 return end;
7714 }
7715 var map = {};
7716 map['M'] = function (cmd) {
7717 firstPoint = getPoint(cmd);
7718 return firstPoint;
7719 };
7720 map['Z'] = function (cmd) {
7721 return lineTo(cmd, firstPoint);
7722 };
7723 map['H'] = function (cmd) {
7724 var end = MakerJs.point.clone(cmd.from);
7725 if (cmd.absolute) {
7726 end[0] = cmd.data[0];
7727 }
7728 else {
7729 end[0] += cmd.data[0];
7730 }
7731 return lineTo(cmd, end);
7732 };
7733 map['V'] = function (cmd) {
7734 var end = MakerJs.point.clone(cmd.from);
7735 //subtract to mirror on y axis: SVG coords
7736 if (cmd.absolute) {
7737 end[1] = -cmd.data[0];
7738 }
7739 else {
7740 end[1] -= cmd.data[0];
7741 }
7742 return lineTo(cmd, end);
7743 };
7744 map['L'] = function (cmd) {
7745 var end = getPoint(cmd);
7746 return lineTo(cmd, end);
7747 };
7748 map['A'] = function (cmd) {
7749 var rx = cmd.data[0];
7750 var ry = cmd.data[1];
7751 var rotation = cmd.data[2];
7752 var large = cmd.data[3] === 1;
7753 var decreasing = cmd.data[4] === 1;
7754 var end = getPoint(cmd, 5);
7755 var elliptic = rx !== ry;
7756 //first, rotate so we are dealing with a zero angle x-axis
7757 var xAxis = new MakerJs.paths.Line(cmd.from, MakerJs.point.rotate(end, rotation, cmd.from));
7758 //next, un-distort any ellipse back into a circle in terms of x axis
7759 if (elliptic) {
7760 xAxis = MakerJs.path.distort(xAxis, 1, rx / ry);
7761 }
7762 //now create an arc, making sure we use the large and decreasing flags
7763 var arc = new MakerJs.paths.Arc(xAxis.origin, xAxis.end, rx, large, decreasing);
7764 if (elliptic) {
7765 //scale up if radius was insufficient.
7766 if (rx < arc.radius) {
7767 var scaleUp = arc.radius / rx;
7768 rx *= scaleUp;
7769 ry *= scaleUp;
7770 }
7771 //create an elliptical arc, this will re-distort
7772 var e = new MakerJs.models.EllipticArc(arc, 1, ry / rx, options.bezierAccuracy);
7773 //un-rotate back to where it should be.
7774 MakerJs.model.rotate(e, -rotation, cmd.from);
7775 addModel(e);
7776 }
7777 else {
7778 //just use the arc
7779 //un-rotate back to where it should be.
7780 MakerJs.path.rotate(arc, -rotation, cmd.from);
7781 addPath(arc);
7782 }
7783 return end;
7784 };
7785 map['C'] = function (cmd) {
7786 var control1 = getPoint(cmd, 0);
7787 var control2 = getPoint(cmd, 2);
7788 var end = getPoint(cmd, 4);
7789 addModel(new MakerJs.models.BezierCurve(cmd.from, control1, control2, end, options.bezierAccuracy));
7790 return end;
7791 };
7792 map['S'] = function (cmd) {
7793 var control1;
7794 var prevControl2;
7795 if (cmd.prev.command === 'C') {
7796 prevControl2 = getPoint(cmd.prev, 2);
7797 control1 = MakerJs.point.rotate(prevControl2, 180, cmd.from);
7798 }
7799 else if (cmd.prev.command === 'S') {
7800 prevControl2 = getPoint(cmd.prev, 0);
7801 control1 = MakerJs.point.rotate(prevControl2, 180, cmd.from);
7802 }
7803 else {
7804 control1 = cmd.from;
7805 }
7806 var control2 = getPoint(cmd, 0);
7807 var end = getPoint(cmd, 2);
7808 addModel(new MakerJs.models.BezierCurve(cmd.from, control1, control2, end, options.bezierAccuracy));
7809 return end;
7810 };
7811 map['Q'] = function (cmd) {
7812 var control = getPoint(cmd, 0);
7813 var end = getPoint(cmd, 2);
7814 addModel(new MakerJs.models.BezierCurve(cmd.from, control, end, options.bezierAccuracy));
7815 return end;
7816 };
7817 map['T'] = function (cmd) {
7818 var control;
7819 var prevControl;
7820 if (cmd.prev.command === 'Q') {
7821 prevControl = getPoint(cmd.prev, 0);
7822 control = MakerJs.point.rotate(prevControl, 180, cmd.from);
7823 }
7824 else if (cmd.prev.command === 'T') {
7825 prevControl = getPoint(cmd.prev, 2); //see below *
7826 control = MakerJs.point.rotate(prevControl, 180, cmd.from);
7827 }
7828 else {
7829 control = cmd.from;
7830 }
7831 //* save the control point in the data list, will be accessible from index 2
7832 var p = MakerJs.point.mirror(control, false, true);
7833 cmd.data.push.apply(cmd.data, p);
7834 var end = getPoint(cmd, 0);
7835 addModel(new MakerJs.models.BezierCurve(cmd.from, control, end, options.bezierAccuracy));
7836 return end;
7837 };
7838 var firstPoint = [0, 0];
7839 var currPoint = [0, 0];
7840 var pathCount = 0;
7841 var prevCommand;
7842 var regexpCommands = /([achlmqstvz])([0-9e\.,\+-\s]*)/ig;
7843 var commandMatches;
7844 while ((commandMatches = regexpCommands.exec(pathData)) !== null) {
7845 if (commandMatches.index === regexpCommands.lastIndex) {
7846 regexpCommands.lastIndex++;
7847 }
7848 var command = commandMatches[1]; //0 = command and data, 1 = command, 2 = data
7849 var dataString = commandMatches[2];
7850 var currCmd = {
7851 command: command.toUpperCase(),
7852 data: [],
7853 from: currPoint,
7854 prev: prevCommand
7855 };
7856 if (command === currCmd.command) {
7857 currCmd.absolute = true;
7858 }
7859 currCmd.data = importer.parseNumericList(dataString);
7860 var fn = map[currCmd.command];
7861 if (fn) {
7862 currPoint = fn(currCmd);
7863 }
7864 prevCommand = currCmd;
7865 }
7866 return result;
7867 }
7868 importer.fromSVGPathData = fromSVGPathData;
7869 })(importer = MakerJs.importer || (MakerJs.importer = {}));
7870})(MakerJs || (MakerJs = {}));
7871var MakerJs;
7872(function (MakerJs) {
7873 var layout;
7874 (function (layout) {
7875 /**
7876 * @private
7877 */
7878 function getChildPlacement(parentModel, baseline) {
7879 //measure everything and cache the results
7880 var atlas = new MakerJs.measure.Atlas(parentModel);
7881 var measureParent = MakerJs.measure.modelExtents(parentModel, atlas);
7882 //measure height of the model from the baseline 0
7883 var parentTop = measureParent.high[1];
7884 var cpa = [];
7885 var xMap = {};
7886 var walkOptions = {
7887 beforeChildWalk: function (context) {
7888 var child = context.childModel;
7889 //get cached measurement of the child
7890 var m = atlas.modelMap[context.routeKey];
7891 if (!m)
7892 return;
7893 var childMeasure = MakerJs.measure.augment(m);
7894 //set a new origin at the x-center and y-baseline of the child
7895 MakerJs.model.originate(child, [childMeasure.center[0], parentTop * baseline]);
7896 //get the x-center of the child
7897 var x = child.origin[0] - measureParent.low[0];
7898 xMap[context.childId] = x;
7899 //get the x-center of the child as a percentage
7900 var xRatio = x / measureParent.width;
7901 cpa.push({ childId: context.childId, xRatio: xRatio });
7902 //do not walk the grandchildren. This is only for immediate children of the parentModel.
7903 return false;
7904 }
7905 };
7906 MakerJs.model.walk(parentModel, walkOptions);
7907 cpa.sort(function (a, b) { return a.xRatio - b.xRatio; });
7908 var first = cpa[0];
7909 var last = cpa[cpa.length - 1];
7910 var min = first.xRatio;
7911 var max = last.xRatio;
7912 var span = max - min;
7913 cpa.forEach(function (cp) { return cp.xRatio = (cp.xRatio - min) / span; });
7914 return {
7915 cpa: cpa,
7916 firstX: xMap[first.childId],
7917 lastX: measureParent.width - xMap[last.childId]
7918 };
7919 }
7920 /**
7921 * @private
7922 */
7923 function moveAndRotate(parentModel, cpa, rotate) {
7924 cpa.forEach(function (cp) {
7925 var child = parentModel.models[cp.childId];
7926 //move the child to the new location
7927 child.origin = cp.origin;
7928 //rotate the child
7929 if (rotate)
7930 MakerJs.model.rotate(child, cp.angle, cp.origin);
7931 });
7932 }
7933 /**
7934 * @private
7935 */
7936 var onPathMap = {};
7937 onPathMap[MakerJs.pathType.Arc] = function (arc, reversed, cpa) {
7938 var arcSpan = MakerJs.angle.ofArcSpan(arc);
7939 cpa.forEach(function (p) { return p.angle = reversed ? arc.endAngle - p.xRatio * arcSpan - 90 : arc.startAngle + p.xRatio * arcSpan + 90; });
7940 };
7941 onPathMap[MakerJs.pathType.Line] = function (line, reversed, cpa) {
7942 var lineAngle = MakerJs.angle.ofLineInDegrees(line);
7943 cpa.forEach(function (p) { return p.angle = lineAngle; });
7944 };
7945 /**
7946 * Layout the children of a model along a path.
7947 * The x-position of each child will be projected onto the path so that the proportion between children is maintained.
7948 * Each child will be rotated such that it will be perpendicular to the path at the child's x-center.
7949 *
7950 * @param parentModel The model containing children to lay out.
7951 * @param onPath The path on which to lay out.
7952 * @param baseline Numeric percentage value of vertical displacement from the path. Default is zero.
7953 * @param reversed Flag to travel along the path in reverse. Default is false.
7954 * @param contain Flag to contain the children layout within the length of the path. Default is false.
7955 * @param rotate Flag to rotate the child to perpendicular. Default is true.
7956 * @returns The parentModel, for cascading.
7957 */
7958 function childrenOnPath(parentModel, onPath, baseline, reversed, contain, rotate) {
7959 if (baseline === void 0) { baseline = 0; }
7960 if (reversed === void 0) { reversed = false; }
7961 if (contain === void 0) { contain = false; }
7962 if (rotate === void 0) { rotate = true; }
7963 var result = getChildPlacement(parentModel, baseline);
7964 var cpa = result.cpa;
7965 var chosenPath = onPath;
7966 if (contain) {
7967 //see if we need to clip
7968 var onPathLength = MakerJs.measure.pathLength(onPath);
7969 if (result.firstX + result.lastX < onPathLength) {
7970 chosenPath = MakerJs.path.clone(onPath);
7971 MakerJs.path.alterLength(chosenPath, -result.firstX, true);
7972 MakerJs.path.alterLength(chosenPath, -result.lastX);
7973 }
7974 }
7975 cpa.forEach(function (p) { return p.origin = MakerJs.point.middle(chosenPath, reversed ? 1 - p.xRatio : p.xRatio); });
7976 var fn = onPathMap[chosenPath.type];
7977 if (fn) {
7978 fn(chosenPath, reversed, cpa);
7979 }
7980 moveAndRotate(parentModel, cpa, rotate);
7981 return parentModel;
7982 }
7983 layout.childrenOnPath = childrenOnPath;
7984 /**
7985 * @private
7986 */
7987 function miterAngles(points, offsetAngle) {
7988 var arc = new MakerJs.paths.Arc([0, 0], 0, 0, 0);
7989 return points.map(function (p, i) {
7990 var a;
7991 if (i === 0) {
7992 a = MakerJs.angle.ofPointInDegrees(p, points[i + 1]) + 90;
7993 }
7994 else if (i === points.length - 1) {
7995 a = MakerJs.angle.ofPointInDegrees(points[i - 1], p) + 90;
7996 }
7997 else {
7998 arc.origin = p;
7999 arc.startAngle = MakerJs.angle.ofPointInDegrees(p, points[i + 1]);
8000 arc.endAngle = MakerJs.angle.ofPointInDegrees(p, points[i - 1]);
8001 a = MakerJs.angle.ofArcMiddle(arc);
8002 }
8003 return a + offsetAngle;
8004 });
8005 }
8006 /**
8007 * Layout the children of a model along a chain.
8008 * The x-position of each child will be projected onto the chain so that the proportion between children is maintained.
8009 * The projected positions of the children will become an array of points that approximate the chain.
8010 * Each child will be rotated such that it will be mitered according to the vertex angles formed by this series of points.
8011 *
8012 * @param parentModel The model containing children to lay out.
8013 * @param onChain The chain on which to lay out.
8014 * @param baseline Numeric percentage value of vertical displacement from the chain. Default is zero.
8015 * @param reversed Flag to travel along the chain in reverse. Default is false.
8016 * @param contain Flag to contain the children layout within the length of the chain. Default is false.
8017 * @param rotate Flag to rotate the child to mitered angle. Default is true.
8018 * @returns The parentModel, for cascading.
8019 */
8020 function childrenOnChain(parentModel, onChain, baseline, reversed, contain, rotated) {
8021 if (baseline === void 0) { baseline = 0; }
8022 if (reversed === void 0) { reversed = false; }
8023 if (contain === void 0) { contain = false; }
8024 if (rotated === void 0) { rotated = true; }
8025 var result = getChildPlacement(parentModel, baseline);
8026 var cpa = result.cpa;
8027 var chainLength = onChain.pathLength;
8028 if (contain)
8029 chainLength -= result.firstX + result.lastX;
8030 var absolutes = cpa.map(function (cp) { return (reversed ? 1 - cp.xRatio : cp.xRatio) * chainLength; });
8031 var relatives;
8032 if (reversed)
8033 absolutes.reverse();
8034 relatives = absolutes.map(function (ab, i) { return Math.abs(ab - (i == 0 ? 0 : absolutes[i - 1])); });
8035 if (contain) {
8036 relatives[0] += reversed ? result.lastX : result.firstX;
8037 }
8038 else {
8039 relatives.shift();
8040 }
8041 //chain.toPoints always follows the chain in its order, from beginning to end. This is why we needed to contort the points input
8042 var points = MakerJs.chain.toPoints(onChain, relatives);
8043 if (points.length < cpa.length) {
8044 //add last point of chain, since our distances exceeded the chain
8045 var endLink = onChain.links[onChain.links.length - 1];
8046 points.push(endLink.endPoints[endLink.reversed ? 0 : 1]);
8047 }
8048 if (contain)
8049 points.shift(); //delete the first point which is the beginning of the chain
8050 if (reversed)
8051 points.reverse();
8052 var angles = miterAngles(points, -90);
8053 cpa.forEach(function (cp, i) {
8054 cp.angle = angles[i];
8055 cp.origin = points[i];
8056 });
8057 moveAndRotate(parentModel, cpa, rotated);
8058 return parentModel;
8059 }
8060 layout.childrenOnChain = childrenOnChain;
8061 /**
8062 * Layout clones in a radial format.
8063 *
8064 * Example:
8065 * ```
8066 * //daisy petals
8067 * var makerjs = require('makerjs');
8068 *
8069 * var belt = new makerjs.models.Belt(5, 50, 20);
8070 *
8071 * makerjs.model.move(belt, [25, 0]);
8072 *
8073 * var petals = makerjs.layout.cloneToRadial(belt, 8, 45);
8074 *
8075 * document.write(makerjs.exporter.toSVG(petals));
8076 * ```
8077 *
8078 * @param itemToClone: Either a model or a path object.
8079 * @param count Number of clones in the radial result.
8080 * @param angleInDegrees angle of rotation between clones..
8081 * @returns A new model with clones in a radial format.
8082 */
8083 function cloneToRadial(itemToClone, count, angleInDegrees, rotationOrigin) {
8084 var result = {};
8085 var add;
8086 var rotateFn;
8087 if (MakerJs.isModel(itemToClone)) {
8088 add = result.models = {};
8089 rotateFn = MakerJs.model.rotate;
8090 }
8091 else {
8092 add = result.paths = {};
8093 rotateFn = MakerJs.path.rotate;
8094 }
8095 for (var i = 0; i < count; i++) {
8096 add[i] = rotateFn(MakerJs.cloneObject(itemToClone), i * angleInDegrees, rotationOrigin);
8097 }
8098 return result;
8099 }
8100 layout.cloneToRadial = cloneToRadial;
8101 /**
8102 * @private
8103 */
8104 function cloneTo(dimension, itemToClone, count, margin) {
8105 var result = {};
8106 var add;
8107 var measureFn;
8108 var moveFn;
8109 if (MakerJs.isModel(itemToClone)) {
8110 measureFn = MakerJs.measure.modelExtents;
8111 add = result.models = {};
8112 moveFn = MakerJs.model.move;
8113 }
8114 else {
8115 measureFn = MakerJs.measure.pathExtents;
8116 add = result.paths = {};
8117 moveFn = MakerJs.path.move;
8118 }
8119 var m = measureFn(itemToClone);
8120 var size = m.high[dimension] - m.low[dimension];
8121 for (var i = 0; i < count; i++) {
8122 var origin = [0, 0];
8123 origin[dimension] = i * (size + margin);
8124 add[i] = moveFn(MakerJs.cloneObject(itemToClone), origin);
8125 }
8126 return result;
8127 }
8128 /**
8129 * Layout clones in a column format.
8130 *
8131 * Example:
8132 * ```
8133 * //Grooves for a finger joint
8134 * var m = require('makerjs');
8135 *
8136 * var dogbone = new m.models.Dogbone(50, 20, 2, -1, false);
8137 *
8138 * var grooves = m.layout.cloneToColumn(dogbone, 5, 20);
8139 *
8140 * document.write(m.exporter.toSVG(grooves));
8141 * ```
8142 *
8143 * @param itemToClone: Either a model or a path object.
8144 * @param count Number of clones in the column.
8145 * @param margin Optional distance between each clone.
8146 * @returns A new model with clones in a column.
8147 */
8148 function cloneToColumn(itemToClone, count, margin) {
8149 if (margin === void 0) { margin = 0; }
8150 return cloneTo(1, itemToClone, count, margin);
8151 }
8152 layout.cloneToColumn = cloneToColumn;
8153 /**
8154 * Layout clones in a row format.
8155 *
8156 * Example:
8157 * ```
8158 * //Tongue and grooves for a box joint
8159 * var m = require('makerjs');
8160 * var tongueWidth = 60;
8161 * var grooveWidth = 50;
8162 * var grooveDepth = 30;
8163 * var groove = new m.models.Dogbone(grooveWidth, grooveDepth, 5, 0, true);
8164 *
8165 * groove.paths['leftTongue'] = new m.paths.Line([-tongueWidth / 2, 0], [0, 0]);
8166 * groove.paths['rightTongue'] = new m.paths.Line([grooveWidth, 0], [grooveWidth + tongueWidth / 2, 0]);
8167 *
8168 * var tongueAndGrooves = m.layout.cloneToRow(groove, 3);
8169 *
8170 * document.write(m.exporter.toSVG(tongueAndGrooves));
8171 * ```
8172 *
8173 * @param itemToClone: Either a model or a path object.
8174 * @param count Number of clones in the row.
8175 * @param margin Optional distance between each clone.
8176 * @returns A new model with clones in a row.
8177 */
8178 function cloneToRow(itemToClone, count, margin) {
8179 if (margin === void 0) { margin = 0; }
8180 return cloneTo(0, itemToClone, count, margin);
8181 }
8182 layout.cloneToRow = cloneToRow;
8183 /**
8184 * Layout clones in a grid format.
8185 *
8186 * Example:
8187 * ```
8188 * //Grid of squares
8189 * var m = require('makerjs');
8190 * var square = new m.models.Square(43);
8191 * var grid = m.layout.cloneToGrid(square, 5, 5, 7);
8192 * document.write(m.exporter.toSVG(grid));
8193 * ```
8194 *
8195 * @param itemToClone: Either a model or a path object.
8196 * @param xCount Number of columns in the grid.
8197 * @param yCount Number of rows in the grid.
8198 * @param margin Optional numeric distance between each clone. Can also be a 2 dimensional array of numbers, to specify distances in x and y dimensions.
8199 * @returns A new model with clones in a grid layout.
8200 */
8201 function cloneToGrid(itemToClone, xCount, yCount, margin) {
8202 var margins = getMargins(margin);
8203 return cloneToColumn(cloneToRow(itemToClone, xCount, margins[0]), yCount, margins[1]);
8204 }
8205 layout.cloneToGrid = cloneToGrid;
8206 /**
8207 * @private
8208 */
8209 function getMargins(margin) {
8210 if (Array.isArray(margin)) {
8211 return margin;
8212 }
8213 else {
8214 return [margin, margin];
8215 }
8216 }
8217 /**
8218 * @private
8219 */
8220 function cloneToAlternatingRows(itemToClone, xCount, yCount, spacingFn) {
8221 var modelToMeasure;
8222 if (MakerJs.isModel(itemToClone)) {
8223 modelToMeasure = itemToClone;
8224 }
8225 else {
8226 modelToMeasure = { paths: { "0": itemToClone } };
8227 }
8228 var spacing = spacingFn(modelToMeasure);
8229 var result = { models: {} };
8230 for (var i = 0; i < yCount; i++) {
8231 var i2 = i % 2;
8232 result.models[i] = MakerJs.model.move(cloneToRow(itemToClone, xCount + i2, spacing.xMargin), [i2 * spacing.x, i * spacing.y]);
8233 }
8234 return result;
8235 }
8236 /**
8237 * Layout clones in a brick format. Alternating rows will have an additional item in each row.
8238 *
8239 * Examples:
8240 * ```
8241 * //Brick wall
8242 * var m = require('makerjs');
8243 * var brick = new m.models.RoundRectangle(50, 30, 4);
8244 * var wall = m.layout.cloneToBrick(brick, 8, 6, 3);
8245 * document.write(m.exporter.toSVG(wall));
8246 * ```
8247 *
8248 * ```
8249 * //Fish scales
8250 * var m = require('makerjs');
8251 * var arc = new m.paths.Arc([0, 0], 50, 20, 160);
8252 * var scales = m.layout.cloneToBrick(arc, 8, 20);
8253 * document.write(m.exporter.toSVG(scales));
8254 * ```
8255 *
8256 * @param itemToClone: Either a model or a path object.
8257 * @param xCount Number of columns in the brick grid.
8258 * @param yCount Number of rows in the brick grid.
8259 * @param margin Optional numeric distance between each clone. Can also be a 2 dimensional array of numbers, to specify distances in x and y dimensions.
8260 * @returns A new model with clones in a brick layout.
8261 */
8262 function cloneToBrick(itemToClone, xCount, yCount, margin) {
8263 var margins = getMargins(margin);
8264 function spacing(modelToMeasure) {
8265 var m = MakerJs.measure.modelExtents(modelToMeasure);
8266 var xMargin = margins[0] || 0;
8267 var yMargin = margins[1] || 0;
8268 return { x: (m.width + xMargin) / -2, y: m.height + yMargin, xMargin: xMargin };
8269 }
8270 return cloneToAlternatingRows(itemToClone, xCount, yCount, spacing);
8271 }
8272 layout.cloneToBrick = cloneToBrick;
8273 /**
8274 * Layout clones in a honeycomb format. Alternating rows will have an additional item in each row.
8275 *
8276 * Examples:
8277 * ```
8278 * //Honeycomb
8279 * var m = require('makerjs');
8280 * var hex = new m.models.Polygon(6, 50, 30);
8281 * var pattern = m.layout.cloneToHoneycomb(hex, 8, 9, 10);
8282 * document.write(m.exporter.toSVG(pattern));
8283 * ```
8284 *
8285 * @param itemToClone: Either a model or a path object.
8286 * @param xCount Number of columns in the honeycomb grid.
8287 * @param yCount Number of rows in the honeycomb grid.
8288 * @param margin Optional distance between each clone.
8289 * @returns A new model with clones in a honeycomb layout.
8290 */
8291 function cloneToHoneycomb(itemToClone, xCount, yCount, margin) {
8292 if (margin === void 0) { margin = 0; }
8293 function spacing(modelToMeasure) {
8294 var hex = MakerJs.measure.boundingHexagon(modelToMeasure);
8295 var width = 2 * MakerJs.solvers.equilateralAltitude(hex.radius);
8296 var s = width + margin;
8297 return { x: s / -2, y: MakerJs.solvers.equilateralAltitude(s), xMargin: margin };
8298 }
8299 return cloneToAlternatingRows(itemToClone, xCount, yCount, spacing);
8300 }
8301 layout.cloneToHoneycomb = cloneToHoneycomb;
8302 })(layout = MakerJs.layout || (MakerJs.layout = {}));
8303})(MakerJs || (MakerJs = {}));
8304var MakerJs;
8305(function (MakerJs) {
8306 var models;
8307 (function (models) {
8308 /**
8309 * @private
8310 */
8311 var hasLib = false;
8312 /**
8313 * @private
8314 */
8315 function ensureBezierLib() {
8316 if (hasLib)
8317 return;
8318 try {
8319 var lib = Bezier.prototype;
8320 hasLib = true;
8321 }
8322 catch (e) {
8323 throw "Bezier library not found. If you are using Node, try running 'npm install' or if you are in the browser, download http://pomax.github.io/bezierjs/bezier.js to your website and add a script tag.";
8324 }
8325 }
8326 /**
8327 * @private
8328 */
8329 var scratch;
8330 /**
8331 * @private
8332 */
8333 function getScratch(seed) {
8334 var points = [seed.origin];
8335 points.push.apply(points, seed.controls);
8336 points.push(seed.end);
8337 var bezierJsPoints = points.map(function (p) {
8338 var bp = {
8339 x: p[0], y: p[1]
8340 };
8341 return bp;
8342 });
8343 if (!scratch) {
8344 ensureBezierLib();
8345 scratch = new Bezier(bezierJsPoints);
8346 }
8347 else {
8348 //invoke the constructor on the same object
8349 Bezier.apply(scratch, bezierJsPoints);
8350 }
8351 return scratch;
8352 }
8353 /**
8354 * @private
8355 */
8356 function BezierToSeed(b, range) {
8357 var points = b.points.map(getIPoint);
8358 var seed = new BezierSeed(points);
8359 if (range) {
8360 seed.parentRange = range;
8361 }
8362 return seed;
8363 }
8364 /**
8365 * @private
8366 */
8367 function seedToBezier(seed) {
8368 var coords = [];
8369 coords.push.apply(coords, seed.origin);
8370 coords.push.apply(coords, seed.controls[0]);
8371 if (seed.controls.length > 1) {
8372 coords.push.apply(coords, seed.controls[1]);
8373 }
8374 coords.push.apply(coords, seed.end);
8375 ensureBezierLib();
8376 return new Bezier(coords);
8377 }
8378 /**
8379 * @private
8380 */
8381 function getExtrema(b) {
8382 var extrema = b.extrema().values
8383 //round the numbers so we can compare them to each other
8384 .map(function (m) { return MakerJs.round(m); })
8385 //remove duplicates
8386 .filter(function (value, index, self) { return self.indexOf(value) === index; })
8387 //and put them in order
8388 .sort();
8389 if (extrema.length === 0)
8390 return [0, 1];
8391 //ensure leading zero
8392 if (extrema[0] !== 0) {
8393 extrema.unshift(0);
8394 }
8395 //ensure ending 1
8396 if (extrema[extrema.length - 1] !== 1) {
8397 extrema.push(1);
8398 }
8399 return extrema;
8400 }
8401 /**
8402 * @private
8403 */
8404 function getIPoint(p) {
8405 return [p.x, p.y];
8406 }
8407 /**
8408 * @private
8409 */
8410 var TPoint = /** @class */ (function () {
8411 function TPoint(b, t, offset) {
8412 this.t = t;
8413 this.point = MakerJs.point.add(getIPoint(b.get(t)), offset);
8414 }
8415 return TPoint;
8416 }());
8417 /**
8418 * @private
8419 */
8420 function getError(b, startT, endT, arc, arcReversed) {
8421 var tSpan = endT - startT;
8422 function m(ratio) {
8423 var t = startT + tSpan * ratio;
8424 var bp = getIPoint(b.get(t));
8425 var ap = MakerJs.point.middle(arc, arcReversed ? 1 - ratio : ratio);
8426 return MakerJs.measure.pointDistance(ap, bp);
8427 }
8428 return m(0.25) + m(0.75);
8429 }
8430 /**
8431 * @private
8432 */
8433 function getLargestArc(b, startT, endT, accuracy) {
8434 var arc, lastGoodArc;
8435 var start = new TPoint(b, startT);
8436 var end = new TPoint(b, endT);
8437 var upper = end;
8438 var lower = start;
8439 var count = 0;
8440 var test = upper;
8441 var reversed;
8442 while (count < 100) {
8443 var middle = getIPoint(b.get((start.t + test.t) / 2));
8444 //if the 3 points are linear, this may throw
8445 try {
8446 arc = new MakerJs.paths.Arc(start.point, middle, test.point);
8447 }
8448 catch (e) {
8449 if (lastGoodArc) {
8450 return lastGoodArc;
8451 }
8452 else {
8453 break;
8454 }
8455 }
8456 //only need to test once to see if this arc is polar / clockwise
8457 if (reversed === undefined) {
8458 reversed = MakerJs.measure.isPointEqual(start.point, MakerJs.point.fromAngleOnCircle(arc.endAngle, arc));
8459 }
8460 //now we have a valid arc, measure the error.
8461 var error = getError(b, startT, test.t, arc, reversed);
8462 //if error is within accuracy, this becomes the lower
8463 if (error <= accuracy) {
8464 arc.bezierData = {
8465 startT: startT,
8466 endT: test.t
8467 };
8468 lower = test;
8469 lastGoodArc = arc;
8470 }
8471 else {
8472 upper = test;
8473 }
8474 //exit if lower is the end
8475 if (lower.t === upper.t || (lastGoodArc && (lastGoodArc !== arc) && (MakerJs.angle.ofArcSpan(arc) - MakerJs.angle.ofArcSpan(lastGoodArc)) < .5)) {
8476 return lastGoodArc;
8477 }
8478 count++;
8479 test = new TPoint(b, (lower.t + upper.t) / 2);
8480 }
8481 //arc failed, so return a line
8482 var line = new MakerJs.paths.Line(start.point, test.point);
8483 line.bezierData = {
8484 startT: startT,
8485 endT: test.t
8486 };
8487 return line;
8488 }
8489 /**
8490 * @private
8491 */
8492 function getArcs(bc, b, accuracy, startT, endT, base) {
8493 var added = 0;
8494 var arc;
8495 while (startT < endT) {
8496 arc = getLargestArc(b, startT, endT, accuracy);
8497 //add an arc
8498 startT = arc.bezierData.endT;
8499 var len = MakerJs.measure.pathLength(arc);
8500 if (len < .0001) {
8501 continue;
8502 }
8503 bc.paths[arc.type + '_' + (base + added)] = arc;
8504 added++;
8505 }
8506 return added;
8507 }
8508 /**
8509 * @private
8510 */
8511 function getActualBezierRange(curve, arc, endpoints, offset) {
8512 var b = getScratch(curve.seed);
8513 var tPoints = [arc.bezierData.startT, arc.bezierData.endT].map(function (t) { return new TPoint(b, t, offset); });
8514 var ends = endpoints.slice();
8515 //clipped arcs will still have endpoints closer to the original endpoints
8516 var endpointDistancetoStart = ends.map(function (e) { return MakerJs.measure.pointDistance(e, tPoints[0].point); });
8517 if (endpointDistancetoStart[0] > endpointDistancetoStart[1])
8518 ends.reverse();
8519 for (var i = 2; i--;) {
8520 if (!MakerJs.measure.isPointEqual(ends[i], tPoints[i].point)) {
8521 return null;
8522 }
8523 }
8524 return arc.bezierData;
8525 }
8526 /**
8527 * @private
8528 */
8529 function getChainBezierRange(curve, c, layer, addToLayer) {
8530 var endLinks = [c.links[0], c.links[c.links.length - 1]];
8531 if (endLinks[0].walkedPath.pathContext.bezierData.startT > endLinks[1].walkedPath.pathContext.bezierData.startT) {
8532 MakerJs.chain.reverse(c);
8533 endLinks.reverse();
8534 }
8535 var actualBezierRanges = endLinks.map(function (endLink) { return getActualBezierRange(curve, endLink.walkedPath.pathContext, endLink.endPoints, endLink.walkedPath.offset); });
8536 var result = {
8537 startT: actualBezierRanges[0] ? actualBezierRanges[0].startT : null,
8538 endT: actualBezierRanges[1] ? actualBezierRanges[1].endT : null
8539 };
8540 if (result.startT !== null && result.endT !== null) {
8541 return result;
8542 }
8543 else if (c.links.length > 2) {
8544 if (result.startT === null) {
8545 //exclude the first from the chain
8546 addToLayer(c.links[0].walkedPath.pathContext, layer, true);
8547 result.startT = c.links[1].walkedPath.pathContext.bezierData.startT;
8548 }
8549 if (result.endT === null) {
8550 //exclude the last from the chain
8551 addToLayer(c.links[c.links.length - 1].walkedPath.pathContext, layer, true);
8552 result.endT = c.links[c.links.length - 2].walkedPath.pathContext.bezierData.endT;
8553 }
8554 return result;
8555 }
8556 return null;
8557 }
8558 /**
8559 * @private
8560 * Class for bezier seed.
8561 */
8562 var BezierSeed = /** @class */ (function () {
8563 function BezierSeed() {
8564 var args = [];
8565 for (var _i = 0; _i < arguments.length; _i++) {
8566 args[_i] = arguments[_i];
8567 }
8568 this.type = MakerJs.pathType.BezierSeed;
8569 switch (args.length) {
8570 case 1: //point array
8571 var points = args[0];
8572 this.origin = points[0];
8573 if (points.length === 3) {
8574 this.controls = [points[1]];
8575 this.end = points[2];
8576 }
8577 else if (points.length === 4) {
8578 this.controls = [points[1], points[2]];
8579 this.end = points[3];
8580 }
8581 else {
8582 this.end = points[1];
8583 }
8584 break;
8585 case 3: //quadratic or cubic
8586 if (Array.isArray(args[1])) {
8587 this.controls = args[1];
8588 }
8589 else {
8590 this.controls = [args[1]];
8591 }
8592 this.end = args[2];
8593 break;
8594 case 4: //cubic params
8595 this.controls = [args[1], args[2]];
8596 this.end = args[3];
8597 break;
8598 }
8599 }
8600 return BezierSeed;
8601 }());
8602 var BezierCurve = /** @class */ (function () {
8603 function BezierCurve() {
8604 var args = [];
8605 for (var _i = 0; _i < arguments.length; _i++) {
8606 args[_i] = arguments[_i];
8607 }
8608 this.type = BezierCurve.typeName;
8609 var isArrayArg0 = Array.isArray(args[0]);
8610 switch (args.length) {
8611 case 2:
8612 if (isArrayArg0) {
8613 this.accuracy = args[1];
8614 }
8615 else {
8616 //seed
8617 this.seed = args[0];
8618 this.accuracy = args[1];
8619 break;
8620 }
8621 //fall through to point array
8622 case 1: //point array or seed
8623 if (isArrayArg0) {
8624 var points = args[0];
8625 this.seed = new BezierSeed(points);
8626 }
8627 else {
8628 this.seed = args[0];
8629 }
8630 break;
8631 default:
8632 switch (args.length) {
8633 case 4:
8634 if (MakerJs.isPoint(args[3])) {
8635 this.seed = new BezierSeed(args);
8636 break;
8637 }
8638 else {
8639 this.accuracy = args[3];
8640 //fall through
8641 }
8642 case 3:
8643 if (isArrayArg0) {
8644 this.seed = new BezierSeed(args.slice(0, 3));
8645 }
8646 break;
8647 case 5:
8648 this.accuracy = args[4];
8649 this.seed = new BezierSeed(args.slice(0, 4));
8650 break;
8651 }
8652 break;
8653 }
8654 this.paths = {};
8655 if (MakerJs.measure.isBezierSeedLinear(this.seed)) {
8656 //use a line and exit
8657 var line = new MakerJs.paths.Line(MakerJs.point.clone(this.seed.origin), MakerJs.point.clone(this.seed.end));
8658 line.bezierData = {
8659 startT: 0,
8660 endT: 1
8661 };
8662 this.paths = {
8663 "0": line
8664 };
8665 return;
8666 }
8667 var b = seedToBezier(this.seed);
8668 var extrema = getExtrema(b);
8669 this.paths = {};
8670 //use arcs
8671 if (!this.accuracy) {
8672 //get a default accuracy relative to the size of the bezier
8673 var len = b.length();
8674 //set the default to be a combination of fast rendering and good smoothing.
8675 this.accuracy = len / 100;
8676 }
8677 var count = 0;
8678 for (var i = 1; i < extrema.length; i++) {
8679 var extremaSpan = extrema[i] - extrema[i - 1];
8680 count += getArcs(this, b, this.accuracy * extremaSpan, extrema[i - 1], extrema[i], count);
8681 }
8682 }
8683 BezierCurve.getBezierSeeds = function (curve, options) {
8684 if (options === void 0) { options = {}; }
8685 options.shallow = true;
8686 options.unifyBeziers = false;
8687 var seedsByLayer = {};
8688 var addToLayer = function (pathToAdd, layer, clone) {
8689 if (clone === void 0) { clone = false; }
8690 if (!seedsByLayer[layer]) {
8691 seedsByLayer[layer] = [];
8692 }
8693 seedsByLayer[layer].push(clone ? MakerJs.path.clone(pathToAdd) : pathToAdd);
8694 };
8695 MakerJs.model.findChains(curve, function (chains, loose, layer) {
8696 chains.forEach(function (c) {
8697 var range = getChainBezierRange(curve, c, layer, addToLayer);
8698 if (range) {
8699 var b = getScratch(curve.seed);
8700 var piece = b.split(range.startT, range.endT);
8701 addToLayer(BezierToSeed(piece), layer);
8702 }
8703 else {
8704 c.links.forEach(function (link) { return addToLayer(link.walkedPath.pathContext, layer, true); });
8705 }
8706 });
8707 loose.forEach(function (wp) {
8708 if (wp.pathContext.type === MakerJs.pathType.Line) {
8709 //bezier is linear
8710 return addToLayer(wp.pathContext, layer, true);
8711 }
8712 var range = getActualBezierRange(curve, wp.pathContext, MakerJs.point.fromPathEnds(wp.pathContext), wp.offset);
8713 if (range) {
8714 var b = getScratch(curve.seed);
8715 var piece = b.split(range.startT, range.endT);
8716 addToLayer(BezierToSeed(piece), layer);
8717 }
8718 else {
8719 addToLayer(wp.pathContext, layer, true);
8720 }
8721 });
8722 }, options);
8723 if (options.byLayers) {
8724 return seedsByLayer;
8725 }
8726 else {
8727 return seedsByLayer[''];
8728 }
8729 };
8730 BezierCurve.computeLength = function (seed) {
8731 var b = seedToBezier(seed);
8732 return b.length();
8733 };
8734 BezierCurve.computePoint = function (seed, t) {
8735 var s = getScratch(seed);
8736 var computedPoint = s.compute(t);
8737 return getIPoint(computedPoint);
8738 };
8739 BezierCurve.typeName = 'BezierCurve';
8740 return BezierCurve;
8741 }());
8742 models.BezierCurve = BezierCurve;
8743 BezierCurve.metaParameters = [
8744 {
8745 title: "points", type: "select", value: [
8746 [[100, 0], [-80, -60], [100, 220], [100, 60]],
8747 [[0, 0], [100, 0], [100, 100]],
8748 [[0, 0], [20, 0], [80, 100], [100, 100]]
8749 ]
8750 }
8751 ];
8752 })(models = MakerJs.models || (MakerJs.models = {}));
8753})(MakerJs || (MakerJs = {}));
8754var MakerJs;
8755(function (MakerJs) {
8756 var models;
8757 (function (models) {
8758 /**
8759 * @private
8760 * Our maximum circular arc span for accurate representation by a cubic curve.
8761 */
8762 var maxBezierArcspan = 45;
8763 /**
8764 * @private
8765 */
8766 function controlYForCircularCubic(arcSpanInRadians) {
8767 //from http://pomax.github.io/bezierinfo/#circles_cubic
8768 return 4 * (Math.tan(arcSpanInRadians / 4) / 3);
8769 }
8770 /**
8771 * @private
8772 */
8773 function controlPointsForCircularCubic(arc) {
8774 var arcSpan = MakerJs.angle.ofArcSpan(arc);
8775 //compute y for radius of 1
8776 var y = controlYForCircularCubic(MakerJs.angle.toRadians(arcSpan));
8777 //multiply by radius
8778 var c1 = [arc.radius, arc.radius * y];
8779 //get second control point by mirroring, then rotating
8780 var c2 = MakerJs.point.rotate(MakerJs.point.mirror(c1, false, true), arcSpan, [0, 0]);
8781 //rotate again to start angle, then offset by arc's origin
8782 return [c1, c2].map(function (p) { return MakerJs.point.add(arc.origin, MakerJs.point.rotate(p, arc.startAngle, [0, 0])); });
8783 }
8784 /**
8785 * @private
8786 */
8787 function bezierSeedFromArc(arc) {
8788 var span = MakerJs.angle.ofArcSpan(arc);
8789 if (span <= 90) {
8790 var endPoints = MakerJs.point.fromPathEnds(arc);
8791 var controls = controlPointsForCircularCubic(arc);
8792 return {
8793 type: MakerJs.pathType.BezierSeed,
8794 origin: endPoints[0],
8795 controls: controls,
8796 end: endPoints[1]
8797 };
8798 }
8799 return null;
8800 }
8801 var Ellipse = /** @class */ (function () {
8802 function Ellipse() {
8803 var args = [];
8804 for (var _i = 0; _i < arguments.length; _i++) {
8805 args[_i] = arguments[_i];
8806 }
8807 var _this = this;
8808 this.models = {};
8809 var n = 360 / maxBezierArcspan;
8810 var accuracy;
8811 var isPointArgs0 = MakerJs.isPoint(args[0]);
8812 var realArgs = function (numArgs) {
8813 switch (numArgs) {
8814 case 2:
8815 if (isPointArgs0) {
8816 //origin, radius
8817 _this.origin = args[0];
8818 }
8819 break;
8820 case 3:
8821 //origin, rx, ry
8822 _this.origin = args[0];
8823 break;
8824 case 4:
8825 //cx, cy, rx, ry
8826 _this.origin = [args[0], args[1]];
8827 break;
8828 }
8829 //construct a bezier approximation for an arc with radius of 1.
8830 var a = 360 / n;
8831 var arc = new MakerJs.paths.Arc([0, 0], 1, 0, a);
8832 //clone and rotate to complete a circle
8833 for (var i = 0; i < n; i++) {
8834 var seed = bezierSeedFromArc(arc);
8835 switch (numArgs) {
8836 case 1:
8837 //radius
8838 seed = MakerJs.path.scale(seed, args[0]);
8839 break;
8840 case 2:
8841 if (isPointArgs0) {
8842 //origin, radius
8843 seed = MakerJs.path.scale(seed, args[1]);
8844 }
8845 else {
8846 //rx, ry
8847 seed = MakerJs.path.distort(seed, args[0], args[1]);
8848 }
8849 break;
8850 case 3:
8851 //origin, rx, ry
8852 seed = MakerJs.path.distort(seed, args[1], args[2]);
8853 break;
8854 case 4:
8855 //cx, cy, rx, ry
8856 seed = MakerJs.path.distort(seed, args[2], args[3]);
8857 break;
8858 }
8859 _this.models['Curve_' + (1 + i)] = new models.BezierCurve(seed, accuracy);
8860 arc.startAngle += a;
8861 arc.endAngle += a;
8862 }
8863 };
8864 switch (args.length) {
8865 case 2:
8866 realArgs(2);
8867 break;
8868 case 3:
8869 if (isPointArgs0) {
8870 realArgs(3);
8871 }
8872 else {
8873 accuracy = args[2];
8874 realArgs(2);
8875 }
8876 break;
8877 case 4:
8878 if (isPointArgs0) {
8879 accuracy = args[3];
8880 realArgs(3);
8881 }
8882 else {
8883 realArgs(4);
8884 }
8885 break;
8886 case 5:
8887 accuracy = args[4];
8888 realArgs(4);
8889 break;
8890 }
8891 }
8892 return Ellipse;
8893 }());
8894 models.Ellipse = Ellipse;
8895 Ellipse.metaParameters = [
8896 { title: "radiusX", type: "range", min: 1, max: 50, value: 50 },
8897 { title: "radiusY", type: "range", min: 1, max: 50, value: 25 }
8898 ];
8899 var EllipticArc = /** @class */ (function () {
8900 function EllipticArc() {
8901 var args = [];
8902 for (var _i = 0; _i < arguments.length; _i++) {
8903 args[_i] = arguments[_i];
8904 }
8905 this.models = {};
8906 var arc;
8907 var accuracy;
8908 var distortX;
8909 var distortY;
8910 if (MakerJs.isPathArc(args[0])) {
8911 arc = args[0];
8912 distortX = args[1];
8913 distortY = args[2];
8914 accuracy = args[3];
8915 }
8916 else {
8917 arc = new MakerJs.paths.Arc([0, 0], 1, args[0], args[1]);
8918 distortX = args[2];
8919 distortY = args[3];
8920 accuracy = args[4];
8921 }
8922 var span = MakerJs.angle.ofArcSpan(arc);
8923 //split into equal chunks, no larger than max chunk size
8924 var count = Math.ceil(span / maxBezierArcspan);
8925 var subSpan = span / count;
8926 var subArc = MakerJs.path.clone(arc);
8927 for (var i = 0; i < count; i++) {
8928 subArc.startAngle = arc.startAngle + (i * subSpan);
8929 subArc.endAngle = subArc.startAngle + subSpan;
8930 var seed = bezierSeedFromArc(subArc);
8931 seed = MakerJs.path.distort(seed, distortX, distortY);
8932 this.models['Curve_' + (1 + i)] = new models.BezierCurve(seed, accuracy);
8933 }
8934 }
8935 return EllipticArc;
8936 }());
8937 models.EllipticArc = EllipticArc;
8938 EllipticArc.metaParameters = [
8939 { title: "startAngle", type: "range", min: 0, max: 90, value: 0 },
8940 { title: "endAngle", type: "range", min: 90, max: 360, value: 180 },
8941 { title: "radiusX", type: "range", min: 1, max: 50, value: 50 },
8942 { title: "radiusY", type: "range", min: 1, max: 50, value: 25 }
8943 ];
8944 })(models = MakerJs.models || (MakerJs.models = {}));
8945})(MakerJs || (MakerJs = {}));
8946var MakerJs;
8947(function (MakerJs) {
8948 var models;
8949 (function (models) {
8950 /**
8951 * @private
8952 */
8953 function getPoints(arg) {
8954 var coords;
8955 if (Array.isArray(arg)) {
8956 if (MakerJs.isPoint(arg[0])) {
8957 return arg;
8958 }
8959 coords = arg;
8960 }
8961 else {
8962 coords = MakerJs.importer.parseNumericList(arg);
8963 }
8964 var points = [];
8965 for (var i = 0; i < coords.length; i += 2) {
8966 points.push([coords[i], coords[i + 1]]);
8967 }
8968 return points;
8969 }
8970 var ConnectTheDots = /** @class */ (function () {
8971 function ConnectTheDots() {
8972 var args = [];
8973 for (var _i = 0; _i < arguments.length; _i++) {
8974 args[_i] = arguments[_i];
8975 }
8976 var _this = this;
8977 this.paths = {};
8978 var isClosed;
8979 var points;
8980 switch (args.length) {
8981 case 1:
8982 isClosed = true;
8983 points = getPoints(args[0]);
8984 break;
8985 case 2:
8986 isClosed = args[0];
8987 points = getPoints(args[1]);
8988 break;
8989 }
8990 var connect = function (a, b, skipZeroDistance) {
8991 if (skipZeroDistance === void 0) { skipZeroDistance = false; }
8992 if (skipZeroDistance && MakerJs.measure.pointDistance(points[a], points[b]) == 0)
8993 return;
8994 _this.paths["ShapeLine" + i] = new MakerJs.paths.Line(points[a], points[b]);
8995 };
8996 for (var i = 1; i < points.length; i++) {
8997 connect(i - 1, i);
8998 }
8999 if (isClosed && points.length > 2) {
9000 connect(points.length - 1, 0, true);
9001 }
9002 }
9003 return ConnectTheDots;
9004 }());
9005 models.ConnectTheDots = ConnectTheDots;
9006 ConnectTheDots.metaParameters = [
9007 { title: "closed", type: "bool", value: true },
9008 {
9009 title: "points", type: "select", value: [
9010 [[0, 0], [40, 40], [60, 20], [100, 100], [60, 60], [40, 80]],
9011 [[0, 0], [100, 0], [50, 87]],
9012 [-10, 0, 10, 0, 0, 20],
9013 '-10 0 10 0 0 20',
9014 ]
9015 }
9016 ];
9017 })(models = MakerJs.models || (MakerJs.models = {}));
9018})(MakerJs || (MakerJs = {}));
9019var MakerJs;
9020(function (MakerJs) {
9021 var models;
9022 (function (models) {
9023 var Polygon = /** @class */ (function () {
9024 function Polygon(numberOfSides, radius, firstCornerAngleInDegrees, circumscribed) {
9025 this.paths = {};
9026 this.paths = new models.ConnectTheDots(true, Polygon.getPoints(numberOfSides, radius, firstCornerAngleInDegrees, circumscribed)).paths;
9027 }
9028 Polygon.circumscribedRadius = function (radius, angleInRadians) {
9029 return radius / Math.cos(angleInRadians / 2);
9030 };
9031 Polygon.getPoints = function (numberOfSides, radius, firstCornerAngleInDegrees, circumscribed) {
9032 if (firstCornerAngleInDegrees === void 0) { firstCornerAngleInDegrees = 0; }
9033 if (circumscribed === void 0) { circumscribed = false; }
9034 var points = [];
9035 var a1 = MakerJs.angle.toRadians(firstCornerAngleInDegrees);
9036 var a = 2 * Math.PI / numberOfSides;
9037 if (circumscribed) {
9038 radius = Polygon.circumscribedRadius(radius, a);
9039 }
9040 for (var i = 0; i < numberOfSides; i++) {
9041 points.push(MakerJs.point.fromPolar(a * i + a1, radius));
9042 }
9043 return points;
9044 };
9045 return Polygon;
9046 }());
9047 models.Polygon = Polygon;
9048 Polygon.metaParameters = [
9049 { title: "number of sides", type: "range", min: 3, max: 24, value: 6 },
9050 { title: "radius", type: "range", min: 1, max: 100, value: 50 },
9051 { title: "offset angle", type: "range", min: 0, max: 180, value: 0 },
9052 { title: "radius on flats (vs radius on vertexes)", type: "bool", value: false }
9053 ];
9054 })(models = MakerJs.models || (MakerJs.models = {}));
9055})(MakerJs || (MakerJs = {}));
9056var MakerJs;
9057(function (MakerJs) {
9058 var models;
9059 (function (models) {
9060 var Holes = /** @class */ (function () {
9061 /**
9062 * Create an array of circles of the same radius from an array of center points.
9063 *
9064 * Example:
9065 * ```
9066 * //Create some holes from an array of points
9067 * var makerjs = require('makerjs');
9068 * var model = new makerjs.models.Holes(10, [[0, 0],[50, 0],[25, 40]]);
9069 * var svg = makerjs.exporter.toSVG(model);
9070 * document.write(svg);
9071 * ```
9072 *
9073 * @param holeRadius Hole radius.
9074 * @param points Array of points for origin of each hole.
9075 * @param ids Optional array of corresponding path ids for the holes.
9076 */
9077 function Holes(holeRadius, points, ids) {
9078 this.paths = {};
9079 for (var i = 0; i < points.length; i++) {
9080 var id = ids ? ids[i] : i.toString();
9081 this.paths[id] = new MakerJs.paths.Circle(points[i], holeRadius);
9082 }
9083 }
9084 return Holes;
9085 }());
9086 models.Holes = Holes;
9087 Holes.metaParameters = [
9088 { title: "holeRadius", type: "range", min: .1, max: 10, step: .1, value: 1 },
9089 {
9090 title: "points", type: "select", value: [
9091 [[0, 0], [10, 10], [20, 20], [30, 30], [40, 40], [50, 50], [60, 60], [70, 70], [80, 80]],
9092 [[0, 0], [0, 25], [0, 50], [0, 75], [0, 100], [25, 50], [50, 50], [75, 50], [100, 100], [100, 75], [100, 50], [100, 25], [100, 0]]
9093 ]
9094 }
9095 ];
9096 })(models = MakerJs.models || (MakerJs.models = {}));
9097})(MakerJs || (MakerJs = {}));
9098var MakerJs;
9099(function (MakerJs) {
9100 var models;
9101 (function (models) {
9102 var BoltCircle = /** @class */ (function () {
9103 function BoltCircle(boltRadius, holeRadius, boltCount, firstBoltAngleInDegrees) {
9104 if (firstBoltAngleInDegrees === void 0) { firstBoltAngleInDegrees = 0; }
9105 this.paths = {};
9106 var points = models.Polygon.getPoints(boltCount, boltRadius, firstBoltAngleInDegrees);
9107 var ids = points.map(function (p, i) { return "bolt " + i; });
9108 this.paths = new models.Holes(holeRadius, points, ids).paths;
9109 }
9110 return BoltCircle;
9111 }());
9112 models.BoltCircle = BoltCircle;
9113 BoltCircle.metaParameters = [
9114 { title: "bolt circle radius", type: "range", min: 1, max: 100, value: 50 },
9115 { title: "hole radius", type: "range", min: 1, max: 50, value: 5 },
9116 { title: "bolt count", type: "range", min: 3, max: 24, value: 12 },
9117 { title: "offset angle", type: "range", min: 0, max: 180, value: 0 }
9118 ];
9119 })(models = MakerJs.models || (MakerJs.models = {}));
9120})(MakerJs || (MakerJs = {}));
9121var MakerJs;
9122(function (MakerJs) {
9123 var models;
9124 (function (models) {
9125 var BoltRectangle = /** @class */ (function () {
9126 function BoltRectangle(width, height, holeRadius) {
9127 this.paths = {};
9128 var points = [[0, 0], [width, 0], [width, height], [0, height]];
9129 var ids = ["BottomLeft_bolt", "BottomRight_bolt", "TopRight_bolt", "TopLeft_bolt"];
9130 this.paths = new models.Holes(holeRadius, points, ids).paths;
9131 }
9132 return BoltRectangle;
9133 }());
9134 models.BoltRectangle = BoltRectangle;
9135 BoltRectangle.metaParameters = [
9136 { title: "width", type: "range", min: 1, max: 100, value: 100 },
9137 { title: "height", type: "range", min: 1, max: 100, value: 50 },
9138 { title: "hole radius", type: "range", min: 1, max: 50, value: 5 }
9139 ];
9140 })(models = MakerJs.models || (MakerJs.models = {}));
9141})(MakerJs || (MakerJs = {}));
9142var MakerJs;
9143(function (MakerJs) {
9144 var models;
9145 (function (models) {
9146 var Dogbone = /** @class */ (function () {
9147 /**
9148 * Create a dogbone from width, height, corner radius, style, and bottomless flag.
9149 *
9150 * Example:
9151 * ```
9152 * var d = new makerjs.models.Dogbone(50, 100, 5);
9153 * ```
9154 *
9155 * @param width Width of the rectangle.
9156 * @param height Height of the rectangle.
9157 * @param radius Corner radius.
9158 * @param style Optional corner style: 0 (default) for dogbone, 1 for vertical, -1 for horizontal.
9159 * @param bottomless Optional flag to omit the bottom line and bottom corners (default false).
9160 */
9161 function Dogbone(width, height, radius, style, bottomless) {
9162 if (style === void 0) { style = 0; }
9163 if (bottomless === void 0) { bottomless = false; }
9164 this.paths = {};
9165 var maxSide = Math.min(height, width) / 2;
9166 var maxRadius;
9167 switch (style) {
9168 case -1: //horizontal
9169 case 1: //vertical
9170 maxRadius = maxSide / 2;
9171 break;
9172 case 0: //equal
9173 default:
9174 maxRadius = maxSide * Math.SQRT2 / 2;
9175 break;
9176 }
9177 radius = Math.min(radius, maxRadius);
9178 var ax;
9179 var ay;
9180 var lx;
9181 var ly;
9182 var apexes;
9183 switch (style) {
9184 case -1:
9185 ax = 0;
9186 ay = radius;
9187 lx = 0;
9188 ly = radius * 2;
9189 apexes = [180, 0, 0, 180];
9190 break;
9191 case 1:
9192 ax = radius;
9193 ay = 0;
9194 lx = radius * 2;
9195 ly = 0;
9196 apexes = [270, 270, 90, 90];
9197 break;
9198 case 0:
9199 default:
9200 ax = ay = radius / Math.SQRT2;
9201 lx = ly = ax * 2;
9202 apexes = [225, 315, 45, 135];
9203 break;
9204 }
9205 if (bottomless) {
9206 this.paths['Left'] = new MakerJs.paths.Line([0, 0], [0, height - ly]);
9207 this.paths['Right'] = new MakerJs.paths.Line([width, 0], [width, height - ly]);
9208 }
9209 else {
9210 this.paths['Left'] = new MakerJs.paths.Line([0, ly], [0, height - ly]);
9211 this.paths['Right'] = new MakerJs.paths.Line([width, ly], [width, height - ly]);
9212 this.paths['Bottom'] = new MakerJs.paths.Line([lx, 0], [width - lx, 0]);
9213 this.paths["BottomLeft"] = new MakerJs.paths.Arc([ax, ay], radius, apexes[0] - 90, apexes[0] + 90);
9214 this.paths["BottomRight"] = new MakerJs.paths.Arc([width - ax, ay], radius, apexes[1] - 90, apexes[1] + 90);
9215 }
9216 this.paths["TopRight"] = new MakerJs.paths.Arc([width - ax, height - ay], radius, apexes[2] - 90, apexes[2] + 90);
9217 this.paths["TopLeft"] = new MakerJs.paths.Arc([ax, height - ay], radius, apexes[3] - 90, apexes[3] + 90);
9218 this.paths['Top'] = new MakerJs.paths.Line([lx, height], [width - lx, height]);
9219 }
9220 return Dogbone;
9221 }());
9222 models.Dogbone = Dogbone;
9223 Dogbone.metaParameters = [
9224 { title: "width", type: "range", min: 1, max: 100, value: 50 },
9225 { title: "height", type: "range", min: 1, max: 100, value: 100 },
9226 { title: "radius", type: "range", min: 0, max: 50, value: 5 },
9227 { title: "style", type: "select", value: [0, 1, -1] },
9228 { title: "bottomless", type: "bool", value: false }
9229 ];
9230 })(models = MakerJs.models || (MakerJs.models = {}));
9231})(MakerJs || (MakerJs = {}));
9232var MakerJs;
9233(function (MakerJs) {
9234 var models;
9235 (function (models) {
9236 var Dome = /** @class */ (function () {
9237 function Dome(width, height, radius, bottomless) {
9238 this.paths = {};
9239 var w2 = width / 2;
9240 if (radius < 0)
9241 radius = 0;
9242 if (radius === void 0)
9243 radius = w2;
9244 radius = Math.min(radius, w2);
9245 radius = Math.min(radius, height);
9246 var wt = Math.max(w2 - radius, 0);
9247 var hr = Math.max(height - radius, 0);
9248 if (!bottomless) {
9249 this.paths["Bottom"] = new MakerJs.paths.Line([-w2, 0], [w2, 0]);
9250 }
9251 if (hr) {
9252 this.paths["Left"] = new MakerJs.paths.Line([-w2, 0], [-w2, hr]);
9253 this.paths["Right"] = new MakerJs.paths.Line([w2, 0], [w2, hr]);
9254 }
9255 if (radius > 0) {
9256 this.paths["TopLeft"] = new MakerJs.paths.Arc([-wt, hr], radius, 90, 180);
9257 this.paths["TopRight"] = new MakerJs.paths.Arc([wt, hr], radius, 0, 90);
9258 }
9259 if (wt) {
9260 this.paths["Top"] = new MakerJs.paths.Line([-wt, height], [wt, height]);
9261 }
9262 }
9263 return Dome;
9264 }());
9265 models.Dome = Dome;
9266 Dome.metaParameters = [
9267 { title: "width", type: "range", min: 1, max: 100, value: 50 },
9268 { title: "height", type: "range", min: 1, max: 100, value: 100 },
9269 { title: "radius", type: "range", min: 0, max: 50, value: 25 },
9270 { title: "bottomless", type: "bool", value: false }
9271 ];
9272 })(models = MakerJs.models || (MakerJs.models = {}));
9273})(MakerJs || (MakerJs = {}));
9274var MakerJs;
9275(function (MakerJs) {
9276 var models;
9277 (function (models) {
9278 var RoundRectangle = /** @class */ (function () {
9279 function RoundRectangle() {
9280 var args = [];
9281 for (var _i = 0; _i < arguments.length; _i++) {
9282 args[_i] = arguments[_i];
9283 }
9284 this.paths = {};
9285 var width;
9286 var height;
9287 var radius = 0;
9288 switch (args.length) {
9289 case 3:
9290 width = args[0];
9291 height = args[1];
9292 radius = args[2];
9293 break;
9294 case 2:
9295 radius = args[1];
9296 //fall through to 1
9297 case 1:
9298 var m = MakerJs.measure.modelExtents(args[0]);
9299 this.origin = MakerJs.point.subtract(m.low, [radius, radius]);
9300 width = m.high[0] - m.low[0] + 2 * radius;
9301 height = m.high[1] - m.low[1] + 2 * radius;
9302 break;
9303 }
9304 var maxRadius = Math.min(height, width) / 2;
9305 radius = Math.min(radius, maxRadius);
9306 var wr = width - radius;
9307 var hr = height - radius;
9308 if (radius > 0) {
9309 this.paths["BottomLeft"] = new MakerJs.paths.Arc([radius, radius], radius, 180, 270);
9310 this.paths["BottomRight"] = new MakerJs.paths.Arc([wr, radius], radius, 270, 0);
9311 this.paths["TopRight"] = new MakerJs.paths.Arc([wr, hr], radius, 0, 90);
9312 this.paths["TopLeft"] = new MakerJs.paths.Arc([radius, hr], radius, 90, 180);
9313 }
9314 if (wr - radius > 0) {
9315 this.paths["Bottom"] = new MakerJs.paths.Line([radius, 0], [wr, 0]);
9316 this.paths["Top"] = new MakerJs.paths.Line([wr, height], [radius, height]);
9317 }
9318 if (hr - radius > 0) {
9319 this.paths["Right"] = new MakerJs.paths.Line([width, radius], [width, hr]);
9320 this.paths["Left"] = new MakerJs.paths.Line([0, hr], [0, radius]);
9321 }
9322 }
9323 return RoundRectangle;
9324 }());
9325 models.RoundRectangle = RoundRectangle;
9326 RoundRectangle.metaParameters = [
9327 { title: "width", type: "range", min: 1, max: 100, value: 50 },
9328 { title: "height", type: "range", min: 1, max: 100, value: 100 },
9329 { title: "radius", type: "range", min: 0, max: 50, value: 11 }
9330 ];
9331 })(models = MakerJs.models || (MakerJs.models = {}));
9332})(MakerJs || (MakerJs = {}));
9333var MakerJs;
9334(function (MakerJs) {
9335 var models;
9336 (function (models) {
9337 var Oval = /** @class */ (function () {
9338 function Oval(width, height) {
9339 this.paths = {};
9340 this.paths = new models.RoundRectangle(width, height, Math.min(height / 2, width / 2)).paths;
9341 }
9342 return Oval;
9343 }());
9344 models.Oval = Oval;
9345 Oval.metaParameters = [
9346 { title: "width", type: "range", min: 1, max: 100, value: 50 },
9347 { title: "height", type: "range", min: 1, max: 100, value: 100 }
9348 ];
9349 })(models = MakerJs.models || (MakerJs.models = {}));
9350})(MakerJs || (MakerJs = {}));
9351var MakerJs;
9352(function (MakerJs) {
9353 var models;
9354 (function (models) {
9355 var OvalArc = /** @class */ (function () {
9356 function OvalArc(startAngle, endAngle, sweepRadius, slotRadius, selfIntersect, isolateCaps) {
9357 if (selfIntersect === void 0) { selfIntersect = false; }
9358 if (isolateCaps === void 0) { isolateCaps = false; }
9359 var _this = this;
9360 this.paths = {};
9361 var capRoot;
9362 if (isolateCaps) {
9363 capRoot = { models: {} };
9364 this.models = { 'Caps': capRoot };
9365 }
9366 if (slotRadius <= 0 || sweepRadius <= 0)
9367 return;
9368 startAngle = MakerJs.angle.noRevolutions(startAngle);
9369 endAngle = MakerJs.angle.noRevolutions(endAngle);
9370 if (MakerJs.round(startAngle - endAngle) == 0)
9371 return;
9372 if (endAngle < startAngle)
9373 endAngle += 360;
9374 var addCap = function (id, tiltAngle, offsetStartAngle, offsetEndAngle) {
9375 var capModel;
9376 if (isolateCaps) {
9377 capModel = { paths: {} };
9378 capRoot.models[id] = capModel;
9379 }
9380 else {
9381 capModel = _this;
9382 }
9383 return capModel.paths[id] = new MakerJs.paths.Arc(MakerJs.point.fromPolar(MakerJs.angle.toRadians(tiltAngle), sweepRadius), slotRadius, tiltAngle + offsetStartAngle, tiltAngle + offsetEndAngle);
9384 };
9385 var addSweep = function (id, offsetRadius) {
9386 return _this.paths[id] = new MakerJs.paths.Arc([0, 0], sweepRadius + offsetRadius, startAngle, endAngle);
9387 };
9388 addSweep("Outer", slotRadius);
9389 var hasInner = (sweepRadius - slotRadius) > 0;
9390 if (hasInner) {
9391 addSweep("Inner", -slotRadius);
9392 }
9393 var caps = [];
9394 caps.push(addCap("StartCap", startAngle, 180, 0));
9395 caps.push(addCap("EndCap", endAngle, 0, 180));
9396 //the distance between the cap origins
9397 var d = MakerJs.measure.pointDistance(caps[0].origin, caps[1].origin);
9398 if ((d / 2) < slotRadius) {
9399 //the caps intersect
9400 var int = MakerJs.path.intersection(caps[0], caps[1]);
9401 if (int) {
9402 if (!hasInner || !selfIntersect) {
9403 caps[0].startAngle = int.path1Angles[0];
9404 caps[1].endAngle = int.path2Angles[0];
9405 }
9406 if (!selfIntersect && hasInner && int.intersectionPoints.length == 2) {
9407 addCap("StartCap2", startAngle, 180, 0).endAngle = int.path1Angles[1];
9408 addCap("EndCap2", endAngle, 0, 180).startAngle = int.path2Angles[1] + 360;
9409 }
9410 }
9411 }
9412 }
9413 return OvalArc;
9414 }());
9415 models.OvalArc = OvalArc;
9416 OvalArc.metaParameters = [
9417 { title: "start angle", type: "range", min: -360, max: 360, step: 1, value: 180 },
9418 { title: "end angle", type: "range", min: -360, max: 360, step: 1, value: 0 },
9419 { title: "sweep", type: "range", min: 0, max: 100, step: 1, value: 50 },
9420 { title: "radius", type: "range", min: 0, max: 100, step: 1, value: 15 },
9421 { title: "self intersect", type: "bool", value: false }
9422 ];
9423 })(models = MakerJs.models || (MakerJs.models = {}));
9424})(MakerJs || (MakerJs = {}));
9425var MakerJs;
9426(function (MakerJs) {
9427 var models;
9428 (function (models) {
9429 var Rectangle = /** @class */ (function () {
9430 function Rectangle() {
9431 var args = [];
9432 for (var _i = 0; _i < arguments.length; _i++) {
9433 args[_i] = arguments[_i];
9434 }
9435 this.paths = {};
9436 var width;
9437 var height;
9438 if (args.length === 2 && !MakerJs.isObject(args[0])) {
9439 width = args[0];
9440 height = args[1];
9441 }
9442 else {
9443 var margin = 0;
9444 var m;
9445 if (MakerJs.isModel(args[0])) {
9446 m = MakerJs.measure.modelExtents(args[0]);
9447 if (args.length === 2) {
9448 margin = args[1];
9449 }
9450 }
9451 else {
9452 //use measurement
9453 m = args[0];
9454 }
9455 this.origin = MakerJs.point.subtract(m.low, [margin, margin]);
9456 width = m.high[0] - m.low[0] + 2 * margin;
9457 height = m.high[1] - m.low[1] + 2 * margin;
9458 }
9459 this.paths = new models.ConnectTheDots(true, [[0, 0], [width, 0], [width, height], [0, height]]).paths;
9460 }
9461 return Rectangle;
9462 }());
9463 models.Rectangle = Rectangle;
9464 Rectangle.metaParameters = [
9465 { title: "width", type: "range", min: 1, max: 100, value: 50 },
9466 { title: "height", type: "range", min: 1, max: 100, value: 100 }
9467 ];
9468 })(models = MakerJs.models || (MakerJs.models = {}));
9469})(MakerJs || (MakerJs = {}));
9470var MakerJs;
9471(function (MakerJs) {
9472 var models;
9473 (function (models) {
9474 var Ring = /** @class */ (function () {
9475 function Ring(outerRadius, innerRadius) {
9476 this.paths = {};
9477 var radii = {
9478 "Ring_outer": outerRadius,
9479 "Ring_inner": innerRadius
9480 };
9481 for (var id in radii) {
9482 if (radii[id] === void 0)
9483 continue;
9484 this.paths[id] = new MakerJs.paths.Circle(MakerJs.point.zero(), radii[id]);
9485 }
9486 }
9487 return Ring;
9488 }());
9489 models.Ring = Ring;
9490 Ring.metaParameters = [
9491 { title: "outer radius", type: "range", min: 0, max: 100, step: 1, value: 50 },
9492 { title: "inner radius", type: "range", min: 0, max: 100, step: 1, value: 20 }
9493 ];
9494 })(models = MakerJs.models || (MakerJs.models = {}));
9495})(MakerJs || (MakerJs = {}));
9496var MakerJs;
9497(function (MakerJs) {
9498 var models;
9499 (function (models) {
9500 var Belt = /** @class */ (function () {
9501 function Belt(leftRadius, distance, rightRadius) {
9502 this.paths = {};
9503 var left = new MakerJs.paths.Arc([0, 0], leftRadius, 0, 360);
9504 var right = new MakerJs.paths.Arc([distance, 0], rightRadius, 0, 360);
9505 var angles = MakerJs.solvers.circleTangentAngles(left, right);
9506 if (!angles) {
9507 this.paths["Belt"] = new MakerJs.paths.Circle(Math.max(leftRadius, rightRadius));
9508 }
9509 else {
9510 angles = angles.sort(function (a, b) { return a - b; });
9511 left.startAngle = angles[0];
9512 left.endAngle = angles[1];
9513 right.startAngle = angles[1];
9514 right.endAngle = angles[0];
9515 this.paths["Left"] = left;
9516 this.paths["Right"] = right;
9517 this.paths["Top"] = new MakerJs.paths.Line(MakerJs.point.fromAngleOnCircle(angles[0], left), MakerJs.point.fromAngleOnCircle(angles[0], right));
9518 this.paths["Bottom"] = new MakerJs.paths.Line(MakerJs.point.fromAngleOnCircle(angles[1], left), MakerJs.point.fromAngleOnCircle(angles[1], right));
9519 }
9520 }
9521 return Belt;
9522 }());
9523 models.Belt = Belt;
9524 Belt.metaParameters = [
9525 { title: "left radius", type: "range", min: 0, max: 100, value: 30 },
9526 { title: "distance between centers", type: "range", min: 0, max: 100, value: 50 },
9527 { title: "right radius", type: "range", min: 0, max: 100, value: 15 }
9528 ];
9529 })(models = MakerJs.models || (MakerJs.models = {}));
9530})(MakerJs || (MakerJs = {}));
9531var MakerJs;
9532(function (MakerJs) {
9533 var models;
9534 (function (models) {
9535 var SCurve = /** @class */ (function () {
9536 function SCurve(width, height) {
9537 this.paths = {};
9538 function findRadius(x, y) {
9539 return x + (y * y - x * x) / (2 * x);
9540 }
9541 var h2 = height / 2;
9542 var w2 = width / 2;
9543 var radius;
9544 var startAngle;
9545 var endAngle;
9546 var arcOrigin;
9547 if (width > height) {
9548 radius = findRadius(h2, w2);
9549 startAngle = 270;
9550 endAngle = 360 - MakerJs.angle.toDegrees(Math.acos(w2 / radius));
9551 arcOrigin = [0, radius];
9552 }
9553 else {
9554 radius = findRadius(w2, h2);
9555 startAngle = 180 - MakerJs.angle.toDegrees(Math.asin(h2 / radius));
9556 endAngle = 180;
9557 arcOrigin = [radius, 0];
9558 }
9559 var curve = new MakerJs.paths.Arc(arcOrigin, radius, startAngle, endAngle);
9560 this.paths['curve_start'] = curve;
9561 this.paths['curve_end'] = MakerJs.path.moveRelative(MakerJs.path.mirror(curve, true, true), [width, height]);
9562 }
9563 return SCurve;
9564 }());
9565 models.SCurve = SCurve;
9566 SCurve.metaParameters = [
9567 { title: "width", type: "range", min: 1, max: 100, value: 50 },
9568 { title: "height", type: "range", min: 1, max: 100, value: 100 }
9569 ];
9570 })(models = MakerJs.models || (MakerJs.models = {}));
9571})(MakerJs || (MakerJs = {}));
9572var MakerJs;
9573(function (MakerJs) {
9574 var models;
9575 (function (models) {
9576 var Slot = /** @class */ (function () {
9577 function Slot(origin, endPoint, radius, isolateCaps) {
9578 if (isolateCaps === void 0) { isolateCaps = false; }
9579 var _this = this;
9580 this.paths = {};
9581 var capRoot;
9582 if (isolateCaps) {
9583 capRoot = { models: {} };
9584 this.models = { 'Caps': capRoot };
9585 }
9586 var addCap = function (id, capPath) {
9587 var capModel;
9588 if (isolateCaps) {
9589 capModel = { paths: {} };
9590 capRoot.models[id] = capModel;
9591 }
9592 else {
9593 capModel = _this;
9594 }
9595 capModel.paths[id] = capPath;
9596 };
9597 var a = MakerJs.angle.ofPointInDegrees(origin, endPoint);
9598 var len = MakerJs.measure.pointDistance(origin, endPoint);
9599 this.paths['Top'] = new MakerJs.paths.Line([0, radius], [len, radius]);
9600 this.paths['Bottom'] = new MakerJs.paths.Line([0, -radius], [len, -radius]);
9601 addCap('StartCap', new MakerJs.paths.Arc([0, 0], radius, 90, 270));
9602 addCap('EndCap', new MakerJs.paths.Arc([len, 0], radius, 270, 90));
9603 MakerJs.model.rotate(this, a, [0, 0]);
9604 this.origin = origin;
9605 }
9606 return Slot;
9607 }());
9608 models.Slot = Slot;
9609 Slot.metaParameters = [
9610 {
9611 title: "origin", type: "select", value: [
9612 [0, 0],
9613 [10, 0],
9614 [10, 10]
9615 ]
9616 },
9617 {
9618 title: "end", type: "select", value: [
9619 [80, 0],
9620 [0, 30],
9621 [10, 30]
9622 ]
9623 },
9624 { title: "radius", type: "range", min: 1, max: 50, value: 10 }
9625 ];
9626 })(models = MakerJs.models || (MakerJs.models = {}));
9627})(MakerJs || (MakerJs = {}));
9628var MakerJs;
9629(function (MakerJs) {
9630 var models;
9631 (function (models) {
9632 var Square = /** @class */ (function () {
9633 function Square(side) {
9634 this.paths = {};
9635 this.paths = new models.Rectangle(side, side).paths;
9636 }
9637 return Square;
9638 }());
9639 models.Square = Square;
9640 Square.metaParameters = [
9641 { title: "side", type: "range", min: 1, max: 100, value: 100 }
9642 ];
9643 })(models = MakerJs.models || (MakerJs.models = {}));
9644})(MakerJs || (MakerJs = {}));
9645var MakerJs;
9646(function (MakerJs) {
9647 var models;
9648 (function (models) {
9649 var Star = /** @class */ (function () {
9650 function Star(numberOfPoints, outerRadius, innerRadius, skipPoints) {
9651 if (skipPoints === void 0) { skipPoints = 2; }
9652 this.paths = {};
9653 if (!innerRadius) {
9654 innerRadius = outerRadius * Star.InnerRadiusRatio(numberOfPoints, skipPoints);
9655 }
9656 var outerPoints = models.Polygon.getPoints(numberOfPoints, outerRadius);
9657 var innerPoints = models.Polygon.getPoints(numberOfPoints, innerRadius, 180 / numberOfPoints);
9658 var allPoints = [];
9659 for (var i = 0; i < numberOfPoints; i++) {
9660 allPoints.push(outerPoints[i]);
9661 allPoints.push(innerPoints[i]);
9662 }
9663 var model = new models.ConnectTheDots(true, allPoints);
9664 this.paths = model.paths;
9665 delete model.paths;
9666 }
9667 Star.InnerRadiusRatio = function (numberOfPoints, skipPoints) {
9668 //formula from http://www.jdawiseman.com/papers/easymath/surds_star_inner_radius.html
9669 //Cos(Pi()*m/n) / Cos(Pi()*(m-1)/n)
9670 if (numberOfPoints > 0 && skipPoints > 1 && skipPoints < numberOfPoints / 2) {
9671 return Math.cos(Math.PI * skipPoints / numberOfPoints) / Math.cos(Math.PI * (skipPoints - 1) / numberOfPoints);
9672 }
9673 return 0;
9674 };
9675 return Star;
9676 }());
9677 models.Star = Star;
9678 Star.metaParameters = [
9679 { title: "number of sides", type: "range", min: 3, max: 24, value: 8 },
9680 { title: "outer radius", type: "range", min: 1, max: 100, value: 50 },
9681 { title: "inner radius", type: "range", min: 0, max: 100, value: 15 },
9682 { title: "skip points (when inner radius is zero)", type: "range", min: 0, max: 12, value: 2 }
9683 ];
9684 })(models = MakerJs.models || (MakerJs.models = {}));
9685})(MakerJs || (MakerJs = {}));
9686var MakerJs;
9687(function (MakerJs) {
9688 var models;
9689 (function (models) {
9690 var Text = /** @class */ (function () {
9691 /**
9692 * Renders text in a given font to a model.
9693 * @param font OpenType.Font object.
9694 * @param text String of text to render.
9695 * @param fontSize Font size.
9696 * @param combine Flag (default false) to perform a combineUnion upon each character with characters to the left and right.
9697 * @param centerCharacterOrigin Flag (default false) to move the x origin of each character to the center. Useful for rotating text characters.
9698 * @param bezierAccuracy Optional accuracy of Bezier curves.
9699 * @param opentypeOptions Optional opentype.RenderOptions object.
9700 * @returns Model of the text.
9701 */
9702 function Text(font, text, fontSize, combine, centerCharacterOrigin, bezierAccuracy, opentypeOptions) {
9703 if (combine === void 0) { combine = false; }
9704 if (centerCharacterOrigin === void 0) { centerCharacterOrigin = false; }
9705 var _this = this;
9706 this.models = {};
9707 var charIndex = 0;
9708 var prevDeleted;
9709 var prevChar;
9710 var cb = function (glyph, x, y, _fontSize, options) {
9711 var charModel = Text.glyphToModel(glyph, _fontSize, bezierAccuracy);
9712 charModel.origin = [x, 0];
9713 if (centerCharacterOrigin && (charModel.paths || charModel.models)) {
9714 var m = MakerJs.measure.modelExtents(charModel);
9715 if (m) {
9716 var w = m.high[0] - m.low[0];
9717 MakerJs.model.originate(charModel, [m.low[0] + w / 2, 0]);
9718 }
9719 }
9720 if (combine && charIndex > 0) {
9721 var combineOptions = {};
9722 var prev;
9723 if (prevDeleted) {
9724 //form a temporary complete geometry of the previous character using the previously deleted segments
9725 prev = {
9726 models: {
9727 deleted: prevDeleted,
9728 char: prevChar
9729 }
9730 };
9731 }
9732 else {
9733 prev = prevChar;
9734 }
9735 MakerJs.model.combine(prev, charModel, false, true, false, true, combineOptions);
9736 //save the deleted segments from this character for the next iteration
9737 prevDeleted = combineOptions.out_deleted[1];
9738 }
9739 _this.models[charIndex] = charModel;
9740 charIndex++;
9741 prevChar = charModel;
9742 };
9743 font.forEachGlyph(text, 0, 0, fontSize, opentypeOptions, cb);
9744 }
9745 /**
9746 * Convert an opentype glyph to a model.
9747 * @param glyph Opentype.Glyph object.
9748 * @param fontSize Font size.
9749 * @param bezierAccuracy Optional accuracy of Bezier curves.
9750 * @returns Model of the glyph.
9751 */
9752 Text.glyphToModel = function (glyph, fontSize, bezierAccuracy) {
9753 var charModel = {};
9754 var firstPoint;
9755 var currPoint;
9756 var pathCount = 0;
9757 function addPath(p) {
9758 if (!charModel.paths) {
9759 charModel.paths = {};
9760 }
9761 charModel.paths['p_' + ++pathCount] = p;
9762 }
9763 function addModel(m) {
9764 if (!charModel.models) {
9765 charModel.models = {};
9766 }
9767 charModel.models['p_' + ++pathCount] = m;
9768 }
9769 var p = glyph.getPath(0, 0, fontSize);
9770 p.commands.map(function (command, i) {
9771 var points = [[command.x, command.y], [command.x1, command.y1], [command.x2, command.y2]].map(function (p) {
9772 if (p[0] !== void 0) {
9773 return MakerJs.point.mirror(p, false, true);
9774 }
9775 });
9776 switch (command.type) {
9777 case 'M':
9778 firstPoint = points[0];
9779 break;
9780 case 'Z':
9781 points[0] = firstPoint;
9782 //fall through to line
9783 case 'L':
9784 if (!MakerJs.measure.isPointEqual(currPoint, points[0])) {
9785 addPath(new MakerJs.paths.Line(currPoint, points[0]));
9786 }
9787 break;
9788 case 'C':
9789 addModel(new models.BezierCurve(currPoint, points[1], points[2], points[0], bezierAccuracy));
9790 break;
9791 case 'Q':
9792 addModel(new models.BezierCurve(currPoint, points[1], points[0], bezierAccuracy));
9793 break;
9794 }
9795 currPoint = points[0];
9796 });
9797 return charModel;
9798 };
9799 return Text;
9800 }());
9801 models.Text = Text;
9802 Text.metaParameters = [
9803 { title: "font", type: "font", value: '*' },
9804 { title: "text", type: "text", value: 'Hello' },
9805 { title: "font size", type: "range", min: 10, max: 200, value: 72 },
9806 { title: "combine", type: "bool", value: false },
9807 { title: "center character origin", type: "bool", value: false }
9808 ];
9809 })(models = MakerJs.models || (MakerJs.models = {}));
9810})(MakerJs || (MakerJs = {}));
9811MakerJs.version = "0.14.0";
9812var Bezier = require('bezier-js');