Skip to content

Commit

Permalink
Merge 3c6adb9 into 3837687
Browse files Browse the repository at this point in the history
  • Loading branch information
Pessimistress committed Jun 7, 2020
2 parents 3837687 + 3c6adb9 commit ada2629
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 66 deletions.
11 changes: 8 additions & 3 deletions modules/polygon/docs/api-reference/cut-polygon-by-grid.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,14 @@ Arguments:
+ `size` (Number) - the number of elements in each vertex. Size `2` will interpret `positions` as `[x0, y0, x1, y1, ...]` and size `3` will interpret `positions` as `[x0, y0, z0, x1, y1, z1, ...]`. Default `2`.
+ `gridResolution` (Number, optional) - the grid size. Default `10`.
+ `gridOffset` (Array, optional) - the grid offset in `[x, y]`. Default `[0, 0]` i.e. the grid starts from the coordinate origin.
+ `vertexTypes` (Boolean) - if `true`, returns an additional array for each polygon that describes the nature of each vertex. See "returns" below.

Returns:

An array of polygons. Each polygons is represented by either:
- a positions array that uses the same vertex size as the input
- an object in the shape of `{positions, holeIndices}`
An array of polygons. Each polygons is represented by an object with the following fields:
- `positions` (Array) - a flat array of the vertex positions that define the polygon's rings.
- `holeIndices` (Array) - the indices in `positions` where each hole starts. Not present if the polygon has no holes.
- `vertexTypes` (Array) - describes the nature of each vertex in `positions`:
+ `0` - a vertex in the original polygon
+ `1` - an interpolated point on the edge of the original polygon
+ `2` - a point inside the original polygon
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,14 @@ Arguments:
+ `size` (Number) - the number of elements in each vertex. Size `2` will interpret `positions` as `[x0, y0, x1, y1, ...]` and size `3` will interpret `positions` as `[x0, y0, z0, x1, y1, z1, ...]`. Default `2`.
+ `normalize` (Boolean) - make sure the output longitudes are within `[-180, 180]`. Default `true`.
+ `maxLatitude` (Number) - since latitude=90 projects to infinity in Web Mercator projection, `maxLatitude` will be used to represent the pole. Default `85.051129` which makes the map square.
+ `vertexTypes` (Boolean) - if `true`, returns an additional array for each polygon that describes the nature of each vertex. See "returns" below.

Returns:

An array of polygons. Each polygons is represented by either:
- a positions array that uses the same vertex size as the input
- an object in the shape of `{positions, holeIndices}`
An array of polygons. Each polygons is represented by an object with the following fields:
- `positions` (Array) - a flat array of the vertex positions that define the polygon's rings.
- `holeIndices` (Array) - the indices in `positions` where each hole starts. Not present if the polygon has no holes.
- `vertexTypes` (Array) - describes the nature of each vertex in `positions`:
+ `0` - a vertex in the original polygon
+ `1` - an interpolated point on the edge of the original polygon
+ `2` - a point inside the original polygon
9 changes: 5 additions & 4 deletions modules/polygon/src/cut-by-grid.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
type Polygon = Array<number> | {positions: Array<number>, holeIndices: Array<number>};
type Polygon = {positions: Array<number>, holeIndices?: Array<number>, vertexTypes?: Array<Number>};

