@deck.gl/layers
Version:
deck.gl core layers
237 lines (189 loc) • 6.25 kB
JavaScript
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