math 0.0.3 → 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 -16
- package/skills/math/SKILL.md +140 -0
- package/AUTHORS +0 -1
- package/Cakefile +0 -28
- package/math.coffee +0 -112
- package/math.js +0 -141
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A 2D polygon is represented as a flat array of vertex coordinates laid out as
|
|
3
|
+
* `[x0, y0, x1, y1, ...]`, together with an explicit vertex count `n`.
|
|
4
|
+
*
|
|
5
|
+
* The count is passed separately (rather than derived from `verts.length`) so a
|
|
6
|
+
* single scratch buffer can be reused across polygons of different sizes: only
|
|
7
|
+
* the first `n` vertices are read, and any trailing slots are ignored. Vertices
|
|
8
|
+
* are assumed to be ordered (clockwise or counter-clockwise) around the polygon.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Returns the signed area of the polygon using the shoelace formula.
|
|
12
|
+
* The result is positive when the vertices wind counter-clockwise and negative
|
|
13
|
+
* when they wind clockwise, so the sign can be used to determine winding order.
|
|
14
|
+
*
|
|
15
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
16
|
+
* @param n number of vertices to read from `vertices`
|
|
17
|
+
* @returns the signed area
|
|
18
|
+
*/
|
|
19
|
+
function signedArea(vertices, n) {
|
|
20
|
+
let area = 0;
|
|
21
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
22
|
+
const xi = vertices[i * 2];
|
|
23
|
+
const yi = vertices[i * 2 + 1];
|
|
24
|
+
const xj = vertices[j * 2];
|
|
25
|
+
const yj = vertices[j * 2 + 1];
|
|
26
|
+
area += xj * yi - xi * yj;
|
|
27
|
+
}
|
|
28
|
+
return area / 2;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns the (non-negative) area of the polygon.
|
|
32
|
+
*
|
|
33
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
34
|
+
* @param n number of vertices to read from `vertices`
|
|
35
|
+
* @returns the absolute area
|
|
36
|
+
*/
|
|
37
|
+
function area(vertices, n) {
|
|
38
|
+
return Math.abs(signedArea(vertices, n));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Tests whether a point lies inside the polygon. Works for both convex and
|
|
42
|
+
* concave polygons. Points exactly on an edge or vertex are considered inside.
|
|
43
|
+
*
|
|
44
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
45
|
+
* @param n number of vertices to read from `vertices`
|
|
46
|
+
* @param point the point to test
|
|
47
|
+
* @returns true if the point is inside (or on the boundary of) the polygon
|
|
48
|
+
*/
|
|
49
|
+
function containsPoint(vertices, n, point) {
|
|
50
|
+
let inside = false;
|
|
51
|
+
const x = point[0];
|
|
52
|
+
const y = point[1];
|
|
53
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
54
|
+
const xi = vertices[i * 2];
|
|
55
|
+
const yi = vertices[i * 2 + 1];
|
|
56
|
+
const xj = vertices[j * 2];
|
|
57
|
+
const yj = vertices[j * 2 + 1];
|
|
58
|
+
// Signed area of the triangle (j, i, point): zero when the point lies on
|
|
59
|
+
// the edge, sign tells which side it is on.
|
|
60
|
+
const where = (yi - yj) * (x - xi) - (xi - xj) * (y - yi);
|
|
61
|
+
if (yj < yi) {
|
|
62
|
+
// Upward edge: count a crossing when the point's y is within [yj, yi).
|
|
63
|
+
if (y >= yj && y < yi) {
|
|
64
|
+
if (where === 0)
|
|
65
|
+
return true; // on edge
|
|
66
|
+
if (where > 0) {
|
|
67
|
+
if (y === yj) {
|
|
68
|
+
// Ray passes exactly through vertex j; only toggle when the
|
|
69
|
+
// previous vertex is below, to avoid double-counting.
|
|
70
|
+
if (y > vertices[(j === 0 ? n - 1 : j - 1) * 2 + 1])
|
|
71
|
+
inside = !inside;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
inside = !inside;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (yi < yj) {
|
|
80
|
+
// Downward edge: count a crossing when the point's y is within (yi, yj].
|
|
81
|
+
if (y > yi && y <= yj) {
|
|
82
|
+
if (where === 0)
|
|
83
|
+
return true; // on edge
|
|
84
|
+
if (where < 0) {
|
|
85
|
+
if (y === yj) {
|
|
86
|
+
if (y < vertices[(j === 0 ? n - 1 : j - 1) * 2 + 1])
|
|
87
|
+
inside = !inside;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
inside = !inside;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else if (y === yi && ((x >= xj && x <= xi) || (x >= xi && x <= xj))) {
|
|
96
|
+
// Point lies on a horizontal edge.
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return inside;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Computes the area-weighted centroid (center of mass) of the polygon.
|
|
104
|
+
* For a degenerate (zero-area) polygon this falls back to the average of the
|
|
105
|
+
* vertices.
|
|
106
|
+
*
|
|
107
|
+
* @param out the vector to store the centroid
|
|
108
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
109
|
+
* @param n number of vertices to read from `vertices`
|
|
110
|
+
* @returns out
|
|
111
|
+
*/
|
|
112
|
+
function centroid(out, vertices, n) {
|
|
113
|
+
let cx = 0;
|
|
114
|
+
let cy = 0;
|
|
115
|
+
let a2 = 0; // twice the signed area
|
|
116
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
117
|
+
const xi = vertices[i * 2];
|
|
118
|
+
const yi = vertices[i * 2 + 1];
|
|
119
|
+
const xj = vertices[j * 2];
|
|
120
|
+
const yj = vertices[j * 2 + 1];
|
|
121
|
+
const cross = xj * yi - xi * yj;
|
|
122
|
+
a2 += cross;
|
|
123
|
+
cx += (xj + xi) * cross;
|
|
124
|
+
cy += (yj + yi) * cross;
|
|
125
|
+
}
|
|
126
|
+
if (a2 === 0) {
|
|
127
|
+
// Degenerate polygon (collinear vertices): use the vertex average.
|
|
128
|
+
let sx = 0;
|
|
129
|
+
let sy = 0;
|
|
130
|
+
for (let i = 0; i < n; i++) {
|
|
131
|
+
sx += vertices[i * 2];
|
|
132
|
+
sy += vertices[i * 2 + 1];
|
|
133
|
+
}
|
|
134
|
+
out[0] = sx / n;
|
|
135
|
+
out[1] = sy / n;
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
const inv = 1 / (3 * a2);
|
|
139
|
+
out[0] = cx * inv;
|
|
140
|
+
out[1] = cy * inv;
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Returns the perimeter (sum of edge lengths) of the polygon.
|
|
145
|
+
*
|
|
146
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
147
|
+
* @param n number of vertices to read from `vertices`
|
|
148
|
+
* @returns the perimeter
|
|
149
|
+
*/
|
|
150
|
+
function perimeter(vertices, n) {
|
|
151
|
+
let total = 0;
|
|
152
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
153
|
+
const dx = vertices[i * 2] - vertices[j * 2];
|
|
154
|
+
const dy = vertices[i * 2 + 1] - vertices[j * 2 + 1];
|
|
155
|
+
total += Math.sqrt(dx * dx + dy * dy);
|
|
156
|
+
}
|
|
157
|
+
return total;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Returns the winding order of the polygon from the sign of its signed area:
|
|
161
|
+
* `1` for counter-clockwise, `-1` for clockwise, and `0` for a degenerate
|
|
162
|
+
* (zero-area) polygon.
|
|
163
|
+
*
|
|
164
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
165
|
+
* @param n number of vertices to read from `vertices`
|
|
166
|
+
* @returns 1 (CCW), -1 (CW), or 0 (degenerate)
|
|
167
|
+
*/
|
|
168
|
+
function winding(vertices, n) {
|
|
169
|
+
const a = signedArea(vertices, n);
|
|
170
|
+
if (a > 0)
|
|
171
|
+
return 1;
|
|
172
|
+
if (a < 0)
|
|
173
|
+
return -1;
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Tests whether the polygon is convex. Works for both winding orders. Assumes a
|
|
178
|
+
* simple (non-self-intersecting) polygon; collinear vertices are allowed.
|
|
179
|
+
*
|
|
180
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
181
|
+
* @param n number of vertices to read from `vertices`
|
|
182
|
+
* @returns true if the polygon is convex
|
|
183
|
+
*/
|
|
184
|
+
function isConvex(vertices, n) {
|
|
185
|
+
if (n < 3)
|
|
186
|
+
return false;
|
|
187
|
+
let sign = 0;
|
|
188
|
+
for (let i = 0; i < n; i++) {
|
|
189
|
+
const p = ((i - 1 + n) % n) * 2;
|
|
190
|
+
const c = i * 2;
|
|
191
|
+
const q = ((i + 1) % n) * 2;
|
|
192
|
+
// Cross product of the incoming and outgoing edges at vertex i.
|
|
193
|
+
const ax = vertices[c] - vertices[p];
|
|
194
|
+
const ay = vertices[c + 1] - vertices[p + 1];
|
|
195
|
+
const bx = vertices[q] - vertices[c];
|
|
196
|
+
const by = vertices[q + 1] - vertices[c + 1];
|
|
197
|
+
const cross = ax * by - ay * bx;
|
|
198
|
+
if (cross !== 0) {
|
|
199
|
+
const s = cross > 0 ? 1 : -1;
|
|
200
|
+
if (sign === 0)
|
|
201
|
+
sign = s;
|
|
202
|
+
else if (s !== sign)
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Tests whether vertex `i` is a reflex (concave) vertex of the polygon — the
|
|
210
|
+
* interior angle at that vertex exceeds 180°. Assumes the polygon is wound
|
|
211
|
+
* counter-clockwise and that `0 <= i < n`.
|
|
212
|
+
*
|
|
213
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
214
|
+
* @param n number of vertices to read from `vertices`
|
|
215
|
+
* @param i index of the vertex to test
|
|
216
|
+
* @returns true if vertex `i` is reflex
|
|
217
|
+
*/
|
|
218
|
+
function isReflexVertex(vertices, n, i) {
|
|
219
|
+
const p = ((i - 1 + n) % n) * 2;
|
|
220
|
+
const c = i * 2;
|
|
221
|
+
const q = ((i + 1) % n) * 2;
|
|
222
|
+
// Cross product of the incoming and outgoing edges; negative = right turn = reflex (CCW).
|
|
223
|
+
const ax = vertices[c] - vertices[p];
|
|
224
|
+
const ay = vertices[c + 1] - vertices[p + 1];
|
|
225
|
+
const bx = vertices[q] - vertices[c];
|
|
226
|
+
const by = vertices[q + 1] - vertices[c + 1];
|
|
227
|
+
return ax * by - ay * bx < 0;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Reverses the winding order of the polygon, writing the result into `out`.
|
|
231
|
+
* `out` may be the same array as `vertices` to reverse in place.
|
|
232
|
+
*
|
|
233
|
+
* @param out the array to write the reversed polygon into
|
|
234
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
235
|
+
* @param n number of vertices to read from `vertices`
|
|
236
|
+
* @returns out
|
|
237
|
+
*/
|
|
238
|
+
function reverse(out, vertices, n) {
|
|
239
|
+
for (let lo = 0, hi = n - 1; lo < hi; lo++, hi--) {
|
|
240
|
+
const x = vertices[lo * 2];
|
|
241
|
+
const y = vertices[lo * 2 + 1];
|
|
242
|
+
out[lo * 2] = vertices[hi * 2];
|
|
243
|
+
out[lo * 2 + 1] = vertices[hi * 2 + 1];
|
|
244
|
+
out[hi * 2] = x;
|
|
245
|
+
out[hi * 2 + 1] = y;
|
|
246
|
+
}
|
|
247
|
+
// Copy the untouched middle vertex when writing to a different buffer.
|
|
248
|
+
if (out !== vertices && n % 2 === 1) {
|
|
249
|
+
const mid = (n >> 1) * 2;
|
|
250
|
+
out[mid] = vertices[mid];
|
|
251
|
+
out[mid + 1] = vertices[mid + 1];
|
|
252
|
+
}
|
|
253
|
+
return out;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Writes the axis-aligned bounding box of the polygon into `out` as a Box2
|
|
257
|
+
* `[minX, minY, maxX, maxY]`.
|
|
258
|
+
*
|
|
259
|
+
* @param out the box to store the result
|
|
260
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
261
|
+
* @param n number of vertices to read from `vertices`
|
|
262
|
+
* @returns out
|
|
263
|
+
*/
|
|
264
|
+
function bounds(out, vertices, n) {
|
|
265
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
266
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
267
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
268
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
269
|
+
for (let i = 0; i < n; i++) {
|
|
270
|
+
const x = vertices[i * 2];
|
|
271
|
+
const y = vertices[i * 2 + 1];
|
|
272
|
+
if (x < minX)
|
|
273
|
+
minX = x;
|
|
274
|
+
if (x > maxX)
|
|
275
|
+
maxX = x;
|
|
276
|
+
if (y < minY)
|
|
277
|
+
minY = y;
|
|
278
|
+
if (y > maxY)
|
|
279
|
+
maxY = y;
|
|
280
|
+
}
|
|
281
|
+
out[0] = minX;
|
|
282
|
+
out[1] = minY;
|
|
283
|
+
out[2] = maxX;
|
|
284
|
+
out[3] = maxY;
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Finds the point on the polygon's boundary closest to `point` and writes it to
|
|
289
|
+
* `out`. `point` may lie inside, outside, or on the polygon; the result is
|
|
290
|
+
* always on an edge.
|
|
291
|
+
*
|
|
292
|
+
* @param out the vector to store the closest point
|
|
293
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
294
|
+
* @param n number of vertices to read from `vertices`
|
|
295
|
+
* @param point the query point
|
|
296
|
+
* @returns out
|
|
297
|
+
*/
|
|
298
|
+
function closestPoint(out, vertices, n, point) {
|
|
299
|
+
const px = point[0];
|
|
300
|
+
const py = point[1];
|
|
301
|
+
let bestDistSq = Number.POSITIVE_INFINITY;
|
|
302
|
+
let bestX = px;
|
|
303
|
+
let bestY = py;
|
|
304
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
305
|
+
const ax = vertices[j * 2];
|
|
306
|
+
const ay = vertices[j * 2 + 1];
|
|
307
|
+
const abx = vertices[i * 2] - ax;
|
|
308
|
+
const aby = vertices[i * 2 + 1] - ay;
|
|
309
|
+
// Project the point onto edge (a -> b), clamped to the segment.
|
|
310
|
+
const d = abx * abx + aby * aby;
|
|
311
|
+
let t = d > 0 ? ((px - ax) * abx + (py - ay) * aby) / d : 0;
|
|
312
|
+
if (t < 0)
|
|
313
|
+
t = 0;
|
|
314
|
+
else if (t > 1)
|
|
315
|
+
t = 1;
|
|
316
|
+
const cx = ax + t * abx;
|
|
317
|
+
const cy = ay + t * aby;
|
|
318
|
+
const dx = px - cx;
|
|
319
|
+
const dy = py - cy;
|
|
320
|
+
const distSq = dx * dx + dy * dy;
|
|
321
|
+
if (distSq < bestDistSq) {
|
|
322
|
+
bestDistSq = distSq;
|
|
323
|
+
bestX = cx;
|
|
324
|
+
bestY = cy;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
out[0] = bestX;
|
|
328
|
+
out[1] = bestY;
|
|
329
|
+
return out;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Returns the distance from `point` to the polygon's boundary, signed so that
|
|
333
|
+
* points inside the polygon get a negative distance and points outside get a
|
|
334
|
+
* positive one.
|
|
335
|
+
*
|
|
336
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
337
|
+
* @param n number of vertices to read from `vertices`
|
|
338
|
+
* @param point the query point
|
|
339
|
+
* @returns the signed distance (negative inside, positive outside)
|
|
340
|
+
*/
|
|
341
|
+
function signedDistance(vertices, n, point) {
|
|
342
|
+
const px = point[0];
|
|
343
|
+
const py = point[1];
|
|
344
|
+
let bestDistSq = Number.POSITIVE_INFINITY;
|
|
345
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
346
|
+
const ax = vertices[j * 2];
|
|
347
|
+
const ay = vertices[j * 2 + 1];
|
|
348
|
+
const abx = vertices[i * 2] - ax;
|
|
349
|
+
const aby = vertices[i * 2 + 1] - ay;
|
|
350
|
+
const d = abx * abx + aby * aby;
|
|
351
|
+
let t = d > 0 ? ((px - ax) * abx + (py - ay) * aby) / d : 0;
|
|
352
|
+
if (t < 0)
|
|
353
|
+
t = 0;
|
|
354
|
+
else if (t > 1)
|
|
355
|
+
t = 1;
|
|
356
|
+
const dx = px - (ax + t * abx);
|
|
357
|
+
const dy = py - (ay + t * aby);
|
|
358
|
+
const distSq = dx * dx + dy * dy;
|
|
359
|
+
if (distSq < bestDistSq)
|
|
360
|
+
bestDistSq = distSq;
|
|
361
|
+
}
|
|
362
|
+
const dist = Math.sqrt(bestDistSq);
|
|
363
|
+
return containsPoint(vertices, n, point) ? -dist : dist;
|
|
364
|
+
}
|
|
365
|
+
const _projA = [0, 0];
|
|
366
|
+
const _projB = [0, 0];
|
|
367
|
+
/** Projects a polygon onto the axis (nx, ny), storing [min, max] in `out`. */
|
|
368
|
+
function projectOntoAxis(out, nx, ny, vertices, n) {
|
|
369
|
+
let min = nx * vertices[0] + ny * vertices[1];
|
|
370
|
+
let max = min;
|
|
371
|
+
for (let i = 1; i < n; i++) {
|
|
372
|
+
const d = nx * vertices[i * 2] + ny * vertices[i * 2 + 1];
|
|
373
|
+
if (d < min)
|
|
374
|
+
min = d;
|
|
375
|
+
else if (d > max)
|
|
376
|
+
max = d;
|
|
377
|
+
}
|
|
378
|
+
out[0] = min;
|
|
379
|
+
out[1] = max;
|
|
380
|
+
}
|
|
381
|
+
/** Returns true if the two polygons are separated along any edge normal of A. */
|
|
382
|
+
function separatedByEdgesOf(verticesA, numA, verticesB, numB) {
|
|
383
|
+
for (let i = 0, j = numA - 1; i < numA; j = i++) {
|
|
384
|
+
// Normal of edge (a -> b); orientation does not matter for separation.
|
|
385
|
+
const nx = verticesA[i * 2 + 1] - verticesA[j * 2 + 1];
|
|
386
|
+
const ny = -(verticesA[i * 2] - verticesA[j * 2]);
|
|
387
|
+
projectOntoAxis(_projA, nx, ny, verticesA, numA);
|
|
388
|
+
projectOntoAxis(_projB, nx, ny, verticesB, numB);
|
|
389
|
+
if (_projA[0] > _projB[1] || _projB[0] > _projA[1])
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Tests whether two convex polygons overlap, using the separating axis theorem.
|
|
396
|
+
* Both polygons must be convex; results are undefined for concave input.
|
|
397
|
+
* Polygons that touch along an edge or vertex are considered overlapping.
|
|
398
|
+
*
|
|
399
|
+
* @param verticesA vertices of the first polygon `[x0, y0, x1, y1, ...]`
|
|
400
|
+
* @param numA number of vertices in the first polygon
|
|
401
|
+
* @param verticesB vertices of the second polygon `[x0, y0, x1, y1, ...]`
|
|
402
|
+
* @param numB number of vertices in the second polygon
|
|
403
|
+
* @returns true if the polygons overlap
|
|
404
|
+
*/
|
|
405
|
+
function overlapConvex(verticesA, numA, verticesB, numB) {
|
|
406
|
+
if (separatedByEdgesOf(verticesA, numA, verticesB, numB))
|
|
407
|
+
return false;
|
|
408
|
+
if (separatedByEdgesOf(verticesB, numB, verticesA, numA))
|
|
409
|
+
return false;
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Tests whether the segment `a`-`b` intersects the polygon, i.e. it has an
|
|
414
|
+
* endpoint inside the polygon or crosses one of its edges. Works for convex and
|
|
415
|
+
* concave polygons.
|
|
416
|
+
*
|
|
417
|
+
* @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`
|
|
418
|
+
* @param n number of vertices to read from `vertices`
|
|
419
|
+
* @param a start of the segment
|
|
420
|
+
* @param b end of the segment
|
|
421
|
+
* @returns true if the segment intersects the polygon
|
|
422
|
+
*/
|
|
423
|
+
function intersectsSegment(vertices, n, a, b) {
|
|
424
|
+
if (containsPoint(vertices, n, a) || containsPoint(vertices, n, b))
|
|
425
|
+
return true;
|
|
426
|
+
const ax = a[0];
|
|
427
|
+
const ay = a[1];
|
|
428
|
+
const abx = b[0] - ax;
|
|
429
|
+
const aby = b[1] - ay;
|
|
430
|
+
for (let i = 0, j = n - 1; i < n; j = i++) {
|
|
431
|
+
const cx = vertices[j * 2];
|
|
432
|
+
const cy = vertices[j * 2 + 1];
|
|
433
|
+
const dx = vertices[i * 2];
|
|
434
|
+
const dy = vertices[i * 2 + 1];
|
|
435
|
+
// Proper segment intersection via perp products (sign-of-area test).
|
|
436
|
+
const a1 = abx * (dy - ay) - aby * (dx - ax); // (b-a) x (d-a)
|
|
437
|
+
const a2 = abx * (cy - ay) - aby * (cx - ax); // (b-a) x (c-a)
|
|
438
|
+
if (a1 * a2 < 0) {
|
|
439
|
+
const cdx = dx - cx;
|
|
440
|
+
const cdy = dy - cy;
|
|
441
|
+
const a3 = cdx * (ay - cy) - cdy * (ax - cx); // (d-c) x (a-c)
|
|
442
|
+
const a4 = a3 + a2 - a1;
|
|
443
|
+
if (a3 * a4 < 0)
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export { area, bounds, centroid, closestPoint, containsPoint, intersectsSegment, isConvex, isReflexVertex, overlapConvex, perimeter, reverse, signedArea, signedDistance, winding };
|
|
451
|
+
//# sourceMappingURL=polygon2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polygon2.js","sources":["../../src/shapes/polygon2.ts"],"sourcesContent":["import type { Vec2 } from '../core/vec2';\nimport type { Box2 } from './box2';\n\n/**\n * A 2D polygon is represented as a flat array of vertex coordinates laid out as\n * `[x0, y0, x1, y1, ...]`, together with an explicit vertex count `n`.\n *\n * The count is passed separately (rather than derived from `verts.length`) so a\n * single scratch buffer can be reused across polygons of different sizes: only\n * the first `n` vertices are read, and any trailing slots are ignored. Vertices\n * are assumed to be ordered (clockwise or counter-clockwise) around the polygon.\n */\n\n/**\n * Returns the signed area of the polygon using the shoelace formula.\n * The result is positive when the vertices wind counter-clockwise and negative\n * when they wind clockwise, so the sign can be used to determine winding order.\n *\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 signed area\n */\nexport function signedArea(vertices: number[], n: number): number {\n let area = 0;\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const xi = vertices[i * 2];\n const yi = vertices[i * 2 + 1];\n const xj = vertices[j * 2];\n const yj = vertices[j * 2 + 1];\n area += xj * yi - xi * yj;\n }\n return area / 2;\n}\n\n/**\n * Returns the (non-negative) area of the polygon.\n *\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 absolute area\n */\nexport function area(vertices: number[], n: number): number {\n return Math.abs(signedArea(vertices, n));\n}\n\n/**\n * Tests whether a point lies inside the polygon. Works for both convex and\n * concave polygons. Points exactly on an edge or vertex are considered inside.\n *\n * @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`\n * @param n number of vertices to read from `vertices`\n * @param point the point to test\n * @returns true if the point is inside (or on the boundary of) the polygon\n */\nexport function containsPoint(vertices: number[], n: number, point: Vec2): boolean {\n let inside = false;\n const x = point[0];\n const y = point[1];\n\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const xi = vertices[i * 2];\n const yi = vertices[i * 2 + 1];\n const xj = vertices[j * 2];\n const yj = vertices[j * 2 + 1];\n\n // Signed area of the triangle (j, i, point): zero when the point lies on\n // the edge, sign tells which side it is on.\n const where = (yi - yj) * (x - xi) - (xi - xj) * (y - yi);\n\n if (yj < yi) {\n // Upward edge: count a crossing when the point's y is within [yj, yi).\n if (y >= yj && y < yi) {\n if (where === 0) return true; // on edge\n if (where > 0) {\n if (y === yj) {\n // Ray passes exactly through vertex j; only toggle when the\n // previous vertex is below, to avoid double-counting.\n if (y > vertices[(j === 0 ? n - 1 : j - 1) * 2 + 1]) inside = !inside;\n } else {\n inside = !inside;\n }\n }\n }\n } else if (yi < yj) {\n // Downward edge: count a crossing when the point's y is within (yi, yj].\n if (y > yi && y <= yj) {\n if (where === 0) return true; // on edge\n if (where < 0) {\n if (y === yj) {\n if (y < vertices[(j === 0 ? n - 1 : j - 1) * 2 + 1]) inside = !inside;\n } else {\n inside = !inside;\n }\n }\n }\n } else if (y === yi && ((x >= xj && x <= xi) || (x >= xi && x <= xj))) {\n // Point lies on a horizontal edge.\n return true;\n }\n }\n\n return inside;\n}\n\n/**\n * Computes the area-weighted centroid (center of mass) of the polygon.\n * For a degenerate (zero-area) polygon this falls back to the average of the\n * vertices.\n *\n * @param out the vector to store the centroid\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 out\n */\nexport function centroid(out: Vec2, vertices: number[], n: number): Vec2 {\n let cx = 0;\n let cy = 0;\n let a2 = 0; // twice the signed area\n\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const xi = vertices[i * 2];\n const yi = vertices[i * 2 + 1];\n const xj = vertices[j * 2];\n const yj = vertices[j * 2 + 1];\n const cross = xj * yi - xi * yj;\n a2 += cross;\n cx += (xj + xi) * cross;\n cy += (yj + yi) * cross;\n }\n\n if (a2 === 0) {\n // Degenerate polygon (collinear vertices): use the vertex average.\n let sx = 0;\n let sy = 0;\n for (let i = 0; i < n; i++) {\n sx += vertices[i * 2];\n sy += vertices[i * 2 + 1];\n }\n out[0] = sx / n;\n out[1] = sy / n;\n return out;\n }\n\n const inv = 1 / (3 * a2);\n out[0] = cx * inv;\n out[1] = cy * inv;\n return out;\n}\n\n/**\n * Returns the perimeter (sum of edge lengths) of the polygon.\n *\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 perimeter\n */\nexport function perimeter(vertices: number[], n: number): number {\n let total = 0;\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const dx = vertices[i * 2] - vertices[j * 2];\n const dy = vertices[i * 2 + 1] - vertices[j * 2 + 1];\n total += Math.sqrt(dx * dx + dy * dy);\n }\n return total;\n}\n\n/**\n * Returns the winding order of the polygon from the sign of its signed area:\n * `1` for counter-clockwise, `-1` for clockwise, and `0` for a degenerate\n * (zero-area) polygon.\n *\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 1 (CCW), -1 (CW), or 0 (degenerate)\n */\nexport function winding(vertices: number[], n: number): number {\n const a = signedArea(vertices, n);\n if (a > 0) return 1;\n if (a < 0) return -1;\n return 0;\n}\n\n/**\n * Tests whether the polygon is convex. Works for both winding orders. Assumes a\n * simple (non-self-intersecting) polygon; collinear vertices are allowed.\n *\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 true if the polygon is convex\n */\nexport function isConvex(vertices: number[], n: number): boolean {\n if (n < 3) return false;\n\n let sign = 0;\n for (let i = 0; i < n; i++) {\n const p = ((i - 1 + n) % n) * 2;\n const c = i * 2;\n const q = ((i + 1) % n) * 2;\n\n // Cross product of the incoming and outgoing edges at vertex i.\n const ax = vertices[c] - vertices[p];\n const ay = vertices[c + 1] - vertices[p + 1];\n const bx = vertices[q] - vertices[c];\n const by = vertices[q + 1] - vertices[c + 1];\n const cross = ax * by - ay * bx;\n\n if (cross !== 0) {\n const s = cross > 0 ? 1 : -1;\n if (sign === 0) sign = s;\n else if (s !== sign) return false;\n }\n }\n\n return true;\n}\n\n/**\n * Tests whether vertex `i` is a reflex (concave) vertex of the polygon — the\n * interior angle at that vertex exceeds 180°. Assumes the polygon is wound\n * counter-clockwise and that `0 <= i < n`.\n *\n * @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`\n * @param n number of vertices to read from `vertices`\n * @param i index of the vertex to test\n * @returns true if vertex `i` is reflex\n */\nexport function isReflexVertex(vertices: number[], n: number, i: number): boolean {\n const p = ((i - 1 + n) % n) * 2;\n const c = i * 2;\n const q = ((i + 1) % n) * 2;\n\n // Cross product of the incoming and outgoing edges; negative = right turn = reflex (CCW).\n const ax = vertices[c] - vertices[p];\n const ay = vertices[c + 1] - vertices[p + 1];\n const bx = vertices[q] - vertices[c];\n const by = vertices[q + 1] - vertices[c + 1];\n return ax * by - ay * bx < 0;\n}\n\n/**\n * Reverses the winding order of the polygon, writing the result into `out`.\n * `out` may be the same array as `vertices` to reverse in place.\n *\n * @param out the array to write the reversed polygon into\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 out\n */\nexport function reverse(out: number[], vertices: number[], n: number): number[] {\n for (let lo = 0, hi = n - 1; lo < hi; lo++, hi--) {\n const x = vertices[lo * 2];\n const y = vertices[lo * 2 + 1];\n out[lo * 2] = vertices[hi * 2];\n out[lo * 2 + 1] = vertices[hi * 2 + 1];\n out[hi * 2] = x;\n out[hi * 2 + 1] = y;\n }\n // Copy the untouched middle vertex when writing to a different buffer.\n if (out !== vertices && n % 2 === 1) {\n const mid = (n >> 1) * 2;\n out[mid] = vertices[mid];\n out[mid + 1] = vertices[mid + 1];\n }\n return out;\n}\n\n/**\n * Writes the axis-aligned bounding box of the polygon into `out` as a Box2\n * `[minX, minY, maxX, maxY]`.\n *\n * @param out the box to store the result\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 out\n */\nexport function bounds(out: Box2, vertices: number[], n: number): Box2 {\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n\n for (let i = 0; i < n; i++) {\n const x = vertices[i * 2];\n const y = vertices[i * 2 + 1];\n if (x < minX) minX = x;\n if (x > maxX) maxX = x;\n if (y < minY) minY = y;\n if (y > maxY) maxY = y;\n }\n\n out[0] = minX;\n out[1] = minY;\n out[2] = maxX;\n out[3] = maxY;\n return out;\n}\n\n/**\n * Finds the point on the polygon's boundary closest to `point` and writes it to\n * `out`. `point` may lie inside, outside, or on the polygon; the result is\n * always on an edge.\n *\n * @param out the vector to store the closest point\n * @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`\n * @param n number of vertices to read from `vertices`\n * @param point the query point\n * @returns out\n */\nexport function closestPoint(out: Vec2, vertices: number[], n: number, point: Vec2): Vec2 {\n const px = point[0];\n const py = point[1];\n let bestDistSq = Number.POSITIVE_INFINITY;\n let bestX = px;\n let bestY = py;\n\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const ax = vertices[j * 2];\n const ay = vertices[j * 2 + 1];\n const abx = vertices[i * 2] - ax;\n const aby = vertices[i * 2 + 1] - ay;\n\n // Project the point onto edge (a -> b), clamped to the segment.\n const d = abx * abx + aby * aby;\n let t = d > 0 ? ((px - ax) * abx + (py - ay) * aby) / d : 0;\n if (t < 0) t = 0;\n else if (t > 1) t = 1;\n\n const cx = ax + t * abx;\n const cy = ay + t * aby;\n const dx = px - cx;\n const dy = py - cy;\n const distSq = dx * dx + dy * dy;\n if (distSq < bestDistSq) {\n bestDistSq = distSq;\n bestX = cx;\n bestY = cy;\n }\n }\n\n out[0] = bestX;\n out[1] = bestY;\n return out;\n}\n\n/**\n * Returns the distance from `point` to the polygon's boundary, signed so that\n * points inside the polygon get a negative distance and points outside get a\n * positive one.\n *\n * @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`\n * @param n number of vertices to read from `vertices`\n * @param point the query point\n * @returns the signed distance (negative inside, positive outside)\n */\nexport function signedDistance(vertices: number[], n: number, point: Vec2): number {\n const px = point[0];\n const py = point[1];\n let bestDistSq = Number.POSITIVE_INFINITY;\n\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const ax = vertices[j * 2];\n const ay = vertices[j * 2 + 1];\n const abx = vertices[i * 2] - ax;\n const aby = vertices[i * 2 + 1] - ay;\n\n const d = abx * abx + aby * aby;\n let t = d > 0 ? ((px - ax) * abx + (py - ay) * aby) / d : 0;\n if (t < 0) t = 0;\n else if (t > 1) t = 1;\n\n const dx = px - (ax + t * abx);\n const dy = py - (ay + t * aby);\n const distSq = dx * dx + dy * dy;\n if (distSq < bestDistSq) bestDistSq = distSq;\n }\n\n const dist = Math.sqrt(bestDistSq);\n return containsPoint(vertices, n, point) ? -dist : dist;\n}\n\nconst _projA: [number, number] = [0, 0];\nconst _projB: [number, number] = [0, 0];\n\n/** Projects a polygon onto the axis (nx, ny), storing [min, max] in `out`. */\nfunction projectOntoAxis(out: [number, number], nx: number, ny: number, vertices: number[], n: number): void {\n let min = nx * vertices[0] + ny * vertices[1];\n let max = min;\n for (let i = 1; i < n; i++) {\n const d = nx * vertices[i * 2] + ny * vertices[i * 2 + 1];\n if (d < min) min = d;\n else if (d > max) max = d;\n }\n out[0] = min;\n out[1] = max;\n}\n\n/** Returns true if the two polygons are separated along any edge normal of A. */\nfunction separatedByEdgesOf(verticesA: number[], numA: number, verticesB: number[], numB: number): boolean {\n for (let i = 0, j = numA - 1; i < numA; j = i++) {\n // Normal of edge (a -> b); orientation does not matter for separation.\n const nx = verticesA[i * 2 + 1] - verticesA[j * 2 + 1];\n const ny = -(verticesA[i * 2] - verticesA[j * 2]);\n\n projectOntoAxis(_projA, nx, ny, verticesA, numA);\n projectOntoAxis(_projB, nx, ny, verticesB, numB);\n\n if (_projA[0] > _projB[1] || _projB[0] > _projA[1]) return true;\n }\n return false;\n}\n\n/**\n * Tests whether two convex polygons overlap, using the separating axis theorem.\n * Both polygons must be convex; results are undefined for concave input.\n * Polygons that touch along an edge or vertex are considered overlapping.\n *\n * @param verticesA vertices of the first polygon `[x0, y0, x1, y1, ...]`\n * @param numA number of vertices in the first polygon\n * @param verticesB vertices of the second polygon `[x0, y0, x1, y1, ...]`\n * @param numB number of vertices in the second polygon\n * @returns true if the polygons overlap\n */\nexport function overlapConvex(verticesA: number[], numA: number, verticesB: number[], numB: number): boolean {\n if (separatedByEdgesOf(verticesA, numA, verticesB, numB)) return false;\n if (separatedByEdgesOf(verticesB, numB, verticesA, numA)) return false;\n return true;\n}\n\n/**\n * Tests whether the segment `a`-`b` intersects the polygon, i.e. it has an\n * endpoint inside the polygon or crosses one of its edges. Works for convex and\n * concave polygons.\n *\n * @param vertices polygon vertices as a flat array `[x0, y0, x1, y1, ...]`\n * @param n number of vertices to read from `vertices`\n * @param a start of the segment\n * @param b end of the segment\n * @returns true if the segment intersects the polygon\n */\nexport function intersectsSegment(vertices: number[], n: number, a: Vec2, b: Vec2): boolean {\n if (containsPoint(vertices, n, a) || containsPoint(vertices, n, b)) return true;\n\n const ax = a[0];\n const ay = a[1];\n const abx = b[0] - ax;\n const aby = b[1] - ay;\n\n for (let i = 0, j = n - 1; i < n; j = i++) {\n const cx = vertices[j * 2];\n const cy = vertices[j * 2 + 1];\n const dx = vertices[i * 2];\n const dy = vertices[i * 2 + 1];\n\n // Proper segment intersection via perp products (sign-of-area test).\n const a1 = abx * (dy - ay) - aby * (dx - ax); // (b-a) x (d-a)\n const a2 = abx * (cy - ay) - aby * (cx - ax); // (b-a) x (c-a)\n if (a1 * a2 < 0) {\n const cdx = dx - cx;\n const cdy = dy - cy;\n const a3 = cdx * (ay - cy) - cdy * (ax - cx); // (d-c) x (a-c)\n const a4 = a3 + a2 - a1;\n if (a3 * a4 < 0) return true;\n }\n }\n\n return false;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a new IntersectsTriangleResult with default values.
|
|
3
|
+
* @returns A new IntersectsTriangleResult.
|
|
4
|
+
*/
|
|
5
|
+
function createIntersectsTriangleResult() {
|
|
6
|
+
return {
|
|
7
|
+
fraction: 0,
|
|
8
|
+
hit: false,
|
|
9
|
+
frontFacing: false,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Ray-triangle intersection test.
|
|
14
|
+
* Based on https://github.com/pmjoniak/GeometricTools/blob/master/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h
|
|
15
|
+
*
|
|
16
|
+
* @param out output object to store result (hit boolean, fraction, frontFacing)
|
|
17
|
+
* @param origin ray origin
|
|
18
|
+
* @param direction ray direction
|
|
19
|
+
* @param length ray length
|
|
20
|
+
* @param a first vertex of triangle
|
|
21
|
+
* @param b second vertex of triangle
|
|
22
|
+
* @param c third vertex of triangle
|
|
23
|
+
* @param backfaceCulling if true, backfaces will not be considered hits
|
|
24
|
+
*/
|
|
25
|
+
function intersectsTriangle(out, origin, direction, length, a, b, c, backfaceCulling) {
|
|
26
|
+
// compute edge1 = b - a
|
|
27
|
+
const e1x = b[0] - a[0];
|
|
28
|
+
const e1y = b[1] - a[1];
|
|
29
|
+
const e1z = b[2] - a[2];
|
|
30
|
+
// compute edge2 = c - a
|
|
31
|
+
const e2x = c[0] - a[0];
|
|
32
|
+
const e2y = c[1] - a[1];
|
|
33
|
+
const e2z = c[2] - a[2];
|
|
34
|
+
// compute normal = edge1 × edge2
|
|
35
|
+
const nx = e1y * e2z - e1z * e2y;
|
|
36
|
+
const ny = e1z * e2x - e1x * e2z;
|
|
37
|
+
const nz = e1x * e2y - e1y * e2x;
|
|
38
|
+
// determine front vs back facing
|
|
39
|
+
const dx = direction[0];
|
|
40
|
+
const dy = direction[1];
|
|
41
|
+
const dz = direction[2];
|
|
42
|
+
let DdN = dx * nx + dy * ny + dz * nz;
|
|
43
|
+
let sign;
|
|
44
|
+
if (DdN > 0) {
|
|
45
|
+
// backface
|
|
46
|
+
if (backfaceCulling) {
|
|
47
|
+
out.hit = false;
|
|
48
|
+
out.fraction = 0;
|
|
49
|
+
out.frontFacing = false;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
sign = 1;
|
|
53
|
+
}
|
|
54
|
+
else if (DdN < 0) {
|
|
55
|
+
// frontface
|
|
56
|
+
sign = -1;
|
|
57
|
+
DdN = -DdN;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// ray is parallel to triangle
|
|
61
|
+
out.hit = false;
|
|
62
|
+
out.fraction = 0;
|
|
63
|
+
out.frontFacing = false;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// compute diff = origin - a
|
|
67
|
+
const diffx = origin[0] - a[0];
|
|
68
|
+
const diffy = origin[1] - a[1];
|
|
69
|
+
const diffz = origin[2] - a[2];
|
|
70
|
+
// compute barycentric coordinate b1
|
|
71
|
+
// DdQxE2 = sign * D · (diff × edge2)
|
|
72
|
+
const diffCrossE2x = diffy * e2z - diffz * e2y;
|
|
73
|
+
const diffCrossE2y = diffz * e2x - diffx * e2z;
|
|
74
|
+
const diffCrossE2z = diffx * e2y - diffy * e2x;
|
|
75
|
+
const DdQxE2 = sign * (dx * diffCrossE2x + dy * diffCrossE2y + dz * diffCrossE2z);
|
|
76
|
+
if (DdQxE2 < 0) {
|
|
77
|
+
out.hit = false;
|
|
78
|
+
out.fraction = 0;
|
|
79
|
+
out.frontFacing = false;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// compute barycentric coordinate b2
|
|
83
|
+
// DdE1xQ = sign * D · (edge1 × diff)
|
|
84
|
+
const e1CrossDiffx = e1y * diffz - e1z * diffy;
|
|
85
|
+
const e1CrossDiffy = e1z * diffx - e1x * diffz;
|
|
86
|
+
const e1CrossDiffz = e1x * diffy - e1y * diffx;
|
|
87
|
+
const DdE1xQ = sign * (dx * e1CrossDiffx + dy * e1CrossDiffy + dz * e1CrossDiffz);
|
|
88
|
+
if (DdE1xQ < 0) {
|
|
89
|
+
out.hit = false;
|
|
90
|
+
out.fraction = 0;
|
|
91
|
+
out.frontFacing = false;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// check if b1 + b2 > 1
|
|
95
|
+
if (DdQxE2 + DdE1xQ > DdN) {
|
|
96
|
+
out.hit = false;
|
|
97
|
+
out.fraction = 0;
|
|
98
|
+
out.frontFacing = false;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// compute intersection distance
|
|
102
|
+
const QdN = -sign * (diffx * nx + diffy * ny + diffz * nz);
|
|
103
|
+
if (QdN < 0) {
|
|
104
|
+
out.hit = false;
|
|
105
|
+
out.fraction = 0;
|
|
106
|
+
out.frontFacing = false;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const t = QdN / DdN;
|
|
110
|
+
// check if intersection is within ray length
|
|
111
|
+
if (t <= length) {
|
|
112
|
+
out.hit = true;
|
|
113
|
+
out.fraction = t / length;
|
|
114
|
+
out.frontFacing = sign < 0;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
out.hit = false;
|
|
118
|
+
out.fraction = 0;
|
|
119
|
+
out.frontFacing = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Test if a ray intersects an axis-aligned bounding box.
|
|
124
|
+
* Uses slab-based algorithm that handles parallel rays correctly.
|
|
125
|
+
*
|
|
126
|
+
* @param origin ray origin
|
|
127
|
+
* @param direction ray direction
|
|
128
|
+
* @param length ray length
|
|
129
|
+
* @param aabb AABB to test against
|
|
130
|
+
* @returns true if ray intersects the AABB, false otherwise
|
|
131
|
+
*/
|
|
132
|
+
function intersectsBox3(origin, direction, length, aabb) {
|
|
133
|
+
let tmin = 0;
|
|
134
|
+
let tmax = length;
|
|
135
|
+
for (let i = 0; i < 3; i++) {
|
|
136
|
+
const d = direction[i];
|
|
137
|
+
if (Math.abs(d) < 1e-10) {
|
|
138
|
+
// ray is parallel to slab: check if origin is within slab
|
|
139
|
+
if (origin[i] < aabb[i] || origin[i] > aabb[i + 3]) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
// compute intersection times with slab
|
|
145
|
+
const invD = 1 / d;
|
|
146
|
+
let t0 = (aabb[i] - origin[i]) * invD;
|
|
147
|
+
let t1 = (aabb[i + 3] - origin[i]) * invD;
|
|
148
|
+
if (invD < 0) {
|
|
149
|
+
const temp = t0;
|
|
150
|
+
t0 = t1;
|
|
151
|
+
t1 = temp;
|
|
152
|
+
}
|
|
153
|
+
tmin = Math.max(tmin, t0);
|
|
154
|
+
tmax = Math.min(tmax, t1);
|
|
155
|
+
if (tmax < tmin) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export { createIntersectsTriangleResult, intersectsBox3, intersectsTriangle };
|
|
164
|
+
//# sourceMappingURL=raycast3.js.map
|