@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/rect.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { Box } from './box.js';
|
|
2
|
+
import { intersectLineRect } from './intersections.js';
|
|
3
|
+
import { Point } from './point.js';
|
|
4
|
+
import { Polygon } from './polygon.js';
|
|
5
|
+
const rot = (r) => r.rotation ?? 0;
|
|
6
|
+
const elev = (r) => r.elevation ?? 0;
|
|
7
|
+
/**
|
|
8
|
+
* A rectangle that may be rotated about its center (center + size + rotation + elevation; the
|
|
9
|
+
* unrotated box is not exposed). Immutable value object: readonly getters; transforms return new
|
|
10
|
+
* unless a method `target` is passed. Rotation in radians, positive = clockwise (y-down).
|
|
11
|
+
*/
|
|
12
|
+
export class Rect {
|
|
13
|
+
/** Marker so callers can narrow the type at runtime. */
|
|
14
|
+
isRect = true;
|
|
15
|
+
/** Internal mutable backing for the center point. */
|
|
16
|
+
_center = new Point();
|
|
17
|
+
/** Internal mutable backing for the size vector (x = width, y = height). */
|
|
18
|
+
_size = new Point();
|
|
19
|
+
/** Internal mutable rotation in radians. */
|
|
20
|
+
_rotation = 0;
|
|
21
|
+
/** Internal mutable elevation (z). */
|
|
22
|
+
_elevation = 0;
|
|
23
|
+
/**
|
|
24
|
+
* Constructs a {@link Rect} from center, size, rotation, and elevation.
|
|
25
|
+
* @param center - center point `{ x, y }`
|
|
26
|
+
* @param size - dimensions `{ x: width, y: height }`
|
|
27
|
+
* @param rotation - rotation in radians, positive = clockwise (y-down); defaults to 0
|
|
28
|
+
* @param elevation - planar elevation (z); defaults to 0
|
|
29
|
+
*/
|
|
30
|
+
constructor(center, size, rotation = 0, elevation = 0) {
|
|
31
|
+
this.set(center.x, center.y, size.x, size.y, rotation, elevation);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Constructs a {@link Rect} from a min/max corner pair.
|
|
35
|
+
* @param min - minimum (top-left) corner `{ x, y }`
|
|
36
|
+
* @param max - maximum (bottom-right) corner `{ x, y }`
|
|
37
|
+
* @param rotation - rotation in radians; defaults to 0
|
|
38
|
+
* @param elevation - planar elevation (z); defaults to 0
|
|
39
|
+
* @returns a new {@link Rect} whose center is the midpoint of min/max
|
|
40
|
+
*/
|
|
41
|
+
static fromMinMax(min, max, rotation = 0, elevation = 0) {
|
|
42
|
+
return new Rect({ x: (min.x + max.x) / 2, y: (min.y + max.y) / 2 }, { x: Math.abs(max.x - min.x), y: Math.abs(max.y - min.y) }, rotation, elevation);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The center point of this rect.
|
|
46
|
+
* @returns a readonly `{ x, y }` view of the center
|
|
47
|
+
*/
|
|
48
|
+
get center() {
|
|
49
|
+
return this._center;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The size (width/height) of this rect. `width` aliases `x`; `height` aliases `y`.
|
|
53
|
+
* @returns a readonly point view with `width` and `height` accessors
|
|
54
|
+
*/
|
|
55
|
+
get size() {
|
|
56
|
+
return this._size;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Rotation in radians (positive = clockwise, y-down).
|
|
60
|
+
* @returns the current rotation
|
|
61
|
+
*/
|
|
62
|
+
get rotation() {
|
|
63
|
+
return this._rotation;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Planar elevation (z coordinate of the rect plane).
|
|
67
|
+
* @returns the current elevation
|
|
68
|
+
*/
|
|
69
|
+
get elevation() {
|
|
70
|
+
return this._elevation;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns the four corners of this rect, rotated about its center.
|
|
74
|
+
* @returns array of four {@link Point} instances: top-left, top-right, bottom-right, bottom-left
|
|
75
|
+
*/
|
|
76
|
+
corners() {
|
|
77
|
+
return rectCorners(this);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The axis-aligned bounding box that contains this (possibly rotated) rect.
|
|
81
|
+
* @returns a {@link Box} representing the AABB
|
|
82
|
+
*/
|
|
83
|
+
get bounds() {
|
|
84
|
+
return rectBounds(this);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Converts this rect to a triangulated {@link Polygon}, embedding the elevation as `z`.
|
|
88
|
+
* @returns a {@link Polygon} with four vertices and two triangle indices
|
|
89
|
+
*/
|
|
90
|
+
toPolygon() {
|
|
91
|
+
return rectToPolygon(this);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Returns whether a point lies inside this (possibly rotated) rect.
|
|
95
|
+
* @param p - the point to test `{ x, y }`
|
|
96
|
+
* @returns `true` if `p` is inside or on the boundary of this rect
|
|
97
|
+
*/
|
|
98
|
+
containsPoint(p) {
|
|
99
|
+
return rectContainsPoint(this, p);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Returns a new rect translated by `offset`. If `target` is provided, writes into `target`
|
|
103
|
+
* and returns it instead of allocating.
|
|
104
|
+
* @param offset - translation vector `{ x, y }`
|
|
105
|
+
* @param target - optional rect to mutate in place
|
|
106
|
+
* @returns the translated rect
|
|
107
|
+
*/
|
|
108
|
+
translate(offset, target) {
|
|
109
|
+
return rectTranslate(this, offset, target);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Returns a new rect with rotation increased by `angle`. If `target` is provided, writes into
|
|
113
|
+
* `target` and returns it instead of allocating.
|
|
114
|
+
* @param angle - angle to add in radians
|
|
115
|
+
* @param target - optional rect to mutate in place
|
|
116
|
+
* @returns the rotated rect
|
|
117
|
+
*/
|
|
118
|
+
rotate(angle, target) {
|
|
119
|
+
return rectRotate(this, angle, target);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Returns a new rect scaled by `factor` about `origin`: the center moves toward/away from `origin`
|
|
123
|
+
* by `factor` and the size is multiplied by `factor`; rotation and elevation are preserved.
|
|
124
|
+
* If `target` is provided, writes into it instead of allocating.
|
|
125
|
+
* @param factor - uniform scale or per-axis `{ x, y }` scale
|
|
126
|
+
* @param origin - the fixed point of the scaling; defaults to this rect's center
|
|
127
|
+
* @param target - optional rect to mutate in place
|
|
128
|
+
* @returns the scaled rect
|
|
129
|
+
*/
|
|
130
|
+
scale(factor, origin = this._center, target) {
|
|
131
|
+
return rectScale(this, factor, origin, target);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Points where `line` crosses this rect. Delegates to {@link intersectLineRect}.
|
|
135
|
+
* @param line - the segment
|
|
136
|
+
* @returns the intersection points
|
|
137
|
+
*/
|
|
138
|
+
intersectLine(line) {
|
|
139
|
+
return intersectLineRect(line, this);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Returns a copy of this rect. If `target` is provided, writes into `target` and returns it.
|
|
143
|
+
* @param target - optional rect to mutate in place
|
|
144
|
+
* @returns the cloned rect
|
|
145
|
+
*/
|
|
146
|
+
clone(target) {
|
|
147
|
+
return (target ?? new Rect(this._center, this._size)).set(this._center.x, this._center.y, this._size.x, this._size.y, this._rotation, this._elevation);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Mutates this rect in place (center, size, rotation, elevation). Prefer the immutable
|
|
151
|
+
* transforms; use this (or a transform `target`) only for hot-path reuse.
|
|
152
|
+
* @param cx - center x
|
|
153
|
+
* @param cy - center y
|
|
154
|
+
* @param sx - size x (width)
|
|
155
|
+
* @param sy - size y (height)
|
|
156
|
+
* @param rotation - rotation in radians
|
|
157
|
+
* @param elevation - planar elevation (z)
|
|
158
|
+
* @returns `this` for chaining
|
|
159
|
+
*/
|
|
160
|
+
set(cx, cy, sx, sy, rotation, elevation) {
|
|
161
|
+
this._center.set(cx, cy);
|
|
162
|
+
this._size.set(sx, sy);
|
|
163
|
+
this._rotation = rotation;
|
|
164
|
+
this._elevation = elevation;
|
|
165
|
+
return this;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Returns the four corners of a {@link RectLike}, rotated about its center.
|
|
170
|
+
* Order: top-left, top-right, bottom-right, bottom-left.
|
|
171
|
+
* @param r - the rect-like to compute corners for
|
|
172
|
+
* @returns array of four {@link Point} instances
|
|
173
|
+
*/
|
|
174
|
+
export function rectCorners(r) {
|
|
175
|
+
const hx = r.size.x / 2;
|
|
176
|
+
const hy = r.size.y / 2;
|
|
177
|
+
const a = rot(r);
|
|
178
|
+
const cos = Math.cos(a);
|
|
179
|
+
const sin = Math.sin(a);
|
|
180
|
+
const cx = r.center.x;
|
|
181
|
+
const cy = r.center.y;
|
|
182
|
+
const corner = (ox, oy) => new Point(cx + ox * cos - oy * sin, cy + ox * sin + oy * cos);
|
|
183
|
+
return [corner(-hx, -hy), corner(hx, -hy), corner(hx, hy), corner(-hx, hy)];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Returns the axis-aligned bounding box (AABB) that contains all four corners of a
|
|
187
|
+
* {@link RectLike}.
|
|
188
|
+
* @param r - the rect-like to compute bounds for
|
|
189
|
+
* @returns a {@link Box} representing the AABB
|
|
190
|
+
*/
|
|
191
|
+
export function rectBounds(r) {
|
|
192
|
+
return Box.fromUnion(...rectCorners(r).map((p) => Box.fromMinMax(p, p)));
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Converts a {@link RectLike} to a triangulated {@link Polygon} via fan-triangulation, embedding
|
|
196
|
+
* the elevation as `z` on each vertex. Rect corners form a convex quad, so
|
|
197
|
+
* {@link Polygon.fromConvexRing} is valid here.
|
|
198
|
+
* @param r - the rect-like to convert
|
|
199
|
+
* @returns a {@link Polygon} with four vertices and two triangle indices
|
|
200
|
+
*/
|
|
201
|
+
export function rectToPolygon(r) {
|
|
202
|
+
return Polygon.fromConvexRing(rectCorners(r), elev(r));
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Returns whether a point lies inside a (possibly rotated) {@link RectLike} by transforming the
|
|
206
|
+
* point into the rect's local axis-aligned space.
|
|
207
|
+
* @param r - the rect-like to test against
|
|
208
|
+
* @param p - the point to test `{ x, y }`
|
|
209
|
+
* @returns `true` if `p` is inside or on the boundary of `r`
|
|
210
|
+
*/
|
|
211
|
+
export function rectContainsPoint(r, p) {
|
|
212
|
+
const a = -rot(r);
|
|
213
|
+
const cos = Math.cos(a);
|
|
214
|
+
const sin = Math.sin(a);
|
|
215
|
+
const dx = p.x - r.center.x;
|
|
216
|
+
const dy = p.y - r.center.y;
|
|
217
|
+
const lx = dx * cos - dy * sin;
|
|
218
|
+
const ly = dx * sin + dy * cos;
|
|
219
|
+
return Math.abs(lx) <= r.size.x / 2 && Math.abs(ly) <= r.size.y / 2;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Returns a new rect translated by `offset`. If `target` is provided, writes into `target` and
|
|
223
|
+
* returns it instead of allocating a new instance.
|
|
224
|
+
* @param r - the source rect-like
|
|
225
|
+
* @param offset - translation vector `{ x, y }`
|
|
226
|
+
* @param target - optional {@link Rect} to mutate in place
|
|
227
|
+
* @returns the translated rect
|
|
228
|
+
*/
|
|
229
|
+
export function rectTranslate(r, offset, target) {
|
|
230
|
+
const cx = r.center.x + offset.x;
|
|
231
|
+
const cy = r.center.y + offset.y;
|
|
232
|
+
return target
|
|
233
|
+
? target.set(cx, cy, r.size.x, r.size.y, rot(r), elev(r))
|
|
234
|
+
: new Rect({ x: cx, y: cy }, r.size, rot(r), elev(r));
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Returns a new rect with rotation increased by `angle`. If `target` is provided, writes into
|
|
238
|
+
* `target` and returns it instead of allocating a new instance.
|
|
239
|
+
* @param r - the source rect-like
|
|
240
|
+
* @param angle - angle to add in radians
|
|
241
|
+
* @param target - optional {@link Rect} to mutate in place
|
|
242
|
+
* @returns the rotated rect
|
|
243
|
+
*/
|
|
244
|
+
export function rectRotate(r, angle, target) {
|
|
245
|
+
return target
|
|
246
|
+
? target.set(r.center.x, r.center.y, r.size.x, r.size.y, rot(r) + angle, elev(r))
|
|
247
|
+
: new Rect(r.center, r.size, rot(r) + angle, elev(r));
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Returns a new rect scaled by `factor` about `origin`: the center moves toward/away from `origin`
|
|
251
|
+
* by `factor` and the size is multiplied by `factor`; rotation and elevation are preserved. If
|
|
252
|
+
* `target` is provided, writes into it instead of allocating a new instance.
|
|
253
|
+
* @param r - the source rect-like
|
|
254
|
+
* @param factor - uniform scale or per-axis `{ x, y }` scale
|
|
255
|
+
* @param origin - the fixed point of the scaling; defaults to `r`'s center
|
|
256
|
+
* @param target - optional {@link Rect} to mutate in place
|
|
257
|
+
* @returns the scaled rect
|
|
258
|
+
*/
|
|
259
|
+
export function rectScale(r, factor, origin = r.center, target) {
|
|
260
|
+
const fx = typeof factor === 'number' ? factor : factor.x;
|
|
261
|
+
const fy = typeof factor === 'number' ? factor : factor.y;
|
|
262
|
+
const cx = origin.x + (r.center.x - origin.x) * fx;
|
|
263
|
+
const cy = origin.y + (r.center.y - origin.y) * fy;
|
|
264
|
+
const sx = r.size.x * fx;
|
|
265
|
+
const sy = r.size.y * fy;
|
|
266
|
+
return target
|
|
267
|
+
? target.set(cx, cy, sx, sy, rot(r), elev(r))
|
|
268
|
+
: new Rect({ x: cx, y: cy }, { x: sx, y: sy }, rot(r), elev(r));
|
|
269
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@expofp/geometry",
|
|
3
|
+
"version": "3.8.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "ExpoFP SDK internal: shared geometry primitives",
|
|
6
|
+
"homepage": "https://developer.expofp.com/",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
"./package.json": "./package.json",
|
|
16
|
+
".": {
|
|
17
|
+
"@expofp/source": "./src/index.ts",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"tslib": "^2.3.0"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"!**/*.tsbuildinfo",
|
|
29
|
+
"!**/*.map"
|
|
30
|
+
]
|
|
31
|
+
}
|