@expofp/geometry 3.8.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/README.md +139 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +42 -0
- package/dist/lib/angles.d.ts +36 -0
- package/dist/lib/angles.js +50 -0
- package/dist/lib/box.d.ts +282 -0
- package/dist/lib/box.js +362 -0
- package/dist/lib/geo.d.ts +98 -0
- package/dist/lib/geo.js +159 -0
- package/dist/lib/intersections.d.ts +30 -0
- package/dist/lib/intersections.js +72 -0
- package/dist/lib/line.d.ts +134 -0
- package/dist/lib/line.js +167 -0
- package/dist/lib/mesh.d.ts +166 -0
- package/dist/lib/mesh.js +162 -0
- package/dist/lib/point.d.ts +206 -0
- package/dist/lib/point.js +237 -0
- package/dist/lib/polygon.d.ts +169 -0
- package/dist/lib/polygon.js +220 -0
- package/dist/lib/rect.d.ts +200 -0
- package/dist/lib/rect.js +269 -0
- package/package.json +31 -0
package/dist/lib/mesh.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { Box } from './box.js';
|
|
2
|
+
import { Point } from './point.js';
|
|
3
|
+
/**
|
|
4
|
+
* A 3D triangle mesh: vertices carry an optional z coordinate, plus triangle index triplets.
|
|
5
|
+
* Immutable value object: readonly getters; transforms return a new instance unless a `target`
|
|
6
|
+
* is passed. For a planar triangulated polygon with area/containment use `Polygon` instead.
|
|
7
|
+
*/
|
|
8
|
+
export class Mesh {
|
|
9
|
+
/** Marker so callers can narrow the type at runtime. */
|
|
10
|
+
isMesh = true;
|
|
11
|
+
/** Internal mutable vertex array (3D). */
|
|
12
|
+
_vertices = [];
|
|
13
|
+
/** Internal triangle index array. */
|
|
14
|
+
_indices = [];
|
|
15
|
+
/** Cached axis-aligned bounding box (xy only). */
|
|
16
|
+
_bounds = new Box();
|
|
17
|
+
/**
|
|
18
|
+
* Constructs a mesh from a vertex list and triangle index triplets.
|
|
19
|
+
* @param vertices - source vertices; each is cloned into a {@link Point} (default `[]`)
|
|
20
|
+
* @param indices - triangle index triplets referencing positions in `vertices` (default `[]`)
|
|
21
|
+
*/
|
|
22
|
+
constructor(vertices = [], indices = []) {
|
|
23
|
+
this.set(vertices, indices);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Readonly array of mesh vertices as {@link Point} instances.
|
|
27
|
+
* @returns the vertex array
|
|
28
|
+
*/
|
|
29
|
+
get vertices() {
|
|
30
|
+
return this._vertices;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Readonly array of triangle index triplets.
|
|
34
|
+
* @returns the index array
|
|
35
|
+
*/
|
|
36
|
+
get indices() {
|
|
37
|
+
return this._indices;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Cached axis-aligned bounding {@link Box} of this mesh (xy only, z ignored).
|
|
41
|
+
* @returns the bounding box
|
|
42
|
+
*/
|
|
43
|
+
get bounds() {
|
|
44
|
+
return this._bounds;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Mutates this mesh in place, replacing vertices/indices and refreshing the cached bounds.
|
|
48
|
+
* Vertices are copied into fresh {@link Point}s — do not optimize this clone away; transform
|
|
49
|
+
* free functions rely on it to avoid aliasing shared vertices. Use only for hot-path reuse.
|
|
50
|
+
* @param vertices - new vertex list
|
|
51
|
+
* @param indices - new triangle index list
|
|
52
|
+
* @returns `this`
|
|
53
|
+
*/
|
|
54
|
+
set(vertices, indices) {
|
|
55
|
+
this._vertices = vertices.map((v) => new Point(v.x, v.y, v.z ?? 0));
|
|
56
|
+
this._indices = indices;
|
|
57
|
+
this._bounds = meshBounds(this);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Merges two or more meshes into a single {@link Mesh}, offsetting each mesh's triangle indices
|
|
62
|
+
* by the cumulative vertex count so they remain valid. Forwards to {@link meshMerge}.
|
|
63
|
+
* @param meshes - meshes to merge
|
|
64
|
+
* @returns a new merged mesh
|
|
65
|
+
*/
|
|
66
|
+
static merge(...meshes) {
|
|
67
|
+
return meshMerge(meshes);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Returns a new mesh translated by `offset`. Forwards to {@link meshTranslate}.
|
|
71
|
+
* @param offset - translation vector
|
|
72
|
+
* @param target - optional mesh to write into instead of allocating a new one
|
|
73
|
+
* @returns translated mesh
|
|
74
|
+
*/
|
|
75
|
+
translate(offset, target) {
|
|
76
|
+
return meshTranslate(this, offset, target ?? new Mesh());
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns a new mesh scaled by `factor` about `origin`. Forwards to {@link meshScale}.
|
|
80
|
+
* @param factor - uniform scale or per-axis `{ x, y }` scale
|
|
81
|
+
* @param origin - the fixed point of the scaling; defaults to the bounding-box center
|
|
82
|
+
* @param target - optional mesh to write into instead of allocating a new one
|
|
83
|
+
* @returns scaled mesh
|
|
84
|
+
*/
|
|
85
|
+
scale(factor, origin = this._bounds.center, target) {
|
|
86
|
+
return meshScale(this, factor, origin, target ?? new Mesh());
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Returns a new mesh rotated by `angle` radians about `origin`. Forwards to {@link meshRotate}.
|
|
90
|
+
* @param angle - rotation angle in radians (positive = clockwise in y-down space)
|
|
91
|
+
* @param origin - the pivot of the rotation; defaults to the bounding-box center
|
|
92
|
+
* @param target - optional mesh to write into instead of allocating a new one
|
|
93
|
+
* @returns rotated mesh
|
|
94
|
+
*/
|
|
95
|
+
rotate(angle, origin = this._bounds.center, target) {
|
|
96
|
+
return meshRotate(this, angle, origin, target ?? new Mesh());
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Computes the axis-aligned bounding {@link Box} of a mesh by sweeping all vertex xy coordinates.
|
|
101
|
+
* The z component is ignored — bounds are always in the xy plane.
|
|
102
|
+
* @param m - mesh or mesh-like object
|
|
103
|
+
* @returns the bounding box
|
|
104
|
+
*/
|
|
105
|
+
export function meshBounds(m) {
|
|
106
|
+
if (m.vertices.length === 0)
|
|
107
|
+
return new Box();
|
|
108
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
109
|
+
for (const v of m.vertices) {
|
|
110
|
+
minX = Math.min(minX, v.x);
|
|
111
|
+
minY = Math.min(minY, v.y);
|
|
112
|
+
maxX = Math.max(maxX, v.x);
|
|
113
|
+
maxY = Math.max(maxY, v.y);
|
|
114
|
+
}
|
|
115
|
+
return Box.fromMinMax({ x: minX, y: minY }, { x: maxX, y: maxY });
|
|
116
|
+
}
|
|
117
|
+
export function meshMerge(meshes, target) {
|
|
118
|
+
const vertices = [];
|
|
119
|
+
const indices = [];
|
|
120
|
+
let offset = 0;
|
|
121
|
+
for (const m of meshes) {
|
|
122
|
+
for (const v of m.vertices)
|
|
123
|
+
vertices.push(v);
|
|
124
|
+
for (const [a, b, c] of m.indices)
|
|
125
|
+
indices.push([a + offset, b + offset, c + offset]);
|
|
126
|
+
offset += m.vertices.length;
|
|
127
|
+
}
|
|
128
|
+
return (target ?? new Mesh()).set(vertices, indices);
|
|
129
|
+
}
|
|
130
|
+
export function meshTranslate(m, offset, target) {
|
|
131
|
+
const dz = offset.z ?? 0;
|
|
132
|
+
const vertices = m.vertices.map((v) => ({
|
|
133
|
+
x: v.x + offset.x,
|
|
134
|
+
y: v.y + offset.y,
|
|
135
|
+
z: (v.z ?? 0) + dz,
|
|
136
|
+
}));
|
|
137
|
+
return (target ?? new Mesh()).set(vertices, m.indices);
|
|
138
|
+
}
|
|
139
|
+
export function meshScale(m, factor, origin = meshBounds(m).center, target) {
|
|
140
|
+
const fx = typeof factor === 'number' ? factor : factor.x;
|
|
141
|
+
const fy = typeof factor === 'number' ? factor : factor.y;
|
|
142
|
+
const vertices = m.vertices.map((v) => ({
|
|
143
|
+
x: origin.x + (v.x - origin.x) * fx,
|
|
144
|
+
y: origin.y + (v.y - origin.y) * fy,
|
|
145
|
+
z: v.z ?? 0,
|
|
146
|
+
}));
|
|
147
|
+
return (target ?? new Mesh()).set(vertices, m.indices);
|
|
148
|
+
}
|
|
149
|
+
export function meshRotate(m, angle, origin = meshBounds(m).center, target) {
|
|
150
|
+
const cos = Math.cos(angle);
|
|
151
|
+
const sin = Math.sin(angle);
|
|
152
|
+
const vertices = m.vertices.map((v) => {
|
|
153
|
+
const dx = v.x - origin.x;
|
|
154
|
+
const dy = v.y - origin.y;
|
|
155
|
+
return {
|
|
156
|
+
x: origin.x + dx * cos - dy * sin,
|
|
157
|
+
y: origin.y + dx * sin + dy * cos,
|
|
158
|
+
z: v.z ?? 0,
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
return (target ?? new Mesh()).set(vertices, m.indices);
|
|
162
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/** A 2D point/vector `{ x, y }`. */
|
|
2
|
+
export interface Point2Like {
|
|
3
|
+
/** Horizontal coordinate. */
|
|
4
|
+
readonly x: number;
|
|
5
|
+
/** Vertical coordinate. */
|
|
6
|
+
readonly y: number;
|
|
7
|
+
}
|
|
8
|
+
/** A 3D point/vector `{ x, y, z }`. */
|
|
9
|
+
export interface Point3Like {
|
|
10
|
+
/** Horizontal coordinate. */
|
|
11
|
+
readonly x: number;
|
|
12
|
+
/** Vertical coordinate. */
|
|
13
|
+
readonly y: number;
|
|
14
|
+
/** Depth coordinate. */
|
|
15
|
+
readonly z: number;
|
|
16
|
+
}
|
|
17
|
+
/** Any structural point — `z` optional. */
|
|
18
|
+
export interface PointLike {
|
|
19
|
+
/** Horizontal coordinate. */
|
|
20
|
+
readonly x: number;
|
|
21
|
+
/** Vertical coordinate. */
|
|
22
|
+
readonly y: number;
|
|
23
|
+
/** Depth coordinate (optional). */
|
|
24
|
+
readonly z?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A readonly 2D vector that also exposes `width`/`height` aliases of `x`/`y` — the return type of the
|
|
28
|
+
* `size` getter on {@link Box} and {@link Rect}. Backed by a {@link Point} (which provides the
|
|
29
|
+
* aliases), so `size.x`/`size.width` and `size.y`/`size.height` both read the same value.
|
|
30
|
+
*/
|
|
31
|
+
export type SizeLike = Readonly<Point2Like> & {
|
|
32
|
+
readonly width: number;
|
|
33
|
+
readonly height: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Mutable point/vector — the low-level building block other primitives and the point free functions
|
|
37
|
+
* write into. `width`/`height` alias `x`/`y` so a point used as a size reads naturally.
|
|
38
|
+
*/
|
|
39
|
+
export declare class Point {
|
|
40
|
+
/** Marker so callers can narrow the type at runtime. */
|
|
41
|
+
readonly isPoint = true;
|
|
42
|
+
/** Horizontal coordinate. */
|
|
43
|
+
x: number;
|
|
44
|
+
/** Vertical coordinate. */
|
|
45
|
+
y: number;
|
|
46
|
+
/** Depth coordinate. */
|
|
47
|
+
z: number;
|
|
48
|
+
/**
|
|
49
|
+
* @param x - horizontal coordinate (default 0)
|
|
50
|
+
* @param y - vertical coordinate (default 0)
|
|
51
|
+
* @param z - depth coordinate (default 0)
|
|
52
|
+
*/
|
|
53
|
+
constructor(x?: number, y?: number, z?: number);
|
|
54
|
+
/**
|
|
55
|
+
* Alias of {@link Point.x}.
|
|
56
|
+
* @returns the x coordinate
|
|
57
|
+
*/
|
|
58
|
+
get width(): number;
|
|
59
|
+
/**
|
|
60
|
+
* Alias of {@link Point.y}.
|
|
61
|
+
* @returns the y coordinate
|
|
62
|
+
*/
|
|
63
|
+
get height(): number;
|
|
64
|
+
/**
|
|
65
|
+
* Sets coordinates in place.
|
|
66
|
+
* @param x - horizontal coordinate
|
|
67
|
+
* @param y - vertical coordinate
|
|
68
|
+
* @param z - depth coordinate (defaults to the current z)
|
|
69
|
+
* @returns this
|
|
70
|
+
*/
|
|
71
|
+
set(x: number, y: number, z?: number): this;
|
|
72
|
+
/**
|
|
73
|
+
* Copies from another point in place.
|
|
74
|
+
* @param p - source point to copy coordinates from
|
|
75
|
+
* @returns this
|
|
76
|
+
*/
|
|
77
|
+
copy(p: PointLike): this;
|
|
78
|
+
/**
|
|
79
|
+
* Returns an independent copy, or writes into `target` if provided. See {@link pointLerp} for the
|
|
80
|
+
* underlying free function.
|
|
81
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
82
|
+
* @returns the copy
|
|
83
|
+
*/
|
|
84
|
+
clone(target?: Point): Point;
|
|
85
|
+
/**
|
|
86
|
+
* True when `p` has the same coordinates.
|
|
87
|
+
* @param p - point to compare against
|
|
88
|
+
* @returns true when all coordinates are equal
|
|
89
|
+
*/
|
|
90
|
+
equals(p: PointLike): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Distance to `p`. See {@link pointDistance} for the underlying free function.
|
|
93
|
+
* @param p - target point
|
|
94
|
+
* @returns the distance
|
|
95
|
+
*/
|
|
96
|
+
distanceTo(p: PointLike): number;
|
|
97
|
+
/**
|
|
98
|
+
* Interpolate toward `p` by `t`. See {@link pointLerp} for the underlying free function.
|
|
99
|
+
* @param p - target point
|
|
100
|
+
* @param t - interpolation factor (0 = this, 1 = p)
|
|
101
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
102
|
+
* @returns the resulting point
|
|
103
|
+
*/
|
|
104
|
+
lerp(p: PointLike, t: number, target?: Point): Point;
|
|
105
|
+
/**
|
|
106
|
+
* Rotate by `angle` radians around `origin`. See {@link pointRotate} for the underlying free
|
|
107
|
+
* function.
|
|
108
|
+
* @param angle - radians to rotate (positive = clockwise, y-down)
|
|
109
|
+
* @param origin - rotation pivot point
|
|
110
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
111
|
+
* @returns the resulting point
|
|
112
|
+
*/
|
|
113
|
+
rotate(angle: number, origin: Point2Like, target?: Point): Point;
|
|
114
|
+
/**
|
|
115
|
+
* Shift by `length` along `angle` radians. See {@link pointShift} for the underlying free function.
|
|
116
|
+
* @param length - distance to shift
|
|
117
|
+
* @param angle - direction in radians
|
|
118
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
119
|
+
* @returns the resulting point
|
|
120
|
+
*/
|
|
121
|
+
shift(length: number, angle: number, target?: Point): Point;
|
|
122
|
+
/**
|
|
123
|
+
* Angle (radians) at this point between rays to `a` and `b`. See {@link pointAngleBetween} for
|
|
124
|
+
* the underlying free function.
|
|
125
|
+
* @param a - first ray endpoint
|
|
126
|
+
* @param b - second ray endpoint
|
|
127
|
+
* @returns the signed angle in radians (positive = clockwise, y-down)
|
|
128
|
+
*/
|
|
129
|
+
angleBetween(a: Point2Like, b: Point2Like): number;
|
|
130
|
+
/**
|
|
131
|
+
* Add a scalar (broadcast to x/y) or another point. See {@link pointAdd} for the underlying free
|
|
132
|
+
* function.
|
|
133
|
+
* @param b - scalar or point to add
|
|
134
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
135
|
+
* @returns the resulting point
|
|
136
|
+
*/
|
|
137
|
+
add(b: number | PointLike, target?: Point): Point;
|
|
138
|
+
/**
|
|
139
|
+
* Multiply by a scalar (x/y) or componentwise by another point. See {@link pointMultiply} for
|
|
140
|
+
* the underlying free function.
|
|
141
|
+
* @param b - scalar or point factor
|
|
142
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
143
|
+
* @returns the resulting point
|
|
144
|
+
*/
|
|
145
|
+
multiply(b: number | PointLike, target?: Point): Point;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Euclidean distance between two points (XY plane).
|
|
149
|
+
* @param a first point
|
|
150
|
+
* @param b second point
|
|
151
|
+
* @returns the distance
|
|
152
|
+
*/
|
|
153
|
+
export declare function pointDistance(a: PointLike, b: PointLike): number;
|
|
154
|
+
/**
|
|
155
|
+
* Linear interpolation from `a` to `b` by `t`.
|
|
156
|
+
* @param a start point
|
|
157
|
+
* @param b end point
|
|
158
|
+
* @param t factor (0 = a, 1 = b)
|
|
159
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
160
|
+
* @returns the interpolated point
|
|
161
|
+
*/
|
|
162
|
+
export declare function pointLerp(a: PointLike, b: PointLike, t: number, target?: Point): Point;
|
|
163
|
+
/**
|
|
164
|
+
* Adds a scalar (broadcast to x/y) or another point (componentwise) to `a`.
|
|
165
|
+
* @param a base point
|
|
166
|
+
* @param b scalar or point to add
|
|
167
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
168
|
+
* @returns the sum
|
|
169
|
+
*/
|
|
170
|
+
export declare function pointAdd(a: PointLike, b: number | PointLike, target?: Point): Point;
|
|
171
|
+
/**
|
|
172
|
+
* Multiplies `a` by a scalar (x/y) or by another point (componentwise).
|
|
173
|
+
* @param a base point
|
|
174
|
+
* @param b scalar or point factor
|
|
175
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
176
|
+
* @returns the product
|
|
177
|
+
*/
|
|
178
|
+
export declare function pointMultiply(a: PointLike, b: number | PointLike, target?: Point): Point;
|
|
179
|
+
/**
|
|
180
|
+
* Rotates `point` by `angle` radians around `origin` in the XY plane.
|
|
181
|
+
* @param point point to rotate
|
|
182
|
+
* @param angle radians (positive = clockwise, y-down)
|
|
183
|
+
* @param origin rotation pivot point
|
|
184
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
185
|
+
* @returns the rotated point, preserving the input depth (`z`)
|
|
186
|
+
*/
|
|
187
|
+
export declare function pointRotate(point: PointLike, angle: number, origin: Point2Like, target?: Point): Point;
|
|
188
|
+
/**
|
|
189
|
+
* Shifts `point` by `length` along `angle` radians.
|
|
190
|
+
* @param point origin
|
|
191
|
+
* @param length distance
|
|
192
|
+
* @param angle direction in radians
|
|
193
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
194
|
+
* @returns the shifted point
|
|
195
|
+
*/
|
|
196
|
+
export declare function pointShift(point: PointLike, length: number, angle: number, target?: Point): Point;
|
|
197
|
+
/**
|
|
198
|
+
* Angle (radians) at `center` between the rays to `a` and `b`. The result is signed — positive means
|
|
199
|
+
* clockwise (screen y-down convention), matching the winding used by {@link pointRotate}.
|
|
200
|
+
* @param center vertex
|
|
201
|
+
* @param a first ray endpoint
|
|
202
|
+
* @param b second ray endpoint
|
|
203
|
+
* @returns signed angle in radians (positive = clockwise, y-down)
|
|
204
|
+
*/
|
|
205
|
+
export declare function pointAngleBetween(center: Point2Like, a: Point2Like, b: Point2Like): number;
|
|
206
|
+
//# sourceMappingURL=point.d.ts.map
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { fromPolar } from './angles.js';
|
|
2
|
+
/**
|
|
3
|
+
* Mutable point/vector — the low-level building block other primitives and the point free functions
|
|
4
|
+
* write into. `width`/`height` alias `x`/`y` so a point used as a size reads naturally.
|
|
5
|
+
*/
|
|
6
|
+
export class Point {
|
|
7
|
+
/** Marker so callers can narrow the type at runtime. */
|
|
8
|
+
isPoint = true;
|
|
9
|
+
/** Horizontal coordinate. */
|
|
10
|
+
x;
|
|
11
|
+
/** Vertical coordinate. */
|
|
12
|
+
y;
|
|
13
|
+
/** Depth coordinate. */
|
|
14
|
+
z;
|
|
15
|
+
/**
|
|
16
|
+
* @param x - horizontal coordinate (default 0)
|
|
17
|
+
* @param y - vertical coordinate (default 0)
|
|
18
|
+
* @param z - depth coordinate (default 0)
|
|
19
|
+
*/
|
|
20
|
+
constructor(x = 0, y = 0, z = 0) {
|
|
21
|
+
this.x = x;
|
|
22
|
+
this.y = y;
|
|
23
|
+
this.z = z;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Alias of {@link Point.x}.
|
|
27
|
+
* @returns the x coordinate
|
|
28
|
+
*/
|
|
29
|
+
get width() {
|
|
30
|
+
return this.x;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Alias of {@link Point.y}.
|
|
34
|
+
* @returns the y coordinate
|
|
35
|
+
*/
|
|
36
|
+
get height() {
|
|
37
|
+
return this.y;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Sets coordinates in place.
|
|
41
|
+
* @param x - horizontal coordinate
|
|
42
|
+
* @param y - vertical coordinate
|
|
43
|
+
* @param z - depth coordinate (defaults to the current z)
|
|
44
|
+
* @returns this
|
|
45
|
+
*/
|
|
46
|
+
set(x, y, z = this.z) {
|
|
47
|
+
this.x = x;
|
|
48
|
+
this.y = y;
|
|
49
|
+
this.z = z;
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Copies from another point in place.
|
|
54
|
+
* @param p - source point to copy coordinates from
|
|
55
|
+
* @returns this
|
|
56
|
+
*/
|
|
57
|
+
copy(p) {
|
|
58
|
+
return this.set(p.x, p.y, p.z ?? 0);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Returns an independent copy, or writes into `target` if provided. See {@link pointLerp} for the
|
|
62
|
+
* underlying free function.
|
|
63
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
64
|
+
* @returns the copy
|
|
65
|
+
*/
|
|
66
|
+
clone(target) {
|
|
67
|
+
return (target ?? new Point()).set(this.x, this.y, this.z);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* True when `p` has the same coordinates.
|
|
71
|
+
* @param p - point to compare against
|
|
72
|
+
* @returns true when all coordinates are equal
|
|
73
|
+
*/
|
|
74
|
+
equals(p) {
|
|
75
|
+
return this.x === p.x && this.y === p.y && this.z === (p.z ?? 0);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Distance to `p`. See {@link pointDistance} for the underlying free function.
|
|
79
|
+
* @param p - target point
|
|
80
|
+
* @returns the distance
|
|
81
|
+
*/
|
|
82
|
+
distanceTo(p) {
|
|
83
|
+
return pointDistance(this, p);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Interpolate toward `p` by `t`. See {@link pointLerp} for the underlying free function.
|
|
87
|
+
* @param p - target point
|
|
88
|
+
* @param t - interpolation factor (0 = this, 1 = p)
|
|
89
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
90
|
+
* @returns the resulting point
|
|
91
|
+
*/
|
|
92
|
+
lerp(p, t, target) {
|
|
93
|
+
return pointLerp(this, p, t, target);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Rotate by `angle` radians around `origin`. See {@link pointRotate} for the underlying free
|
|
97
|
+
* function.
|
|
98
|
+
* @param angle - radians to rotate (positive = clockwise, y-down)
|
|
99
|
+
* @param origin - rotation pivot point
|
|
100
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
101
|
+
* @returns the resulting point
|
|
102
|
+
*/
|
|
103
|
+
rotate(angle, origin, target) {
|
|
104
|
+
return pointRotate(this, angle, origin, target);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Shift by `length` along `angle` radians. See {@link pointShift} for the underlying free function.
|
|
108
|
+
* @param length - distance to shift
|
|
109
|
+
* @param angle - direction in radians
|
|
110
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
111
|
+
* @returns the resulting point
|
|
112
|
+
*/
|
|
113
|
+
shift(length, angle, target) {
|
|
114
|
+
return pointShift(this, length, angle, target);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Angle (radians) at this point between rays to `a` and `b`. See {@link pointAngleBetween} for
|
|
118
|
+
* the underlying free function.
|
|
119
|
+
* @param a - first ray endpoint
|
|
120
|
+
* @param b - second ray endpoint
|
|
121
|
+
* @returns the signed angle in radians (positive = clockwise, y-down)
|
|
122
|
+
*/
|
|
123
|
+
angleBetween(a, b) {
|
|
124
|
+
return pointAngleBetween(this, a, b);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Add a scalar (broadcast to x/y) or another point. See {@link pointAdd} for the underlying free
|
|
128
|
+
* function.
|
|
129
|
+
* @param b - scalar or point to add
|
|
130
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
131
|
+
* @returns the resulting point
|
|
132
|
+
*/
|
|
133
|
+
add(b, target) {
|
|
134
|
+
return pointAdd(this, b, target);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Multiply by a scalar (x/y) or componentwise by another point. See {@link pointMultiply} for
|
|
138
|
+
* the underlying free function.
|
|
139
|
+
* @param b - scalar or point factor
|
|
140
|
+
* @param target - optional point to write into; defaults to a new {@link Point}
|
|
141
|
+
* @returns the resulting point
|
|
142
|
+
*/
|
|
143
|
+
multiply(b, target) {
|
|
144
|
+
return pointMultiply(this, b, target);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Euclidean distance between two points (XY plane).
|
|
149
|
+
* @param a first point
|
|
150
|
+
* @param b second point
|
|
151
|
+
* @returns the distance
|
|
152
|
+
*/
|
|
153
|
+
export function pointDistance(a, b) {
|
|
154
|
+
const dx = a.x - b.x;
|
|
155
|
+
const dy = a.y - b.y;
|
|
156
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Linear interpolation from `a` to `b` by `t`.
|
|
160
|
+
* @param a start point
|
|
161
|
+
* @param b end point
|
|
162
|
+
* @param t factor (0 = a, 1 = b)
|
|
163
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
164
|
+
* @returns the interpolated point
|
|
165
|
+
*/
|
|
166
|
+
export function pointLerp(a, b, t, target) {
|
|
167
|
+
const az = a.z ?? 0;
|
|
168
|
+
const bz = b.z ?? 0;
|
|
169
|
+
return (target ?? new Point()).set(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, az + (bz - az) * t);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Adds a scalar (broadcast to x/y) or another point (componentwise) to `a`.
|
|
173
|
+
* @param a base point
|
|
174
|
+
* @param b scalar or point to add
|
|
175
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
176
|
+
* @returns the sum
|
|
177
|
+
*/
|
|
178
|
+
export function pointAdd(a, b, target) {
|
|
179
|
+
const isNum = typeof b === 'number';
|
|
180
|
+
const bx = isNum ? b : b.x;
|
|
181
|
+
const by = isNum ? b : b.y;
|
|
182
|
+
const bz = isNum ? 0 : (b.z ?? 0);
|
|
183
|
+
return (target ?? new Point()).set(a.x + bx, a.y + by, (a.z ?? 0) + bz);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Multiplies `a` by a scalar (x/y) or by another point (componentwise).
|
|
187
|
+
* @param a base point
|
|
188
|
+
* @param b scalar or point factor
|
|
189
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
190
|
+
* @returns the product
|
|
191
|
+
*/
|
|
192
|
+
export function pointMultiply(a, b, target) {
|
|
193
|
+
const isNum = typeof b === 'number';
|
|
194
|
+
const bx = isNum ? b : b.x;
|
|
195
|
+
const by = isNum ? b : b.y;
|
|
196
|
+
const bz = isNum ? 1 : (b.z ?? 1);
|
|
197
|
+
return (target ?? new Point()).set(a.x * bx, a.y * by, (a.z ?? 0) * bz);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Rotates `point` by `angle` radians around `origin` in the XY plane.
|
|
201
|
+
* @param point point to rotate
|
|
202
|
+
* @param angle radians (positive = clockwise, y-down)
|
|
203
|
+
* @param origin rotation pivot point
|
|
204
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
205
|
+
* @returns the rotated point, preserving the input depth (`z`)
|
|
206
|
+
*/
|
|
207
|
+
export function pointRotate(point, angle, origin, target) {
|
|
208
|
+
const cos = Math.cos(angle);
|
|
209
|
+
const sin = Math.sin(angle);
|
|
210
|
+
const dx = point.x - origin.x;
|
|
211
|
+
const dy = point.y - origin.y;
|
|
212
|
+
return (target ?? new Point()).set(origin.x + dx * cos - dy * sin, origin.y + dx * sin + dy * cos, point.z ?? 0);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Shifts `point` by `length` along `angle` radians.
|
|
216
|
+
* @param point origin
|
|
217
|
+
* @param length distance
|
|
218
|
+
* @param angle direction in radians
|
|
219
|
+
* @param target optional point to write into; defaults to a new {@link Point}
|
|
220
|
+
* @returns the shifted point
|
|
221
|
+
*/
|
|
222
|
+
export function pointShift(point, length, angle, target) {
|
|
223
|
+
return pointAdd(point, fromPolar(length, angle), target);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Angle (radians) at `center` between the rays to `a` and `b`. The result is signed — positive means
|
|
227
|
+
* clockwise (screen y-down convention), matching the winding used by {@link pointRotate}.
|
|
228
|
+
* @param center vertex
|
|
229
|
+
* @param a first ray endpoint
|
|
230
|
+
* @param b second ray endpoint
|
|
231
|
+
* @returns signed angle in radians (positive = clockwise, y-down)
|
|
232
|
+
*/
|
|
233
|
+
export function pointAngleBetween(center, a, b) {
|
|
234
|
+
const aa = Math.atan2(a.y - center.y, a.x - center.x);
|
|
235
|
+
const bb = Math.atan2(b.y - center.y, b.x - center.x);
|
|
236
|
+
return bb - aa;
|
|
237
|
+
}
|