UNPKG

flatten-js

Version:

Javascript library for 2d geometry

1 lines 25.9 kB
{"dependencies":[{"name":"C:\\Users\\alexbol\\WebstormProjects\\flatten-js\\package.json","includedInParent":true,"mtime":1520238055570}],"generated":{"js":"/**\r\n * Created by Alex Bol on 3/10/2017.\r\n */\n\n\"use strict\";\n\nvar _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i[\"return\"]) _i[\"return\"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError(\"Invalid attempt to destructure non-iterable instance\"); } }; }();\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nmodule.exports = function (Flatten) {\n /**\r\n * Class representing a circular arc\r\n * @type {Arc}\r\n */\n Flatten.Arc = function () {\n /**\r\n *\r\n * @param {Point} pc - arc center\r\n * @param {number} r - arc radius\r\n * @param {number} startAngle - start angle in radians from 0 to 2*PI\r\n * @param {number} endAngle - end angle in radians from 0 to 2*PI\r\n * @param {boolean} counterClockwise - arc direction, true - clockwise (or {@link Flatten.CCW}), false - counter clockwise (or {@link Flatten.CW)}\r\n */\n function Arc() {\n var pc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Flatten.Point();\n var r = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;\n var startAngle = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;\n var endAngle = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 2 * Math.PI;\n var counterClockwise = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;\n\n _classCallCheck(this, Arc);\n\n this.pc = pc.clone();\n this.r = r;\n this.startAngle = startAngle;\n this.endAngle = endAngle;\n this.counterClockwise = counterClockwise;\n }\n\n /**\r\n * Return new instance of arc\r\n * @returns {Arc}\r\n */\n\n\n _createClass(Arc, [{\n key: \"clone\",\n value: function clone() {\n return new Flatten.Arc(this.pc.clone(), this.r, this.startAngle, this.endAngle, this.counterClockwise);\n }\n\n /**\r\n * Get sweep angle in radians. Sweep angle is non-negative number from 0 to 2*PI\r\n * @returns {number}\r\n */\n\n }, {\n key: \"contains\",\n\n\n /**\r\n * Returns true if arc contains point, false otherwise\r\n * @param {Point} pt - point to test\r\n * @returns {boolean}\r\n */\n value: function contains(pt) {\n // first check if point on circle (pc,r)\n if (!Flatten.Utils.EQ(this.pc.distanceTo(pt)[0], this.r)) return false;\n\n // point on circle\n\n if (pt.equalTo(this.start)) return true;\n\n var angle = new Flatten.Vector(this.pc, pt).slope;\n var test_arc = new Flatten.Arc(this.pc, this.r, this.startAngle, angle, this.counterClockwise);\n return Flatten.Utils.LE(test_arc.length, this.length);\n }\n\n /**\r\n * When given point belongs to arc, return array of two arcs split by this point. If points is incident\r\n * to start or end point of the arc, return clone of the arc. If point does not belong to the arcs, return\r\n * empty array.\r\n * @param {Point} pt Query point\r\n * @returns {Arc[]}\r\n */\n\n }, {\n key: \"split\",\n value: function split(pt) {\n if (!this.contains(pt)) return [];\n\n if (Flatten.Utils.EQ_0(this.sweep)) return [this.clone()];\n\n if (this.start.equalTo(pt) || this.end.equalTo(pt)) return [this.clone()];\n\n var angle = new Flatten.Vector(this.pc, pt).slope;\n\n return [new Flatten.Arc(this.pc, this.r, this.startAngle, angle, this.counterClockwise), new Flatten.Arc(this.pc, this.r, angle, this.endAngle, this.counterClockwise)];\n }\n\n /**\r\n * Return middle point of the arc\r\n * @returns {Point}\r\n */\n\n }, {\n key: \"middle\",\n value: function middle() {\n var endAngle = this.counterClockwise === Flatten.CCW ? this.startAngle + this.sweep / 2 : this.startAngle - this.sweep / 2;\n var arc = new Flatten.Arc(this.pc, this.r, this.startAngle, endAngle, this.counterClockwise);\n return arc.end;\n }\n\n /**\r\n * Returns chord height (\"sagitta\") of the arc\r\n * @returns {number}\r\n */\n\n }, {\n key: \"chordHeight\",\n value: function chordHeight() {\n return (1.0 - Math.cos(Math.abs(this.sweep / 2.0))) * this.r;\n }\n\n /**\r\n * Returns array of intersection points between arc and other shape\r\n * @param {Shape} shape Shape of the one of supported types Line, Circle, Segment, Arc <br/>\r\n * TODO: support Polygon and Planar Set\r\n * @returns {Points[]}\r\n */\n\n }, {\n key: \"intersect\",\n value: function intersect(shape) {\n if (shape instanceof Flatten.Line) {\n return shape.intersect(this);\n }\n if (shape instanceof Flatten.Circle) {\n return Arc.intersectArc2Circle(this, shape);\n }\n if (shape instanceof Flatten.Segment) {\n return shape.intersect(this);\n }\n if (shape instanceof Flatten.Arc) {\n return Arc.intersectArc2Arc(this, shape);\n }\n }\n\n /**\r\n * Calculate distance and shortest segment from arc to shape and return array [distance, shortest segment]\r\n * @param {Shape} shape Shape of the one of supported types Point, Line, Circle, Segment, Arc, Polygon or Planar Set\r\n * @returns {number} distance from arc to shape\r\n * @returns {Segment} shortest segment between arc and shape (started at arc, ended at shape)\r\n */\n\n }, {\n key: \"distanceTo\",\n value: function distanceTo(shape) {\n var Distance = Flatten.Distance;\n\n\n if (shape instanceof Flatten.Point) {\n var _Distance$point2arc = Distance.point2arc(shape, this),\n _Distance$point2arc2 = _slicedToArray(_Distance$point2arc, 2),\n dist = _Distance$point2arc2[0],\n shortest_segment = _Distance$point2arc2[1];\n\n shortest_segment = shortest_segment.reverse();\n return [dist, shortest_segment];\n }\n\n if (shape instanceof Flatten.Circle) {\n var _Distance$arc2circle = Distance.arc2circle(this, shape),\n _Distance$arc2circle2 = _slicedToArray(_Distance$arc2circle, 2),\n _dist = _Distance$arc2circle2[0],\n _shortest_segment = _Distance$arc2circle2[1];\n\n return [_dist, _shortest_segment];\n }\n\n if (shape instanceof Flatten.Line) {\n var _Distance$arc2line = Distance.arc2line(this, shape),\n _Distance$arc2line2 = _slicedToArray(_Distance$arc2line, 2),\n _dist2 = _Distance$arc2line2[0],\n _shortest_segment2 = _Distance$arc2line2[1];\n\n return [_dist2, _shortest_segment2];\n }\n\n if (shape instanceof Flatten.Segment) {\n var _Distance$segment2arc = Distance.segment2arc(shape, this),\n _Distance$segment2arc2 = _slicedToArray(_Distance$segment2arc, 2),\n _dist3 = _Distance$segment2arc2[0],\n _shortest_segment3 = _Distance$segment2arc2[1];\n\n _shortest_segment3 = _shortest_segment3.reverse();\n return [_dist3, _shortest_segment3];\n }\n\n if (shape instanceof Flatten.Arc) {\n var _Distance$arc2arc = Distance.arc2arc(this, shape),\n _Distance$arc2arc2 = _slicedToArray(_Distance$arc2arc, 2),\n _dist4 = _Distance$arc2arc2[0],\n _shortest_segment4 = _Distance$arc2arc2[1];\n\n return [_dist4, _shortest_segment4];\n }\n\n if (shape instanceof Flatten.Polygon) {\n var _Distance$shape2polyg = Distance.shape2polygon(this, shape),\n _Distance$shape2polyg2 = _slicedToArray(_Distance$shape2polyg, 2),\n _dist5 = _Distance$shape2polyg2[0],\n _shortest_segment5 = _Distance$shape2polyg2[1];\n\n return [_dist5, _shortest_segment5];\n }\n\n if (shape instanceof Flatten.PlanarSet) {\n var _Distance$shape2plana = Distance.shape2planarSet(this, shape),\n _Distance$shape2plana2 = _slicedToArray(_Distance$shape2plana, 2),\n _dist6 = _Distance$shape2plana2[0],\n _shortest_segment6 = _Distance$shape2plana2[1];\n\n return [_dist6, _shortest_segment6];\n }\n }\n\n /**\r\n * Breaks arc in extreme point 0, pi/2, pi, 3*pi/2 and returns array of sub-arcs\r\n * @returns {Arcs[]}\r\n */\n\n }, {\n key: \"breakToFunctional\",\n value: function breakToFunctional() {\n var func_arcs_array = [];\n var angles = [0, Math.PI / 2, 2 * Math.PI / 2, 3 * Math.PI / 2];\n var pts = [this.pc.translate(this.r, 0), this.pc.translate(0, this.r), this.pc.translate(-this.r, 0), this.pc.translate(0, -this.r)];\n\n // If arc contains extreme point,\n // create test arc started at start point and ended at this extreme point\n var test_arcs = [];\n for (var i = 0; i < 4; i++) {\n if (pts[i].on(this)) {\n test_arcs.push(new Flatten.Arc(this.pc, this.r, this.startAngle, angles[i], this.counterClockwise));\n }\n }\n\n if (test_arcs.length == 0) {\n // arc does contain any extreme point\n func_arcs_array.push(this.clone());\n } else {\n // arc passes extreme point\n // sort these arcs by length\n test_arcs.sort(function (arc1, arc2) {\n return arc1.length - arc2.length;\n });\n\n for (var _i = 0; _i < test_arcs.length; _i++) {\n var _prev_arc = func_arcs_array.length > 0 ? func_arcs_array[func_arcs_array.length - 1] : undefined;\n var _new_arc = void 0;\n if (_prev_arc) {\n _new_arc = new Flatten.Arc(this.pc, this.r, _prev_arc.endAngle, test_arcs[_i].endAngle, this.counterClockwise);\n } else {\n _new_arc = new Flatten.Arc(this.pc, this.r, this.startAngle, test_arcs[_i].endAngle, this.counterClockwise);\n }\n if (!Flatten.Utils.EQ_0(_new_arc.length)) {\n func_arcs_array.push(_new_arc.clone());\n }\n }\n\n // add last sub arc\n var prev_arc = func_arcs_array.length > 0 ? func_arcs_array[func_arcs_array.length - 1] : undefined;\n var new_arc = void 0;\n if (prev_arc) {\n new_arc = new Flatten.Arc(this.pc, this.r, prev_arc.endAngle, this.endAngle, this.counterClockwise);\n } else {\n new_arc = new Flatten.Arc(this.pc, this.r, this.startAngle, this.endAngle, this.counterClockwise);\n }\n if (!Flatten.Utils.EQ_0(new_arc.length)) {\n func_arcs_array.push(new_arc.clone());\n }\n }\n return func_arcs_array;\n }\n\n /**\r\n * Return tangent unit vector in the start point in the direction from start to end\r\n * @returns {Vector}\r\n */\n\n }, {\n key: \"tangentInStart\",\n value: function tangentInStart() {\n var vec = new Flatten.Vector(this.pc, this.start);\n var angle = this.counterClockwise ? Math.PI / 2. : -Math.PI / 2.;\n var tangent = vec.rotate(angle).normalize();\n return tangent;\n }\n\n /**\r\n * Return tangent unit vector in the end point in the direction from end to start\r\n * @returns {Vector}\r\n */\n\n }, {\n key: \"tangentInEnd\",\n value: function tangentInEnd() {\n var vec = new Flatten.Vector(this.pc, this.end);\n var angle = this.counterClockwise ? -Math.PI / 2. : Math.PI / 2.;\n var tangent = vec.rotate(angle).normalize();\n return tangent;\n }\n\n /**\r\n * Returns new arc with swapped start and end angles and reversed direction\r\n * @returns {Arc}\r\n */\n\n }, {\n key: \"reverse\",\n value: function reverse() {\n return new Arc(this.pc, this.r, this.endAngle, this.startAngle, !this.counterClockwise);\n }\n }, {\n key: \"definiteIntegral\",\n value: function definiteIntegral() {\n var ymin = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n\n var f_arcs = this.breakToFunctional();\n var area = f_arcs.reduce(function (acc, arc) {\n return acc + arc.circularSegmentDefiniteIntegral(ymin);\n }, 0.0);\n return area;\n }\n }, {\n key: \"circularSegmentDefiniteIntegral\",\n value: function circularSegmentDefiniteIntegral(ymin) {\n var line = new Flatten.Line(this.start, this.end);\n var onLeftSide = this.pc.leftTo(line);\n var segment = new Flatten.Segment(this.start, this.end);\n var areaTrapez = segment.definiteIntegral(ymin);\n var areaCircularSegment = this.circularSegmentArea();\n var area = onLeftSide ? areaTrapez - areaCircularSegment : areaTrapez + areaCircularSegment;\n return area;\n }\n }, {\n key: \"circularSegmentArea\",\n value: function circularSegmentArea() {\n return 0.5 * this.r * this.r * (this.sweep - Math.sin(this.sweep));\n }\n\n /**\r\n * Return string to draw arc in svg\r\n * @param {Object} attrs - json structure with attributes of svg path element,\r\n * like \"stroke\", \"strokeWidth\", \"fill\" <br/>\r\n * Defaults are stroke:\"black\", strokeWidth:\"3\", fill:\"none\"\r\n * @returns {string}\r\n */\n\n }, {\n key: \"svg\",\n value: function svg() {\n var attrs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { stroke: \"black\", strokeWidth: \"3\", fill: \"none\" };\n\n var largeArcFlag = this.sweep <= Math.PI ? \"0\" : \"1\";\n var sweepFlag = this.counterClockwise ? \"1\" : \"0\";\n var stroke = attrs.stroke,\n strokeWidth = attrs.strokeWidth,\n fill = attrs.fill;\n\n\n if (Flatten.Utils.EQ(this.sweep, 2 * Math.PI)) {\n var circle = new Flatten.Circle(this.pc, this.r);\n return circle.svg(attrs);\n } else {\n return \"\\n<path d=\\\"M\" + this.start.x + \",\" + this.start.y + \"\\n A\" + this.r + \",\" + this.r + \" 0 \" + largeArcFlag + \",\" + sweepFlag + \" \" + this.end.x + \",\" + this.end.y + \"\\\"\\n stroke=\\\"\" + stroke + \"\\\" stroke-width=\\\"\" + strokeWidth + \"\\\" fill=\\\"\" + fill + \"\\\"/>\";\n }\n }\n }, {\n key: \"sweep\",\n get: function get() {\n if (Flatten.Utils.EQ(this.startAngle, this.endAngle)) return 0.0;\n if (Flatten.Utils.EQ(Math.abs(this.startAngle - this.endAngle), Flatten.PIx2)) {\n return Flatten.PIx2;\n }\n var sweep = void 0;\n if (this.counterClockwise) {\n sweep = Flatten.Utils.GT(this.endAngle, this.startAngle) ? this.endAngle - this.startAngle : this.endAngle - this.startAngle + Flatten.PIx2;\n } else {\n sweep = Flatten.Utils.GT(this.startAngle, this.endAngle) ? this.startAngle - this.endAngle : this.startAngle - this.endAngle + Flatten.PIx2;\n }\n\n if (Flatten.Utils.GT(sweep, Flatten.PIx2)) {\n sweep -= Flatten.PIx2;\n }\n if (Flatten.Utils.LT(sweep, 0)) {\n sweep += Flatten.PIx2;\n }\n return sweep;\n }\n\n /**\r\n * Get start point of arc\r\n * @returns {Point}\r\n */\n\n }, {\n key: \"start\",\n get: function get() {\n var p0 = new Flatten.Point(this.pc.x + this.r, this.pc.y);\n return p0.rotate(this.startAngle, this.pc);\n }\n\n /**\r\n * Get end point of arc\r\n * @returns {Point}\r\n */\n\n }, {\n key: \"end\",\n get: function get() {\n var p0 = new Flatten.Point(this.pc.x + this.r, this.pc.y);\n return p0.rotate(this.endAngle, this.pc);\n }\n\n /**\r\n * Get center of arc\r\n * @returns {Point}\r\n */\n\n }, {\n key: \"center\",\n get: function get() {\n return this.pc.clone();\n }\n }, {\n key: \"vertices\",\n get: function get() {\n return [this.start.clone(), this.end.clone()];\n }\n\n /**\r\n * Get arc length\r\n * @returns {number}\r\n */\n\n }, {\n key: \"length\",\n get: function get() {\n return Math.abs(this.sweep * this.r);\n }\n\n /**\r\n * Get bounding box of the arc\r\n * @returns {Box}\r\n */\n\n }, {\n key: \"box\",\n get: function get() {\n var func_arcs = this.breakToFunctional();\n var box = func_arcs.reduce(function (acc, arc) {\n return acc.merge(arc.start.box);\n }, new Flatten.Box());\n box = box.merge(this.end.box);\n return box;\n }\n }], [{\n key: \"intersectArc2Arc\",\n value: function intersectArc2Arc(arc1, arc2) {\n var ip = [];\n\n if (arc1.box.notIntersect(arc2.box)) {\n return ip;\n }\n\n // Special case: overlapping arcs\n // May return up to 4 intersection points\n if (arc1.pc.equalTo(arc2.pc) && Flatten.Utils.EQ(arc1.r, arc2.r)) {\n var pt = void 0;\n\n pt = arc1.start;\n if (pt.on(arc2)) ip.push(pt);\n\n pt = arc1.end;\n if (pt.on(arc2)) ip.push(pt);\n\n pt = arc2.start;\n if (pt.on(arc1)) ip.push(pt);\n\n pt = arc2.end;\n if (pt.on(arc1)) ip.push(pt);\n\n return ip;\n }\n\n // Common case\n var circle1 = new Flatten.Circle(arc1.pc, arc1.r);\n var circle2 = new Flatten.Circle(arc2.pc, arc2.r);\n var ip_tmp = circle1.intersect(circle2);\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = ip_tmp[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var _pt = _step.value;\n\n if (_pt.on(arc1) && _pt.on(arc2)) {\n ip.push(_pt);\n }\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n return ip;\n }\n }, {\n key: \"intersectArc2Circle\",\n value: function intersectArc2Circle(arc, circle) {\n var ip = [];\n\n if (arc.box.notIntersect(circle.box)) {\n return ip;\n }\n\n // Case when arc center incident to circle center\n // Return arc's end points as 2 intersection points\n if (circle.pc.equalTo(arc.pc) && Flatten.Utils.EQ(circle.r, arc.r)) {\n ip.push(arc.start);\n ip.push(arc.end);\n return ip;\n }\n\n // Common case\n var circle1 = circle;\n var circle2 = new Flatten.Circle(arc.pc, arc.r);\n var ip_tmp = circle1.intersect(circle2);\n var _iteratorNormalCompletion2 = true;\n var _didIteratorError2 = false;\n var _iteratorError2 = undefined;\n\n try {\n for (var _iterator2 = ip_tmp[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {\n var pt = _step2.value;\n\n if (pt.on(arc)) {\n ip.push(pt);\n }\n }\n } catch (err) {\n _didIteratorError2 = true;\n _iteratorError2 = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion2 && _iterator2.return) {\n _iterator2.return();\n }\n } finally {\n if (_didIteratorError2) {\n throw _iteratorError2;\n }\n }\n }\n\n return ip;\n }\n }]);\n\n return Arc;\n }();\n\n /**\r\n * Function to create arc equivalent to \"new\" constructor\r\n * @param args\r\n */\n Flatten.arc = function () {\n for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return new (Function.prototype.bind.apply(Flatten.Arc, [null].concat(args)))();\n };\n};"},"hash":"13a617aefced5bcdb41aaa9587e4791f","cacheData":{"env":{}}}