math 0.0.0 → 0.1.0
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/API.md +1605 -0
- package/LICENSE +21 -0
- package/README.md +197 -0
- package/dist/color/color.js +136 -0
- package/dist/color/color.js.map +1 -0
- package/dist/color/colorspace.js +43 -0
- package/dist/color/colorspace.js.map +1 -0
- package/dist/color/hsl.js +123 -0
- package/dist/color/hsl.js.map +1 -0
- package/dist/color/index.js +7 -0
- package/dist/color/index.js.map +1 -0
- package/dist/color/parse.js +276 -0
- package/dist/color/parse.js.map +1 -0
- package/dist/core/angle.js +43 -0
- package/dist/core/angle.js.map +1 -0
- package/dist/core/euler.js +212 -0
- package/dist/core/euler.js.map +1 -0
- package/dist/core/mat2.js +385 -0
- package/dist/core/mat2.js.map +1 -0
- package/dist/core/mat2d.js +422 -0
- package/dist/core/mat2d.js.map +1 -0
- package/dist/core/mat3.js +749 -0
- package/dist/core/mat3.js.map +1 -0
- package/dist/core/mat4.js +2197 -0
- package/dist/core/mat4.js.map +1 -0
- package/dist/core/polar.js +200 -0
- package/dist/core/polar.js.map +1 -0
- package/dist/core/quat.js +730 -0
- package/dist/core/quat.js.map +1 -0
- package/dist/core/quat2.js +883 -0
- package/dist/core/quat2.js.map +1 -0
- package/dist/core/scalar.js +112 -0
- package/dist/core/scalar.js.map +1 -0
- package/dist/core/spherical.js +248 -0
- package/dist/core/spherical.js.map +1 -0
- package/dist/core/vec2.js +597 -0
- package/dist/core/vec2.js.map +1 -0
- package/dist/core/vec3.js +916 -0
- package/dist/core/vec3.js.map +1 -0
- package/dist/core/vec4.js +573 -0
- package/dist/core/vec4.js.map +1 -0
- package/dist/geometry/circumcircle.js +39 -0
- package/dist/geometry/circumcircle.js.map +1 -0
- package/dist/geometry/index.js +6 -0
- package/dist/geometry/index.js.map +1 -0
- package/dist/geometry/polygon2-decompose.js +411 -0
- package/dist/geometry/polygon2-decompose.js.map +1 -0
- package/dist/geometry/polygon2-triangulate.js +222 -0
- package/dist/geometry/polygon2-triangulate.js.map +1 -0
- package/dist/geometry/quickhull2.js +123 -0
- package/dist/geometry/quickhull2.js.map +1 -0
- package/dist/geometry/quickhull3.js +605 -0
- package/dist/geometry/quickhull3.js.map +1 -0
- package/dist/ik/fabrik2.js +675 -0
- package/dist/ik/fabrik2.js.map +1 -0
- package/dist/ik/fabrik3.js +912 -0
- package/dist/ik/fabrik3.js.map +1 -0
- package/dist/ik/index.js +5 -0
- package/dist/ik/index.js.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/noise/fractal.js +185 -0
- package/dist/noise/fractal.js.map +1 -0
- package/dist/noise/index.js +16 -0
- package/dist/noise/index.js.map +1 -0
- package/dist/noise/perlin2d.js +50 -0
- package/dist/noise/perlin2d.js.map +1 -0
- package/dist/noise/perlin3d.js +72 -0
- package/dist/noise/perlin3d.js.map +1 -0
- package/dist/noise/permutation.js +120 -0
- package/dist/noise/permutation.js.map +1 -0
- package/dist/noise/simplex2d.js +84 -0
- package/dist/noise/simplex2d.js.map +1 -0
- package/dist/noise/simplex3d.js +147 -0
- package/dist/noise/simplex3d.js.map +1 -0
- package/dist/noise/simplex4d.js +149 -0
- package/dist/noise/simplex4d.js.map +1 -0
- package/dist/noise/worley2d.js +50 -0
- package/dist/noise/worley2d.js.map +1 -0
- package/dist/noise/worley3d.js +61 -0
- package/dist/noise/worley3d.js.map +1 -0
- package/dist/random/index.js +9 -0
- package/dist/random/index.js.map +1 -0
- package/dist/random/isaac32.js +164 -0
- package/dist/random/isaac32.js.map +1 -0
- package/dist/random/isaac64.js +215 -0
- package/dist/random/isaac64.js.map +1 -0
- package/dist/random/mulberry32.js +48 -0
- package/dist/random/mulberry32.js.map +1 -0
- package/dist/random/random.js +120 -0
- package/dist/random/random.js.map +1 -0
- package/dist/shapes/box2.js +300 -0
- package/dist/shapes/box2.js.map +1 -0
- package/dist/shapes/box3.js +550 -0
- package/dist/shapes/box3.js.map +1 -0
- package/dist/shapes/circle.js +6 -0
- package/dist/shapes/circle.js.map +1 -0
- package/dist/shapes/frustum.js +662 -0
- package/dist/shapes/frustum.js.map +1 -0
- package/dist/shapes/index.js +25 -0
- package/dist/shapes/index.js.map +1 -0
- package/dist/shapes/obb3.js +557 -0
- package/dist/shapes/obb3.js.map +1 -0
- package/dist/shapes/plane3.js +267 -0
- package/dist/shapes/plane3.js.map +1 -0
- package/dist/shapes/polygon2.js +451 -0
- package/dist/shapes/polygon2.js.map +1 -0
- package/dist/shapes/raycast3.js +164 -0
- package/dist/shapes/raycast3.js.map +1 -0
- package/dist/shapes/segment2.js +82 -0
- package/dist/shapes/segment2.js.map +1 -0
- package/dist/shapes/sphere.js +23 -0
- package/dist/shapes/sphere.js.map +1 -0
- package/dist/shapes/triangle2.js +79 -0
- package/dist/shapes/triangle2.js.map +1 -0
- package/dist/shapes/triangle3.js +60 -0
- package/dist/shapes/triangle3.js.map +1 -0
- package/dist/src/color/color.d.ts +50 -0
- package/dist/src/color/colorspace.d.ts +15 -0
- package/dist/src/color/hsl.d.ts +28 -0
- package/dist/src/color/index.d.ts +5 -0
- package/dist/src/color/parse.d.ts +15 -0
- package/dist/src/core/angle.d.ts +29 -0
- package/dist/src/core/arrays.d.ts +4 -0
- package/dist/src/core/euler.d.ts +78 -0
- package/dist/src/core/index.d.ts +27 -0
- package/dist/src/core/mat2.d.ts +217 -0
- package/dist/src/core/mat2d.d.ts +218 -0
- package/dist/src/core/mat3.d.ts +295 -0
- package/dist/src/core/mat4.d.ts +667 -0
- package/dist/src/core/polar.d.ts +151 -0
- package/dist/src/core/quat.d.ts +370 -0
- package/dist/src/core/quat2.d.ts +344 -0
- package/dist/src/core/scalar.d.ts +75 -0
- package/dist/src/core/spherical.d.ts +165 -0
- package/dist/src/core/vec2.d.ts +403 -0
- package/dist/src/core/vec3.d.ts +493 -0
- package/dist/src/core/vec4.d.ts +336 -0
- package/dist/src/geometry/circumcircle.d.ts +9 -0
- package/dist/src/geometry/index.d.ts +5 -0
- package/dist/src/geometry/polygon2-decompose.d.ts +25 -0
- package/dist/src/geometry/polygon2-triangulate.d.ts +15 -0
- package/dist/src/geometry/quickhull2.d.ts +10 -0
- package/dist/src/geometry/quickhull3.d.ts +27 -0
- package/dist/src/ik/fabrik2.d.ts +314 -0
- package/dist/src/ik/fabrik3.d.ts +371 -0
- package/dist/src/ik/index.d.ts +2 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/noise/fractal.d.ts +95 -0
- package/dist/src/noise/index.d.ts +9 -0
- package/dist/src/noise/perlin2d.d.ts +19 -0
- package/dist/src/noise/perlin3d.d.ts +20 -0
- package/dist/src/noise/permutation.d.ts +22 -0
- package/dist/src/noise/simplex2d.d.ts +19 -0
- package/dist/src/noise/simplex3d.d.ts +20 -0
- package/dist/src/noise/simplex4d.d.ts +24 -0
- package/dist/src/noise/worley2d.d.ts +22 -0
- package/dist/src/noise/worley3d.d.ts +23 -0
- package/dist/src/random/index.d.ts +8 -0
- package/dist/src/random/isaac32.d.ts +62 -0
- package/dist/src/random/isaac64.d.ts +75 -0
- package/dist/src/random/mulberry32.d.ts +43 -0
- package/dist/src/random/random.d.ts +67 -0
- package/dist/src/shapes/box2.d.ts +174 -0
- package/dist/src/shapes/box3.d.ts +213 -0
- package/dist/src/shapes/circle.d.ts +7 -0
- package/dist/src/shapes/frustum.d.ts +147 -0
- package/dist/src/shapes/index.d.ts +20 -0
- package/dist/src/shapes/obb3.d.ts +105 -0
- package/dist/src/shapes/plane3.d.ts +130 -0
- package/dist/src/shapes/polygon2.d.ts +155 -0
- package/dist/src/shapes/raycast3.d.ts +42 -0
- package/dist/src/shapes/segment2.d.ts +34 -0
- package/dist/src/shapes/sphere.d.ts +19 -0
- package/dist/src/shapes/triangle2.d.ts +53 -0
- package/dist/src/shapes/triangle3.d.ts +29 -0
- package/dist/src/time/easing.d.ts +21 -0
- package/dist/src/time/index.d.ts +6 -0
- package/dist/src/time/spring-core.d.ts +15 -0
- package/dist/src/time/spring.d.ts +36 -0
- package/dist/src/time/spring2.d.ts +11 -0
- package/dist/src/time/spring3.d.ts +11 -0
- package/dist/src/time/spring4.d.ts +11 -0
- package/dist/time/easing.js +24 -0
- package/dist/time/easing.js.map +1 -0
- package/dist/time/index.js +11 -0
- package/dist/time/index.js.map +1 -0
- package/dist/time/spring-core.js +48 -0
- package/dist/time/spring-core.js.map +1 -0
- package/dist/time/spring.js +62 -0
- package/dist/time/spring.js.map +1 -0
- package/dist/time/spring2.js +31 -0
- package/dist/time/spring2.js.map +1 -0
- package/dist/time/spring3.js +32 -0
- package/dist/time/spring3.js.map +1 -0
- package/dist/time/spring4.js +32 -0
- package/dist/time/spring4.js.map +1 -0
- package/package.json +90 -12
- package/skills/math/SKILL.md +140 -0
- package/math.coffee +0 -36
- package/math.js +0 -67
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { signedArea } from '../shapes/polygon2.js';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Polygon triangulation (ear clipping)
|
|
5
|
+
*
|
|
6
|
+
* Triangulates a simple (non-self-intersecting) polygon into a fan of
|
|
7
|
+
* triangles, emitting triangle indices into the input vertex array. No new
|
|
8
|
+
* vertices are introduced.
|
|
9
|
+
*
|
|
10
|
+
* based on the algorithm from Joseph O'Rourke's "Computational Geometry in C").
|
|
11
|
+
* Uses the standard high-bit ear-mark trick on the working index list to stay allocation-light.
|
|
12
|
+
*/
|
|
13
|
+
// The working index list packs an "is-ear" flag into the high bit of each entry;
|
|
14
|
+
// the low bits hold the actual vertex index.
|
|
15
|
+
const IDX_MASK = 0x0fffffff;
|
|
16
|
+
const EAR_FLAG = 0x80000000;
|
|
17
|
+
const nextIdx = (i, n) => (i + 1 < n ? i + 1 : 0);
|
|
18
|
+
const prevIdx = (i, n) => (i - 1 >= 0 ? i - 1 : n - 1);
|
|
19
|
+
/** Twice the signed area of triangle (a, b, c); positive when counter-clockwise. */
|
|
20
|
+
function area2(ax, ay, bx, by, cx, cy) {
|
|
21
|
+
return (bx - ax) * (cy - ay) - (cx - ax) * (by - ay);
|
|
22
|
+
}
|
|
23
|
+
function collinear(ax, ay, bx, by, cx, cy) {
|
|
24
|
+
return area2(ax, ay, bx, by, cx, cy) === 0;
|
|
25
|
+
}
|
|
26
|
+
/** True if segments (a,b) and (c,d) properly cross (no collinear/endpoint contact). */
|
|
27
|
+
function intersectProp(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
28
|
+
if (collinear(ax, ay, bx, by, cx, cy) ||
|
|
29
|
+
collinear(ax, ay, bx, by, dx, dy) ||
|
|
30
|
+
collinear(cx, cy, dx, dy, ax, ay) ||
|
|
31
|
+
collinear(cx, cy, dx, dy, bx, by)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const l1 = area2(ax, ay, bx, by, cx, cy) < 0;
|
|
35
|
+
const l2 = area2(ax, ay, bx, by, dx, dy) < 0;
|
|
36
|
+
const l3 = area2(cx, cy, dx, dy, ax, ay) < 0;
|
|
37
|
+
const l4 = area2(cx, cy, dx, dy, bx, by) < 0;
|
|
38
|
+
return l1 !== l2 && l3 !== l4;
|
|
39
|
+
}
|
|
40
|
+
/** True if point c lies on the closed segment (a,b), assuming the three are collinear. */
|
|
41
|
+
function between(ax, ay, bx, by, cx, cy) {
|
|
42
|
+
if (!collinear(ax, ay, bx, by, cx, cy))
|
|
43
|
+
return false;
|
|
44
|
+
if (ax !== bx)
|
|
45
|
+
return (ax <= cx && cx <= bx) || (ax >= cx && cx >= bx);
|
|
46
|
+
return (ay <= cy && cy <= by) || (ay >= cy && cy >= by);
|
|
47
|
+
}
|
|
48
|
+
/** True if segments (a,b) and (c,d) intersect, including collinear/endpoint contact. */
|
|
49
|
+
function intersectSeg(ax, ay, bx, by, cx, cy, dx, dy) {
|
|
50
|
+
if (intersectProp(ax, ay, bx, by, cx, cy, dx, dy))
|
|
51
|
+
return true;
|
|
52
|
+
return (between(ax, ay, bx, by, cx, cy) ||
|
|
53
|
+
between(ax, ay, bx, by, dx, dy) ||
|
|
54
|
+
between(cx, cy, dx, dy, ax, ay) ||
|
|
55
|
+
between(cx, cy, dx, dy, bx, by));
|
|
56
|
+
}
|
|
57
|
+
/** X of the original vertex referenced by working slot `s`. */
|
|
58
|
+
const vX = (vertices, indices, s) => vertices[(indices[s] & IDX_MASK) * 2];
|
|
59
|
+
/** Y of the original vertex referenced by working slot `s`. */
|
|
60
|
+
const vY = (vertices, indices, s) => vertices[(indices[s] & IDX_MASK) * 2 + 1];
|
|
61
|
+
/**
|
|
62
|
+
* True if the segment between working slots `i` and `j` does not intersect any
|
|
63
|
+
* polygon edge. `loose` uses proper-intersection only (tolerating collinear
|
|
64
|
+
* contact), used as a fallback when no strict ear can be found.
|
|
65
|
+
*/
|
|
66
|
+
function diagonalie(i, j, n, vertices, indices, loose) {
|
|
67
|
+
const d0x = vX(vertices, indices, i);
|
|
68
|
+
const d0y = vY(vertices, indices, i);
|
|
69
|
+
const d1x = vX(vertices, indices, j);
|
|
70
|
+
const d1y = vY(vertices, indices, j);
|
|
71
|
+
for (let k = 0; k < n; k++) {
|
|
72
|
+
const k1 = nextIdx(k, n);
|
|
73
|
+
if (k === i || k1 === i || k === j || k1 === j)
|
|
74
|
+
continue;
|
|
75
|
+
const e0x = vX(vertices, indices, k);
|
|
76
|
+
const e0y = vY(vertices, indices, k);
|
|
77
|
+
const e1x = vX(vertices, indices, k1);
|
|
78
|
+
const e1y = vY(vertices, indices, k1);
|
|
79
|
+
// Ignore edges that share an endpoint with the diagonal.
|
|
80
|
+
if ((d0x === e0x && d0y === e0y) ||
|
|
81
|
+
(d1x === e0x && d1y === e0y) ||
|
|
82
|
+
(d0x === e1x && d0y === e1y) ||
|
|
83
|
+
(d1x === e1x && d1y === e1y)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const hit = loose
|
|
87
|
+
? intersectProp(d0x, d0y, d1x, d1y, e0x, e0y, e1x, e1y)
|
|
88
|
+
: intersectSeg(d0x, d0y, d1x, d1y, e0x, e0y, e1x, e1y);
|
|
89
|
+
if (hit)
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
/** True if the diagonal from slot `i` to slot `j` stays inside the cone at vertex `i`. */
|
|
95
|
+
function inCone(i, j, n, vertices, indices, loose) {
|
|
96
|
+
const ax = vX(vertices, indices, i);
|
|
97
|
+
const ay = vY(vertices, indices, i);
|
|
98
|
+
const bx = vX(vertices, indices, j);
|
|
99
|
+
const by = vY(vertices, indices, j);
|
|
100
|
+
const ni = nextIdx(i, n);
|
|
101
|
+
const pi = prevIdx(i, n);
|
|
102
|
+
const nx = vX(vertices, indices, ni);
|
|
103
|
+
const ny = vY(vertices, indices, ni);
|
|
104
|
+
const px = vX(vertices, indices, pi);
|
|
105
|
+
const py = vY(vertices, indices, pi);
|
|
106
|
+
// Convex cone vertex: prev is left-on of (cone -> next).
|
|
107
|
+
if (area2(px, py, ax, ay, nx, ny) <= 0) {
|
|
108
|
+
const c1 = area2(ax, ay, bx, by, px, py); // (cone, test, prev)
|
|
109
|
+
const c2 = area2(bx, by, ax, ay, nx, ny); // (test, cone, next)
|
|
110
|
+
return loose ? c1 <= 0 && c2 <= 0 : c1 < 0 && c2 < 0;
|
|
111
|
+
}
|
|
112
|
+
// Reflex cone vertex.
|
|
113
|
+
return !(area2(ax, ay, bx, by, nx, ny) <= 0 && area2(bx, by, ax, ay, px, py) <= 0);
|
|
114
|
+
}
|
|
115
|
+
function diagonal(i, j, n, vertices, indices, loose) {
|
|
116
|
+
return inCone(i, j, n, vertices, indices, loose) && diagonalie(i, j, n, vertices, indices, loose);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Triangulates a simple polygon by ear clipping, writing triangle indices into
|
|
120
|
+
* `out` as `[i0, i1, i2, i3, i4, i5, ...]`, where each index refers to a vertex
|
|
121
|
+
* of the input polygon. Either winding is accepted (normalised internally).
|
|
122
|
+
*
|
|
123
|
+
* `out` must have room for at least `3 * (n - 2)` indices. A complete
|
|
124
|
+
* triangulation returns exactly `n - 2` triangles; a smaller count indicates
|
|
125
|
+
* the polygon could not be fully triangulated (e.g. degenerate input).
|
|
126
|
+
*
|
|
127
|
+
* @param out output array of triangle indices
|
|
128
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
129
|
+
* @param n number of vertices to read from `vertices`
|
|
130
|
+
* @returns the number of triangles written
|
|
131
|
+
*/
|
|
132
|
+
function triangulatePolygon2(out, vertices, n) {
|
|
133
|
+
if (n < 3)
|
|
134
|
+
return 0;
|
|
135
|
+
// Order the working list so the resolved polygon winds clockwise, which is
|
|
136
|
+
// what this ear-clipping formulation expects. The stored values are original
|
|
137
|
+
// vertex indices, so emitted triangles refer back to the caller's vertices
|
|
138
|
+
// regardless of input winding.
|
|
139
|
+
const ccw = signedArea(vertices, n) >= 0;
|
|
140
|
+
const indices = new Array(n);
|
|
141
|
+
for (let i = 0; i < n; i++)
|
|
142
|
+
indices[i] = ccw ? n - 1 - i : i;
|
|
143
|
+
let ntris = 0;
|
|
144
|
+
let dst = 0;
|
|
145
|
+
// Mark removable ears.
|
|
146
|
+
for (let i = 0; i < n; i++) {
|
|
147
|
+
const i1 = nextIdx(i, n);
|
|
148
|
+
if (diagonal(i, nextIdx(i1, n), n, vertices, indices, false))
|
|
149
|
+
indices[i1] |= EAR_FLAG;
|
|
150
|
+
}
|
|
151
|
+
let nv = n;
|
|
152
|
+
while (nv > 3) {
|
|
153
|
+
let minLen = -1;
|
|
154
|
+
let mini = -1;
|
|
155
|
+
// Clip the shortest available ear for well-shaped triangles.
|
|
156
|
+
for (let i = 0; i < nv; i++) {
|
|
157
|
+
const i1 = nextIdx(i, nv);
|
|
158
|
+
if (indices[i1] & EAR_FLAG) {
|
|
159
|
+
const p0 = indices[i] & IDX_MASK;
|
|
160
|
+
const p2 = indices[nextIdx(i1, nv)] & IDX_MASK;
|
|
161
|
+
const dx = vertices[p2 * 2] - vertices[p0 * 2];
|
|
162
|
+
const dy = vertices[p2 * 2 + 1] - vertices[p0 * 2 + 1];
|
|
163
|
+
const len = dx * dx + dy * dy;
|
|
164
|
+
if (minLen < 0 || len < minLen) {
|
|
165
|
+
minLen = len;
|
|
166
|
+
mini = i;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (mini === -1) {
|
|
171
|
+
// No strict ear: retry with the loose diagonal test.
|
|
172
|
+
for (let i = 0; i < nv; i++) {
|
|
173
|
+
const i1 = nextIdx(i, nv);
|
|
174
|
+
const i2 = nextIdx(i1, nv);
|
|
175
|
+
if (diagonal(i, i2, nv, vertices, indices, true)) {
|
|
176
|
+
const p0 = indices[i] & IDX_MASK;
|
|
177
|
+
const p2 = indices[nextIdx(i2, nv)] & IDX_MASK;
|
|
178
|
+
const dx = vertices[p2 * 2] - vertices[p0 * 2];
|
|
179
|
+
const dy = vertices[p2 * 2 + 1] - vertices[p0 * 2 + 1];
|
|
180
|
+
const len = dx * dx + dy * dy;
|
|
181
|
+
if (minLen < 0 || len < minLen) {
|
|
182
|
+
minLen = len;
|
|
183
|
+
mini = i;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (mini === -1)
|
|
188
|
+
return ntris; // give up on degenerate input
|
|
189
|
+
}
|
|
190
|
+
const i = mini;
|
|
191
|
+
let i1 = nextIdx(i, nv);
|
|
192
|
+
const i2 = nextIdx(i1, nv);
|
|
193
|
+
out[dst++] = indices[i] & IDX_MASK;
|
|
194
|
+
out[dst++] = indices[i1] & IDX_MASK;
|
|
195
|
+
out[dst++] = indices[i2] & IDX_MASK;
|
|
196
|
+
ntris++;
|
|
197
|
+
// Remove the clipped vertex (slot i1) and re-evaluate its neighbours.
|
|
198
|
+
nv--;
|
|
199
|
+
for (let k = i1; k < nv; k++)
|
|
200
|
+
indices[k] = indices[k + 1];
|
|
201
|
+
if (i1 >= nv)
|
|
202
|
+
i1 = 0;
|
|
203
|
+
const iPrev = prevIdx(i1, nv);
|
|
204
|
+
if (diagonal(prevIdx(iPrev, nv), i1, nv, vertices, indices, false))
|
|
205
|
+
indices[iPrev] |= EAR_FLAG;
|
|
206
|
+
else
|
|
207
|
+
indices[iPrev] &= IDX_MASK;
|
|
208
|
+
if (diagonal(iPrev, nextIdx(i1, nv), nv, vertices, indices, false))
|
|
209
|
+
indices[i1] |= EAR_FLAG;
|
|
210
|
+
else
|
|
211
|
+
indices[i1] &= IDX_MASK;
|
|
212
|
+
}
|
|
213
|
+
// The final remaining triangle.
|
|
214
|
+
out[dst++] = indices[0] & IDX_MASK;
|
|
215
|
+
out[dst++] = indices[1] & IDX_MASK;
|
|
216
|
+
out[dst++] = indices[2] & IDX_MASK;
|
|
217
|
+
ntris++;
|
|
218
|
+
return ntris;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export { triangulatePolygon2 };
|
|
222
|
+
//# sourceMappingURL=polygon2-triangulate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polygon2-triangulate.js","sources":["../../src/geometry/polygon2-triangulate.ts"],"sourcesContent":["import { signedArea } from '../shapes/polygon2';\n\n/*\n * Polygon triangulation (ear clipping)\n *\n * Triangulates a simple (non-self-intersecting) polygon into a fan of\n * triangles, emitting triangle indices into the input vertex array. No new\n * vertices are introduced.\n *\n * based on the algorithm from Joseph O'Rourke's \"Computational Geometry in C\").\n * Uses the standard high-bit ear-mark trick on the working index list to stay allocation-light.\n */\n\n// The working index list packs an \"is-ear\" flag into the high bit of each entry;\n// the low bits hold the actual vertex index.\nconst IDX_MASK = 0x0fffffff;\nconst EAR_FLAG = 0x80000000;\n\nconst nextIdx = (i: number, n: number): number => (i + 1 < n ? i + 1 : 0);\nconst prevIdx = (i: number, n: number): number => (i - 1 >= 0 ? i - 1 : n - 1);\n\n/** Twice the signed area of triangle (a, b, c); positive when counter-clockwise. */\nfunction area2(ax: number, ay: number, bx: number, by: number, cx: number, cy: number): number {\n return (bx - ax) * (cy - ay) - (cx - ax) * (by - ay);\n}\n\nfunction collinear(ax: number, ay: number, bx: number, by: number, cx: number, cy: number): boolean {\n return area2(ax, ay, bx, by, cx, cy) === 0;\n}\n\n/** True if segments (a,b) and (c,d) properly cross (no collinear/endpoint contact). */\nfunction intersectProp(ax: number, ay: number, bx: number, by: number, cx: number, cy: number, dx: number, dy: number): boolean {\n if (\n collinear(ax, ay, bx, by, cx, cy) ||\n collinear(ax, ay, bx, by, dx, dy) ||\n collinear(cx, cy, dx, dy, ax, ay) ||\n collinear(cx, cy, dx, dy, bx, by)\n ) {\n return false;\n }\n const l1 = area2(ax, ay, bx, by, cx, cy) < 0;\n const l2 = area2(ax, ay, bx, by, dx, dy) < 0;\n const l3 = area2(cx, cy, dx, dy, ax, ay) < 0;\n const l4 = area2(cx, cy, dx, dy, bx, by) < 0;\n return l1 !== l2 && l3 !== l4;\n}\n\n/** True if point c lies on the closed segment (a,b), assuming the three are collinear. */\nfunction between(ax: number, ay: number, bx: number, by: number, cx: number, cy: number): boolean {\n if (!collinear(ax, ay, bx, by, cx, cy)) return false;\n if (ax !== bx) return (ax <= cx && cx <= bx) || (ax >= cx && cx >= bx);\n return (ay <= cy && cy <= by) || (ay >= cy && cy >= by);\n}\n\n/** True if segments (a,b) and (c,d) intersect, including collinear/endpoint contact. */\nfunction intersectSeg(ax: number, ay: number, bx: number, by: number, cx: number, cy: number, dx: number, dy: number): boolean {\n if (intersectProp(ax, ay, bx, by, cx, cy, dx, dy)) return true;\n return (\n between(ax, ay, bx, by, cx, cy) ||\n between(ax, ay, bx, by, dx, dy) ||\n between(cx, cy, dx, dy, ax, ay) ||\n between(cx, cy, dx, dy, bx, by)\n );\n}\n\n/** X of the original vertex referenced by working slot `s`. */\nconst vX = (vertices: number[], indices: number[], s: number): number => vertices[(indices[s] & IDX_MASK) * 2];\n/** Y of the original vertex referenced by working slot `s`. */\nconst vY = (vertices: number[], indices: number[], s: number): number => vertices[(indices[s] & IDX_MASK) * 2 + 1];\n\n/**\n * True if the segment between working slots `i` and `j` does not intersect any\n * polygon edge. `loose` uses proper-intersection only (tolerating collinear\n * contact), used as a fallback when no strict ear can be found.\n */\nfunction diagonalie(i: number, j: number, n: number, vertices: number[], indices: number[], loose: boolean): boolean {\n const d0x = vX(vertices, indices, i);\n const d0y = vY(vertices, indices, i);\n const d1x = vX(vertices, indices, j);\n const d1y = vY(vertices, indices, j);\n\n for (let k = 0; k < n; k++) {\n const k1 = nextIdx(k, n);\n if (k === i || k1 === i || k === j || k1 === j) continue;\n\n const e0x = vX(vertices, indices, k);\n const e0y = vY(vertices, indices, k);\n const e1x = vX(vertices, indices, k1);\n const e1y = vY(vertices, indices, k1);\n\n // Ignore edges that share an endpoint with the diagonal.\n if (\n (d0x === e0x && d0y === e0y) ||\n (d1x === e0x && d1y === e0y) ||\n (d0x === e1x && d0y === e1y) ||\n (d1x === e1x && d1y === e1y)\n ) {\n continue;\n }\n\n const hit = loose\n ? intersectProp(d0x, d0y, d1x, d1y, e0x, e0y, e1x, e1y)\n : intersectSeg(d0x, d0y, d1x, d1y, e0x, e0y, e1x, e1y);\n if (hit) return false;\n }\n return true;\n}\n\n/** True if the diagonal from slot `i` to slot `j` stays inside the cone at vertex `i`. */\nfunction inCone(i: number, j: number, n: number, vertices: number[], indices: number[], loose: boolean): boolean {\n const ax = vX(vertices, indices, i);\n const ay = vY(vertices, indices, i);\n const bx = vX(vertices, indices, j);\n const by = vY(vertices, indices, j);\n\n const ni = nextIdx(i, n);\n const pi = prevIdx(i, n);\n const nx = vX(vertices, indices, ni);\n const ny = vY(vertices, indices, ni);\n const px = vX(vertices, indices, pi);\n const py = vY(vertices, indices, pi);\n\n // Convex cone vertex: prev is left-on of (cone -> next).\n if (area2(px, py, ax, ay, nx, ny) <= 0) {\n const c1 = area2(ax, ay, bx, by, px, py); // (cone, test, prev)\n const c2 = area2(bx, by, ax, ay, nx, ny); // (test, cone, next)\n return loose ? c1 <= 0 && c2 <= 0 : c1 < 0 && c2 < 0;\n }\n // Reflex cone vertex.\n return !(area2(ax, ay, bx, by, nx, ny) <= 0 && area2(bx, by, ax, ay, px, py) <= 0);\n}\n\nfunction diagonal(i: number, j: number, n: number, vertices: number[], indices: number[], loose: boolean): boolean {\n return inCone(i, j, n, vertices, indices, loose) && diagonalie(i, j, n, vertices, indices, loose);\n}\n\n/**\n * Triangulates a simple polygon by ear clipping, writing triangle indices into\n * `out` as `[i0, i1, i2, i3, i4, i5, ...]`, where each index refers to a vertex\n * of the input polygon. Either winding is accepted (normalised internally).\n *\n * `out` must have room for at least `3 * (n - 2)` indices. A complete\n * triangulation returns exactly `n - 2` triangles; a smaller count indicates\n * the polygon could not be fully triangulated (e.g. degenerate input).\n *\n * @param out output array of triangle indices\n * @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`\n * @param n number of vertices to read from `vertices`\n * @returns the number of triangles written\n */\nexport function triangulatePolygon2(out: number[], vertices: number[], n: number): number {\n if (n < 3) return 0;\n\n // Order the working list so the resolved polygon winds clockwise, which is\n // what this ear-clipping formulation expects. The stored values are original\n // vertex indices, so emitted triangles refer back to the caller's vertices\n // regardless of input winding.\n const ccw = signedArea(vertices, n) >= 0;\n const indices = new Array<number>(n);\n for (let i = 0; i < n; i++) indices[i] = ccw ? n - 1 - i : i;\n\n let ntris = 0;\n let dst = 0;\n\n // Mark removable ears.\n for (let i = 0; i < n; i++) {\n const i1 = nextIdx(i, n);\n if (diagonal(i, nextIdx(i1, n), n, vertices, indices, false)) indices[i1] |= EAR_FLAG;\n }\n\n let nv = n;\n while (nv > 3) {\n let minLen = -1;\n let mini = -1;\n\n // Clip the shortest available ear for well-shaped triangles.\n for (let i = 0; i < nv; i++) {\n const i1 = nextIdx(i, nv);\n if (indices[i1] & EAR_FLAG) {\n const p0 = indices[i] & IDX_MASK;\n const p2 = indices[nextIdx(i1, nv)] & IDX_MASK;\n const dx = vertices[p2 * 2] - vertices[p0 * 2];\n const dy = vertices[p2 * 2 + 1] - vertices[p0 * 2 + 1];\n const len = dx * dx + dy * dy;\n if (minLen < 0 || len < minLen) {\n minLen = len;\n mini = i;\n }\n }\n }\n\n if (mini === -1) {\n // No strict ear: retry with the loose diagonal test.\n for (let i = 0; i < nv; i++) {\n const i1 = nextIdx(i, nv);\n const i2 = nextIdx(i1, nv);\n if (diagonal(i, i2, nv, vertices, indices, true)) {\n const p0 = indices[i] & IDX_MASK;\n const p2 = indices[nextIdx(i2, nv)] & IDX_MASK;\n const dx = vertices[p2 * 2] - vertices[p0 * 2];\n const dy = vertices[p2 * 2 + 1] - vertices[p0 * 2 + 1];\n const len = dx * dx + dy * dy;\n if (minLen < 0 || len < minLen) {\n minLen = len;\n mini = i;\n }\n }\n }\n if (mini === -1) return ntris; // give up on degenerate input\n }\n\n const i = mini;\n let i1 = nextIdx(i, nv);\n const i2 = nextIdx(i1, nv);\n\n out[dst++] = indices[i] & IDX_MASK;\n out[dst++] = indices[i1] & IDX_MASK;\n out[dst++] = indices[i2] & IDX_MASK;\n ntris++;\n\n // Remove the clipped vertex (slot i1) and re-evaluate its neighbours.\n nv--;\n for (let k = i1; k < nv; k++) indices[k] = indices[k + 1];\n if (i1 >= nv) i1 = 0;\n const iPrev = prevIdx(i1, nv);\n\n if (diagonal(prevIdx(iPrev, nv), i1, nv, vertices, indices, false)) indices[iPrev] |= EAR_FLAG;\n else indices[iPrev] &= IDX_MASK;\n\n if (diagonal(iPrev, nextIdx(i1, nv), nv, vertices, indices, false)) indices[i1] |= EAR_FLAG;\n else indices[i1] &= IDX_MASK;\n }\n\n // The final remaining triangle.\n out[dst++] = indices[0] & IDX_MASK;\n out[dst++] = indices[1] & IDX_MASK;\n out[dst++] = indices[2] & IDX_MASK;\n ntris++;\n\n return ntris;\n}\n"],"names":[],"mappings":";;AAEA;;;;;;;;;AASG;AAEH;AACA;AACA,MAAM,QAAQ,GAAG,UAAU;AAC3B,MAAM,QAAQ,GAAG,UAAU;AAE3B,MAAM,OAAO,GAAG,CAAC,CAAS,EAAE,CAAS,MAAc,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AACzE,MAAM,OAAO,GAAG,CAAC,CAAS,EAAE,CAAS,MAAc,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAE9E;AACA,SAAS,KAAK,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAA;IACjF,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxD;AAEA,SAAS,SAAS,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAA;AACrF,IAAA,OAAO,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC;AAC9C;AAEA;AACA,SAAS,aAAa,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAA;AACjH,IAAA,IACI,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AACjC,QAAA,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AACjC,QAAA,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AACjC,QAAA,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EACnC;AACE,QAAA,OAAO,KAAK;IAChB;AACA,IAAA,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC;AAC5C,IAAA,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC;AAC5C,IAAA,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC;AAC5C,IAAA,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC;AAC5C,IAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE;AACjC;AAEA;AACA,SAAS,OAAO,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAA;AACnF,IAAA,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAAE,QAAA,OAAO,KAAK;IACpD,IAAI,EAAE,KAAK,EAAE;AAAE,QAAA,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACtE,IAAA,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC3D;AAEA;AACA,SAAS,YAAY,CAAC,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAE,EAAU,EAAA;AAChH,IAAA,IAAI,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAAE,QAAA,OAAO,IAAI;AAC9D,IAAA,QACI,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAC/B,QAAA,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAC/B,QAAA,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAC/B,QAAA,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAEvC;AAEA;AACA,MAAM,EAAE,GAAG,CAAC,QAAkB,EAAE,OAAiB,EAAE,CAAS,KAAa,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,CAAC,CAAC;AAC9G;AACA,MAAM,EAAE,GAAG,CAAC,QAAkB,EAAE,OAAiB,EAAE,CAAS,KAAa,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC;AAElH;;;;AAIG;AACH,SAAS,UAAU,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,QAAkB,EAAE,OAAiB,EAAE,KAAc,EAAA;IACtG,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;AAEpC,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;QACxB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;AACxB,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC;YAAE;QAEhD,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACpC,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACpC,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC;;QAGrC,IACI,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG;AAC3B,aAAC,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,CAAC;AAC5B,aAAC,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,CAAC;aAC3B,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,CAAC,EAC9B;YACE;QACJ;QAEA,MAAM,GAAG,GAAG;AACR,cAAE,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;AACtD,cAAE,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC;AAC1D,QAAA,IAAI,GAAG;AAAE,YAAA,OAAO,KAAK;IACzB;AACA,IAAA,OAAO,IAAI;AACf;AAEA;AACA,SAAS,MAAM,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,QAAkB,EAAE,OAAiB,EAAE,KAAc,EAAA;IAClG,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACnC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAEnC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACxB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACxB,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC;IACpC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC;IACpC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC;IACpC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC;;AAGpC,IAAA,IAAI,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE;AACpC,QAAA,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AACzC,QAAA,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACzC,OAAO,KAAK,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC;IACxD;;AAEA,IAAA,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;AACtF;AAEA,SAAS,QAAQ,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,QAAkB,EAAE,OAAiB,EAAE,KAAc,EAAA;AACpG,IAAA,OAAO,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC;AACrG;AAEA;;;;;;;;;;;;;AAaG;SACa,mBAAmB,CAAC,GAAa,EAAE,QAAkB,EAAE,CAAS,EAAA;IAC5E,IAAI,CAAC,GAAG,CAAC;AAAE,QAAA,OAAO,CAAC;;;;;IAMnB,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC;AACxC,IAAA,MAAM,OAAO,GAAG,IAAI,KAAK,CAAS,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;AAAE,QAAA,OAAO,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC;IAE5D,IAAI,KAAK,GAAG,CAAC;IACb,IAAI,GAAG,GAAG,CAAC;;AAGX,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;QACxB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;AACxB,QAAA,IAAI,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC;AAAE,YAAA,OAAO,CAAC,EAAE,CAAC,IAAI,QAAQ;IACzF;IAEA,IAAI,EAAE,GAAG,CAAC;AACV,IAAA,OAAO,EAAE,GAAG,CAAC,EAAE;AACX,QAAA,IAAI,MAAM,GAAG,EAAE;AACf,QAAA,IAAI,IAAI,GAAG,EAAE;;AAGb,QAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;YACzB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC;AACzB,YAAA,IAAI,OAAO,CAAC,EAAE,CAAC,GAAG,QAAQ,EAAE;gBACxB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ;AAChC,gBAAA,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ;AAC9C,gBAAA,MAAM,EAAE,GAAG,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;AAC9C,gBAAA,MAAM,EAAE,GAAG,QAAQ,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;gBACtD,MAAM,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;gBAC7B,IAAI,MAAM,GAAG,CAAC,IAAI,GAAG,GAAG,MAAM,EAAE;oBAC5B,MAAM,GAAG,GAAG;oBACZ,IAAI,GAAG,CAAC;gBACZ;YACJ;QACJ;AAEA,QAAA,IAAI,IAAI,KAAK,EAAE,EAAE;;AAEb,YAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;gBACzB,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC;gBACzB,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC;AAC1B,gBAAA,IAAI,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE;oBAC9C,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ;AAChC,oBAAA,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,QAAQ;AAC9C,oBAAA,MAAM,EAAE,GAAG,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,CAAC,CAAC;AAC9C,oBAAA,MAAM,EAAE,GAAG,QAAQ,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;oBACtD,MAAM,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;oBAC7B,IAAI,MAAM,GAAG,CAAC,IAAI,GAAG,GAAG,MAAM,EAAE;wBAC5B,MAAM,GAAG,GAAG;wBACZ,IAAI,GAAG,CAAC;oBACZ;gBACJ;YACJ;YACA,IAAI,IAAI,KAAK,EAAE;gBAAE,OAAO,KAAK,CAAC;QAClC;QAEA,MAAM,CAAC,GAAG,IAAI;QACd,IAAI,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC;QAE1B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ;QAClC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC,GAAG,QAAQ;QACnC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC,GAAG,QAAQ;AACnC,QAAA,KAAK,EAAE;;AAGP,QAAA,EAAE,EAAE;QACJ,KAAK,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;YAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;QACzD,IAAI,EAAE,IAAI,EAAE;YAAE,EAAE,GAAG,CAAC;QACpB,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC;AAE7B,QAAA,IAAI,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC;AAAE,YAAA,OAAO,CAAC,KAAK,CAAC,IAAI,QAAQ;;AACzF,YAAA,OAAO,CAAC,KAAK,CAAC,IAAI,QAAQ;AAE/B,QAAA,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC;AAAE,YAAA,OAAO,CAAC,EAAE,CAAC,IAAI,QAAQ;;AACtF,YAAA,OAAO,CAAC,EAAE,CAAC,IAAI,QAAQ;IAChC;;IAGA,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ;IAClC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ;IAClC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,QAAQ;AAClC,IAAA,KAAK,EAAE;AAEP,IAAA,OAAO,KAAK;AAChB;;;;"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const EPSILON = 1e-10;
|
|
2
|
+
/**
|
|
3
|
+
* Computes the convex hull of a set of 2D points using the QuickHull algorithm.
|
|
4
|
+
* The hull is returned as an array of indices in counter-clockwise order.
|
|
5
|
+
*
|
|
6
|
+
* Implementation of pseudocode from: https://en.wikipedia.org/wiki/Quickhull
|
|
7
|
+
*
|
|
8
|
+
* @param points flat array of 2D points: [x0, y0, x1, y1, ...]
|
|
9
|
+
* @returns indices of hull vertices in ccw order
|
|
10
|
+
*/
|
|
11
|
+
function quickhull2(points) {
|
|
12
|
+
const n = Math.floor(points.length / 2);
|
|
13
|
+
if (n < 3)
|
|
14
|
+
return Array.from({ length: n }, (_, i) => i);
|
|
15
|
+
// find left and right most points
|
|
16
|
+
let leftIdx = 0;
|
|
17
|
+
let rightIdx = 0;
|
|
18
|
+
let minX = points[0];
|
|
19
|
+
let maxX = points[0];
|
|
20
|
+
for (let i = 1; i < n; i++) {
|
|
21
|
+
const x = points[i * 2];
|
|
22
|
+
if (x < minX) {
|
|
23
|
+
minX = x;
|
|
24
|
+
leftIdx = i;
|
|
25
|
+
}
|
|
26
|
+
if (x > maxX) {
|
|
27
|
+
maxX = x;
|
|
28
|
+
rightIdx = i;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (Math.abs(maxX - minX) < EPSILON)
|
|
32
|
+
return [leftIdx];
|
|
33
|
+
// convex Hull starts with A and B
|
|
34
|
+
const hull = [];
|
|
35
|
+
// partition points into S1 and S2
|
|
36
|
+
// S1 = points on the right side of oriented line from A to B
|
|
37
|
+
// S2 = points on the right side of oriented line from B to A
|
|
38
|
+
const s1 = [];
|
|
39
|
+
const s2 = [];
|
|
40
|
+
for (let i = 0; i < n; i++) {
|
|
41
|
+
if (i === leftIdx || i === rightIdx)
|
|
42
|
+
continue;
|
|
43
|
+
const side = crossProduct(points, leftIdx, rightIdx, i);
|
|
44
|
+
// positive = left side of A→B = right side of B→A
|
|
45
|
+
// negative = right side of A→B = left side of B→A
|
|
46
|
+
if (side > EPSILON) {
|
|
47
|
+
s2.push(i); // left of A→B is right of B→A
|
|
48
|
+
}
|
|
49
|
+
else if (side < -EPSILON) {
|
|
50
|
+
s1.push(i); // right of A→B
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// build hull: start with leftmost, process upper hull, add rightmost, process lower hull
|
|
54
|
+
hull.push(leftIdx);
|
|
55
|
+
findHull(points, s1, leftIdx, rightIdx, hull);
|
|
56
|
+
hull.push(rightIdx);
|
|
57
|
+
findHull(points, s2, rightIdx, leftIdx, hull);
|
|
58
|
+
return hull;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Finds points on convex hull from set Sk that are on the right side of oriented line from P to Q.
|
|
62
|
+
* Points are inserted into hull array at the end (before the final endpoint).
|
|
63
|
+
*/
|
|
64
|
+
function findHull(points, sk, p, q, hull) {
|
|
65
|
+
if (sk.length === 0)
|
|
66
|
+
return;
|
|
67
|
+
// find farthest point C from segment PQ
|
|
68
|
+
let maxIdx = -1;
|
|
69
|
+
let maxDist = -1;
|
|
70
|
+
for (const idx of sk) {
|
|
71
|
+
const dist = Math.abs(crossProduct(points, p, q, idx));
|
|
72
|
+
if (dist > maxDist) {
|
|
73
|
+
maxDist = dist;
|
|
74
|
+
maxIdx = idx;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (maxIdx === -1)
|
|
78
|
+
return;
|
|
79
|
+
// partition remaining points into S1 and S2
|
|
80
|
+
// S1 = points on right side of oriented line from P to C
|
|
81
|
+
// S2 = points on right side of oriented line from C to Q
|
|
82
|
+
// S0 = points inside triangle PCQ (discarded)
|
|
83
|
+
const s1 = [];
|
|
84
|
+
const s2 = [];
|
|
85
|
+
for (const idx of sk) {
|
|
86
|
+
if (idx === maxIdx)
|
|
87
|
+
continue;
|
|
88
|
+
const sidePC = crossProduct(points, p, maxIdx, idx);
|
|
89
|
+
const sideCQ = crossProduct(points, maxIdx, q, idx);
|
|
90
|
+
// point is on right of P→C if cross product is negative
|
|
91
|
+
if (sidePC < -EPSILON) {
|
|
92
|
+
s1.push(idx);
|
|
93
|
+
}
|
|
94
|
+
// point is on right of C→Q if cross product is negative
|
|
95
|
+
else if (sideCQ < -EPSILON) {
|
|
96
|
+
s2.push(idx);
|
|
97
|
+
}
|
|
98
|
+
// else: point is inside triangle PCQ, discard it
|
|
99
|
+
}
|
|
100
|
+
// recursively process the two subsets
|
|
101
|
+
findHull(points, s1, p, maxIdx, hull);
|
|
102
|
+
hull.push(maxIdx); // Add point C to hull between P and Q
|
|
103
|
+
findHull(points, s2, maxIdx, q, hull);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Cross product to determine orientation.
|
|
107
|
+
* Returns (p2 - p1) × (p3 - p1)
|
|
108
|
+
* > 0: p3 is on the left of line p1→p2 (counter-clockwise)
|
|
109
|
+
* < 0: p3 is on the right of line p1→p2 (clockwise)
|
|
110
|
+
* = 0: collinear
|
|
111
|
+
*/
|
|
112
|
+
function crossProduct(points, p1, p2, p3) {
|
|
113
|
+
const x1 = points[p1 * 2];
|
|
114
|
+
const y1 = points[p1 * 2 + 1];
|
|
115
|
+
const x2 = points[p2 * 2];
|
|
116
|
+
const y2 = points[p2 * 2 + 1];
|
|
117
|
+
const x3 = points[p3 * 2];
|
|
118
|
+
const y3 = points[p3 * 2 + 1];
|
|
119
|
+
return (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export { quickhull2 };
|
|
123
|
+
//# sourceMappingURL=quickhull2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quickhull2.js","sources":["../../src/geometry/quickhull2.ts"],"sourcesContent":["const EPSILON = 1e-10;\n\n/**\n * Computes the convex hull of a set of 2D points using the QuickHull algorithm.\n * The hull is returned as an array of indices in counter-clockwise order.\n *\n * Implementation of pseudocode from: https://en.wikipedia.org/wiki/Quickhull\n *\n * @param points flat array of 2D points: [x0, y0, x1, y1, ...]\n * @returns indices of hull vertices in ccw order\n */\nexport function quickhull2(points: number[]): number[] {\n const n = Math.floor(points.length / 2);\n if (n < 3) return Array.from({ length: n }, (_, i) => i);\n\n // find left and right most points\n let leftIdx = 0;\n let rightIdx = 0;\n let minX = points[0];\n let maxX = points[0];\n\n for (let i = 1; i < n; i++) {\n const x = points[i * 2];\n if (x < minX) {\n minX = x;\n leftIdx = i;\n }\n if (x > maxX) {\n maxX = x;\n rightIdx = i;\n }\n }\n\n if (Math.abs(maxX - minX) < EPSILON) return [leftIdx];\n\n // convex Hull starts with A and B\n const hull: number[] = [];\n\n // partition points into S1 and S2\n // S1 = points on the right side of oriented line from A to B\n // S2 = points on the right side of oriented line from B to A\n const s1: number[] = [];\n const s2: number[] = [];\n\n for (let i = 0; i < n; i++) {\n if (i === leftIdx || i === rightIdx) continue;\n\n const side = crossProduct(points, leftIdx, rightIdx, i);\n\n // positive = left side of A→B = right side of B→A\n // negative = right side of A→B = left side of B→A\n if (side > EPSILON) {\n s2.push(i); // left of A→B is right of B→A\n } else if (side < -EPSILON) {\n s1.push(i); // right of A→B\n }\n }\n\n // build hull: start with leftmost, process upper hull, add rightmost, process lower hull\n hull.push(leftIdx);\n findHull(points, s1, leftIdx, rightIdx, hull);\n hull.push(rightIdx);\n findHull(points, s2, rightIdx, leftIdx, hull);\n\n return hull;\n}\n\n/**\n * Finds points on convex hull from set Sk that are on the right side of oriented line from P to Q.\n * Points are inserted into hull array at the end (before the final endpoint).\n */\nfunction findHull(points: number[], sk: number[], p: number, q: number, hull: number[]): void {\n if (sk.length === 0) return;\n\n // find farthest point C from segment PQ\n let maxIdx = -1;\n let maxDist = -1;\n\n for (const idx of sk) {\n const dist = Math.abs(crossProduct(points, p, q, idx));\n if (dist > maxDist) {\n maxDist = dist;\n maxIdx = idx;\n }\n }\n\n if (maxIdx === -1) return;\n\n // partition remaining points into S1 and S2\n // S1 = points on right side of oriented line from P to C\n // S2 = points on right side of oriented line from C to Q\n // S0 = points inside triangle PCQ (discarded)\n const s1: number[] = [];\n const s2: number[] = [];\n\n for (const idx of sk) {\n if (idx === maxIdx) continue;\n\n const sidePC = crossProduct(points, p, maxIdx, idx);\n const sideCQ = crossProduct(points, maxIdx, q, idx);\n\n // point is on right of P→C if cross product is negative\n if (sidePC < -EPSILON) {\n s1.push(idx);\n }\n // point is on right of C→Q if cross product is negative\n else if (sideCQ < -EPSILON) {\n s2.push(idx);\n }\n // else: point is inside triangle PCQ, discard it\n }\n\n // recursively process the two subsets\n findHull(points, s1, p, maxIdx, hull);\n hull.push(maxIdx); // Add point C to hull between P and Q\n findHull(points, s2, maxIdx, q, hull);\n}\n\n/**\n * Cross product to determine orientation.\n * Returns (p2 - p1) × (p3 - p1)\n * > 0: p3 is on the left of line p1→p2 (counter-clockwise)\n * < 0: p3 is on the right of line p1→p2 (clockwise)\n * = 0: collinear\n */\nfunction crossProduct(points: number[], p1: number, p2: number, p3: number): number {\n const x1 = points[p1 * 2];\n const y1 = points[p1 * 2 + 1];\n const x2 = points[p2 * 2];\n const y2 = points[p2 * 2 + 1];\n const x3 = points[p3 * 2];\n const y3 = points[p3 * 2 + 1];\n\n return (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1);\n}\n"],"names":[],"mappings":"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"}
|