UNPKG

@deck.gl/layers

Version:
237 lines (189 loc) 6.25 kB
import earcut from 'earcut'; import { modifyPolygonWindingDirection, WINDING } from '@math.gl/polygon'; const OUTER_POLYGON_WINDING = WINDING.CLOCKWISE; const HOLE_POLYGON_WINDING = WINDING.COUNTER_CLOCKWISE; const windingOptions = { isClosed: true }; function validate(polygon) { polygon = polygon && polygon.positions || polygon; if (!Array.isArray(polygon) && !ArrayBuffer.isView(polygon)) { throw new Error('invalid polygon'); } } export function getPositions(polygon) { return 'positions' in polygon ? polygon.positions : polygon; } export function getHoleIndices(polygon) { return 'holeIndices' in polygon ? polygon.holeIndices : null; } function isNested(polygon) { return Array.isArray(polygon[0]); } function isSimple(polygon) { return polygon.length >= 1 && polygon[0].length >= 2 && Number.isFinite(polygon[0][0]); } function isNestedRingClosed(simplePolygon) { const p0 = simplePolygon[0]; const p1 = simplePolygon[simplePolygon.length - 1]; return p0[0] === p1[0] && p0[1] === p1[1] && p0[2] === p1[2]; } function isFlatRingClosed(positions, size, startIndex, endIndex) { for (let i = 0; i < size; i++) { if (positions[startIndex + i] !== positions[endIndex - size + i]) { return false; } } return true; } function copyNestedRing(target, targetStartIndex, simplePolygon, size, windingDirection) { let targetIndex = targetStartIndex; const len = simplePolygon.length; for (let i = 0; i < len; i++) { for (let j = 0; j < size; j++) { target[targetIndex++] = simplePolygon[i][j] || 0; } } if (!isNestedRingClosed(simplePolygon)) { for (let j = 0; j < size; j++) { target[targetIndex++] = simplePolygon[0][j] || 0; } } windingOptions.start = targetStartIndex; windingOptions.end = targetIndex; windingOptions.size = size; modifyPolygonWindingDirection(target, windingDirection, windingOptions); return targetIndex; } function copyFlatRing(target, targetStartIndex, positions, size, srcStartIndex = 0, srcEndIndex, windingDirection) { srcEndIndex = srcEndIndex || positions.length; const srcLength = srcEndIndex - srcStartIndex; if (srcLength <= 0) { return targetStartIndex; } let targetIndex = targetStartIndex; for (let i = 0; i < srcLength; i++) { target[targetIndex++] = positions[srcStartIndex + i]; } if (!isFlatRingClosed(positions, size, srcStartIndex, srcEndIndex)) { for (let i = 0; i < size; i++) { target[targetIndex++] = positions[srcStartIndex + i]; } } windingOptions.start = targetStartIndex; windingOptions.end = targetIndex; windingOptions.size = size; modifyPolygonWindingDirection(target, windingDirection, windingOptions); return targetIndex; } export function normalize(polygon, positionSize) { validate(polygon); const positions = []; const holeIndices = []; if ('positions' in polygon) { const { positions: srcPositions, holeIndices: srcHoleIndices } = polygon; if (srcHoleIndices) { let targetIndex = 0; for (let i = 0; i <= srcHoleIndices.length; i++) { targetIndex = copyFlatRing(positions, targetIndex, srcPositions, positionSize, srcHoleIndices[i - 1], srcHoleIndices[i], i === 0 ? OUTER_POLYGON_WINDING : HOLE_POLYGON_WINDING); holeIndices.push(targetIndex); } holeIndices.pop(); return { positions, holeIndices }; } polygon = srcPositions; } if (!isNested(polygon)) { copyFlatRing(positions, 0, polygon, positionSize, 0, positions.length, OUTER_POLYGON_WINDING); return positions; } if (!isSimple(polygon)) { let targetIndex = 0; for (const [polygonIndex, simplePolygon] of polygon.entries()) { targetIndex = copyNestedRing(positions, targetIndex, simplePolygon, positionSize, polygonIndex === 0 ? OUTER_POLYGON_WINDING : HOLE_POLYGON_WINDING); holeIndices.push(targetIndex); } holeIndices.pop(); return { positions, holeIndices }; } copyNestedRing(positions, 0, polygon, positionSize, OUTER_POLYGON_WINDING); return positions; } function getPlaneArea(positions, xIndex, yIndex) { const numVerts = positions.length / 3; let area = 0; for (let i = 0; i < numVerts; i++) { const j = (i + 1) % numVerts; area += positions[i * 3 + xIndex] * positions[j * 3 + yIndex]; area -= positions[j * 3 + xIndex] * positions[i * 3 + yIndex]; } return Math.abs(area / 2); } function permutePositions(positions, xIndex, yIndex, zIndex) { const numVerts = positions.length / 3; for (let i = 0; i < numVerts; i++) { const o = i * 3; const x = positions[o + 0]; const y = positions[o + 1]; const z = positions[o + 2]; positions[o + xIndex] = x; positions[o + yIndex] = y; positions[o + zIndex] = z; } } export function getSurfaceIndices(polygon, positionSize, preproject, full3d) { let holeIndices = getHoleIndices(polygon); if (holeIndices) { holeIndices = holeIndices.map(positionIndex => positionIndex / positionSize); } let positions = getPositions(polygon); const is3d = full3d && positionSize === 3; if (preproject) { const n = positions.length; positions = positions.slice(); const p = []; for (let i = 0; i < n; i += positionSize) { p[0] = positions[i]; p[1] = positions[i + 1]; if (is3d) { p[2] = positions[i + 2]; } const xy = preproject(p); positions[i] = xy[0]; positions[i + 1] = xy[1]; if (is3d) { positions[i + 2] = xy[2]; } } } if (is3d) { const xyArea = getPlaneArea(positions, 0, 1); const xzArea = getPlaneArea(positions, 0, 2); const yzArea = getPlaneArea(positions, 1, 2); if (!xyArea && !xzArea && !yzArea) { return []; } if (xyArea > xzArea && xyArea > yzArea) {} else if (xzArea > yzArea) { if (!preproject) { positions = positions.slice(); } permutePositions(positions, 0, 2, 1); } else { if (!preproject) { positions = positions.slice(); } permutePositions(positions, 1, 2, 0); } } return earcut(positions, holeIndices, positionSize); } //# sourceMappingURL=polygon.js.map