@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 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/).
@@ -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