export function cutPolylineByGrid(
positions : Array<number>,
options : {
options? : {
size? : number,
broken? : boolean,
gridResolution? : number,
Expand All @@ -15,9 +15,10 @@ export function cutPolylineByGrid(
export function cutPolygonByGrid(
positions : Array<number>,
holeIndices : Array<number> | null,
options : {
options? : {
size? : number,
gridResolution? : number,
gridOffset? : [number, number]
gridOffset? : [number, number],
vertexTypes? : boolean
}
) : Array<Polygon>;
82 changes: 70 additions & 12 deletions modules/polygon/src/cut-by-grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,76 +54,117 @@ export function cutPolylineByGrid(positions, options = {}) {
return broken ? result : result[0];
}

const TYPE_VERTEX = 0;
const TYPE_EDGE = 1;
const TYPE_INSIDE = 2;
const FLAG_NEW_EDGE = 4;
const TYPE_ALL = TYPE_VERTEX | TYPE_EDGE | TYPE_INSIDE;

export function cutPolygonByGrid(positions, holeIndices, options = {}) {
if (!positions.length) {
// input is empty
return [];
}
const {size = 2, gridResolution = 10, gridOffset = [0, 0]} = options;
const {size = 2, gridResolution = 10, gridOffset = [0, 0], vertexTypes = false} = options;
const result = [];
const queue = [{pos: positions, holes: holeIndices || []}];
const queue = [{pos: positions, types: vertexTypes && [], holes: holeIndices || []}];
const bbox = [[], []];
let cell = [];

// Recursively bisect polygon until every part fit in a single grid cell
while (queue.length) {
const {pos, holes} = queue.shift();
const {pos, types, holes} = queue.shift();

getBoundingBox(pos, size, bbox);
cell = getGridCell(bbox[0], gridResolution, gridOffset, cell);
const code = bitCode(bbox[1], cell);

if (code) {
// Split the outer ring at the boundary
let parts = bisectPolygon(pos, size, 0, holes[0] || pos.length, cell, code);
const polygonLow = {pos: parts[0], holes: []};
const polygonHigh = {pos: parts[1], holes: []};
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);

// Split each hole at the boundary
for (let i = 0; i < holes.length; i++) {
parts = bisectPolygon(pos, size, holes[i], holes[i + 1] || pos.length, cell, code);
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 = polygonLow.pos.concat(parts[0]);
polygonLow.pos = polygonLow.pos.concat(parts[0].pos);
if (vertexTypes) {
polygonLow.types = polygonLow.types.concat(parts[0].types);
}
}
if (parts[1]) {
polygonHigh.holes.push(polygonHigh.pos.length);
polygonHigh.pos = polygonHigh.pos.concat(parts[1]);
polygonHigh.pos = polygonHigh.pos.concat(parts[1].pos);
if (vertexTypes) {
polygonHigh.types = polygonHigh.types.concat(parts[1].types);
}
}
}
} else {
// Polygon fits in a single cell, no more processing required
result.push(holes.length ? {positions: pos, holeIndices: holes} : pos);
const polygon = {positions: pos};
if (vertexTypes) {
for (let i = 0; i < types.length; i++) {
types[i] = types[i] & TYPE_ALL;
}
polygon.vertexTypes = types;
}
if (holes.length) {
polygon.holeIndices = holes;
}

result.push(polygon);
}
}
return result;
}

// vertexTypes:
// TYPE_VERTEX - vertex from the original polygon
// TYPE_EDGE - interpolated point on the edge of the original polygon
// TYPE_INSIDE - inside the original polygon
// FLAG_NEW_EDGE marks a vertex if the edge between this vertex and the next is added by slicing
// eslint-disable-next-line max-params
function bisectPolygon(positions, size, startIndex, endIndex, bbox, edge) {
function bisectPolygon(positions, vertexTypes, size, startIndex, endIndex, bbox, edge) {
const numPoints = (endIndex - startIndex) / size;
const resultLow = [];
const resultHigh = [];
const typesLow = vertexTypes && [];
const typesHigh = vertexTypes && [];
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 = vertexTypes && vertexTypes[endIndex / size - 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 = (vertexTypes && vertexTypes[startIndex / size + i]) || TYPE_VERTEX;

// if segment goes through the boundary, add an intersection
if (side && prevSide && prevSide !== side) {
intersect(prev, p, edge, bbox, scratchPoint);
push(resultLow, scratchPoint);
push(resultHigh, scratchPoint);
if (vertexTypes) {
const insertedType =
type & TYPE_INSIDE || prevType & TYPE_INSIDE || prevType & FLAG_NEW_EDGE
? TYPE_INSIDE
: TYPE_EDGE;
typesLow.push(insertedType);
typesHigh.push(insertedType);
}
}

if (side <= 0) {
Expand All @@ -135,11 +176,28 @@ function bisectPolygon(positions, size, startIndex, endIndex, bbox, edge) {
highPointCount += side;
}

if (vertexTypes) {
if (side <= 0) {
typesLow.push(type);
} else {
typesLow[typesLow.length - 1] |= FLAG_NEW_EDGE;
}
if (side >= 0) {
typesHigh.push(type);
} else {
typesHigh[typesHigh.length - 1] |= FLAG_NEW_EDGE;
}
}

copy(prev, p);
prevSide = side;
prevType = type;
}

return [lowPointCount ? resultLow : null, highPointCount ? resultHigh : null];
return [
lowPointCount ? {pos: resultLow, types: typesLow} : null,
highPointCount ? {pos: resultHigh, types: typesHigh} : null
];
}

function getGridCell(p, gridResolution, gridOffset, out) {
Expand Down
7 changes: 4 additions & 3 deletions modules/polygon/src/cut-by-mercator-bounds.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Polygon} from "./cut-by-grid";

export function cutPolylineByMercatorBounds(
positions : Array<number>,
options : {
options? : {
size? : number,
startIndex? : number,
endIndex? : number,
Expand All @@ -13,10 +13,11 @@ export function cutPolylineByMercatorBounds(
export function cutPolygonByMercatorBounds(
positions : Array<number>,
holeIndices : Array<number> | null,
options : {
options? : {
size? : number,
normalize? : boolean,
maxLatitude: number
maxLatitude?: number,
vertexTypes? : boolean
}
) : Array<Polygon>;

7 changes: 4 additions & 3 deletions modules/polygon/src/cut-by-mercator-bounds.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function cutPolylineByMercatorBounds(positions, options = {}) {

// https://user-images.githubusercontent.com/2059298/78465770-94241080-76ae-11ea-809a-6a8534dac1d9.png
export function cutPolygonByMercatorBounds(positions, holeIndices, options = {}) {
const {size = 2, normalize = true} = options;
const {size = 2, normalize = true, vertexTypes = false} = options;
holeIndices = holeIndices || [];
const newPositions = [];
const newHoleIndices = [];
Expand Down Expand Up @@ -69,14 +69,15 @@ export function cutPolygonByMercatorBounds(positions, holeIndices, options = {})
const parts = cutPolygonByGrid(newPositions, newHoleIndices, {
size,
gridResolution: 360,
gridOffset: [-180, -180]
gridOffset: [-180, -180],
vertexTypes
});

if (normalize) {
// Each part is guaranteed to be in a single copy of the world
// Map longitudes back to [-180, 180]
for (const part of parts) {
shiftLongitudesIntoRange(Array.isArray(part) ? part : part.positions, size);
shiftLongitudesIntoRange(part.positions, size);
}
}
return parts;
Expand Down
4 changes: 2 additions & 2 deletions modules/polygon/src/lineclip.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ type BoundingBox = [number, number, number, number];
export function clipPolyline(
positions : Array<number>,
bbox : BoundingBox,
options : {
options? : {
size? : number,
startIndex? : number,
endIndex? : number
Expand All @@ -13,7 +13,7 @@ export function clipPolyline(
export function clipPolygon(
positions: Array<number>,
bbox: BoundingBox,
options : {
options? : {
size? : number,
startIndex? : number,
endIndex? : number
Expand Down

0 comments on commit ada2629

Please sign in to comment.