Make clockwise check work when coordinates are duplicated
This commit is contained in:
@@ -16,44 +16,85 @@ import {coordinates as reverseCoordinates} from './reverse.js';
|
|||||||
export function linearRingIsClockwise(flatCoordinates, offset, end, stride) {
|
export function linearRingIsClockwise(flatCoordinates, offset, end, stride) {
|
||||||
// https://stackoverflow.com/a/1180256/2389327
|
// https://stackoverflow.com/a/1180256/2389327
|
||||||
// https://en.wikipedia.org/wiki/Curve_orientation#Orientation_of_a_simple_polygon
|
// https://en.wikipedia.org/wiki/Curve_orientation#Orientation_of_a_simple_polygon
|
||||||
|
if ((end - offset) / stride >= 3) {
|
||||||
let firstVertexRepeated = true;
|
const minVertex = findCornerVertex(flatCoordinates, offset, end, stride);
|
||||||
for (let i = 0; i < stride; ++i) {
|
|
||||||
if (flatCoordinates[offset + i] !== flatCoordinates[end - stride + i]) {
|
|
||||||
firstVertexRepeated = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (firstVertexRepeated) {
|
|
||||||
end -= stride;
|
|
||||||
}
|
|
||||||
const iMinVertex = findCornerVertex(flatCoordinates, offset, end, stride);
|
|
||||||
// Orientation matrix:
|
// Orientation matrix:
|
||||||
// [ 1 xa ya ]
|
// [ 1 xa ya ]
|
||||||
// O = | 1 xb yb |
|
// O = | 1 xb yb |
|
||||||
// [ 1 xc yc ]
|
// [ 1 xc yc ]
|
||||||
let iPreviousVertex = iMinVertex - stride;
|
const previousVertex = findUniqueVertex(
|
||||||
if (iPreviousVertex < offset) {
|
flatCoordinates,
|
||||||
iPreviousVertex = end - stride;
|
offset,
|
||||||
}
|
end,
|
||||||
let iNextVertex = iMinVertex + stride;
|
stride,
|
||||||
if (iNextVertex >= end) {
|
minVertex,
|
||||||
iNextVertex = offset;
|
-1
|
||||||
}
|
);
|
||||||
const aX = flatCoordinates[iPreviousVertex];
|
const nextVertex = findUniqueVertex(
|
||||||
const aY = flatCoordinates[iPreviousVertex + 1];
|
flatCoordinates,
|
||||||
const bX = flatCoordinates[iMinVertex];
|
offset,
|
||||||
const bY = flatCoordinates[iMinVertex + 1];
|
end,
|
||||||
const cX = flatCoordinates[iNextVertex];
|
stride,
|
||||||
const cY = flatCoordinates[iNextVertex + 1];
|
minVertex,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
const aX = flatCoordinates[previousVertex];
|
||||||
|
const aY = flatCoordinates[previousVertex + 1];
|
||||||
|
const bX = flatCoordinates[minVertex];
|
||||||
|
const bY = flatCoordinates[minVertex + 1];
|
||||||
|
const cX = flatCoordinates[nextVertex];
|
||||||
|
const cY = flatCoordinates[nextVertex + 1];
|
||||||
const determinant =
|
const determinant =
|
||||||
bX * cY + aX * bY + aY * cX - (aY * bX + bY * cX + aX * cY);
|
bX * cY + aX * bY + aY * cX - (aY * bX + bY * cX + aX * cY);
|
||||||
|
|
||||||
return determinant < 0;
|
return determinant < 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Find vertex along one edge of bounding box.
|
/**
|
||||||
// In this case, we find smallest y; in case of tie also smallest x.
|
* Finds the next unique vertex in forward or backward direction of a ring.
|
||||||
|
* @param {Array<number>} flatCoordinates Flat coordinates.
|
||||||
|
* @param {number} offset Offset.
|
||||||
|
* @param {number} end End.
|
||||||
|
* @param {number} stride Stride.
|
||||||
|
* @param {number} start Start vertex.
|
||||||
|
* @param {number} direction 1 for forward, -1 for backward.
|
||||||
|
* @return {number} vertex Index of the found vertex.
|
||||||
|
*/
|
||||||
|
function findUniqueVertex(
|
||||||
|
flatCoordinates,
|
||||||
|
offset,
|
||||||
|
end,
|
||||||
|
stride,
|
||||||
|
start,
|
||||||
|
direction
|
||||||
|
) {
|
||||||
|
let previousX, previousY, x, y;
|
||||||
|
let i = start;
|
||||||
|
while (x === previousX && y === previousY) {
|
||||||
|
previousX = flatCoordinates[i];
|
||||||
|
previousY = flatCoordinates[i + 1];
|
||||||
|
i += direction * stride;
|
||||||
|
if (i >= end) {
|
||||||
|
i = offset;
|
||||||
|
} else if (i <= offset) {
|
||||||
|
i = end - stride;
|
||||||
|
}
|
||||||
|
x = flatCoordinates[i];
|
||||||
|
y = flatCoordinates[i + 1];
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find vertex along one edge of bounding box.
|
||||||
|
* In this case, we find smallest y; in case of tie also smallest x.
|
||||||
|
* @param {Array<number>} flatCoordinates Flat coordinates.
|
||||||
|
* @param {number} offset Offset.
|
||||||
|
* @param {number} end End.
|
||||||
|
* @param {number} stride Stride.
|
||||||
|
* @return {number} Corner vertex.
|
||||||
|
*/
|
||||||
function findCornerVertex(flatCoordinates, offset, end, stride) {
|
function findCornerVertex(flatCoordinates, offset, end, stride) {
|
||||||
let iMinVertex = -1;
|
let iMinVertex = -1;
|
||||||
let minY = Infinity;
|
let minY = Infinity;
|
||||||
|
|||||||
@@ -29,6 +29,61 @@ describe('ol.geom.flat.orient', function () {
|
|||||||
);
|
);
|
||||||
expect(isClockwise).to.be(false);
|
expect(isClockwise).to.be(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('identifies clockwise with duplicated coordinates', function () {
|
||||||
|
const flatCoordinates = [0, 1, 0, 1, 1, 4, 1, 4, 4, 3, 4, 3, 3, 0, 3, 0];
|
||||||
|
const isClockwise = linearRingIsClockwise(
|
||||||
|
flatCoordinates,
|
||||||
|
0,
|
||||||
|
flatCoordinates.length,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
expect(isClockwise).to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('identifies anti-clockwise with duplicated coordinates', function () {
|
||||||
|
const flatCoordinates = [2, 2, 2, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 3];
|
||||||
|
const isClockwise = linearRingIsClockwise(
|
||||||
|
flatCoordinates,
|
||||||
|
0,
|
||||||
|
flatCoordinates.length,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
expect(isClockwise).to.be(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('identifies clockwise when last coordinate equals first', function () {
|
||||||
|
const flatCoordinates = [0, 1, 1, 4, 4, 3, 3, 0, 0, 1];
|
||||||
|
const isClockwise = linearRingIsClockwise(
|
||||||
|
flatCoordinates,
|
||||||
|
0,
|
||||||
|
flatCoordinates.length,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
expect(isClockwise).to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('identifies anti-clockwise when last coordinate equals first', function () {
|
||||||
|
const flatCoordinates = [2, 2, 3, 2, 3, 3, 2, 3, 2, 2];
|
||||||
|
const isClockwise = linearRingIsClockwise(
|
||||||
|
flatCoordinates,
|
||||||
|
0,
|
||||||
|
flatCoordinates.length,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
expect(isClockwise).to.be(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined when ring has too few vertices', function () {
|
||||||
|
const flatCoordinates = [2, 2, 3, 2];
|
||||||
|
const isClockwise = linearRingIsClockwise(
|
||||||
|
flatCoordinates,
|
||||||
|
0,
|
||||||
|
flatCoordinates.length,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
expect(isClockwise).to.be(undefined);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ol.geom.flat.orient.linearRingsAreOriented', function () {
|
describe('ol.geom.flat.orient.linearRingsAreOriented', function () {
|
||||||
|
|||||||
Reference in New Issue
Block a user