@math.gl/polygon
Version:
Polygon/polyline processing utilities
1,135 lines (1,125 loc) • 34.2 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// dist/index.js
var dist_exports = {};
__export(dist_exports, {
Polygon: () => Polygon,
WINDING: () => WINDING,
_Polygon: () => Polygon,
clipPolygon: () => clipPolygon,
clipPolyline: () => clipPolyline,
cutPolygonByGrid: () => cutPolygonByGrid,
cutPolygonByMercatorBounds: () => cutPolygonByMercatorBounds,
cutPolylineByGrid: () => cutPolylineByGrid,
cutPolylineByMercatorBounds: () => cutPolylineByMercatorBounds,
earcut: () => earcut,
forEachSegmentInPolygon: () => forEachSegmentInPolygon,
getPolygonSignedArea: () => getPolygonSignedArea,
getPolygonWindingDirection: () => getPolygonWindingDirection,
modifyPolygonWindingDirection: () => modifyPolygonWindingDirection
});
module.exports = __toCommonJS(dist_exports);
// dist/polygon.js
var import_core2 = require("@math.gl/core");
// dist/polygon-utils.js
var import_core = require("@math.gl/core");
var WINDING = {
CLOCKWISE: 1,
COUNTER_CLOCKWISE: -1
};
function modifyPolygonWindingDirection(points, direction, options = {}) {
const windingDirection = getPolygonWindingDirection(points, options);
if (windingDirection !== direction) {
reversePolygon(points, options);
return true;
}
return false;
}
function getPolygonWindingDirection(points, options = {}) {
return Math.sign(getPolygonSignedArea(points, options));
}
var DimIndex = {
x: 0,
y: 1,
z: 2
};
function getPolygonSignedArea(points, options = {}) {
const { start = 0, end = points.length, plane = "xy" } = options;
const dim = options.size || 2;
let area2 = 0;
const i0 = DimIndex[plane[0]];
const i1 = DimIndex[plane[1]];
for (let i = start, j = end - dim; i < end; i += dim) {
area2 += (points[i + i0] - points[j + i0]) * (points[i + i1] + points[j + i1]);
j = i;
}
return area2 / 2;
}
function forEachSegmentInPolygon(points, visitor, options = {}) {
const { start = 0, end = points.length, size = 2, isClosed } = options;
const numPoints = (end - start) / size;
for (let i = 0; i < numPoints - 1; ++i) {
visitor(points[start + i * size], points[start + i * size + 1], points[start + (i + 1) * size], points[start + (i + 1) * size + 1], i, i + 1);
}
const endPointIndex = start + (numPoints - 1) * size;
const isClosedEx = isClosed || (0, import_core.equals)(points[start], points[endPointIndex]) && (0, import_core.equals)(points[start + 1], points[endPointIndex + 1]);
if (!isClosedEx) {
visitor(points[endPointIndex], points[endPointIndex + 1], points[start], points[start + 1], numPoints - 1, 0);
}
}
function reversePolygon(points, options) {
const { start = 0, end = points.length, size = 2 } = options;
const numPoints = (end - start) / size;
const numSwaps = Math.floor(numPoints / 2);
for (let i = 0; i < numSwaps; ++i) {
const b1 = start + i * size;
const b2 = start + (numPoints - 1 - i) * size;
for (let j = 0; j < size; ++j) {
const tmp = points[b1 + j];
points[b1 + j] = points[b2 + j];
points[b2 + j] = tmp;
}
}
}
function modifyPolygonWindingDirectionPoints(points, direction, options = {}) {
const currentDirection = getPolygonWindingDirectionPoints(points, options);
if (currentDirection !== direction) {
points.reverse();
return true;
}
return false;
}
function getPolygonWindingDirectionPoints(points, options = {}) {
return Math.sign(getPolygonSignedAreaPoints(points, options));
}
function getPolygonSignedAreaPoints(points, options = {}) {
const { start = 0, end = points.length, plane = "xy" } = options;
let area2 = 0;
const i0 = DimIndex[plane[0]];
const i1 = DimIndex[plane[1]];
for (let i = start, j = end - 1; i < end; ++i) {
area2 += (points[i][i0] - points[j][i0]) * (points[i][i1] + points[j][i1]);
j = i;
}
return area2 / 2;
}
function forEachSegmentInPolygonPoints(points, visitor, options = {}) {
const { start = 0, end = points.length, isClosed } = options;
for (let i = start; i < end - 1; ++i) {
visitor(points[i], points[i + 1], i, i + 1);
}
const isClosedEx = isClosed || (0, import_core.equals)(points[end - 1], points[0]);
if (!isClosedEx) {
visitor(points[end - 1], points[0], end - 1, 0);
}
}
// dist/polygon.js
var Polygon = class {
constructor(points, options = {}) {
this.points = points;
this.isFlatArray = !(0, import_core2.isArray)(points[0]);
this.options = {
start: options.start || 0,
end: options.end || points.length,
size: options.size || 2,
isClosed: options.isClosed
};
Object.freeze(this);
}
/**
* Returns signed area of the polygon.
* @returns Signed area of the polygon.
*/
getSignedArea() {
if (this.isFlatArray)
return getPolygonSignedArea(this.points, this.options);
return getPolygonSignedAreaPoints(this.points, this.options);
}
/**
* Returns absolute area of the polygon.
* @returns Absolute area of the polygon.
*/
getArea() {
return Math.abs(this.getSignedArea());
}
/**
* Returns winding direction of the polygon.
* @returns Winding direction of the polygon. 1 is for clockwise, -1 for counterclockwise winding direction.
*/
getWindingDirection() {
return Math.sign(this.getSignedArea());
}
/**
* Calls the visitor callback for each segment in the polygon.
* @param visitor A callback to call for each segment.
*/
forEachSegment(visitor) {
if (this.isFlatArray) {
forEachSegmentInPolygon(
this.points,
// eslint-disable-next-line max-params
(x1, y1, x2, y2, i1, i2) => {
visitor([x1, y1], [x2, y2], i1, i2);
},
this.options
);
} else {
forEachSegmentInPolygonPoints(this.points, visitor, this.options);
}
}
/**
* Checks winding direction of the polygon and reverses the polygon in case of opposite winding direction.
* @param direction Requested winding direction. 1 is for clockwise, -1 for counterclockwise winding direction.
* @return Returns true if the winding direction was changed.
*/
modifyWindingDirection(direction) {
if (this.isFlatArray) {
return modifyPolygonWindingDirection(this.points, direction, this.options);
}
return modifyPolygonWindingDirectionPoints(this.points, direction, this.options);
}
};
// dist/earcut.js
function earcut(positions, holeIndices, dim = 2, areas, plane = "xy") {
const hasHoles = holeIndices && holeIndices.length;
const outerLen = hasHoles ? holeIndices[0] * dim : positions.length;
let outerNode = linkedList(positions, 0, outerLen, dim, true, areas && areas[0], plane);
const triangles = [];
if (!outerNode || outerNode.next === outerNode.prev)
return triangles;
let invSize;
let maxX;
let maxY;
let minX;
let minY;
let x;
let y;
if (hasHoles)
outerNode = eliminateHoles(positions, holeIndices, outerNode, dim, areas, plane);
if (positions.length > 80 * dim) {
minX = maxX = positions[0];
minY = maxY = positions[1];
for (let i = dim; i < outerLen; i += dim) {
x = positions[i];
y = positions[i + 1];
if (x < minX)
minX = x;
if (y < minY)
minY = y;
if (x > maxX)
maxX = x;
if (y > maxY)
maxY = y;
}
invSize = Math.max(maxX - minX, maxY - minY);
invSize = invSize !== 0 ? 32767 / invSize : 0;
}
earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
return triangles;
}
function linkedList(data, start, end, dim, clockwise, area2, plane) {
let i;
let last;
if (area2 === void 0) {
area2 = getPolygonSignedArea(data, { start, end, size: dim, plane });
}
let i0 = DimIndex[plane[0]];
let i1 = DimIndex[plane[1]];
if (clockwise === area2 < 0) {
for (i = start; i < end; i += dim)
last = insertNode(i, data[i + i0], data[i + i1], last);
} else {
for (i = end - dim; i >= start; i -= dim)
last = insertNode(i, data[i + i0], data[i + i1], last);
}
if (last && equals2(last, last.next)) {
removeNode(last);
last = last.next;
}
return last;
}
function filterPoints(start, end) {
if (!start)
return start;
if (!end)
end = start;
let p = start;
let again;
do {
again = false;
if (!p.steiner && (equals2(p, p.next) || area(p.prev, p, p.next) === 0)) {
removeNode(p);
p = end = p.prev;
if (p === p.next)
break;
again = true;
} else {
p = p.next;
}
} while (again || p !== end);
return end;
}
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
if (!ear)
return;
if (!pass && invSize)
indexCurve(ear, minX, minY, invSize);
let stop = ear;
let prev;
let next;
while (ear.prev !== ear.next) {
prev = ear.prev;
next = ear.next;
if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
triangles.push(prev.i / dim | 0);
triangles.push(ear.i / dim | 0);
triangles.push(next.i / dim | 0);
removeNode(ear);
ear = next.next;
stop = next.next;
continue;
}
ear = next;
if (ear === stop) {
if (!pass) {
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
} else if (pass === 1) {
ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
} else if (pass === 2) {
splitEarcut(ear, triangles, dim, minX, minY, invSize);
}
break;
}
}
}
function isEar(ear) {
const a = ear.prev;
const b = ear;
const c = ear.next;
if (area(a, b, c) >= 0)
return false;
const ax = a.x;
const bx = b.x;
const cx = c.x;
const ay = a.y;
const by = b.y;
const cy = c.y;
const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx;
const y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy;
const x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx;
const y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
let p = c.next;
while (p !== a) {
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0)
return false;
p = p.next;
}
return true;
}
function isEarHashed(ear, minX, minY, invSize) {
const a = ear.prev;
const b = ear;
const c = ear.next;
if (area(a, b, c) >= 0)
return false;
const ax = a.x;
const bx = b.x;
const cx = c.x;
const ay = a.y;
const by = b.y;
const cy = c.y;
const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx;
const y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy;
const x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx;
const y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
const minZ = zOrder(x0, y0, minX, minY, invSize);
const maxZ = zOrder(x1, y1, minX, minY, invSize);
let p = ear.prevZ;
let n = ear.nextZ;
while (p && p.z >= minZ && n && n.z <= maxZ) {
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0)
return false;
p = p.prevZ;
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0)
return false;
n = n.nextZ;
}
while (p && p.z >= minZ) {
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0)
return false;
p = p.prevZ;
}
while (n && n.z <= maxZ) {
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0)
return false;
n = n.nextZ;
}
return true;
}
function cureLocalIntersections(start, triangles, dim) {
let p = start;
do {
const a = p.prev;
const b = p.next.next;
if (!equals2(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
triangles.push(a.i / dim | 0);
triangles.push(p.i / dim | 0);
triangles.push(b.i / dim | 0);
removeNode(p);
removeNode(p.next);
p = start = b;
}
p = p.next;
} while (p !== start);
return filterPoints(p);
}
function splitEarcut(start, triangles, dim, minX, minY, invSize) {
let a = start;
do {
let b = a.next.next;
while (b !== a.prev) {
if (a.i !== b.i && isValidDiagonal(a, b)) {
let c = splitPolygon(a, b);
a = filterPoints(a, a.next);
c = filterPoints(c, c.next);
earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
return;
}
b = b.next;
}
a = a.next;
} while (a !== start);
}
function eliminateHoles(data, holeIndices, outerNode, dim, areas, plane) {
const queue = [];
let i;
let len;
let start;
let end;
let list;
for (i = 0, len = holeIndices.length; i < len; i++) {
start = holeIndices[i] * dim;
end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
list = linkedList(data, start, end, dim, false, areas && areas[i + 1], plane);
if (list === list.next)
list.steiner = true;
queue.push(getLeftmost(list));
}
queue.sort(compareX);
for (i = 0; i < queue.length; i++) {
outerNode = eliminateHole(queue[i], outerNode);
}
return outerNode;
}
function compareX(a, b) {
return a.x - b.x;
}
function eliminateHole(hole, outerNode) {
const bridge = findHoleBridge(hole, outerNode);
if (!bridge) {
return outerNode;
}
const bridgeReverse = splitPolygon(bridge, hole);
filterPoints(bridgeReverse, bridgeReverse.next);
return filterPoints(bridge, bridge.next);
}
function findHoleBridge(hole, outerNode) {
let p = outerNode;
const hx = hole.x;
const hy = hole.y;
let qx = -Infinity;
let m;
do {
if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
if (x <= hx && x > qx) {
qx = x;
m = p.x < p.next.x ? p : p.next;
if (x === hx)
return m;
}
}
p = p.next;
} while (p !== outerNode);
if (!m)
return null;
const stop = m;
const mx = m.x;
const my = m.y;
let tanMin = Infinity;
let tan;
p = m;
do {
if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
tan = Math.abs(hy - p.y) / (hx - p.x);
if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
m = p;
tanMin = tan;
}
}
p = p.next;
} while (p !== stop);
return m;
}
function sectorContainsSector(m, p) {
return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
}
function indexCurve(start, minX, minY, invSize) {
let p = start;
do {
if (p.z === 0)
p.z = zOrder(p.x, p.y, minX, minY, invSize);
p.prevZ = p.prev;
p.nextZ = p.next;
p = p.next;
} while (p !== start);
p.prevZ.nextZ = null;
p.prevZ = null;
sortLinked(p);
}
function sortLinked(list) {
let e;
let i;
let inSize = 1;
let numMerges;
let p;
let pSize;
let q;
let qSize;
let tail;
do {
p = list;
list = null;
tail = null;
numMerges = 0;
while (p) {
numMerges++;
q = p;
pSize = 0;
for (i = 0; i < inSize; i++) {
pSize++;
q = q.nextZ;
if (!q)
break;
}
qSize = inSize;
while (pSize > 0 || qSize > 0 && q) {
if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
e = p;
p = p.nextZ;
pSize--;
} else {
e = q;
q = q.nextZ;
qSize--;
}
if (tail)
tail.nextZ = e;
else
list = e;
e.prevZ = tail;
tail = e;
}
p = q;
}
tail.nextZ = null;
inSize *= 2;
} while (numMerges > 1);
return list;
}
function zOrder(x, y, minX, minY, invSize) {
x = (x - minX) * invSize | 0;
y = (y - minY) * invSize | 0;
x = (x | x << 8) & 16711935;
x = (x | x << 4) & 252645135;
x = (x | x << 2) & 858993459;
x = (x | x << 1) & 1431655765;
y = (y | y << 8) & 16711935;
y = (y | y << 4) & 252645135;
y = (y | y << 2) & 858993459;
y = (y | y << 1) & 1431655765;
return x | y << 1;
}
function getLeftmost(start) {
let p = start;
let leftmost = start;
do {
if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y)
leftmost = p;
p = p.next;
} while (p !== start);
return leftmost;
}
function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py);
}
function isValidDiagonal(a, b) {
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
(locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
(area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
equals2(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
}
function area(p, q, r) {
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
}
function equals2(p1, p2) {
return p1.x === p2.x && p1.y === p2.y;
}
function intersects(p1, q1, p2, q2) {
const o1 = sign(area(p1, q1, p2));
const o2 = sign(area(p1, q1, q2));
const o3 = sign(area(p2, q2, p1));
const o4 = sign(area(p2, q2, q1));
if (o1 !== o2 && o3 !== o4)
return true;
if (o1 === 0 && onSegment(p1, p2, q1))
return true;
if (o2 === 0 && onSegment(p1, q2, q1))
return true;
if (o3 === 0 && onSegment(p2, p1, q2))
return true;
if (o4 === 0 && onSegment(p2, q1, q2))
return true;
return false;
}
function onSegment(p, q, r) {
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
}
function sign(num) {
return num > 0 ? 1 : num < 0 ? -1 : 0;
}
function intersectsPolygon(a, b) {
let p = a;
do {
if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b))
return true;
p = p.next;
} while (p !== a);
return false;
}
function locallyInside(a, b) {
return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
}
function middleInside(a, b) {
let p = a;
let inside = false;
const px = (a.x + b.x) / 2;
const py = (a.y + b.y) / 2;
do {
if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)
inside = !inside;
p = p.next;
} while (p !== a);
return inside;
}
function splitPolygon(a, b) {
const a2 = new Vertex(a.i, a.x, a.y);
const b2 = new Vertex(b.i, b.x, b.y);
const an = a.next;
const bp = b.prev;
a.next = b;
b.prev = a;
a2.next = an;
an.prev = a2;
b2.next = a2;
a2.prev = b2;
bp.next = b2;
b2.prev = bp;
return b2;
}
function insertNode(i, x, y, last) {
const p = new Vertex(i, x, y);
if (!last) {
p.prev = p;
p.next = p;
} else {
p.next = last.next;
p.prev = last;
last.next.prev = p;
last.next = p;
}
return p;
}
function removeNode(p) {
p.next.prev = p.prev;
p.prev.next = p.next;
if (p.prevZ)
p.prevZ.nextZ = p.nextZ;
if (p.nextZ)
p.nextZ.prevZ = p.prevZ;
}
var Vertex = class {
constructor(i, x, y) {
this.prev = null;
this.next = null;
this.z = 0;
this.prevZ = null;
this.nextZ = null;
this.steiner = false;
this.i = i;
this.x = x;
this.y = y;
}
};
// dist/utils.js
function push(target, source) {
const size = source.length;
const startIndex = target.length;
if (startIndex > 0) {
let isDuplicate = true;
for (let i = 0; i < size; i++) {
if (target[startIndex - size + i] !== source[i]) {
isDuplicate = false;
break;
}
}
if (isDuplicate) {
return false;
}
}
for (let i = 0; i < size; i++) {
target[startIndex + i] = source[i];
}
return true;
}
function copy(target, source) {
const size = source.length;
for (let i = 0; i < size; i++) {
target[i] = source[i];
}
}
function getPointAtIndex(positions, index, size, offset, out = []) {
const startI = offset + index * size;
for (let i = 0; i < size; i++) {
out[i] = positions[startI + i];
}
return out;
}
// dist/lineclip.js
function clipPolyline(positions, bbox, options) {
const { size = 2, startIndex = 0, endIndex = positions.length } = options || {};
const numPoints = (endIndex - startIndex) / size;
const result = [];
let part = [];
let a;
let b;
let codeA = -1;
let codeB;
let lastCode;
for (let i = 1; i < numPoints; i++) {
a = getPointAtIndex(positions, i - 1, size, startIndex, a);
b = getPointAtIndex(positions, i, size, startIndex, b);
if (codeA < 0) {
codeA = bitCode(a, bbox);
}
codeB = lastCode = bitCode(b, bbox);
while (true) {
if (!(codeA | codeB)) {
push(part, a);
if (codeB !== lastCode) {
push(part, b);
if (i < numPoints - 1) {
result.push(part);
part = [];
}
} else if (i === numPoints - 1) {
push(part, b);
}
break;
} else if (codeA & codeB) {
break;
} else if (codeA) {
intersect(a, b, codeA, bbox, a);
codeA = bitCode(a, bbox);
} else {
intersect(a, b, codeB, bbox, b);
codeB = bitCode(b, bbox);
}
}
codeA = lastCode;
}
if (part.length)
result.push(part);
return result;
}
function clipPolygon(positions, bbox, options) {
const { size = 2, endIndex = positions.length } = options || {};
let { startIndex = 0 } = options || {};
let numPoints = (endIndex - startIndex) / size;
let result;
let p;
let prev;
let inside;
let prevInside;
for (let edge = 1; edge <= 8; edge *= 2) {
result = [];
prev = getPointAtIndex(positions, numPoints - 1, size, startIndex, prev);
prevInside = !(bitCode(prev, bbox) & edge);
for (let i = 0; i < numPoints; i++) {
p = getPointAtIndex(positions, i, size, startIndex, p);
inside = !(bitCode(p, bbox) & edge);
if (inside !== prevInside)
push(result, intersect(prev, p, edge, bbox));
if (inside)
push(result, p);
copy(prev, p);
prevInside = inside;
}
positions = result;
startIndex = 0;
numPoints = result.length / size;
if (!numPoints)
break;
}
return result;
}
function intersect(a, b, edge, bbox, out = []) {
let t;
let snap;
if (edge & 8) {
t = (bbox[3] - a[1]) / (b[1] - a[1]);
snap = 3;
} else if (edge & 4) {
t = (bbox[1] - a[1]) / (b[1] - a[1]);
snap = 1;
} else if (edge & 2) {
t = (bbox[2] - a[0]) / (b[0] - a[0]);
snap = 2;
} else if (edge & 1) {
t = (bbox[0] - a[0]) / (b[0] - a[0]);
snap = 0;
} else {
return null;
}
for (let i = 0; i < a.length; i++) {
out[i] = (snap & 1) === i ? bbox[snap] : t * (b[i] - a[i]) + a[i];
}
return out;
}
function bitCode(p, bbox) {
let code = 0;
if (p[0] < bbox[0])
code |= 1;
else if (p[0] > bbox[2])
code |= 2;
if (p[1] < bbox[1])
code |= 4;
else if (p[1] > bbox[3])
code |= 8;
return code;
}
// dist/cut-by-grid.js
function cutPolylineByGrid(positions, options) {
const { size = 2, broken = false, gridResolution = 10, gridOffset = [0, 0], startIndex = 0, endIndex = positions.length } = options || {};
const numPoints = (endIndex - startIndex) / size;
let part = [];
const result = [part];
const a = getPointAtIndex(positions, 0, size, startIndex);
let b;
let codeB;
const cell = getGridCell(a, gridResolution, gridOffset, []);
const scratchPoint = [];
push(part, a);
for (let i = 1; i < numPoints; i++) {
b = getPointAtIndex(positions, i, size, startIndex, b);
codeB = bitCode(b, cell);
while (codeB) {
intersect(a, b, codeB, cell, scratchPoint);
const codeAlt = bitCode(scratchPoint, cell);
if (codeAlt) {
intersect(a, scratchPoint, codeAlt, cell, scratchPoint);
codeB = codeAlt;
}
push(part, scratchPoint);
copy(a, scratchPoint);
moveToNeighborCell(cell, gridResolution, codeB);
if (broken && part.length > size) {
part = [];
result.push(part);
push(part, a);
}
codeB = bitCode(b, cell);
}
push(part, b);
copy(a, b);
}
return broken ? result : result[0];
}
var TYPE_INSIDE = 0;
var TYPE_BORDER = 1;
function cutPolygonByGrid(positions, holeIndices = null, options) {
if (!positions.length) {
return [];
}
const { size = 2, gridResolution = 10, gridOffset = [0, 0], edgeTypes = false } = options || {};
const result = [];
const queue = [
{
pos: positions,
types: edgeTypes ? new Array(positions.length / size).fill(TYPE_BORDER) : null,
holes: holeIndices || []
}
];
const bbox = [[], []];
let cell = [];
while (queue.length) {
const { pos, types, holes } = queue.shift();
getBoundingBox(pos, size, holes[0] || pos.length, bbox);
cell = getGridCell(bbox[0], gridResolution, gridOffset, cell);
const code = bitCode(bbox[1], cell);
if (code) {
let parts = bisectPolygon(pos, types, size, 0, holes[0] || pos.length, cell, code);
const polygonLow = { pos: parts[0].pos, types: parts[0].types, holes: [] };
const polygonHigh = { pos: parts[1].pos, types: parts[1].types, holes: [] };
queue.push(polygonLow, polygonHigh);
for (let i = 0; i < holes.length; i++) {
parts = bisectPolygon(pos, types, size, holes[i], holes[i + 1] || pos.length, cell, code);
if (parts[0]) {
polygonLow.holes.push(polygonLow.pos.length);
polygonLow.pos = concatInPlace(polygonLow.pos, parts[0].pos);
if (edgeTypes) {
polygonLow.types = concatInPlace(polygonLow.types, parts[0].types);
}
}
if (parts[1]) {
polygonHigh.holes.push(polygonHigh.pos.length);
polygonHigh.pos = concatInPlace(polygonHigh.pos, parts[1].pos);
if (edgeTypes) {
polygonHigh.types = concatInPlace(polygonHigh.types, parts[1].types);
}
}
}
} else {
const polygon = { positions: pos };
if (edgeTypes) {
polygon.edgeTypes = types;
}
if (holes.length) {
polygon.holeIndices = holes;
}
result.push(polygon);
}
}
return result;
}
function bisectPolygon(positions, edgeTypes, size, startIndex, endIndex, bbox, edge) {
const numPoints = (endIndex - startIndex) / size;
const resultLow = [];
const resultHigh = [];
const typesLow = [];
const typesHigh = [];
const scratchPoint = [];
let p;
let side;
let type;
const prev = getPointAtIndex(positions, numPoints - 1, size, startIndex);
let prevSide = Math.sign(edge & 8 ? prev[1] - bbox[3] : prev[0] - bbox[2]);
let prevType = edgeTypes && edgeTypes[numPoints - 1];
let lowPointCount = 0;
let highPointCount = 0;
for (let i = 0; i < numPoints; i++) {
p = getPointAtIndex(positions, i, size, startIndex, p);
side = Math.sign(edge & 8 ? p[1] - bbox[3] : p[0] - bbox[2]);
type = edgeTypes && edgeTypes[startIndex / size + i];
if (side && prevSide && prevSide !== side) {
intersect(prev, p, edge, bbox, scratchPoint);
push(resultLow, scratchPoint) && typesLow.push(prevType);
push(resultHigh, scratchPoint) && typesHigh.push(prevType);
}
if (side <= 0) {
push(resultLow, p) && typesLow.push(type);
lowPointCount -= side;
} else if (typesLow.length) {
typesLow[typesLow.length - 1] = TYPE_INSIDE;
}
if (side >= 0) {
push(resultHigh, p) && typesHigh.push(type);
highPointCount += side;
} else if (typesHigh.length) {
typesHigh[typesHigh.length - 1] = TYPE_INSIDE;
}
copy(prev, p);
prevSide = side;
prevType = type;
}
return [
lowPointCount ? { pos: resultLow, types: edgeTypes && typesLow } : null,
highPointCount ? { pos: resultHigh, types: edgeTypes && typesHigh } : null
];
}
function getGridCell(p, gridResolution, gridOffset, out) {
const left = Math.floor((p[0] - gridOffset[0]) / gridResolution) * gridResolution + gridOffset[0];
const bottom = Math.floor((p[1] - gridOffset[1]) / gridResolution) * gridResolution + gridOffset[1];
out[0] = left;
out[1] = bottom;
out[2] = left + gridResolution;
out[3] = bottom + gridResolution;
return out;
}
function moveToNeighborCell(cell, gridResolution, edge) {
if (edge & 8) {
cell[1] += gridResolution;
cell[3] += gridResolution;
} else if (edge & 4) {
cell[1] -= gridResolution;
cell[3] -= gridResolution;
} else if (edge & 2) {
cell[0] += gridResolution;
cell[2] += gridResolution;
} else if (edge & 1) {
cell[0] -= gridResolution;
cell[2] -= gridResolution;
}
}
function getBoundingBox(positions, size, endIndex, out) {
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
for (let i = 0; i < endIndex; i += size) {
const x = positions[i];
const y = positions[i + 1];
minX = x < minX ? x : minX;
maxX = x > maxX ? x : maxX;
minY = y < minY ? y : minY;
maxY = y > maxY ? y : maxY;
}
out[0][0] = minX;
out[0][1] = minY;
out[1][0] = maxX;
out[1][1] = maxY;
return out;
}
function concatInPlace(arr1, arr2) {
for (let i = 0; i < arr2.length; i++) {
arr1.push(arr2[i]);
}
return arr1;
}
// dist/cut-by-mercator-bounds.js
var DEFAULT_MAX_LATITUDE = 85.051129;
function cutPolylineByMercatorBounds(positions, options) {
const { size = 2, startIndex = 0, endIndex = positions.length, normalize = true } = options || {};
const newPositions = positions.slice(startIndex, endIndex);
wrapLongitudesForShortestPath(newPositions, size, 0, endIndex - startIndex);
const parts = cutPolylineByGrid(newPositions, {
size,
broken: true,
gridResolution: 360,
gridOffset: [-180, -180]
});
if (normalize) {
for (const part of parts) {
shiftLongitudesIntoRange(part, size);
}
}
return parts;
}
function cutPolygonByMercatorBounds(positions, holeIndices = null, options) {
const { size = 2, normalize = true, edgeTypes = false } = options || {};
holeIndices = holeIndices || [];
const newPositions = [];
const newHoleIndices = [];
let srcStartIndex = 0;
let targetIndex = 0;
for (let ringIndex = 0; ringIndex <= holeIndices.length; ringIndex++) {
const srcEndIndex = holeIndices[ringIndex] || positions.length;
const targetStartIndex = targetIndex;
const splitIndex = findSplitIndex(positions, size, srcStartIndex, srcEndIndex);
for (let i = splitIndex; i < srcEndIndex; i++) {
newPositions[targetIndex++] = positions[i];
}
for (let i = srcStartIndex; i < splitIndex; i++) {
newPositions[targetIndex++] = positions[i];
}
wrapLongitudesForShortestPath(newPositions, size, targetStartIndex, targetIndex);
insertPoleVertices(newPositions, size, targetStartIndex, targetIndex, options == null ? void 0 : options.maxLatitude);
srcStartIndex = srcEndIndex;
newHoleIndices[ringIndex] = targetIndex;
}
newHoleIndices.pop();
const parts = cutPolygonByGrid(newPositions, newHoleIndices, {
size,
gridResolution: 360,
gridOffset: [-180, -180],
edgeTypes
});
if (normalize) {
for (const part of parts) {
shiftLongitudesIntoRange(part.positions, size);
}
}
return parts;
}
function findSplitIndex(positions, size, startIndex, endIndex) {
let maxLat = -1;
let pointIndex = -1;
for (let i = startIndex + 1; i < endIndex; i += size) {
const lat = Math.abs(positions[i]);
if (lat > maxLat) {
maxLat = lat;
pointIndex = i - 1;
}
}
return pointIndex;
}
function insertPoleVertices(positions, size, startIndex, endIndex, maxLatitude = DEFAULT_MAX_LATITUDE) {
const firstLng = positions[startIndex];
const lastLng = positions[endIndex - size];
if (Math.abs(firstLng - lastLng) > 180) {
const p = getPointAtIndex(positions, 0, size, startIndex);
p[0] += Math.round((lastLng - firstLng) / 360) * 360;
push(positions, p);
p[1] = Math.sign(p[1]) * maxLatitude;
push(positions, p);
p[0] = firstLng;
push(positions, p);
}
}
function wrapLongitudesForShortestPath(positions, size, startIndex, endIndex) {
let prevLng = positions[0];
let lng;
for (let i = startIndex; i < endIndex; i += size) {
lng = positions[i];
const delta = lng - prevLng;
if (delta > 180 || delta < -180) {
lng -= Math.round(delta / 360) * 360;
}
positions[i] = prevLng = lng;
}
}
function shiftLongitudesIntoRange(positions, size) {
let refLng;
const pointCount = positions.length / size;
for (let i = 0; i < pointCount; i++) {
refLng = positions[i * size];
if ((refLng + 180) % 360 !== 0) {
break;
}
}
const delta = -Math.round(refLng / 360) * 360;
if (delta === 0) {
return;
}
for (let i = 0; i < pointCount; i++) {
positions[i * size] += delta;
}
}
//# sourceMappingURL=index.cjs.map