// loaders.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors // Forked from https://github.com/mapbox/geojson-vt under compatible ISC license /** * Calculate simplification data using optimized Douglas-Peucker algorithm * * @param coords contiguous list of coordinates * @param first first coord to simplify * @param last last coord to simplify * @param sqTolerance tolerance (square distance) */ export function simplifyPath( coords: number[], first: number, last: number, sqTolerance: number ): void { let maxSqDist = sqTolerance; const mid = (last - first) >> 1; let minPosToMid = last - first; let index; const ax = coords[first]; const ay = coords[first + 1]; const bx = coords[last]; const by = coords[last + 1]; for (let i = first + 3; i < last; i += 3) { const d = getSqSegDist(coords[i], coords[i + 1], ax, ay, bx, by); if (d > maxSqDist) { index = i; maxSqDist = d; } else if (d === maxSqDist) { // a workaround to ensure we choose a pivot close to the middle of the list, // reducing recursion depth, for certain degenerate inputs // https://github.com/mapbox/geojson-vt/issues/104 const posToMid = Math.abs(i - mid); if (posToMid < minPosToMid) { index = i; minPosToMid = posToMid; } } } if (maxSqDist > sqTolerance) { if (index - first > 3) simplifyPath(coords, first, index, sqTolerance); coords[index + 2] = maxSqDist; if (last - index > 3) simplifyPath(coords, index, last, sqTolerance); } } /** square distance from a point to a segment */ // eslint-disable-next-line max-params function getSqSegDist( px: number, py: number, x: number, y: number, bx: number, by: number ): number { let dx = bx - x; let dy = by - y; if (dx !== 0 || dy !== 0) { const t = ((px - x) * dx + (py - y) * dy) / (dx * dx + dy * dy); if (t > 1) { x = bx; y = by; } else if (t > 0) { x += dx * t; y += dy * t; } } dx = px - x; dy = py - y; return dx * dx + dy * dy; }