@immugio/three-math-extensions 0.2.33 → 0.2.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -1
- package/cjs/Polygon.js +29 -3
- package/cjs/containsPoint.js +70 -0
- package/cjs/ensurePolygonClockwise.js +11 -0
- package/cjs/getPolygonArea.js +24 -0
- package/cjs/index.js +9 -1
- package/cjs/isPointInPolygon.js +7 -0
- package/cjs/isPolygonClockwise.js +18 -0
- package/esm/Polygon.js +29 -3
- package/esm/containsPoint.js +65 -0
- package/esm/ensurePolygonClockwise.js +7 -0
- package/esm/getPolygonArea.js +20 -0
- package/esm/index.js +4 -0
- package/esm/isPointInPolygon.js +7 -0
- package/esm/isPolygonClockwise.js +14 -0
- package/package.json +1 -1
- package/src/Polygon.ts +36 -3
- package/src/containsPoint.ts +66 -0
- package/src/ensurePolygonClockwise.ts +10 -0
- package/src/getPolygonArea.ts +22 -0
- package/src/index.ts +5 -1
- package/src/isPointInPolygon.ts +7 -2
- package/src/isPolygonClockwise.ts +16 -0
- package/types/Polygon.d.ts +5 -1
- package/types/containsPoint.d.ts +12 -0
- package/types/ensurePolygonClockwise.d.ts +2 -0
- package/types/getPolygonArea.d.ts +7 -0
- package/types/index.d.ts +4 -0
- package/types/isPointInPolygon.d.ts +7 -0
- package/types/isPolygonClockwise.d.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|
9
9
|
|
|
10
|
-
## [0.2.
|
|
10
|
+
## [0.2.34](https://github.com/Immugio/three-math-extensions/compare/0.2.33...0.2.34)
|
|
11
|
+
|
|
12
|
+
### Commits
|
|
13
|
+
|
|
14
|
+
- Add containsPoint [`8e15aa2`](https://github.com/Immugio/three-math-extensions/commit/8e15aa275efa2b8c939bb3c85e456a269c360054)
|
|
15
|
+
- Add isPolygonClockwise [`9fb7f1c`](https://github.com/Immugio/three-math-extensions/commit/9fb7f1c6f9f185ffacdf933158618588a12e4b33)
|
|
16
|
+
- Add getPolygonArea [`f2f2c32`](https://github.com/Immugio/three-math-extensions/commit/f2f2c32ca19790972520c1d8827d76f20052a1fe)
|
|
17
|
+
- Improve Polygon.containsPoint [`805822d`](https://github.com/Immugio/three-math-extensions/commit/805822df97009d4f4004227e642562760607bc9f)
|
|
18
|
+
- Add Polygon.ensureOpen [`12d96bc`](https://github.com/Immugio/three-math-extensions/commit/12d96bcef0e06da62927b2fd336f821040a6c6c5)
|
|
19
|
+
- Add ensurePolygonClockwise [`a8b1f03`](https://github.com/Immugio/three-math-extensions/commit/a8b1f03e85d86d56b341c2636e4e92eeb652af77)
|
|
20
|
+
|
|
21
|
+
## [0.2.33](https://github.com/Immugio/three-math-extensions/compare/0.2.32...0.2.33) - 2024-10-21
|
|
11
22
|
|
|
12
23
|
### Commits
|
|
13
24
|
|
package/cjs/Polygon.js
CHANGED
|
@@ -5,8 +5,11 @@ const Vec2_1 = require("./Vec2");
|
|
|
5
5
|
const Rectangle_1 = require("./Rectangle");
|
|
6
6
|
const BoundingBox_1 = require("./BoundingBox");
|
|
7
7
|
const polygonPerimeter_1 = require("./polygonPerimeter");
|
|
8
|
-
const isPointInPolygon_1 = require("./isPointInPolygon");
|
|
9
8
|
const Line2D_1 = require("./Line2D");
|
|
9
|
+
const isPolygonClockwise_1 = require("./isPolygonClockwise");
|
|
10
|
+
const ensurePolygonClockwise_1 = require("./ensurePolygonClockwise");
|
|
11
|
+
const containsPoint_1 = require("./containsPoint");
|
|
12
|
+
const getPolygonArea_1 = require("./getPolygonArea");
|
|
10
13
|
class Polygon {
|
|
11
14
|
contour;
|
|
12
15
|
holes;
|
|
@@ -61,6 +64,21 @@ class Polygon {
|
|
|
61
64
|
}
|
|
62
65
|
return this;
|
|
63
66
|
}
|
|
67
|
+
ensureOpen() {
|
|
68
|
+
function ensure(points) {
|
|
69
|
+
if (points.length > 2 && points[0].equals(points.at(-1))) {
|
|
70
|
+
points.pop();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
ensure(this.contour);
|
|
74
|
+
for (const hole of this.holes || []) {
|
|
75
|
+
ensure(hole);
|
|
76
|
+
}
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
get area() {
|
|
80
|
+
return (0, getPolygonArea_1.getPolygonArea)(this.contour);
|
|
81
|
+
}
|
|
64
82
|
boundingBox() {
|
|
65
83
|
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
66
84
|
for (const p of this.contour) {
|
|
@@ -93,8 +111,16 @@ class Polygon {
|
|
|
93
111
|
perimeter() {
|
|
94
112
|
return (0, polygonPerimeter_1.polygonPerimeter)(this.contour);
|
|
95
113
|
}
|
|
96
|
-
|
|
97
|
-
return (0,
|
|
114
|
+
get isClockwise() {
|
|
115
|
+
return (0, isPolygonClockwise_1.isPolygonClockwise)(this.contour);
|
|
116
|
+
}
|
|
117
|
+
ensureClockwise() {
|
|
118
|
+
(0, ensurePolygonClockwise_1.ensurePolygonClockwise)(this.contour);
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
containsPoint(...points) {
|
|
122
|
+
return points.every(point => (0, containsPoint_1.containsPoint)(this.contour, point)) &&
|
|
123
|
+
(this.holes || []).every(hole => !points.some(point => (0, containsPoint_1.containsPoint)(hole, point)));
|
|
98
124
|
}
|
|
99
125
|
flipSingle(centerX, poly) {
|
|
100
126
|
for (const point of poly) {
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.containsPoints = exports.containsPoint = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Return if polygon contains @point or when the point is on the polygon boundary.
|
|
6
|
+
* Works consistently with points on the boundary.
|
|
7
|
+
* Works with simple polygons only.
|
|
8
|
+
* The code is copied from PolyK library.
|
|
9
|
+
* @returns {boolean} depth
|
|
10
|
+
* @param polygon
|
|
11
|
+
* @param point
|
|
12
|
+
*/
|
|
13
|
+
function containsPoint(polygon, point) {
|
|
14
|
+
const p = [];
|
|
15
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
16
|
+
p.push(polygon[i].x);
|
|
17
|
+
p.push(polygon[i].y);
|
|
18
|
+
}
|
|
19
|
+
const px = point.x;
|
|
20
|
+
const py = point.y;
|
|
21
|
+
const n = p.length >> 1;
|
|
22
|
+
let ax;
|
|
23
|
+
let ay = p[2 * n - 3] - py;
|
|
24
|
+
let bx = p[2 * n - 2] - px;
|
|
25
|
+
let by = p[2 * n - 1] - py;
|
|
26
|
+
let lup;
|
|
27
|
+
// var lup = by > ay;
|
|
28
|
+
for (let i = 0; i < n; i++) {
|
|
29
|
+
ax = bx;
|
|
30
|
+
ay = by;
|
|
31
|
+
bx = p[2 * i] - px;
|
|
32
|
+
by = p[2 * i + 1] - py;
|
|
33
|
+
if (ay === by)
|
|
34
|
+
continue;
|
|
35
|
+
lup = by > ay;
|
|
36
|
+
}
|
|
37
|
+
let depth = 0;
|
|
38
|
+
for (let i = 0; i < n; i++) {
|
|
39
|
+
ax = bx;
|
|
40
|
+
ay = by;
|
|
41
|
+
bx = p[2 * i] - px;
|
|
42
|
+
by = p[2 * i + 1] - py;
|
|
43
|
+
if (ay < 0 && by < 0)
|
|
44
|
+
continue; // both "up" or both "down"
|
|
45
|
+
if (ay > 0 && by > 0)
|
|
46
|
+
continue; // both "up" or both "down"
|
|
47
|
+
if (ax < 0 && bx < 0)
|
|
48
|
+
continue; // both points on the left
|
|
49
|
+
if (ay === by && Math.min(ax, bx) <= 0)
|
|
50
|
+
return true;
|
|
51
|
+
if (ay === by)
|
|
52
|
+
continue;
|
|
53
|
+
const lx = ax + (bx - ax) * (-ay) / (by - ay);
|
|
54
|
+
if (lx === 0)
|
|
55
|
+
return true; // point on edge
|
|
56
|
+
if (lx > 0)
|
|
57
|
+
depth++;
|
|
58
|
+
if (ay === 0 && lup && by > ay)
|
|
59
|
+
depth--; // hit vertex, both up
|
|
60
|
+
if (ay === 0 && !lup && by < ay)
|
|
61
|
+
depth--; // hit vertex, both down(x < (p[j].x - p[i].x) * (y - p[i].y) / (p[j].y - p[i].y) + p[i].x)) {
|
|
62
|
+
lup = by > ay;
|
|
63
|
+
}
|
|
64
|
+
return (depth & 1) === 1;
|
|
65
|
+
}
|
|
66
|
+
exports.containsPoint = containsPoint;
|
|
67
|
+
function containsPoints(polygon, points) {
|
|
68
|
+
return points.every(p => containsPoint(polygon, p));
|
|
69
|
+
}
|
|
70
|
+
exports.containsPoints = containsPoints;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensurePolygonClockwise = void 0;
|
|
4
|
+
const isPolygonClockwise_1 = require("./isPolygonClockwise");
|
|
5
|
+
function ensurePolygonClockwise(poly) {
|
|
6
|
+
if (!(0, isPolygonClockwise_1.isPolygonClockwise)(poly)) {
|
|
7
|
+
return poly.reverse();
|
|
8
|
+
}
|
|
9
|
+
return poly;
|
|
10
|
+
}
|
|
11
|
+
exports.ensurePolygonClockwise = ensurePolygonClockwise;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getPolygonArea = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Returns the area of polygon.
|
|
6
|
+
* @returns {number}
|
|
7
|
+
* @param polygon {Point2[]}
|
|
8
|
+
*/
|
|
9
|
+
function getPolygonArea(polygon) {
|
|
10
|
+
const p = [];
|
|
11
|
+
for (const point of polygon) {
|
|
12
|
+
p.push(point.x, point.y);
|
|
13
|
+
}
|
|
14
|
+
if (p.length < 6)
|
|
15
|
+
return 0;
|
|
16
|
+
const l = p.length - 2;
|
|
17
|
+
let sum = 0;
|
|
18
|
+
for (let i = 0; i < l; i += 2) {
|
|
19
|
+
sum += (p[i + 2] - p[i]) * (p[i + 1] + p[i + 3]);
|
|
20
|
+
}
|
|
21
|
+
sum += (p[0] - p[l]) * (p[l + 1] + p[1]);
|
|
22
|
+
return Math.abs(-sum * 0.5); // Handles -0
|
|
23
|
+
}
|
|
24
|
+
exports.getPolygonArea = getPolygonArea;
|
package/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sortLinesByConnections = exports.extendOrTrimPolylinesAtIntersections = exports.offsetPolyline = exports.polygonPerimeter = exports.isContinuousClosedShape = exports.directions2d = exports.directions = exports.isPointInPolygon = exports.HalfPI = exports.TwoPI = exports.normalizeAngleRadians = exports.normalizeAngleDegrees = exports.Rectangle = exports.BoundingBox = exports.Polygon = exports.Size2 = exports.Line3D = exports.Line2D = exports.Vec3 = exports.Vec2 = void 0;
|
|
3
|
+
exports.getPolygonArea = exports.containsPoint = exports.ensurePolygonClockwise = exports.isPolygonClockwise = exports.sortLinesByConnections = exports.extendOrTrimPolylinesAtIntersections = exports.offsetPolyline = exports.polygonPerimeter = exports.isContinuousClosedShape = exports.directions2d = exports.directions = exports.isPointInPolygon = exports.HalfPI = exports.TwoPI = exports.normalizeAngleRadians = exports.normalizeAngleDegrees = exports.Rectangle = exports.BoundingBox = exports.Polygon = exports.Size2 = exports.Line3D = exports.Line2D = exports.Vec3 = exports.Vec2 = void 0;
|
|
4
4
|
var Vec2_1 = require("./Vec2");
|
|
5
5
|
Object.defineProperty(exports, "Vec2", { enumerable: true, get: function () { return Vec2_1.Vec2; } });
|
|
6
6
|
var Vec3_1 = require("./Vec3");
|
|
@@ -40,3 +40,11 @@ var extendOrTrimPolylinesAtIntersections_1 = require("./extendOrTrimPolylinesAtI
|
|
|
40
40
|
Object.defineProperty(exports, "extendOrTrimPolylinesAtIntersections", { enumerable: true, get: function () { return extendOrTrimPolylinesAtIntersections_1.extendOrTrimPolylinesAtIntersections; } });
|
|
41
41
|
var sortLinesByConnections_1 = require("./sortLinesByConnections");
|
|
42
42
|
Object.defineProperty(exports, "sortLinesByConnections", { enumerable: true, get: function () { return sortLinesByConnections_1.sortLinesByConnections; } });
|
|
43
|
+
var isPolygonClockwise_1 = require("./isPolygonClockwise");
|
|
44
|
+
Object.defineProperty(exports, "isPolygonClockwise", { enumerable: true, get: function () { return isPolygonClockwise_1.isPolygonClockwise; } });
|
|
45
|
+
var ensurePolygonClockwise_1 = require("./ensurePolygonClockwise");
|
|
46
|
+
Object.defineProperty(exports, "ensurePolygonClockwise", { enumerable: true, get: function () { return ensurePolygonClockwise_1.ensurePolygonClockwise; } });
|
|
47
|
+
var containsPoint_1 = require("./containsPoint");
|
|
48
|
+
Object.defineProperty(exports, "containsPoint", { enumerable: true, get: function () { return containsPoint_1.containsPoint; } });
|
|
49
|
+
var getPolygonArea_1 = require("./getPolygonArea");
|
|
50
|
+
Object.defineProperty(exports, "getPolygonArea", { enumerable: true, get: function () { return getPolygonArea_1.getPolygonArea; } });
|
package/cjs/isPointInPolygon.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isPointInPolygon = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Check if a point is inside a polygon
|
|
6
|
+
* Warning: The function returns unreliable results for points on the polygon boundary.
|
|
7
|
+
* Obsolete, use containsPoint instead.
|
|
8
|
+
* @param p
|
|
9
|
+
* @param point
|
|
10
|
+
*/
|
|
4
11
|
function isPointInPolygon(p, point) {
|
|
5
12
|
const x = point.x, y = point.y;
|
|
6
13
|
let i, j, c = false;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPolygonClockwise = void 0;
|
|
4
|
+
/*
|
|
5
|
+
* Determines if a polygon is clockwise or counter-clockwise.
|
|
6
|
+
* X increases to the right, Y increases downwards.
|
|
7
|
+
* Based on this answer https://stackoverflow.com/a/18472899/1837173 - the result is inverted, they assume the inverse y-axis
|
|
8
|
+
*/
|
|
9
|
+
function isPolygonClockwise(vertices) {
|
|
10
|
+
let sum = 0.0;
|
|
11
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
12
|
+
const v1 = vertices[i];
|
|
13
|
+
const v2 = vertices[(i + 1) % vertices.length];
|
|
14
|
+
sum += (v2.x - v1.x) * (v2.y + v1.y);
|
|
15
|
+
}
|
|
16
|
+
return sum < 0.0;
|
|
17
|
+
}
|
|
18
|
+
exports.isPolygonClockwise = isPolygonClockwise;
|
package/esm/Polygon.js
CHANGED
|
@@ -2,8 +2,11 @@ import { Vec2 } from "./Vec2";
|
|
|
2
2
|
import { Rectangle } from "./Rectangle";
|
|
3
3
|
import { BoundingBox } from "./BoundingBox";
|
|
4
4
|
import { polygonPerimeter } from "./polygonPerimeter";
|
|
5
|
-
import { isPointInPolygon } from "./isPointInPolygon";
|
|
6
5
|
import { Line2D } from "./Line2D";
|
|
6
|
+
import { isPolygonClockwise } from "./isPolygonClockwise";
|
|
7
|
+
import { ensurePolygonClockwise } from "./ensurePolygonClockwise";
|
|
8
|
+
import { containsPoint } from "./containsPoint";
|
|
9
|
+
import { getPolygonArea } from "./getPolygonArea";
|
|
7
10
|
export class Polygon {
|
|
8
11
|
contour;
|
|
9
12
|
holes;
|
|
@@ -58,6 +61,21 @@ export class Polygon {
|
|
|
58
61
|
}
|
|
59
62
|
return this;
|
|
60
63
|
}
|
|
64
|
+
ensureOpen() {
|
|
65
|
+
function ensure(points) {
|
|
66
|
+
if (points.length > 2 && points[0].equals(points.at(-1))) {
|
|
67
|
+
points.pop();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
ensure(this.contour);
|
|
71
|
+
for (const hole of this.holes || []) {
|
|
72
|
+
ensure(hole);
|
|
73
|
+
}
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
get area() {
|
|
77
|
+
return getPolygonArea(this.contour);
|
|
78
|
+
}
|
|
61
79
|
boundingBox() {
|
|
62
80
|
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
63
81
|
for (const p of this.contour) {
|
|
@@ -90,8 +108,16 @@ export class Polygon {
|
|
|
90
108
|
perimeter() {
|
|
91
109
|
return polygonPerimeter(this.contour);
|
|
92
110
|
}
|
|
93
|
-
|
|
94
|
-
return
|
|
111
|
+
get isClockwise() {
|
|
112
|
+
return isPolygonClockwise(this.contour);
|
|
113
|
+
}
|
|
114
|
+
ensureClockwise() {
|
|
115
|
+
ensurePolygonClockwise(this.contour);
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
containsPoint(...points) {
|
|
119
|
+
return points.every(point => containsPoint(this.contour, point)) &&
|
|
120
|
+
(this.holes || []).every(hole => !points.some(point => containsPoint(hole, point)));
|
|
95
121
|
}
|
|
96
122
|
flipSingle(centerX, poly) {
|
|
97
123
|
for (const point of poly) {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return if polygon contains @point or when the point is on the polygon boundary.
|
|
3
|
+
* Works consistently with points on the boundary.
|
|
4
|
+
* Works with simple polygons only.
|
|
5
|
+
* The code is copied from PolyK library.
|
|
6
|
+
* @returns {boolean} depth
|
|
7
|
+
* @param polygon
|
|
8
|
+
* @param point
|
|
9
|
+
*/
|
|
10
|
+
export function containsPoint(polygon, point) {
|
|
11
|
+
const p = [];
|
|
12
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
13
|
+
p.push(polygon[i].x);
|
|
14
|
+
p.push(polygon[i].y);
|
|
15
|
+
}
|
|
16
|
+
const px = point.x;
|
|
17
|
+
const py = point.y;
|
|
18
|
+
const n = p.length >> 1;
|
|
19
|
+
let ax;
|
|
20
|
+
let ay = p[2 * n - 3] - py;
|
|
21
|
+
let bx = p[2 * n - 2] - px;
|
|
22
|
+
let by = p[2 * n - 1] - py;
|
|
23
|
+
let lup;
|
|
24
|
+
// var lup = by > ay;
|
|
25
|
+
for (let i = 0; i < n; i++) {
|
|
26
|
+
ax = bx;
|
|
27
|
+
ay = by;
|
|
28
|
+
bx = p[2 * i] - px;
|
|
29
|
+
by = p[2 * i + 1] - py;
|
|
30
|
+
if (ay === by)
|
|
31
|
+
continue;
|
|
32
|
+
lup = by > ay;
|
|
33
|
+
}
|
|
34
|
+
let depth = 0;
|
|
35
|
+
for (let i = 0; i < n; i++) {
|
|
36
|
+
ax = bx;
|
|
37
|
+
ay = by;
|
|
38
|
+
bx = p[2 * i] - px;
|
|
39
|
+
by = p[2 * i + 1] - py;
|
|
40
|
+
if (ay < 0 && by < 0)
|
|
41
|
+
continue; // both "up" or both "down"
|
|
42
|
+
if (ay > 0 && by > 0)
|
|
43
|
+
continue; // both "up" or both "down"
|
|
44
|
+
if (ax < 0 && bx < 0)
|
|
45
|
+
continue; // both points on the left
|
|
46
|
+
if (ay === by && Math.min(ax, bx) <= 0)
|
|
47
|
+
return true;
|
|
48
|
+
if (ay === by)
|
|
49
|
+
continue;
|
|
50
|
+
const lx = ax + (bx - ax) * (-ay) / (by - ay);
|
|
51
|
+
if (lx === 0)
|
|
52
|
+
return true; // point on edge
|
|
53
|
+
if (lx > 0)
|
|
54
|
+
depth++;
|
|
55
|
+
if (ay === 0 && lup && by > ay)
|
|
56
|
+
depth--; // hit vertex, both up
|
|
57
|
+
if (ay === 0 && !lup && by < ay)
|
|
58
|
+
depth--; // hit vertex, both down(x < (p[j].x - p[i].x) * (y - p[i].y) / (p[j].y - p[i].y) + p[i].x)) {
|
|
59
|
+
lup = by > ay;
|
|
60
|
+
}
|
|
61
|
+
return (depth & 1) === 1;
|
|
62
|
+
}
|
|
63
|
+
export function containsPoints(polygon, points) {
|
|
64
|
+
return points.every(p => containsPoint(polygon, p));
|
|
65
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the area of polygon.
|
|
3
|
+
* @returns {number}
|
|
4
|
+
* @param polygon {Point2[]}
|
|
5
|
+
*/
|
|
6
|
+
export function getPolygonArea(polygon) {
|
|
7
|
+
const p = [];
|
|
8
|
+
for (const point of polygon) {
|
|
9
|
+
p.push(point.x, point.y);
|
|
10
|
+
}
|
|
11
|
+
if (p.length < 6)
|
|
12
|
+
return 0;
|
|
13
|
+
const l = p.length - 2;
|
|
14
|
+
let sum = 0;
|
|
15
|
+
for (let i = 0; i < l; i += 2) {
|
|
16
|
+
sum += (p[i + 2] - p[i]) * (p[i + 1] + p[i + 3]);
|
|
17
|
+
}
|
|
18
|
+
sum += (p[0] - p[l]) * (p[l + 1] + p[1]);
|
|
19
|
+
return Math.abs(-sum * 0.5); // Handles -0
|
|
20
|
+
}
|
package/esm/index.js
CHANGED
|
@@ -17,3 +17,7 @@ export { polygonPerimeter } from "./polygonPerimeter";
|
|
|
17
17
|
export { offsetPolyline } from "./offsetPolyline";
|
|
18
18
|
export { extendOrTrimPolylinesAtIntersections } from "./extendOrTrimPolylinesAtIntersections";
|
|
19
19
|
export { sortLinesByConnections } from "./sortLinesByConnections";
|
|
20
|
+
export { isPolygonClockwise } from "./isPolygonClockwise";
|
|
21
|
+
export { ensurePolygonClockwise } from "./ensurePolygonClockwise";
|
|
22
|
+
export { containsPoint } from "./containsPoint";
|
|
23
|
+
export { getPolygonArea } from "./getPolygonArea";
|
package/esm/isPointInPolygon.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a point is inside a polygon
|
|
3
|
+
* Warning: The function returns unreliable results for points on the polygon boundary.
|
|
4
|
+
* Obsolete, use containsPoint instead.
|
|
5
|
+
* @param p
|
|
6
|
+
* @param point
|
|
7
|
+
*/
|
|
1
8
|
export function isPointInPolygon(p, point) {
|
|
2
9
|
const x = point.x, y = point.y;
|
|
3
10
|
let i, j, c = false;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Determines if a polygon is clockwise or counter-clockwise.
|
|
3
|
+
* X increases to the right, Y increases downwards.
|
|
4
|
+
* Based on this answer https://stackoverflow.com/a/18472899/1837173 - the result is inverted, they assume the inverse y-axis
|
|
5
|
+
*/
|
|
6
|
+
export function isPolygonClockwise(vertices) {
|
|
7
|
+
let sum = 0.0;
|
|
8
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
9
|
+
const v1 = vertices[i];
|
|
10
|
+
const v2 = vertices[(i + 1) % vertices.length];
|
|
11
|
+
sum += (v2.x - v1.x) * (v2.y + v1.y);
|
|
12
|
+
}
|
|
13
|
+
return sum < 0.0;
|
|
14
|
+
}
|
package/package.json
CHANGED
package/src/Polygon.ts
CHANGED
|
@@ -3,8 +3,11 @@ import { Vec2 } from "./Vec2";
|
|
|
3
3
|
import { Rectangle } from "./Rectangle";
|
|
4
4
|
import { BoundingBox } from "./BoundingBox";
|
|
5
5
|
import { polygonPerimeter } from "./polygonPerimeter";
|
|
6
|
-
import { isPointInPolygon } from "./isPointInPolygon";
|
|
7
6
|
import { Line2D } from "./Line2D";
|
|
7
|
+
import { isPolygonClockwise } from "./isPolygonClockwise";
|
|
8
|
+
import { ensurePolygonClockwise } from "./ensurePolygonClockwise";
|
|
9
|
+
import { containsPoint } from "./containsPoint";
|
|
10
|
+
import { getPolygonArea } from "./getPolygonArea";
|
|
8
11
|
|
|
9
12
|
export class Polygon {
|
|
10
13
|
|
|
@@ -73,6 +76,26 @@ export class Polygon {
|
|
|
73
76
|
return this;
|
|
74
77
|
}
|
|
75
78
|
|
|
79
|
+
public ensureOpen(): this {
|
|
80
|
+
function ensure(points: Vec2[]): void {
|
|
81
|
+
if (points.length > 2 && points[0].equals(points.at(-1))) {
|
|
82
|
+
points.pop();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
ensure(this.contour);
|
|
87
|
+
|
|
88
|
+
for (const hole of this.holes || []) {
|
|
89
|
+
ensure(hole);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public get area(): number {
|
|
96
|
+
return getPolygonArea(this.contour);
|
|
97
|
+
}
|
|
98
|
+
|
|
76
99
|
public boundingBox(): BoundingBox {
|
|
77
100
|
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
78
101
|
|
|
@@ -107,8 +130,18 @@ export class Polygon {
|
|
|
107
130
|
return polygonPerimeter(this.contour);
|
|
108
131
|
}
|
|
109
132
|
|
|
110
|
-
public
|
|
111
|
-
return
|
|
133
|
+
public get isClockwise(): boolean {
|
|
134
|
+
return isPolygonClockwise(this.contour);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public ensureClockwise(): this {
|
|
138
|
+
ensurePolygonClockwise(this.contour);
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public containsPoint(...points: Vec2[]): boolean {
|
|
143
|
+
return points.every(point => containsPoint(this.contour, point)) &&
|
|
144
|
+
(this.holes || []).every(hole => !points.some(point => containsPoint(hole, point)));
|
|
112
145
|
}
|
|
113
146
|
|
|
114
147
|
private flipSingle(centerX: number, poly: Vec2[]): void {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Point2 } from "./Point2";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Return if polygon contains @point or when the point is on the polygon boundary.
|
|
5
|
+
* Works consistently with points on the boundary.
|
|
6
|
+
* Works with simple polygons only.
|
|
7
|
+
* The code is copied from PolyK library.
|
|
8
|
+
* @returns {boolean} depth
|
|
9
|
+
* @param polygon
|
|
10
|
+
* @param point
|
|
11
|
+
*/
|
|
12
|
+
export function containsPoint(polygon: Point2[], point: Point2): boolean {
|
|
13
|
+
const p: number[] = [];
|
|
14
|
+
for (let i = 0; i < polygon.length; i++) {
|
|
15
|
+
p.push(polygon[i].x);
|
|
16
|
+
p.push(polygon[i].y);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const px = point.x;
|
|
20
|
+
const py = point.y;
|
|
21
|
+
const n = p.length >> 1;
|
|
22
|
+
|
|
23
|
+
let ax: number;
|
|
24
|
+
|
|
25
|
+
let ay = p[2 * n - 3] - py;
|
|
26
|
+
let bx = p[2 * n - 2] - px;
|
|
27
|
+
let by = p[2 * n - 1] - py;
|
|
28
|
+
let lup: boolean;
|
|
29
|
+
|
|
30
|
+
// var lup = by > ay;
|
|
31
|
+
for (let i = 0; i < n; i++) {
|
|
32
|
+
ax = bx;
|
|
33
|
+
ay = by;
|
|
34
|
+
bx = p[2 * i] - px;
|
|
35
|
+
by = p[2 * i + 1] - py;
|
|
36
|
+
if (ay === by) continue;
|
|
37
|
+
lup = by > ay;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let depth = 0;
|
|
41
|
+
for (let i = 0; i < n; i++) {
|
|
42
|
+
ax = bx;
|
|
43
|
+
ay = by;
|
|
44
|
+
bx = p[2 * i] - px;
|
|
45
|
+
by = p[2 * i + 1] - py;
|
|
46
|
+
if (ay < 0 && by < 0) continue; // both "up" or both "down"
|
|
47
|
+
if (ay > 0 && by > 0) continue; // both "up" or both "down"
|
|
48
|
+
if (ax < 0 && bx < 0) continue; // both points on the left
|
|
49
|
+
|
|
50
|
+
if (ay === by && Math.min(ax, bx) <= 0) return true;
|
|
51
|
+
if (ay === by) continue;
|
|
52
|
+
|
|
53
|
+
const lx = ax + (bx - ax) * (-ay) / (by - ay);
|
|
54
|
+
if (lx === 0) return true; // point on edge
|
|
55
|
+
if (lx > 0) depth++;
|
|
56
|
+
if (ay === 0 && lup && by > ay) depth--; // hit vertex, both up
|
|
57
|
+
if (ay === 0 && !lup && by < ay) depth--; // hit vertex, both down(x < (p[j].x - p[i].x) * (y - p[i].y) / (p[j].y - p[i].y) + p[i].x)) {
|
|
58
|
+
lup = by > ay;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (depth & 1) === 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function containsPoints(polygon: Point2[], points: Point2[]): boolean {
|
|
65
|
+
return points.every(p => containsPoint(polygon, p));
|
|
66
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Point2 } from "./Point2";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the area of polygon.
|
|
5
|
+
* @returns {number}
|
|
6
|
+
* @param polygon {Point2[]}
|
|
7
|
+
*/
|
|
8
|
+
export function getPolygonArea(polygon: Point2[]): number {
|
|
9
|
+
const p: number[] = [];
|
|
10
|
+
for (const point of polygon) {
|
|
11
|
+
p.push(point.x, point.y);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (p.length < 6) return 0;
|
|
15
|
+
const l = p.length - 2;
|
|
16
|
+
let sum = 0;
|
|
17
|
+
for (let i = 0; i < l; i += 2) {
|
|
18
|
+
sum += (p[i + 2] - p[i]) * (p[i + 1] + p[i + 3]);
|
|
19
|
+
}
|
|
20
|
+
sum += (p[0] - p[l]) * (p[l + 1] + p[1]);
|
|
21
|
+
return Math.abs(-sum * 0.5); // Handles -0
|
|
22
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -18,4 +18,8 @@ export { isContinuousClosedShape } from "./isContinuousClosedShape";
|
|
|
18
18
|
export { polygonPerimeter } from "./polygonPerimeter";
|
|
19
19
|
export { offsetPolyline } from "./offsetPolyline";
|
|
20
20
|
export { extendOrTrimPolylinesAtIntersections } from "./extendOrTrimPolylinesAtIntersections";
|
|
21
|
-
export { sortLinesByConnections } from "./sortLinesByConnections";
|
|
21
|
+
export { sortLinesByConnections } from "./sortLinesByConnections";
|
|
22
|
+
export { isPolygonClockwise } from "./isPolygonClockwise";
|
|
23
|
+
export { ensurePolygonClockwise } from "./ensurePolygonClockwise";
|
|
24
|
+
export { containsPoint } from "./containsPoint";
|
|
25
|
+
export { getPolygonArea } from "./getPolygonArea";
|
package/src/isPointInPolygon.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { Point2 } from "./Point2";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Check if a point is inside a polygon
|
|
5
|
+
* Warning: The function returns unreliable results for points on the polygon boundary.
|
|
6
|
+
* Obsolete, use containsPoint instead.
|
|
7
|
+
* @param p
|
|
8
|
+
* @param point
|
|
9
|
+
*/
|
|
3
10
|
export function isPointInPolygon(p: Point2[], point: Point2): boolean {
|
|
4
|
-
|
|
5
11
|
const x = point.x, y = point.y;
|
|
6
12
|
|
|
7
13
|
let i: number, j: number, c = false;
|
|
8
14
|
|
|
9
15
|
for (i = 0, j = p.length - 1; i < p.length; j = i++) {
|
|
10
|
-
|
|
11
16
|
if ((((p[i].y <= y) && (y < p[j].y)) ||
|
|
12
17
|
((p[j].y <= y) && (y < p[i].y))) &&
|
|
13
18
|
(x < (p[j].x - p[i].x) * (y - p[i].y) / (p[j].y - p[i].y) + p[i].x)) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Point2 } from "./Point2";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Determines if a polygon is clockwise or counter-clockwise.
|
|
5
|
+
* X increases to the right, Y increases downwards.
|
|
6
|
+
* Based on this answer https://stackoverflow.com/a/18472899/1837173 - the result is inverted, they assume the inverse y-axis
|
|
7
|
+
*/
|
|
8
|
+
export function isPolygonClockwise(vertices: Point2[]): boolean {
|
|
9
|
+
let sum = 0.0;
|
|
10
|
+
for (let i = 0; i < vertices.length; i++) {
|
|
11
|
+
const v1 = vertices[i];
|
|
12
|
+
const v2 = vertices[(i + 1) % vertices.length];
|
|
13
|
+
sum += (v2.x - v1.x) * (v2.y + v1.y);
|
|
14
|
+
}
|
|
15
|
+
return sum < 0.0;
|
|
16
|
+
}
|
package/types/Polygon.d.ts
CHANGED
|
@@ -12,11 +12,15 @@ export declare class Polygon {
|
|
|
12
12
|
centerOnOrigin(): this;
|
|
13
13
|
center(): Vec2;
|
|
14
14
|
ensureLastPoint(): this;
|
|
15
|
+
ensureOpen(): this;
|
|
16
|
+
get area(): number;
|
|
15
17
|
boundingBox(): BoundingBox;
|
|
16
18
|
toBoundingPolygon(): Polygon;
|
|
17
19
|
flip(): this;
|
|
18
20
|
perimeter(): number;
|
|
19
|
-
|
|
21
|
+
get isClockwise(): boolean;
|
|
22
|
+
ensureClockwise(): this;
|
|
23
|
+
containsPoint(...points: Vec2[]): boolean;
|
|
20
24
|
private flipSingle;
|
|
21
25
|
translate(translate: Vec2): this;
|
|
22
26
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Point2 } from "./Point2";
|
|
2
|
+
/**
|
|
3
|
+
* Return if polygon contains @point or when the point is on the polygon boundary.
|
|
4
|
+
* Works consistently with points on the boundary.
|
|
5
|
+
* Works with simple polygons only.
|
|
6
|
+
* The code is copied from PolyK library.
|
|
7
|
+
* @returns {boolean} depth
|
|
8
|
+
* @param polygon
|
|
9
|
+
* @param point
|
|
10
|
+
*/
|
|
11
|
+
export declare function containsPoint(polygon: Point2[], point: Point2): boolean;
|
|
12
|
+
export declare function containsPoints(polygon: Point2[], points: Point2[]): boolean;
|
package/types/index.d.ts
CHANGED
|
@@ -19,3 +19,7 @@ export { polygonPerimeter } from "./polygonPerimeter";
|
|
|
19
19
|
export { offsetPolyline } from "./offsetPolyline";
|
|
20
20
|
export { extendOrTrimPolylinesAtIntersections } from "./extendOrTrimPolylinesAtIntersections";
|
|
21
21
|
export { sortLinesByConnections } from "./sortLinesByConnections";
|
|
22
|
+
export { isPolygonClockwise } from "./isPolygonClockwise";
|
|
23
|
+
export { ensurePolygonClockwise } from "./ensurePolygonClockwise";
|
|
24
|
+
export { containsPoint } from "./containsPoint";
|
|
25
|
+
export { getPolygonArea } from "./getPolygonArea";
|
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
import { Point2 } from "./Point2";
|
|
2
|
+
/**
|
|
3
|
+
* Check if a point is inside a polygon
|
|
4
|
+
* Warning: The function returns unreliable results for points on the polygon boundary.
|
|
5
|
+
* Obsolete, use containsPoint instead.
|
|
6
|
+
* @param p
|
|
7
|
+
* @param point
|
|
8
|
+
*/
|
|
2
9
|
export declare function isPointInPolygon(p: Point2[], point: Point2): boolean;
|