@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/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# @expofp/geometry
|
|
2
|
+
|
|
3
|
+
Zero-dependency **2.5D** geometry primitives for the ExpoFP SDK: `Point`, `Line`, `Box`, `Rect`,
|
|
4
|
+
`Polygon`, `Mesh`, plus the free functions that operate on them. No runtime dependencies (not even
|
|
5
|
+
`three`).
|
|
6
|
+
|
|
7
|
+
**"2.5D"** means: a 3D `Point` vector, but the **shapes are 2D geometry positioned in 3D by an
|
|
8
|
+
`elevation`** — a flat shape at a given height. All spatial math (distance, angle, area, containment,
|
|
9
|
+
intersection) is computed in the **xy plane**; `elevation` positions the shape and gates intersections
|
|
10
|
+
(see Coordinate convention). The one genuinely-3D shape is `Mesh` (a triangle mesh whose vertices carry
|
|
11
|
+
real `z`), for renderer geometry that isn't flat.
|
|
12
|
+
|
|
13
|
+
## Primitives
|
|
14
|
+
|
|
15
|
+
| Type | What it is |
|
|
16
|
+
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| `Point` | A mutable 3D vector (`x`, `y`, `z`). `width`/`height` alias `x`/`y` so a point can double as a size. The low-level building block the shapes write into. |
|
|
18
|
+
| `Line` | A segment between two **2D** points (`p0`, `p1`) at a single `elevation` (default 0). |
|
|
19
|
+
| `Box` | An **axis-aligned** rectangle (min/max corners, cached center/size). 2D, **infinite in z** (no elevation) — for bounding boxes and screen sizes. A `Box` structurally satisfies `RectLike`, so anything taking a `RectLike` accepts a `Box` (an unrotated, elevation-less rect that intersects at any height). |
|
|
20
|
+
| `Rect` | A rectangle that **may be rotated**: center + size + `rotation` + `elevation` (both default 0). For oriented rectangular shapes at a given height. |
|
|
21
|
+
| `Polygon` | A **planar** triangulated polygon: 2D `vertices` + triangle `indices` + an `elevation`, with a cached bounding `Box`. Has `containsPoint`/`area` (well-defined because it's flat). Build a convex one from an ordered ring with `Polygon.fromConvexRing`. |
|
|
22
|
+
| `Mesh` | A **3D** triangle mesh: 3D `vertices` + `indices`, with a cached (xy) bounding `Box`. Transforms only — **no `containsPoint`/`area`** (undefined for a non-planar mesh). For renderer geometry that isn't flat. |
|
|
23
|
+
|
|
24
|
+
`Shape = Box | Rect | Polygon | Mesh`, discriminated by `is*` brand fields and narrowed with the exported
|
|
25
|
+
guards `isBox` / `isRect` / `isPolygon` / `isMesh`.
|
|
26
|
+
|
|
27
|
+
**Coordinate convention:** `x` increases left→right, `y` increases top→bottom (screen, y-down). Rotation
|
|
28
|
+
is in **radians, positive = clockwise**, uniform across every primitive and helper (`Rect`, `Polygon`,
|
|
29
|
+
`Mesh`, and the `point*`/`line*` angle functions). `elevation` (and `Point.z`) is the out-of-plane height;
|
|
30
|
+
positive is toward the viewer. **Spatial operations are computed in the xy plane** — distances, angles,
|
|
31
|
+
areas, and `containsPoint` ignore `z`/`elevation`. **Intersection** tests (`lineIntersection`,
|
|
32
|
+
`intersectLineRect`) compare `elevation` only when **both** operands define it (an `undefined` elevation —
|
|
33
|
+
a `Box`, or a bare `LineLike` — means "any height" and always intersects); when both are defined they
|
|
34
|
+
intersect only within an `elevationTolerance` (default `1e-3`), and the result point's `z` is the shared
|
|
35
|
+
elevation.
|
|
36
|
+
|
|
37
|
+
## Design decisions
|
|
38
|
+
|
|
39
|
+
### 2.5D: 2D shapes + elevation, one 3D `Mesh`
|
|
40
|
+
|
|
41
|
+
`Point` is the only inherently-3D type (a vector). Every _shape_ is 2D positioned by `elevation` (`Box`
|
|
42
|
+
has none — it is infinite in z). `Polygon` is planar, so its `containsPoint`/`area` are exact; genuinely
|
|
43
|
+
3D geometry uses `Mesh`, which deliberately omits those operations (a "contains point" on a non-planar
|
|
44
|
+
mesh would only be an xy-silhouette test).
|
|
45
|
+
|
|
46
|
+
### Immutable by default, with `set` mutators
|
|
47
|
+
|
|
48
|
+
Shapes are immutable value objects: getters return readonly views and every transform returns a **new**
|
|
49
|
+
instance, so shared geometry (e.g. a booth's rect) can't be mutated out from under another reader. Each
|
|
50
|
+
primitive (`Point`, `Line`, `Box`, `Rect`, `Polygon`, `Mesh`) exposes a single public, docs-flagged
|
|
51
|
+
`set(...)` mutator — the escape hatch the transforms write through, and the way to update fields like
|
|
52
|
+
`elevation` in place.
|
|
53
|
+
|
|
54
|
+
### Opt-in mutation via a `target` parameter
|
|
55
|
+
|
|
56
|
+
Because immutable transforms allocate, every transform also accepts an optional **`target`** to write the
|
|
57
|
+
result into instead of allocating — the same idiom three.js uses, but inverted so the default is pure:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const moved = box.translate(offset); // new Box (immutable default)
|
|
61
|
+
box.translate(offset, box); // writes into box itself (opt-in mutation)
|
|
62
|
+
box.translate(offset, scratch); // reuse a scratch Box in a hot loop
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Free functions read all inputs before writing the target, so passing the input as the target
|
|
66
|
+
(`f(x, …, x)`) is safe. Use `target` only where profiling shows allocation matters.
|
|
67
|
+
|
|
68
|
+
### Classes + free functions (the duality)
|
|
69
|
+
|
|
70
|
+
Every operation is a **pure free function** over structural `*Like` inputs, and each class exposes a
|
|
71
|
+
**thin method** that forwards to it:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
lineLength(line); // free function, accepts any { p0, p1 }
|
|
75
|
+
line.length(); // method → lineLength(this)
|
|
76
|
+
|
|
77
|
+
boxTranslate(box, offset); // free function
|
|
78
|
+
box.translate(offset); // method → boxTranslate(this, offset)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Free functions accept structural shapes (`Point2Like`, `LineLike`, `RectLike`, `MeshLike`, …), so callers
|
|
82
|
+
don't need class instances and there is no class-identity coupling. Methods give discoverability and
|
|
83
|
+
chaining.
|
|
84
|
+
|
|
85
|
+
### Transform/merge logic defined once (`Polygon` reuses `Mesh`)
|
|
86
|
+
|
|
87
|
+
`Polygon` structurally satisfies `MeshLike` (its 2D vertices satisfy `PointLike`), so the transform and
|
|
88
|
+
merge logic lives **once** on the mesh free functions — `meshTranslate` / `meshScale` / `meshRotate` /
|
|
89
|
+
`meshMerge` (plus `meshBounds`). Both `Mesh`'s and `Polygon`'s methods forward to them via the optional
|
|
90
|
+
`target`: a `Polygon` target keeps the result a `Polygon` (elevation preserved), a `Mesh` target keeps it
|
|
91
|
+
a `Mesh`.
|
|
92
|
+
|
|
93
|
+
### Structural input types
|
|
94
|
+
|
|
95
|
+
Inputs are the loose `*Like` types (`Point2Like = { x, y }`, `RectLike = { center, size, rotation?,
|
|
96
|
+
elevation? }`, `MeshLike`, `PolygonLike`, …); outputs are concrete class instances. Ops like `translate` /
|
|
97
|
+
`expand` / `scale` take `number | Point2Like` (planar shapes such as `Box`) or `number | PointLike` (the
|
|
98
|
+
3D `Point` ops) — a scalar broadcasts to both axes, a point gives per-axis values.
|
|
99
|
+
|
|
100
|
+
### Brands instead of `instanceof`
|
|
101
|
+
|
|
102
|
+
Each primitive carries a `readonly is<Name> = true` field (three.js style) so type discrimination
|
|
103
|
+
survives duplicate module copies and serialization. The `Shape` guards check the brand rather than
|
|
104
|
+
`instanceof`, and accept only a `Shape` (a stray `{ isBox: true }` object cannot masquerade as a box).
|
|
105
|
+
|
|
106
|
+
## Geographic (`geo`)
|
|
107
|
+
|
|
108
|
+
The `geo` module provides pure geodetic math on a WGS-84 sphere. It is a **separate
|
|
109
|
+
coordinate space** from the planar primitives above — do not mix them without an
|
|
110
|
+
explicit bridge.
|
|
111
|
+
|
|
112
|
+
| Symbol | Description |
|
|
113
|
+
| ----------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
114
|
+
| `LatLng` | `{ lat: number; lng: number }` — decimal degrees; lat north-positive, lng east-positive |
|
|
115
|
+
| `GeoAnchor` | `{ local: Point2Like; geo: LatLng }` — a correspondence used by the projection bridge |
|
|
116
|
+
| `haversineDistance(a, b)` | Great-circle distance in metres |
|
|
117
|
+
| `bearing(from, to)` | Initial compass bearing in degrees `[0, 360)` |
|
|
118
|
+
| `destinationPoint(from, distanceM, bearingDeg)` | Destination `LatLng` given origin, distance, and bearing |
|
|
119
|
+
| `projectLocalToGps(point, a, b)` | Maps a local `Point2Like` to `LatLng` via two anchors |
|
|
120
|
+
| `projectGpsToLocal(geo, a, b)` | Maps a `LatLng` to a local `Point2Like` via two anchors — inverse of above |
|
|
121
|
+
|
|
122
|
+
**Convention (different from the planar primitives):**
|
|
123
|
+
|
|
124
|
+
- Coordinates are in **degrees** (not radians).
|
|
125
|
+
- Bearings are **compass**: **0 = North, 90 = East, 180 = South, 270 = West**, increasing clockwise.
|
|
126
|
+
- Earth radius constant: **6 371 000 m** (WGS-84 mean radius).
|
|
127
|
+
|
|
128
|
+
This is deliberately firewalled from the planar convention (angles in radians,
|
|
129
|
+
positive-clockwise starting from the +x axis, y-down screen coordinates,
|
|
130
|
+
Euclidean distance). The only bridge is `projectLocalToGps` / `projectGpsToLocal`,
|
|
131
|
+
which takes validated `GeoAnchor` inputs — no floorplan-domain logic enters `geo`.
|
|
132
|
+
|
|
133
|
+
## Building
|
|
134
|
+
|
|
135
|
+
Run `nx build geometry` to build the library.
|
|
136
|
+
|
|
137
|
+
## Running unit tests
|
|
138
|
+
|
|
139
|
+
Run `nx test geometry` to execute the unit tests via [Vitest](https://vitest.dev/).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Box } from './lib/box.js';
|
|
2
|
+
import type { Mesh } from './lib/mesh.js';
|
|
3
|
+
import type { Polygon } from './lib/polygon.js';
|
|
4
|
+
import type { Rect } from './lib/rect.js';
|
|
5
|
+
export * from './lib/angles.js';
|
|
6
|
+
export * from './lib/box.js';
|
|
7
|
+
export * from './lib/geo.js';
|
|
8
|
+
export * from './lib/intersections.js';
|
|
9
|
+
export * from './lib/line.js';
|
|
10
|
+
export * from './lib/mesh.js';
|
|
11
|
+
export * from './lib/point.js';
|
|
12
|
+
export * from './lib/polygon.js';
|
|
13
|
+
export * from './lib/rect.js';
|
|
14
|
+
/** Any renderable geometry shape, discriminated by its `is*` brand. */
|
|
15
|
+
export type Shape = Box | Rect | Polygon | Mesh;
|
|
16
|
+
/**
|
|
17
|
+
* Narrows a {@link Shape} to {@link Box}. Accepts only shapes (not arbitrary values) so a stray
|
|
18
|
+
* `{ isBox: true }` object cannot masquerade as a box.
|
|
19
|
+
* @param shape - the shape to test
|
|
20
|
+
* @returns true when `shape` is a {@link Box}
|
|
21
|
+
*/
|
|
22
|
+
export declare function isBox(shape: Shape): shape is Box;
|
|
23
|
+
/**
|
|
24
|
+
* Narrows a {@link Shape} to {@link Rect}. Accepts only shapes (not arbitrary values).
|
|
25
|
+
* @param shape - the shape to test
|
|
26
|
+
* @returns true when `shape` is a {@link Rect}
|
|
27
|
+
*/
|
|
28
|
+
export declare function isRect(shape: Shape): shape is Rect;
|
|
29
|
+
/**
|
|
30
|
+
* Narrows a {@link Shape} to {@link Polygon}. Accepts only shapes (not arbitrary values).
|
|
31
|
+
* @param shape - the shape to test
|
|
32
|
+
* @returns true when `shape` is a {@link Polygon}
|
|
33
|
+
*/
|
|
34
|
+
export declare function isPolygon(shape: Shape): shape is Polygon;
|
|
35
|
+
/**
|
|
36
|
+
* Narrows a {@link Shape} to {@link Mesh}. Accepts only shapes (not arbitrary values).
|
|
37
|
+
* @param shape - the shape to test
|
|
38
|
+
* @returns true when `shape` is a {@link Mesh}
|
|
39
|
+
*/
|
|
40
|
+
export declare function isMesh(shape: Shape): shape is Mesh;
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export * from './lib/angles.js';
|
|
2
|
+
export * from './lib/box.js';
|
|
3
|
+
export * from './lib/geo.js';
|
|
4
|
+
export * from './lib/intersections.js';
|
|
5
|
+
export * from './lib/line.js';
|
|
6
|
+
export * from './lib/mesh.js';
|
|
7
|
+
export * from './lib/point.js';
|
|
8
|
+
export * from './lib/polygon.js';
|
|
9
|
+
export * from './lib/rect.js';
|
|
10
|
+
/**
|
|
11
|
+
* Narrows a {@link Shape} to {@link Box}. Accepts only shapes (not arbitrary values) so a stray
|
|
12
|
+
* `{ isBox: true }` object cannot masquerade as a box.
|
|
13
|
+
* @param shape - the shape to test
|
|
14
|
+
* @returns true when `shape` is a {@link Box}
|
|
15
|
+
*/
|
|
16
|
+
export function isBox(shape) {
|
|
17
|
+
return 'isBox' in shape;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Narrows a {@link Shape} to {@link Rect}. Accepts only shapes (not arbitrary values).
|
|
21
|
+
* @param shape - the shape to test
|
|
22
|
+
* @returns true when `shape` is a {@link Rect}
|
|
23
|
+
*/
|
|
24
|
+
export function isRect(shape) {
|
|
25
|
+
return 'isRect' in shape;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Narrows a {@link Shape} to {@link Polygon}. Accepts only shapes (not arbitrary values).
|
|
29
|
+
* @param shape - the shape to test
|
|
30
|
+
* @returns true when `shape` is a {@link Polygon}
|
|
31
|
+
*/
|
|
32
|
+
export function isPolygon(shape) {
|
|
33
|
+
return 'isPolygon' in shape;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Narrows a {@link Shape} to {@link Mesh}. Accepts only shapes (not arbitrary values).
|
|
37
|
+
* @param shape - the shape to test
|
|
38
|
+
* @returns true when `shape` is a {@link Mesh}
|
|
39
|
+
*/
|
|
40
|
+
export function isMesh(shape) {
|
|
41
|
+
return 'isMesh' in shape;
|
|
42
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Point2Like } from './point.js';
|
|
2
|
+
/**
|
|
3
|
+
* Converts degrees to radians.
|
|
4
|
+
* @param deg angle in degrees
|
|
5
|
+
* @returns the angle in radians
|
|
6
|
+
*/
|
|
7
|
+
export declare function degToRad(deg: number): number;
|
|
8
|
+
/**
|
|
9
|
+
* Converts radians to degrees.
|
|
10
|
+
* @param rad angle in radians
|
|
11
|
+
* @returns the angle in degrees
|
|
12
|
+
*/
|
|
13
|
+
export declare function radToDeg(rad: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Cartesian coordinates of a polar vector. Returns a `Point2Like`.
|
|
16
|
+
* @param radius distance from the origin
|
|
17
|
+
* @param angle angle in radians
|
|
18
|
+
* @returns `{ x: cos(angle)·radius, y: sin(angle)·radius }`
|
|
19
|
+
*/
|
|
20
|
+
export declare function fromPolar(radius: number, angle: number): Point2Like;
|
|
21
|
+
/**
|
|
22
|
+
* Wraps `rad` into the half-open interval `(-π, π]`. Useful for comparing headings and computing
|
|
23
|
+
* signed angular deltas regardless of how many full turns the input contains.
|
|
24
|
+
* @param rad - angle in radians (any magnitude)
|
|
25
|
+
* @returns the equivalent angle in `(-π, π]`
|
|
26
|
+
*/
|
|
27
|
+
export declare function normalizeAngle(rad: number): number;
|
|
28
|
+
/**
|
|
29
|
+
* Signed angular difference `a − b`, normalized to `(-π, π]`. Equivalent to
|
|
30
|
+
* `normalizeAngle(a - b)`. See {@link normalizeAngle}.
|
|
31
|
+
* @param a - first angle in radians
|
|
32
|
+
* @param b - second angle in radians
|
|
33
|
+
* @returns the difference `a − b` wrapped to `(-π, π]`
|
|
34
|
+
*/
|
|
35
|
+
export declare function angleDiff(a: number, b: number): number;
|
|
36
|
+
//# sourceMappingURL=angles.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts degrees to radians.
|
|
3
|
+
* @param deg angle in degrees
|
|
4
|
+
* @returns the angle in radians
|
|
5
|
+
*/
|
|
6
|
+
export function degToRad(deg) {
|
|
7
|
+
return (deg * Math.PI) / 180;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Converts radians to degrees.
|
|
11
|
+
* @param rad angle in radians
|
|
12
|
+
* @returns the angle in degrees
|
|
13
|
+
*/
|
|
14
|
+
export function radToDeg(rad) {
|
|
15
|
+
return (rad * 180) / Math.PI;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Cartesian coordinates of a polar vector. Returns a `Point2Like`.
|
|
19
|
+
* @param radius distance from the origin
|
|
20
|
+
* @param angle angle in radians
|
|
21
|
+
* @returns `{ x: cos(angle)·radius, y: sin(angle)·radius }`
|
|
22
|
+
*/
|
|
23
|
+
export function fromPolar(radius, angle) {
|
|
24
|
+
return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Wraps `rad` into the half-open interval `(-π, π]`. Useful for comparing headings and computing
|
|
28
|
+
* signed angular deltas regardless of how many full turns the input contains.
|
|
29
|
+
* @param rad - angle in radians (any magnitude)
|
|
30
|
+
* @returns the equivalent angle in `(-π, π]`
|
|
31
|
+
*/
|
|
32
|
+
export function normalizeAngle(rad) {
|
|
33
|
+
const TWO_PI = 2 * Math.PI;
|
|
34
|
+
let r = rad % TWO_PI;
|
|
35
|
+
if (r <= -Math.PI)
|
|
36
|
+
r += TWO_PI;
|
|
37
|
+
else if (r > Math.PI)
|
|
38
|
+
r -= TWO_PI;
|
|
39
|
+
return r;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Signed angular difference `a − b`, normalized to `(-π, π]`. Equivalent to
|
|
43
|
+
* `normalizeAngle(a - b)`. See {@link normalizeAngle}.
|
|
44
|
+
* @param a - first angle in radians
|
|
45
|
+
* @param b - second angle in radians
|
|
46
|
+
* @returns the difference `a − b` wrapped to `(-π, π]`
|
|
47
|
+
*/
|
|
48
|
+
export function angleDiff(a, b) {
|
|
49
|
+
return normalizeAngle(a - b);
|
|
50
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import type { LineLike } from './line.js';
|
|
2
|
+
import { Point, type Point2Like, type SizeLike } from './point.js';
|
|
3
|
+
/** Structural axis-aligned box defined by its corners. */
|
|
4
|
+
export interface BoxLike {
|
|
5
|
+
/** Top-left corner. */
|
|
6
|
+
readonly min: Point2Like;
|
|
7
|
+
/** Bottom-right corner. */
|
|
8
|
+
readonly max: Point2Like;
|
|
9
|
+
}
|
|
10
|
+
/** One animated length on an SVG geometry element — the `{ baseVal: { value } }` chain we read. */
|
|
11
|
+
interface SvgAnimatedLengthLike {
|
|
12
|
+
readonly baseVal: {
|
|
13
|
+
readonly value: number;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Structural shape of an SVG `<rect>` / `<image>` element — the subset {@link Box.fromSvg}
|
|
18
|
+
* reads. Declared structurally (rather than as `SVGRectElement | SVGImageElement`) so this
|
|
19
|
+
* package stays DOM-free; both DOM elements satisfy it.
|
|
20
|
+
*/
|
|
21
|
+
export interface SvgRectLike {
|
|
22
|
+
/** Horizontal position. */
|
|
23
|
+
readonly x: SvgAnimatedLengthLike;
|
|
24
|
+
/** Vertical position. */
|
|
25
|
+
readonly y: SvgAnimatedLengthLike;
|
|
26
|
+
/** Width. */
|
|
27
|
+
readonly width: SvgAnimatedLengthLike;
|
|
28
|
+
/** Height. */
|
|
29
|
+
readonly height: SvgAnimatedLengthLike;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Axis-aligned bounding box (AABB). Pure 2D, no elevation. Immutable value object: readonly getters;
|
|
33
|
+
* transforms return a new instance unless a method `target` is passed. For a rotated rectangle use
|
|
34
|
+
* {@link Rect}.
|
|
35
|
+
*/
|
|
36
|
+
export declare class Box {
|
|
37
|
+
/** Marker so callers can narrow the type at runtime. */
|
|
38
|
+
readonly isBox = true;
|
|
39
|
+
/** Internal mutable backing for the minimum corner. */
|
|
40
|
+
private readonly _min;
|
|
41
|
+
/** Internal mutable backing for the maximum corner. */
|
|
42
|
+
private readonly _max;
|
|
43
|
+
/** Internal mutable backing for the center point. */
|
|
44
|
+
private readonly _center;
|
|
45
|
+
/** Internal mutable backing for the size vector (x = width, y = height). */
|
|
46
|
+
private readonly _size;
|
|
47
|
+
/**
|
|
48
|
+
* Creates a box from an origin and dimensions. Negative width/height are normalized so
|
|
49
|
+
* {@link Box.min} is always the lesser corner.
|
|
50
|
+
* @param x - left edge x coordinate (default 0)
|
|
51
|
+
* @param y - top edge y coordinate (default 0)
|
|
52
|
+
* @param w - width; may be negative (default 0)
|
|
53
|
+
* @param h - height; may be negative (default 0)
|
|
54
|
+
*/
|
|
55
|
+
constructor(x?: number, y?: number, w?: number, h?: number);
|
|
56
|
+
/**
|
|
57
|
+
* The lesser corner of the box (minimum x and y).
|
|
58
|
+
* @returns a readonly `{ x, y }` view of the minimum corner
|
|
59
|
+
*/
|
|
60
|
+
get min(): Readonly<Point2Like>;
|
|
61
|
+
/**
|
|
62
|
+
* The greater corner of the box (maximum x and y).
|
|
63
|
+
* @returns a readonly `{ x, y }` view of the maximum corner
|
|
64
|
+
*/
|
|
65
|
+
get max(): Readonly<Point2Like>;
|
|
66
|
+
/**
|
|
67
|
+
* The geometric center of the box.
|
|
68
|
+
* @returns a readonly `{ x, y }` view of the center point
|
|
69
|
+
*/
|
|
70
|
+
get center(): Readonly<Point2Like>;
|
|
71
|
+
/**
|
|
72
|
+
* The size of the box. `width` aliases `x` and `height` aliases `y`.
|
|
73
|
+
* @returns a readonly size view with `x`, `y`, `width`, and `height`
|
|
74
|
+
*/
|
|
75
|
+
get size(): SizeLike;
|
|
76
|
+
/**
|
|
77
|
+
* Area of the box (`width × height`). Delegates to {@link boxArea}.
|
|
78
|
+
* @returns the area in square units
|
|
79
|
+
*/
|
|
80
|
+
get area(): number;
|
|
81
|
+
/**
|
|
82
|
+
* True when the box has zero extent in BOTH axes (collapsed to a point, or the
|
|
83
|
+
* default / `fromPoints([])` empty box). A degenerate line — extent in only one
|
|
84
|
+
* axis — is NOT considered empty. Delegates to {@link boxIsEmpty}.
|
|
85
|
+
* @returns true when `min === max` in both x and y
|
|
86
|
+
*/
|
|
87
|
+
isEmpty(): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Constructs a box from two arbitrary corners. Corners are normalized so
|
|
90
|
+
* {@link Box.min} is always the lesser corner regardless of argument order.
|
|
91
|
+
* @param min - one corner of the box
|
|
92
|
+
* @param max - the opposite corner of the box
|
|
93
|
+
* @returns a new normalized {@link Box}
|
|
94
|
+
*/
|
|
95
|
+
static fromMinMax(min: Point2Like, max: Point2Like): Box;
|
|
96
|
+
/**
|
|
97
|
+
* Constructs a box centred on `center` with the given `size`.
|
|
98
|
+
* @param center - the desired center point of the box
|
|
99
|
+
* @param size - `{ x: width, y: height }` of the box
|
|
100
|
+
* @returns a new {@link Box} centred at `center`
|
|
101
|
+
*/
|
|
102
|
+
static fromCenter(center: Point2Like, size: Point2Like): Box;
|
|
103
|
+
/**
|
|
104
|
+
* Constructs the smallest box that contains every box in `boxes`.
|
|
105
|
+
* Delegates to {@link Box.fromPoints} over each box's two corners so the
|
|
106
|
+
* min/max sweep lives in one place. Zero args returns `new Box()`.
|
|
107
|
+
* @param boxes - zero or more boxes to union
|
|
108
|
+
* @returns a new {@link Box} that is the axis-aligned union of all inputs
|
|
109
|
+
*/
|
|
110
|
+
static fromUnion(...boxes: BoxLike[]): Box;
|
|
111
|
+
/**
|
|
112
|
+
* Constructs the smallest axis-aligned box that encloses every point in `points`.
|
|
113
|
+
* An empty iterable returns a zero-size box at the origin (equivalent to `new Box()`).
|
|
114
|
+
* @param points - iterable of `{ x, y }` points
|
|
115
|
+
* @returns a new {@link Box} that is the AABB of all inputs, or a zero box when `points` is empty
|
|
116
|
+
*/
|
|
117
|
+
static fromPoints(points: Iterable<Point2Like>): Box;
|
|
118
|
+
/**
|
|
119
|
+
* Constructs a box from an SVG `<rect>` or `<image>` element's base values.
|
|
120
|
+
* @param rect - the SVG element (or any {@link SvgRectLike}) to read dimensions from
|
|
121
|
+
* @returns a new {@link Box} matching the element's position and size
|
|
122
|
+
*/
|
|
123
|
+
static fromSvg(rect: SvgRectLike): Box;
|
|
124
|
+
/**
|
|
125
|
+
* Returns true when this box overlaps `b`. Delegates to {@link boxIntersects}.
|
|
126
|
+
* @param b - the box to test intersection against
|
|
127
|
+
* @returns true when the boxes share at least one point
|
|
128
|
+
*/
|
|
129
|
+
intersects(b: BoxLike): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Returns true when this box fully contains `b`. Delegates to {@link boxContains}.
|
|
132
|
+
* @param b - the box to test containment of
|
|
133
|
+
* @returns true when `b` lies entirely within this box
|
|
134
|
+
*/
|
|
135
|
+
contains(b: BoxLike): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Returns true when `p` lies inside or on the boundary of this box.
|
|
138
|
+
* Delegates to {@link boxContainsPoint}.
|
|
139
|
+
* @param p - the point to test
|
|
140
|
+
* @returns true when `p` is within this box
|
|
141
|
+
*/
|
|
142
|
+
containsPoint(p: Point2Like): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Returns true when this box has the same corners as `b`. Delegates to {@link boxEquals}.
|
|
145
|
+
* @param b - the box to compare against
|
|
146
|
+
* @returns true when both boxes share identical min and max corners
|
|
147
|
+
*/
|
|
148
|
+
equals(b: BoxLike): boolean;
|
|
149
|
+
/**
|
|
150
|
+
* Returns the intersection of this box and `b`, or `null` when they do not overlap.
|
|
151
|
+
* Delegates to {@link boxIntersection}.
|
|
152
|
+
* @param b - the box to intersect with
|
|
153
|
+
* @param target - optional box to write the result into; defaults to a new {@link Box}
|
|
154
|
+
* @returns the intersection box, or `null` if the boxes do not overlap
|
|
155
|
+
*/
|
|
156
|
+
intersection(b: BoxLike, target?: Box): Box | null;
|
|
157
|
+
/**
|
|
158
|
+
* Returns a copy of this box shifted by `offset`. Delegates to {@link boxTranslate}.
|
|
159
|
+
* @param offset - scalar (same shift on both axes) or `{ x, y }` amount to shift by
|
|
160
|
+
* @param target - optional box to write the result into; defaults to a new {@link Box}
|
|
161
|
+
* @returns the translated box
|
|
162
|
+
*/
|
|
163
|
+
translate(offset: number | Point2Like, target?: Box): Box;
|
|
164
|
+
/**
|
|
165
|
+
* Returns a copy of this box expanded outward by `amount` on all sides.
|
|
166
|
+
* Delegates to {@link boxExpand}.
|
|
167
|
+
* @param amount - scalar or `{ x, y }` expansion per side
|
|
168
|
+
* @param target - optional box to write the result into; defaults to a new {@link Box}
|
|
169
|
+
* @returns the expanded box
|
|
170
|
+
*/
|
|
171
|
+
expand(amount: number | Point2Like, target?: Box): Box;
|
|
172
|
+
/**
|
|
173
|
+
* Returns a copy of this box scaled by `factor` about `origin`. Delegates to {@link boxScale}.
|
|
174
|
+
* @param factor - uniform scale or per-axis `{ x, y }` scale
|
|
175
|
+
* @param origin - the fixed point of the scaling; defaults to this box's center
|
|
176
|
+
* @param target - optional box to write the result into; defaults to a new {@link Box}
|
|
177
|
+
* @returns the scaled box
|
|
178
|
+
*/
|
|
179
|
+
scale(factor: number | Point2Like, origin?: Point2Like, target?: Box): Box;
|
|
180
|
+
/**
|
|
181
|
+
* Points where `line` crosses this box. Delegates to {@link intersectLineRect}.
|
|
182
|
+
* @param line - the segment
|
|
183
|
+
* @returns the intersection points
|
|
184
|
+
*/
|
|
185
|
+
intersectLine(line: LineLike): Point[];
|
|
186
|
+
/**
|
|
187
|
+
* Returns an independent copy of this box, or writes into `target` when provided.
|
|
188
|
+
* @param target - optional box to write the result into; defaults to a new {@link Box}
|
|
189
|
+
* @returns the cloned box
|
|
190
|
+
*/
|
|
191
|
+
clone(target?: Box): Box;
|
|
192
|
+
/**
|
|
193
|
+
* Mutates this box in place, normalizing corners so {@link Box.min} is always the lesser corner,
|
|
194
|
+
* and refreshes the derived center and size. Prefer the immutable transforms; use this (or a
|
|
195
|
+
* transform `target`) only for hot-path reuse.
|
|
196
|
+
* @param x0 - x coordinate of the first corner
|
|
197
|
+
* @param y0 - y coordinate of the first corner
|
|
198
|
+
* @param x1 - x coordinate of the opposite corner
|
|
199
|
+
* @param y1 - y coordinate of the opposite corner
|
|
200
|
+
* @returns `this`
|
|
201
|
+
*/
|
|
202
|
+
set(x0: number, y0: number, x1: number, y1: number): this;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Area of an axis-aligned box (`width × height`).
|
|
206
|
+
* @param b - the box to measure
|
|
207
|
+
* @returns the area in square units
|
|
208
|
+
*/
|
|
209
|
+
export declare function boxArea(b: BoxLike): number;
|
|
210
|
+
/**
|
|
211
|
+
* True when the box has zero extent in BOTH axes (collapsed to a point, or the
|
|
212
|
+
* default / `fromPoints([])` empty box). A degenerate line — extent in only one
|
|
213
|
+
* axis — is NOT empty.
|
|
214
|
+
* @param b - the box to test
|
|
215
|
+
* @returns true when `b.min === b.max` in both x and y
|
|
216
|
+
*/
|
|
217
|
+
export declare function boxIsEmpty(b: BoxLike): boolean;
|
|
218
|
+
/**
|
|
219
|
+
* Returns true when boxes `a` and `b` share at least one point (edge-touching counts).
|
|
220
|
+
* @param a - first box
|
|
221
|
+
* @param b - second box
|
|
222
|
+
* @returns true when the boxes overlap or touch
|
|
223
|
+
*/
|
|
224
|
+
export declare function boxIntersects(a: BoxLike, b: BoxLike): boolean;
|
|
225
|
+
/**
|
|
226
|
+
* Returns true when box `a` fully contains box `b` (boundary-inclusive).
|
|
227
|
+
* @param a - outer box
|
|
228
|
+
* @param b - inner box to test containment of
|
|
229
|
+
* @returns true when `b` lies entirely within `a`
|
|
230
|
+
*/
|
|
231
|
+
export declare function boxContains(a: BoxLike, b: BoxLike): boolean;
|
|
232
|
+
/**
|
|
233
|
+
* Returns true when `p` lies inside or on the boundary of `b`.
|
|
234
|
+
* @param b - the box to test against
|
|
235
|
+
* @param p - the point to test
|
|
236
|
+
* @returns true when `p` is within `b`
|
|
237
|
+
*/
|
|
238
|
+
export declare function boxContainsPoint(b: BoxLike, p: Point2Like): boolean;
|
|
239
|
+
/**
|
|
240
|
+
* Returns true when `a` and `b` have identical min and max corners.
|
|
241
|
+
* @param a - first box
|
|
242
|
+
* @param b - second box
|
|
243
|
+
* @returns true when both corners match exactly
|
|
244
|
+
*/
|
|
245
|
+
export declare function boxEquals(a: BoxLike, b: BoxLike): boolean;
|
|
246
|
+
/**
|
|
247
|
+
* Returns the intersection of boxes `a` and `b`, or `null` when they do not overlap.
|
|
248
|
+
* @param a - first box
|
|
249
|
+
* @param b - second box
|
|
250
|
+
* @param target - optional box to write the result into; defaults to a new {@link Box}
|
|
251
|
+
* @returns the intersection box, or `null` if the boxes do not overlap
|
|
252
|
+
*/
|
|
253
|
+
export declare function boxIntersection(a: BoxLike, b: BoxLike, target?: Box): Box | null;
|
|
254
|
+
/**
|
|
255
|
+
* Returns a copy of `b` shifted by `offset`. Pure — does not mutate `b`.
|
|
256
|
+
* @param b - source box
|
|
257
|
+
* @param offset - scalar (same shift on both axes) or `{ x, y }` translation vector
|
|
258
|
+
* @param target - optional box to write the result into; defaults to a new {@link Box}
|
|
259
|
+
* @returns the translated box
|
|
260
|
+
*/
|
|
261
|
+
export declare function boxTranslate(b: BoxLike, offset: number | Point2Like, target?: Box): Box;
|
|
262
|
+
/**
|
|
263
|
+
* Returns a copy of `b` expanded outward by `amount` on all sides. A positive amount grows the box;
|
|
264
|
+
* a negative amount shrinks it. Shrinking an axis past zero collapses it to its midpoint (clamped to
|
|
265
|
+
* zero size) rather than inverting.
|
|
266
|
+
* @param b - source box
|
|
267
|
+
* @param amount - scalar (broadcast to x/y) or `{ x, y }` expansion per side
|
|
268
|
+
* @param target - optional box to write the result into; defaults to a new {@link Box}
|
|
269
|
+
* @returns the expanded (or zero-clamped) box
|
|
270
|
+
*/
|
|
271
|
+
export declare function boxExpand(b: BoxLike, amount: number | Point2Like, target?: Box): Box;
|
|
272
|
+
/**
|
|
273
|
+
* Returns a copy of `b` scaled by `factor` about `origin`.
|
|
274
|
+
* @param b - source box
|
|
275
|
+
* @param factor - uniform scale or per-axis `{ x, y }` scale
|
|
276
|
+
* @param origin - the fixed point of the scaling; defaults to `b`'s center
|
|
277
|
+
* @param target - optional box to write the result into; defaults to a new {@link Box}
|
|
278
|
+
* @returns the scaled box
|
|
279
|
+
*/
|
|
280
|
+
export declare function boxScale(b: BoxLike, factor: number | Point2Like, origin?: Point2Like, target?: Box): Box;
|
|
281
|
+
export {};
|
|
282
|
+
//# sourceMappingURL=box.d.ts.map
|