fernandez-polygon-decomposition
Version:
An algorithm to decompose polygons with holes from "A practical algorithm for decomposing polygonal domains into convex polygons by diagonals" by J Fernández
1,682 lines (1,415 loc) • 53.9 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var robustCompare = _interopDefault(require('robust-compare'));
var robustCompress = _interopDefault(require('robust-compress'));
var robustOrientation = _interopDefault(require('robust-orientation'));
var robustProduct = _interopDefault(require('robust-product'));
var robustDiff = _interopDefault(require('robust-subtract'));
function _typeof(obj) {
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function (obj) {
return typeof obj;
};
} else {
_typeof = function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
}
return _typeof(obj);
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(source, true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(source).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _iterableToArrayLimit(arr, i) {
if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
return;
}
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"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance");
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
/**
* Useful to avoid floating point problems.
*/
var EPSILON = 0.00000001;
var robust = true;
var setRobustness = function setRobustness(bool) {
robust = bool;
};
var getRobustness = function getRobustness() {
return robust;
};
/**
* Compares two vertices of the same polygon. Both of the vertex must be defined by an unique id.
*
* @param {{ x: number, y: number, id: number }} vertex1
* @param {{ x: number, y: number, id: number }} vertex2
* @returns {boolean}
*/
function vertexEquality(vertex1, vertex2) {
if (vertex1.id === undefined || vertex2.id === undefined) {
throw new Error('A vertex must be defined by an unique id.');
}
return vertex1.id === vertex2.id;
}
/**
* Compares two vertices. Both of the vertex must be defined by an unique id and optionnally by an originalId.
* This method is used to compare vertices after the absoption phase of the absHol procedure.
*
* @param {{ x: number, y: number, id: number, originalId: number }} vertex1
* @param {{ x: number, y: number, id: number, originalId: number }} vertex2
* @returns {boolean}
*/
function vertexEqualityAfterAbsorption(vertex1, vertex2) {
if (vertex1.id === undefined || vertex2.id === undefined) {
throw new Error('A vertex must be defined by an unique id.');
}
return (vertex1.originalId || vertex1.id) === (vertex2.originalId || vertex2.id);
}
/**
* @param {{ x: number, y: number }} point1
* @param {{ x: number, y: number }} point2
* @returns {boolean}
*/
function pointEquality(point1, point2) {
return point1.x === point2.x && point1.y === point2.y;
}
/**
* @param {{ x: number, y: number }} point1
* @param {{ x: number, y: number }} point2
* @returns {number}
*/
function squaredDistance(point1, point2) {
return (point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y);
}
/**
* Checks if the polygon is flat (its area is 0).
* Using orientation for robustness
*
* @param {{{ x: number, y: number, id: number }[]}} polygon
* @returns {boolean}
*/
function isFlat(polygon) {
var polygonLength = polygon.length;
return polygon.every(function (point, index) {
var previousPoint = polygon[(index - 1 + polygonLength) % polygonLength];
var nextPoint = polygon[(index + 1) % polygonLength];
return orientation(previousPoint, point, nextPoint) === 0;
});
}
/**
* Check if the polygon vertices are clockwise ordered.
* Using orientation for robustness
*
* @param {{ x: number, y: number}[]} polygon
* @returns {boolean}
*/
function isClockwiseOrdered(polygon) {
if (!isSimple(polygon)) {
throw new Error('The isClockwiseOrdered method only works with simple polygons');
}
var polygonLength = polygon.length; // find bottom right vertex
var bottomRightVertexIndex = 0;
var bottomRightVertex = polygon[0];
for (var i = 1; i < polygonLength; i++) {
var vertex = polygon[i];
if (vertex.y < bottomRightVertex.y || vertex.y === bottomRightVertex.y && vertex.x > bottomRightVertex.x) {
bottomRightVertex = vertex;
bottomRightVertexIndex = i;
}
}
return orientation(polygon[(bottomRightVertexIndex - 1 + polygonLength) % polygonLength], bottomRightVertex, polygon[(bottomRightVertexIndex + 1) % polygonLength]) > 0;
}
/**
* This method always returns a new array
*
* @param {{ x: number, y: number}[]} polygon
* @returns {{ x: number, y: number}[]}
*/
function orderClockwise(polygon) {
if (!isClockwiseOrdered(polygon)) {
return _toConsumableArray(polygon).reverse();
}
return _toConsumableArray(polygon);
}
/**
* See https://stackoverflow.com/questions/38856588/given-three-coordinate-points-how-do-you-detect-when-the-angle-between-them-cro.
* The three points are in clockwise order.
* If the result if positive, then it is a clockwise turn, if it is negative, a ccw one.
* If the result is 0, the points are collinear.
*
* @param {{ x: number, y: number}} point1
* @param {{ x: number, y: number}} point2
* @param {{ x: number, y: number}} point3
* @returns {number}
*/
function orientation(point1, point2, point3) {
if (robust) {
var o = robustOrientation([point1.x, point1.y], [point2.x, point2.y], [point3.x, point3.y]);
return o === 0 ? o : -o; // the y-axis is inverted
} else {
// return -((point1.y - point3.y) * (point2.x - point3.x) - (point1.x - point3.x) * (point2.y - point3.y));
return (point2.x - point1.x) * (point3.y - point1.y) - (point2.y - point1.y) * (point3.x - point1.x);
}
}
/**
* Checks on which side of the line (point2, point3) the point point1 is.
*
* @param {{ x: number, y: number}} point1
* @param {{ x: number, y: number}} point2
* @param {{ x: number, y: number}} point3
* @returns {number}
*/
function sideOfLine(point1, point2, point3) {
return orientation(point2, point3, point1);
}
var VERTEX_CODE = 100;
var EDGE_CODE = 101;
/**
* Winding number algorithm.
* See https://en.wikipedia.org/wiki/Point_in_polygon?#Winding_number_algorithm
* And more specifically http://www.inf.usi.ch/hormann/papers/Hormann.2001.TPI.pdf
*
* @param {{ x: number, y: number}} point
* @param {{ x: number, y: number}[]} polygon
* @returns {number}
*/
function windingNumber(point, polygon) {
var polygonPoint = polygon[0];
if (polygonPoint.x === point.x && polygonPoint.y === point.y) {
return VERTEX_CODE;
}
var polygonLength = polygon.length;
var wn = 0;
for (var i = 0; i < polygonLength; i++) {
var _polygonPoint = polygon[i];
var nextPolygonPoint = polygon[(i + 1) % polygonLength];
if (nextPolygonPoint.y === point.y) {
if (nextPolygonPoint.x === point.x) {
return VERTEX_CODE;
} else {
if (_polygonPoint.y === point.y && nextPolygonPoint.x > point.x === _polygonPoint.x < point.x) {
return EDGE_CODE;
}
}
}
if (_polygonPoint.y < point.y !== nextPolygonPoint.y < point.y) {
// crossing
if (_polygonPoint.x >= point.x) {
if (nextPolygonPoint.x > point.x) {
// wn += 2 * (nextPolygonPoint.y > polygonPoint.y ? 1 : -1) - 1;
wn += nextPolygonPoint.y > _polygonPoint.y ? 1 : -1;
} else {
var det = (_polygonPoint.x - point.x) * (nextPolygonPoint.y - point.y) - (nextPolygonPoint.x - point.x) * (_polygonPoint.y - point.y);
if (det === 0) {
return EDGE_CODE;
} else if (det > 0 === nextPolygonPoint.y > _polygonPoint.y) {
// right_crossing
// wn += 2 * (nextPolygonPoint.y > polygonPoint.y ? 1 : -1) - 1;
wn += nextPolygonPoint.y > _polygonPoint.y ? 1 : -1;
}
}
} else {
if (nextPolygonPoint.x > point.x) {
var _det = (_polygonPoint.x - point.x) * (nextPolygonPoint.y - point.y) - (nextPolygonPoint.x - point.x) * (_polygonPoint.y - point.y);
if (_det === 0) {
return EDGE_CODE;
} else if (_det > 0 === nextPolygonPoint.y > _polygonPoint.y) {
// right_crossing
// wn += 2 * (nextPolygonPoint.y > polygonPoint.y ? 1 : -1) - 1;
wn += nextPolygonPoint.y > _polygonPoint.y ? 1 : -1;
}
}
}
}
}
return wn;
}
/**
* Winding number algorithm.
* Using robust arithmetic.
*
* @param {{ x: number, y: number}} point
* @param {{ x: number, y: number}[]} polygon
* @returns {number}
*/
function robustWindingNumber(point, polygon) {
var polygonPoint = polygon[0];
if (polygonPoint.x === point.x && polygonPoint.y === point.y) {
return VERTEX_CODE;
}
var polygonLength = polygon.length;
var wn = 0;
for (var i = 0; i < polygonLength; i++) {
var _polygonPoint2 = polygon[i];
var nextPolygonPoint = polygon[(i + 1) % polygonLength];
if (nextPolygonPoint.y === point.y) {
if (nextPolygonPoint.x === point.x) {
return VERTEX_CODE;
} else {
if (_polygonPoint2.y === point.y && nextPolygonPoint.x > point.x === _polygonPoint2.x < point.x) {
return EDGE_CODE;
}
}
}
if (_polygonPoint2.y < point.y !== nextPolygonPoint.y < point.y) {
// crossing
if (_polygonPoint2.x >= point.x) {
if (nextPolygonPoint.x > point.x) {
// wn += 2 * ((nextPolygonPoint.y > polygonPoint.y) | 0) - 1;
wn += nextPolygonPoint.y > _polygonPoint2.y ? 1 : -1;
} else {
var det = robustDiff(robustProduct(robustDiff([_polygonPoint2.x], [point.x]), robustDiff([nextPolygonPoint.y], [point.y])), robustProduct(robustDiff([nextPolygonPoint.x], [point.x]), robustDiff([_polygonPoint2.y], [point.y])));
var detComparison = robustCompare(det, [0]);
if (detComparison === 0) {
return EDGE_CODE;
} else if (detComparison > 0 === nextPolygonPoint.y > _polygonPoint2.y) {
// right_crossing
// wn += 2 * ((nextPolygonPoint.y > polygonPoint.y) | 0) - 1;
wn += nextPolygonPoint.y > _polygonPoint2.y ? 1 : -1;
}
}
} else {
if (nextPolygonPoint.x > point.x) {
var _det2 = robustDiff(robustProduct(robustDiff([_polygonPoint2.x], [point.x]), robustDiff([nextPolygonPoint.y], [point.y])), robustProduct(robustDiff([nextPolygonPoint.x], [point.x]), robustDiff([_polygonPoint2.y], [point.y])));
var _detComparison = robustCompare(_det2, [0]);
if (_detComparison === 0) {
return EDGE_CODE;
} else if (_detComparison > 0 === nextPolygonPoint.y > _polygonPoint2.y) {
// right_crossing
// wn += 2 * ((nextPolygonPoint.y > polygonPoint.y) | 0) - 1;
wn += nextPolygonPoint.y > _polygonPoint2.y ? 1 : -1;
}
}
}
}
}
return wn;
}
/**
* Checks if the point is inside (or on the edge) of the polygon.
*
* @param {{ x: number, y: number}} point
* @param {{ x: number, y: number}[]} polygon
* @returns {boolean}
*/
function inPolygon(point, polygon) {
if (robust) {
// return classifyPoint(polygon.map(({ x, y }) => [x, y]), [point.x, point.y]) <= 0;
return robustWindingNumber(point, polygon) !== 0;
} else {
return windingNumber(point, polygon) !== 0;
}
}
/**
* Checks if the point is inside (or on the edge) of a convex polygon.
* We assume that the vertices are in clockwise order.
*
* @param {{ x: number, y: number}} point
* @param {{ x: number, y: number}[]} convexPolygon
* @returns {boolean}
*/
function inConvexPolygon(point, convexPolygon) {
var polygonLength = convexPolygon.length;
return convexPolygon.every(function (previousPoint, index) {
var nextPoint = convexPolygon[(index + 1) % polygonLength];
return orientation(previousPoint, point, nextPoint) <= 0;
});
}
/**
* Check if the polygon polygon2 is (at least partially) contained by the polygon polygon1.
*
* @param {{ x: number, y: number, id: number }[]} polygon1
* @param {{ x: number, y: number, id: number }[]} polygon2
* @returns {boolean}
*/
function containsPolygon(polygon1, polygon2) {
return polygon2.some(function (vertex) {
return inPolygon(vertex, polygon1);
});
}
/**
* Check if the polygon polygon2 is totally contained by the polygon polygon1.
*
* @param {{ x: number, y: number, id: number }[]} polygon1
* @param {{ x: number, y: number, id: number }[]} polygon2
* @returns {boolean}
*/
function containsEntirePolygon(polygon1, polygon2) {
return polygon2.every(function (vertex) {
return inPolygon(vertex, polygon1);
});
}
/**
* Given a vertex of one polygon, returns the next vertex (in clockwise order) of this polygon.
*
* @param {{ x: number, y: number, id: number }} vertex
* @param {{ x: number, y: number, id: number }[]} polygon
* @returns {{ x: number, y: number, id: number }}
*/
function nextVertex(vertex, polygon) {
var polygonLength = polygon.length;
var vertexIndex = polygon.findIndex(function (v) {
return vertexEquality(vertex, v);
});
if (vertexIndex === -1) {
throw new Error('could not find vertex');
}
return polygon[(vertexIndex + 1) % polygonLength];
}
/**
* Given a vertex of one polygon, returns the previous vertex (in clockwise order) of this polygon.
*
* @param {{ x: number, y: number, id: number }} vertex
* @param {{ x: number, y: number, id: number }[]} polygon
* @returns {{ x: number, y: number, id: number }}
*/
function previousVertex(vertex, polygon) {
var polygonLength = polygon.length;
var vertexIndex = polygon.findIndex(function (v) {
return vertexEquality(vertex, v);
});
if (vertexIndex === -1) {
throw new Error('could not find vertex');
}
return polygon[(vertexIndex - 1 + polygonLength) % polygonLength];
}
/**
* Checks if a point is one of the vertex of a polygon.
*
* @param {{ x: number, y: number }} point
* @param {{ x: number, y: number, id: number }[]} polygon
* @returns {boolean}
*/
function isAVertex(point, polygon) {
return polygon.some(function (v) {
return pointEquality(v, point);
});
}
/**
* Returns all the notches of a given polygon.
*
* @param {{ x: number, y: number, id: number }[]} polygon
* @returns {{ x: number, y: number, id: number }[]}
*/
function getNotches(polygon) {
var polygonLength = polygon.length;
return polygon.filter(function (vertex, vertexIndex) {
return orientation(polygon[(vertexIndex - 1 + polygonLength) % polygonLength], vertex, polygon[(vertexIndex + 1) % polygonLength]) < 0;
});
}
/**
* Returns all the edges of a given polygon.
* An edge is one segment between two consecutive vertex of the polygon.
*
* @param {{ x: number, y: number, id: number }[]} polygon
* @returns {{ a: { x: number, y: number, id: number }, b: { x: number, y: number, id: number }}[]}
*/
function getEdges(polygon) {
var edges = [];
var polygonLength = polygon.length;
for (var i = 0; i < polygonLength; i++) {
edges.push({
a: polygon[i],
b: polygon[(i + 1) % polygonLength]
});
}
return edges;
}
/**
* Given a vertex of one polygon, returns the next notch (in clockwise order) of this polygon.
* Returns null if there are no notch in the polygon.
*
* @param {{ x: number, y: number, id: number }} vertex
* @param {{ x: number, y: number, id: number }[]} polygon
* @returns {({ x: number, y: number, id: number }|null)}
*/
function nextNotch(vertex, polygon) {
var polygonLength = polygon.length;
var vertexIndex = polygon.findIndex(function (v) {
return vertexEquality(vertex, v);
});
var notchIndex = (vertexIndex + 1) % polygonLength;
while (notchIndex !== vertexIndex) {
if (orientation(polygon[(notchIndex - 1 + polygonLength) % polygonLength], polygon[notchIndex], polygon[(notchIndex + 1) % polygonLength]) < 0) {
return polygon[notchIndex];
}
notchIndex = (notchIndex + 1) % polygonLength;
} // if we started by the only notch, it will return the notch.
if (orientation(polygon[(notchIndex - 1 + polygonLength) % polygonLength], polygon[notchIndex], polygon[(notchIndex + 1) % polygonLength]) < 0) {
return polygon[notchIndex];
}
return null;
}
/**
* Given a vertex of one polygon, returns the previous notch (in clockwise order) of this polygon.
* Returns null if there are no notch in the polygon.
*
* @param {{ x: number, y: number, id: number }} vertex
* @param {{ x: number, y: number, id: number }[]} polygon
* @returns {({ x: number, y: number, id: number }|null)}
*/
function previousNotch(vertex, polygon) {
var polygonLength = polygon.length;
var vertexIndex = polygon.findIndex(function (v) {
return vertexEquality(vertex, v);
});
var notchIndex = (vertexIndex - 1 + polygonLength) % polygonLength;
while (notchIndex !== vertexIndex) {
if (orientation(polygon[(notchIndex - 1 + polygonLength) % polygonLength], polygon[notchIndex], polygon[(notchIndex + 1) % polygonLength]) < 0) {
return polygon[notchIndex];
}
notchIndex = (notchIndex - 1 + polygonLength) % polygonLength;
} // if we started by the only notch, it will return the notch.
if (orientation(polygon[(notchIndex - 1 + polygonLength) % polygonLength], polygon[notchIndex], polygon[(notchIndex + 1) % polygonLength]) < 0) {
return polygon[notchIndex];
}
return null;
}
/**
* Removes polygon2 from polygon1.
* polygon2 vertices must be a subset of polygon1 vertices
*
* @param {{ x: number, y: number, id: number }[]} polygon1
* @param {{ x: number, y: number, id: number }[]} polygon2
* @returns {{ x: number, y: number, id: number }[]}
*/
function substractPolygons(polygon1, polygon2) {
// const firstIndex = polygon1.findIndex(p => pointEquality(p, polygon2[0]));
// const lastIndex = polygon1.findIndex(p => pointEquality(p, polygon2[polygon2.length - 1]));
var firstIndex = polygon1.findIndex(function (p) {
return vertexEquality(p, polygon2[0]);
});
var lastIndex = polygon1.findIndex(function (p) {
return vertexEquality(p, polygon2[polygon2.length - 1]);
});
if (firstIndex < lastIndex) {
return [].concat(_toConsumableArray(polygon1.slice(0, firstIndex)), [polygon1[firstIndex]], _toConsumableArray(polygon1.slice(lastIndex)));
} else {
return _toConsumableArray(polygon1.slice(lastIndex, firstIndex + 1));
}
}
/**
* @param {{{ x: number, y: number, id: number }[]}} polygon
* @return {boolean}
*/
function isConvex(polygon) {
var polygonLength = polygon.length;
return polygon.every(function (vertex, vertexIndex) {
return orientation(polygon[(vertexIndex - 1 + polygonLength) % polygonLength], vertex, polygon[(vertexIndex + 1) % polygonLength]) >= 0;
});
}
/**
* Quick hack to convert a non-overlapping increasing sequence into a number.
*
* @param {number[]} sequence
* @returns {number}
*/
function robustSequenceToNumber(sequence) {
var compressedSequence = robustCompress(_toConsumableArray(sequence));
return compressedSequence[compressedSequence.length - 1];
}
/**
* See http://paulbourke.net/geometry/pointlineplane/
* This method performs exact arithmetic calculations (except for the x and y values)
*
* @param {{ a: { x: number, y: number }, b: { x: number, y: number }}} line1
* @param {{ a: { x: number, y: number }, b: { x: number, y: number }}} line2
* @returns {{ x: number, y: number, insideSegment1: boolean, onEdgeSegment1: boolean, insideSegment2: boolean, onEdgeSegment2: boolean }}
*/
function robustLineIntersection(line1, line2) {
var _line1$a = line1.a,
x1 = _line1$a.x,
y1 = _line1$a.y,
_line1$b = line1.b,
x2 = _line1$b.x,
y2 = _line1$b.y;
var _line2$a = line2.a,
x3 = _line2$a.x,
y3 = _line2$a.y,
_line2$b = line2.b,
x4 = _line2$b.x,
y4 = _line2$b.y;
var y4y3 = robustDiff([y4], [y3]);
var x2x1 = robustDiff([x2], [x1]);
var x4x3 = robustDiff([x4], [x3]);
var y2y1 = robustDiff([y2], [y1]);
var y1y3 = robustDiff([y1], [y3]);
var x1x3 = robustDiff([x1], [x3]);
var denom = robustDiff(robustProduct(y4y3, x2x1), robustProduct(x4x3, y2y1));
var robustDenomComparison = robustCompare(denom, [0]);
if (robustDenomComparison === 0) {
return null;
}
var ua = robustDiff(robustProduct(x4x3, y1y3), robustProduct(y4y3, x1x3));
var ub = robustDiff(robustProduct(x2x1, y1y3), robustProduct(y2y1, x1x3));
var comparisonUaMin, comparisonUaMax, comparisonUbMin, comparisonUbMax;
if (robustDenomComparison > 0) {
comparisonUaMin = robustCompare(ua, [0]);
comparisonUaMax = robustCompare(ua, denom);
comparisonUbMin = robustCompare(ub, [0]);
comparisonUbMax = robustCompare(ub, denom);
} else {
comparisonUaMin = robustCompare([0], ua);
comparisonUaMax = robustCompare(denom, ua);
comparisonUbMin = robustCompare([0], ub);
comparisonUbMax = robustCompare(denom, ub);
}
var nonRobustDenom = robustSequenceToNumber(denom); // x and y are not exact numbers, but it is enough for the algo
return {
x: x1 + robustSequenceToNumber(robustProduct(ua, x2x1)) / nonRobustDenom,
y: y1 + robustSequenceToNumber(robustProduct(ua, y2y1)) / nonRobustDenom,
insideSegment1: comparisonUaMin > 0 && comparisonUaMax < 0,
onEdgeSegment1: comparisonUaMin === 0 || comparisonUaMax === 0,
insideSegment2: comparisonUbMin > 0 && comparisonUbMax < 0,
onEdgeSegment2: comparisonUbMin === 0 || comparisonUbMax === 0
};
}
/**
* See http://paulbourke.net/geometry/pointlineplane/
*
* @param {{ a: { x: number, y: number }, b: { x: number, y: number }}} line1
* @param {{ a: { x: number, y: number }, b: { x: number, y: number }}} line2
* @returns {{ x: number, y: number, insideSegment1: boolean, onEdgeSegment1: boolean, insideSegment2: boolean, onEdgeSegment2: boolean }}
*/
function lineIntersection(line1, line2) {
if (robust) {
return robustLineIntersection(line1, line2);
}
var _line1$a2 = line1.a,
x1 = _line1$a2.x,
y1 = _line1$a2.y,
_line1$b2 = line1.b,
x2 = _line1$b2.x,
y2 = _line1$b2.y;
var _line2$a2 = line2.a,
x3 = _line2$a2.x,
y3 = _line2$a2.y,
_line2$b2 = line2.b,
x4 = _line2$b2.x,
y4 = _line2$b2.y;
var denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
if (Math.abs(denom) < EPSILON) {
return null;
}
var ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
var ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
return {
x: x1 + ua * (x2 - x1),
y: y1 + ua * (y2 - y1),
insideSegment1: ua > 0 && ua < 1,
onEdgeSegment1: ua === 0 || ua === 1,
insideSegment2: ub > 0 && ub < 1,
onEdgeSegment2: ub === 0 || ub === 1
};
}
/**
* Checks if the polygon is simple.
* See https://en.wikipedia.org/wiki/Simple_polygon
*
* @param {{ x: number, y: number, id: number }[]} polygon
* @returns {boolean}
*/
function isSimple(polygon) {
if (polygon.length < 3) {
return true;
}
var segments = [];
for (var i = 0; i < polygon.length - 1; i++) {
segments.push({
a: polygon[i],
b: polygon[i + 1]
});
}
segments.push({
a: polygon[polygon.length - 1],
b: polygon[0]
});
return !segments.some(function (segment1) {
return segments.some(function (segment2) {
if (segment1 === segment2) {
return false;
}
var intersection = lineIntersection(segment1, segment2);
if (intersection === null) {
return false;
}
var insideSegment1 = intersection.insideSegment1,
insideSegment2 = intersection.insideSegment2;
return insideSegment1 && insideSegment2;
});
});
}
/**
* This is the MP1 procedure taken from "Algorithms for the decomposition of a polygon into convex polygons" by J. Fernandez et al.
* The variable names (for the most part) are named according to the publication.
*
* @param {{ x: number, y: number, id: number }[]} P P is a polygon with clockwise ordered vertices.
* @param {{ x: number, y: number, id: number }[]} initialLVertices The initial vertices for the polygon L
* @returns {{ L: { x: number, y: number, id: number }[], end: boolean }}
*/
function MP1(P, initialLVertices) {
if (P.length < 4) {
return {
L: _toConsumableArray(P),
end: true
};
}
var L = _toConsumableArray(initialLVertices);
if (L.length < 1) {
L.push(P[0]);
}
if (L.length < 2) {
L.push(nextVertex(L[0], P));
}
var v1 = L[0];
var v2 = L[1];
var vim1 = v1;
var vi = v2;
var vip1 = nextVertex(vi, P);
while (L.length < P.length) {
if (orientation(vim1, vi, vip1) >= 0 && orientation(vi, vip1, v1) >= 0 && orientation(vip1, v1, v2) >= 0) {
L.push(vip1);
} else {
break;
}
vim1 = vi;
vi = vip1;
vip1 = nextVertex(vi, P);
}
if (P.length === L.length) {
return {
L: L,
end: true
};
} else if (L.length < 2) {
return {
L: L,
end: true
};
} else {
var _loop = function _loop() {
var PmL = substractPolygons(P, L); // filter on L's bounding rectangle first ?
var notches = getNotches(PmL).filter(function (point) {
return !isAVertex(point, L);
}).filter(function (point) {
return inConvexPolygon(point, L);
});
if (notches.length === 0) {
return "break";
}
var v1 = L[0];
var k = L.length;
var vk = L[k - 1];
L = L.slice(0, -1); // L.filter(point => !vertexEquality(point, vk));
notches.forEach(function (notch) {
var sideOfVk = Math.sign(sideOfLine(vk, v1, notch));
if (sideOfVk !== 0) {
L = L.filter(function (point) {
return Math.sign(sideOfLine(point, v1, notch)) !== sideOfVk;
});
}
});
};
while (L.length > 2) {
var _ret = _loop();
if (_ret === "break") break;
}
return {
L: L,
end: false
};
}
}
/**
* This is the MP1' procedure taken from "Algorithms for the decomposition of a polygon into convex polygons" by J. Fernandez et al.
* The variable names (for the most part) are named according to the publication.
*
* @param {{ x: number, y: number, id: number }[]} P P is a polygon with clockwise ordered vertices.
* @param {{ x: number, y: number, id: number }[]} initialLVertices The initial vertices for the polygon L
* @returns {{ L: { x: number, y: number, id: number }[], end: boolean }}
*/
function MP1Prime(P, initialLVertices) {
if (P.length < 4) {
return {
L: _toConsumableArray(P),
end: true
};
}
var L = _toConsumableArray(initialLVertices);
if (L.length < 1) {
L.unshift(P[0]);
}
if (L.length < 2) {
L.unshift(previousVertex(L[0], P));
}
var vk = L[L.length - 1];
var vkm1 = L[L.length - 2];
var vim1 = L[1];
var vi = L[0];
var vip1 = previousVertex(vi, P);
while (L.length < P.length) {
if (orientation(vim1, vi, vip1) <= 0 && orientation(vi, vip1, vk) <= 0 && orientation(vip1, vk, vkm1) <= 0) {
L.unshift(vip1);
} else {
break;
}
vim1 = vi;
vi = vip1;
vip1 = previousVertex(vi, P);
}
if (P.length === L.length) {
return {
L: L,
end: true
};
} else if (L.length < 2) {
return {
L: L,
end: true
};
} else {
var _loop2 = function _loop2() {
var PmL = substractPolygons(P, L);
var notches = getNotches(PmL).filter(function (point) {
return !isAVertex(point, L);
}).filter(function (point) {
return inConvexPolygon(point, L);
});
if (notches.length === 0) {
return "break";
} // CCW order
var v1 = L[L.length - 1];
var vk = L[0];
L = L.slice(1); // L.filter(point => !vertexEquality(point, vk));
notches.forEach(function (notch) {
var sideOfVk = Math.sign(sideOfLine(vk, v1, notch));
if (sideOfVk !== 0) {
L = L.filter(function (point) {
return Math.sign(sideOfLine(point, v1, notch)) !== sideOfVk;
});
}
});
};
while (L.length > 2) {
var _ret2 = _loop2();
if (_ret2 === "break") break;
}
return {
L: L,
end: false
};
}
}
function rotateRight(arr, n) {
var length = arr.length;
return [].concat(_toConsumableArray(arr.slice(length - n)), _toConsumableArray(arr.slice(0, length - n)));
}
/**
* Merges two polygons.
* polygon1 and polygon2 should be convex and share an edge (= two consecutive vertices)
*
* @param {{ x: number, y: number, id: number }[]} polygon1
* @param {{ x: number, y: number, id: number }[]} polygon2
* @returns {{ x: number, y: number, id: number }[]}
*/
function mergePolygons(polygon1, polygon2) {
// const sharedVertices = polygon1.map((v1, index) => [index, polygon2.findIndex(v2 => pointEquality(v1, v2))]).filter(([_, v2Index]) => v2Index > -1); // can be problematic when a point corresponds to several vertices
var sharedVertices = polygon1.map(function (v1, index) {
return [index, polygon2.findIndex(function (v2) {
return vertexEqualityAfterAbsorption(v1, v2);
})];
}).filter(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
_ = _ref2[0],
v2Index = _ref2[1];
return v2Index > -1;
});
var polygon1Length = polygon1.length;
if (sharedVertices.length !== 2) {
throw new Error("sharedVertices length should be 2 : ".concat(JSON.stringify(sharedVertices)));
}
if ((sharedVertices[0][0] + 1) % polygon1Length === sharedVertices[1][0]) {
return [].concat(_toConsumableArray(rotateRight(polygon1, polygon1Length - sharedVertices[1][0])), _toConsumableArray(rotateRight(polygon2, polygon2.length - sharedVertices[0][1]).slice(1, -1)));
} else {
return [].concat(_toConsumableArray(rotateRight(polygon1, polygon1Length - sharedVertices[0][0])), _toConsumableArray(rotateRight(polygon2, polygon2.length - sharedVertices[1][1]).slice(1, -1)));
}
}
/**
* Procedure to remove inessential diagonals from the partition of convex polygons.
*
* @params {{ x: number, y: number, id: number }[][]} polygons
* @params {{ i2: { x: number, y: number, id: number }, j2: { x: number, y: number, id: number }, rightPolygon: { x: number, y: number, id: number }[][], leftPolygon: { x: number, y: number, id: number }[][] }[]}
* @returns {{ x: number, y: number, id: number }[][]}
*/
function mergingAlgorithm(polygons, LLE) {
if (polygons.length < 2) {
return polygons;
} // LDP[poly] = true means that the polygon `poly` is one of the definitive polygons of the partition after the merging process.
var LDP = new Map(); // LUP[poly1] = poly2 means that the polygon `poly1` is part of the polygon `poly2`.
var LUP = new Map();
polygons.forEach(function (poly) {
LDP.set(poly, true);
LUP.set(poly, poly);
});
if (LLE.length + 1 !== polygons.length) {
throw new Error('wtf ? LLE + 1 !== polygons.length (' + (LLE.length + 1) + ', ' + polygons.length + ')');
}
var _loop = function _loop(j) {
var _LLE$j = LLE[j],
i2 = _LLE$j.i2,
j2 = _LLE$j.j2,
rightPolygon = _LLE$j.rightPolygon,
leftPolygon = _LLE$j.leftPolygon;
var Pj = LUP.get(leftPolygon);
var Pu = LUP.get(rightPolygon);
var PjLength = Pj.length;
var PuLength = Pu.length; // custom nextVertex & previousVertex to take into account the originalId (for absHol)
var i1 = Pu[(Pu.findIndex(function (v) {
return vertexEqualityAfterAbsorption(v, i2);
}) + PuLength - 1) % PuLength]; // previousVertex(i2, Pu);
var i3 = Pj[(Pj.findIndex(function (v) {
return vertexEqualityAfterAbsorption(v, i2);
}) + 1) % PjLength]; // nextVertex(i2, Pj);
var j1 = Pj[(Pj.findIndex(function (v) {
return vertexEqualityAfterAbsorption(v, j2);
}) + PjLength - 1) % PjLength]; // previousVertex(j2, Pj)
var j3 = Pu[(Pu.findIndex(function (v) {
return vertexEqualityAfterAbsorption(v, j2);
}) + 1) % PuLength]; // nextVertex(j2, Pu);
if (orientation(i1, i2, i3) >= 0 && orientation(j1, j2, j3) >= 0) {
var P = mergePolygons(Pj, Pu);
if (!isConvex(P)) {
throw new Error('mergePolygons is not convex !');
}
LDP.set(Pj, false);
LDP.set(Pu, false);
LDP.set(P, true);
LUP.set(P, P);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = LUP.keys()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var poly = _step.value;
if (LUP.get(poly) === Pj || LUP.get(poly) === Pu) {
LUP.set(poly, P);
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"] != null) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
};
for (var j = 0; j < LLE.length; j++) {
_loop(j);
}
return _toConsumableArray(LDP.entries()).filter(function (_ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
_ = _ref4[0],
inPartition = _ref4[1];
return inPartition;
}).map(function (_ref5) {
var _ref6 = _slicedToArray(_ref5, 1),
polygon = _ref6[0];
return polygon;
});
}
function preprocessPolygon(polygon) {
var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var polygonLength = polygon.length;
return polygon.filter(function (vertex, index) {
return !pointEquality(vertex, polygon[(index + 1) % polygonLength]);
}).map(function (vertex, index) {
return _objectSpread2({}, vertex, {
id: index + offset
});
});
}
/**
* This is the MP5 procedure taken from "A practical algorithm for decomposing polygonal domains into convex polygons by diagonals"
*
* @param {{ x: number, y: number, id: number }[]} polygon
* @param {{ x: number, y: number, id: number }} startingVertex
* @returns {{ L: { x: number, y: number, id: number }[], end: boolean }}
*/
function MP5Procedure(polygon) {
var startingVertex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : polygon[0];
if (polygon.length < 4) {
return {
convexPolygon: _toConsumableArray(polygon),
end: true
};
}
var startingNotch = nextNotch(startingVertex, polygon);
if (startingNotch === null) {
// the polygon is convex if there is no notch in it
return {
convexPolygon: _toConsumableArray(polygon),
end: true
};
}
var currentNotch = startingNotch;
var _loop = function _loop() {
var _MP = MP1(polygon, [currentNotch]),
cwL = _MP.L,
cwEnd = _MP.end;
if (cwEnd) {
return {
v: {
convexPolygon: cwL,
end: true
}
};
} // MP1 + notch checking = MP3
if (cwL.length > 2 && getNotches(polygon).some(function (vertex) {
return vertexEquality(vertex, cwL[0]) || vertexEquality(vertex, cwL[cwL.length - 1]);
})) {
return {
v: {
convexPolygon: cwL,
end: false
}
};
}
currentNotch = nextNotch(currentNotch, polygon);
};
do {
var _ret = _loop();
if (_typeof(_ret) === "object") return _ret.v;
} while (!vertexEquality(currentNotch, startingNotch));
currentNotch = startingNotch;
var _loop2 = function _loop2() {
var _MP1Prime = MP1Prime(polygon, [currentNotch]),
ccwL = _MP1Prime.L,
ccwEnd = _MP1Prime.end;
if (ccwEnd) {
return {
v: {
convexPolygon: ccwL,
end: true
}
};
} // MP1 + notch checking = MP3
if (ccwL.length > 2 && getNotches(polygon).some(function (vertex) {
return vertexEquality(vertex, ccwL[0]) || vertexEquality(vertex, ccwL[ccwL.length - 1]);
})) {
return {
v: {
convexPolygon: ccwL,
end: false
}
};
}
currentNotch = previousNotch(currentNotch, polygon);
};
do {
var _ret2 = _loop2();
if (_typeof(_ret2) === "object") return _ret2.v;
} while (!vertexEquality(currentNotch, startingNotch));
throw new Error('ERROR MP5Procedure 3');
}
/**
* This is the full MP5 algorithm taken from "A practical algorithm for decomposing polygonal domains into convex polygons by diagonals"
*
* @param {{ x: number, y: number }[]} polygon
* @returns {{ x: number, y: number }[][]} The partition of convex polygons
*/
function MP5(polygon) {
if (!Array.isArray(polygon)) {
throw new Error('MP5 can only take an array of points {x, y} as input');
}
if (polygon.length <= 2) {
return [polygon];
}
if (!isClockwiseOrdered(polygon)) {
throw new Error('MP5 can only work with clockwise ordered polygon');
} // L is containing the convex polygons.
var L = []; // LLE is a list containing the diagonals of the partition. It will be used to merge inessential diagonals.
var LLE = []; // Adds an id to each vertex.
var P = preprocessPolygon(polygon);
var _loop3 = function _loop3() {
var _MP5Procedure = MP5Procedure(P),
convexPolygon = _MP5Procedure.convexPolygon,
end = _MP5Procedure.end;
var diagonal = {
a: convexPolygon[0],
b: convexPolygon[convexPolygon.length - 1]
};
L.push(convexPolygon);
getEdges(convexPolygon).forEach(function (edge) {
for (var i = 0; i < LLE.length; i++) {
var _LLE$i = LLE[i],
i2 = _LLE$i.i2,
j2 = _LLE$i.j2;
if (vertexEquality(i2, edge.b) && vertexEquality(j2, edge.a)) {
LLE[i].leftPolygon = convexPolygon;
break;
}
}
});
if (end) {
return "break";
}
LLE.push({
i2: diagonal.b,
j2: diagonal.a,
rightPolygon: convexPolygon
});
P = substractPolygons(P, convexPolygon);
};
while (true) {
var _ret3 = _loop3();
if (_ret3 === "break") break;
}
return mergingAlgorithm(L, LLE).map(function (poly) {
return poly.map(function (_ref) {
var x = _ref.x,
y = _ref.y;
return {
x: x,
y: y
};
});
});
}
/**
* Checks if the given segment instersects the polygon
*
* @param {{ a: { x: number, y: number }, b: { x: number, y: number }}} segment
* @param {{ x: number, y: number }[]} polygon
* @returns {boolean}
*/
var segmentIntersectsPolygon = function segmentIntersectsPolygon(segment, polygon) {
var polygonLength = polygon.length;
for (var i = 0; i < polygonLength; i++) {
var edge = {
a: polygon[(i - 1 + polygonLength) % polygonLength],
b: polygon[i]
};
var intersection = lineIntersection(segment, edge);
if (intersection === null) {
continue;
}
var insideSegment1 = intersection.insideSegment1,
insideSegment2 = intersection.insideSegment2,
onEdgeSegment1 = intersection.onEdgeSegment1,
onEdgeSegment2 = intersection.onEdgeSegment2;
if ((insideSegment1 || onEdgeSegment1) && (insideSegment2 || onEdgeSegment2)) {
return true;
}
}
return false;
};
/**
* Returns all the edges of the given hole that intersects the given segment.
*
* @param {{ a: { x: number, y: number }, b: { x: number, y: number }}} segment
* @param {{ x: number, y: number, id: number }[]} hole
* @returns {{ x: number, y: number, edge: { a: { x: number, y: number }, b: { x: number, y: number }}, hole: { x: number, y: number, id: number }[] }[]}
*/
var getSegmentHoleIntersectionEdges = function getSegmentHoleIntersectionEdges(segment, hole) {
var edges = [];
var holeLength = hole.length;
for (var i = 0; i < holeLength; i++) {
var edge = {
a: hole[(i - 1 + holeLength) % holeLength],
b: hole[i]
};
var intersection = lineIntersection(segment, edge);
if (intersection === null) {
continue;
}
var x = intersection.x,
y = intersection.y,
insideSegment1 = intersection.insideSegment1,
insideSegment2 = intersection.insideSegment2,
onEdgeSegment1 = intersection.onEdgeSegment1,
onEdgeSegment2 = intersection.onEdgeSegment2;
if ((insideSegment1 || onEdgeSegment1) && (insideSegment2 || onEdgeSegment2)) {
edges.push({
x: x,
y: y,
edge: edge,
hole: hole
});
}
}
return edges;
};
var rotateLeft = function rotateLeft(a) {
var i = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
return [].concat(_toConsumableArray(a.slice(i)), _toConsumableArray(a.slice(0, i)));
};
/**
* This is the DrawTrueDiagonal procedure taken from "A practical algorithm for decomposing polygonal domains into convex polygons by diagonals"
*
* @param {{ a: { x: number, y: number }, b: { x: number, y: number }}} diagonal
* @param {{ x: number, y: number, id: number }[]} C
* @param {{ x: number, y: number, id: number }[][]} holesInC
* @returns {{ a: { x: number, y: number }, b: { x: number, y: number }, hole: { x: number, y: number, id: number }[]}}
*/
var drawTrueDiagonal = function drawTrueDiagonal(diagonal, C, holesInC) {
var comparator = function comparator(a, b) {
return squaredDistance(diagonal.a, a) - squaredDistance(diagonal.a, b);
};
var edges = [];
var holesInCLength = holesInC.length;
for (var i = 0; i < holesInCLength; i++) {
var _edges;
(_edges = edges).push.apply(_edges, _toConsumableArray(getSegmentHoleIntersectionEdges(diagonal, holesInC[i])));
}
var previousClosestVertexId = diagonal.b.id;
while (edges.length > 0) {
/* const closestEdge = edges.sort(comparator).find(({ edge }) => {
return inConvexPolygon(edge.a, C) || inConvexPolygon(edge.b, C);
}); */
var closestEdge = edges.sort(comparator)[0];
var closestVertex = Object.values(closestEdge.edge).filter(function (v) {
return inConvexPolygon(v, C);
}).sort(comparator)[0];
if (closestVertex.id === previousClosestVertexId) {
return diagonal;
}
diagonal = {
a: diagonal.a,
b: closestVertex,
hole: closestEdge.hole
};
previousClosestVertexId = closestVertex.id;
edges = [];
for (var _i = 0; _i < holesInCLength; _i++) {
var _edges2;
(_edges2 = edges).push.apply(_edges2, _toConsumableArray(getSegmentHoleIntersectionEdges(diagonal, holesInC[_i])));
}
}
return diagonal;
};
/**
* This is the AbsHol algorithm taken from "A practical algorithm for decomposing polygonal domains into convex polygons by diagonals"
*
* @param {{ x: number, y: number, id: number }[]} P
* @param {{ x: number, y: number, id: number }[][]} holes
* @param {number} idOffset
* @returns {{ LPCP: { x: number, y: number, id: number }[][], trueDiagonals: { a: { x: number, y: number, id: number }, b: { x: number, y: number, id: number } }[] , LLE: { i2: { x: number, y: number, id: number }, j2: { x: number, y: number, id: number }, rightPolygon: { x: number, y: number, id: number }[][], leftPolygon: { x: number, y: number, id: number }[][] }[] }} The partition of convex polygons
*/
function absHolProcedure(P, holes, idOffset) {
var LLE = [];
var trueDiagonals = [];
var LPCP = [];
var Q = _toConsumableArray(P);
var _loop = function _loop() {
var _MP5Procedure = MP5Procedure(Q),
C = _MP5Procedure.convexPolygon,
end = _MP5Procedure.end;
var diagonal = {
a: C[0],
b: C[C.length - 1],
hole: null
};
var holesLength = holes.length;
var diagonalIsCutByAHole = false;
var holesInC = [];
for (var i = 0; i < holesLength; i++) {
var hole = holes[i];
if (!diagonalIsCutByAHole && segmentIntersectsPolygon(diagonal, hole)) {
diagonalIsCutByAHole = true;
diagonal.hole = hole;
}
if (containsPolygon(C, hole)) {
holesInC.push(hole);
}
}
if (diagonalIsCutByAHole || holesInC.length > 0) {
if (!diagonalIsCutByAHole) {
diagonal = {
a: C[0],
b: holesInC[0][0],
hole: holesInC[0]
};
}
var _drawTrueDiagonal = drawTrueDiagonal(diagonal, C, holesInC),
HPrime = _drawTrueDiagonal.hole,
dPrime = _objectWithoutProperties(_drawTrueDiagonal, ["hole"]);
trueDiagonals.push(dPrime); // Absorption of H'
holes = holes.filter(function (hole) {
return hole !== HPrime;
});
var vi = C[0];
var id1 = ++idOffset;
var id2 = ++idOffset;
var rotatedHPrime = rotateLeft(HPrime, HPrime.findIndex(function (v) {
return vertexEquality(v, dPrime.b);
}) + 1).reverse();
var viIndexInQ = Q.findIndex(function (v) {
return vertexEquality(v, vi);
});
Q = [].concat(_toConsumableArray(Q.slice(0, viIndexInQ + 1)), _toConsumableArray(rotatedHPrime), [_objectSpread2({}, rotatedHPrime[0], {
id: id1,
originalId: rotatedHPrime[0].id.originalId || rotatedHPrime[0].id
}), _objectSpread2({}, vi, {
id: id2,
originalId: vi.originalId || vi.id
})], _toConsumableArray(Q.slice(viIndexInQ + 1)));
} else {
LPCP.push(C);
getEdges(C).forEach(function (edge) {
for (var _i2 = 0; _i2 < LLE.length; _i2++) {
var _LLE$_i = LLE[_i2],
diagonalA = _LLE$_i.i2,
diagonalB = _LLE$_i.j2;
if (vertexEqualityAfterAbsorption(diagonalA, edge.b) && vertexEqualityAfterAbsorption(diagonalB, edge.a)) {
LLE[_i2].leftPolygon = C;
break;
}
}
});
if (end) {
return "break";
}
LLE.push({
i2: diagonal.b,
j2: diagonal.a,
rightPolygon: C
});
Q = substractPolygons(Q, C);
}
};
while (true) {
var _ret = _loop();
if (_ret === "break") break;
}
return {
LPCP: LPCP,
trueDiagonals: trueDiagonals,
LLE: LLE
};
}
/**
* An implementation of the algorithm presented in "A practical algorithm for decomposing polygonal domains into convex polygons by diagonals"
* It will decompose a polygon (with or without holes) in a partition of convex polygons.
*
* @param {{ x: number, y: number }[]} polygon
* @param {{ x: number, y: number }[][]} holes
* @returns {{ x: number, y: number }[][]} The partition of convex polygons
*/
function absHol(polygon) {
var