@expofp/geometry 3.26.0 → 3.27.1
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/dist/lib/intersections.d.ts +2 -2
- package/dist/lib/intersections.js +6 -8
- package/dist/lib/line.d.ts +2 -2
- package/dist/lib/line.js +2 -2
- package/dist/lib/mesh.d.ts +14 -0
- package/dist/lib/mesh.js +45 -1
- package/dist/lib/point.d.ts +12 -0
- package/dist/lib/point.js +12 -0
- package/package.json +6 -1
|
@@ -8,7 +8,7 @@ import { type RectLike } from './rect.js';
|
|
|
8
8
|
* have a defined elevation and they differ by more than `elevationTolerance`.
|
|
9
9
|
* @param a - first line segment, defined by `p0` and `p1`
|
|
10
10
|
* @param b - second line segment, defined by `p0` and `p1`
|
|
11
|
-
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to
|
|
11
|
+
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to {@link ELEVATION_TOLERANCE}
|
|
12
12
|
* @returns `{ point, onLine1, onLine2 }`, or `null` when the lines are parallel or elevation-incompatible
|
|
13
13
|
*/
|
|
14
14
|
export declare function lineIntersection(a: LineLike, b: LineLike, elevationTolerance?: number): {
|
|
@@ -23,7 +23,7 @@ export declare function lineIntersection(a: LineLike, b: LineLike, elevationTole
|
|
|
23
23
|
* by more than `elevationTolerance`, returns an empty array.
|
|
24
24
|
* @param line - the segment, defined by `p0` and `p1`
|
|
25
25
|
* @param rect - the rect (possibly rotated; a `Box` is accepted as an unrotated rect)
|
|
26
|
-
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to
|
|
26
|
+
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to {@link ELEVATION_TOLERANCE}
|
|
27
27
|
* @returns the intersection points (0–2 for a convex rect)
|
|
28
28
|
*/
|
|
29
29
|
export declare function intersectLineRect(line: LineLike, rect: RectLike, elevationTolerance?: number): Point[];
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { pointLerp } from './point.js';
|
|
1
|
+
import { ELEVATION_TOLERANCE, pointLerp } from './point.js';
|
|
2
2
|
import { rectCorners } from './rect.js';
|
|
3
|
-
/** Default tolerance for elevation comparison. */
|
|
4
|
-
const DEFAULT_ELEVATION_TOLERANCE = 1e-3;
|
|
5
3
|
/**
|
|
6
4
|
* Computes the crossing of lines `a` and `b` using the parametric form. Returns the crossing
|
|
7
5
|
* point plus flags indicating whether the crossing lies within each segment's `[0, 1]` parameter
|
|
@@ -9,10 +7,10 @@ const DEFAULT_ELEVATION_TOLERANCE = 1e-3;
|
|
|
9
7
|
* have a defined elevation and they differ by more than `elevationTolerance`.
|
|
10
8
|
* @param a - first line segment, defined by `p0` and `p1`
|
|
11
9
|
* @param b - second line segment, defined by `p0` and `p1`
|
|
12
|
-
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to
|
|
10
|
+
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to {@link ELEVATION_TOLERANCE}
|
|
13
11
|
* @returns `{ point, onLine1, onLine2 }`, or `null` when the lines are parallel or elevation-incompatible
|
|
14
12
|
*/
|
|
15
|
-
export function lineIntersection(a, b, elevationTolerance =
|
|
13
|
+
export function lineIntersection(a, b, elevationTolerance = ELEVATION_TOLERANCE) {
|
|
16
14
|
if (a.elevation != null &&
|
|
17
15
|
b.elevation != null &&
|
|
18
16
|
Math.abs(a.elevation - b.elevation) > elevationTolerance) {
|
|
@@ -35,7 +33,7 @@ export function lineIntersection(a, b, elevationTolerance = DEFAULT_ELEVATION_TO
|
|
|
35
33
|
* flags). See `intersectLineRect` for the higher-level helper.
|
|
36
34
|
* @param a - first segment, defined by `p0` and `p1`
|
|
37
35
|
* @param b - second segment, defined by `p0` and `p1`
|
|
38
|
-
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to
|
|
36
|
+
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to {@link ELEVATION_TOLERANCE}
|
|
39
37
|
* @returns the intersection {@link Point}, or `null` if the segments are parallel or non-crossing
|
|
40
38
|
*/
|
|
41
39
|
function segmentIntersection(a, b, elevationTolerance) {
|
|
@@ -49,10 +47,10 @@ function segmentIntersection(a, b, elevationTolerance) {
|
|
|
49
47
|
* by more than `elevationTolerance`, returns an empty array.
|
|
50
48
|
* @param line - the segment, defined by `p0` and `p1`
|
|
51
49
|
* @param rect - the rect (possibly rotated; a `Box` is accepted as an unrotated rect)
|
|
52
|
-
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to
|
|
50
|
+
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to {@link ELEVATION_TOLERANCE}
|
|
53
51
|
* @returns the intersection points (0–2 for a convex rect)
|
|
54
52
|
*/
|
|
55
|
-
export function intersectLineRect(line, rect, elevationTolerance =
|
|
53
|
+
export function intersectLineRect(line, rect, elevationTolerance = ELEVATION_TOLERANCE) {
|
|
56
54
|
if (line.elevation != null &&
|
|
57
55
|
rect.elevation != null &&
|
|
58
56
|
Math.abs(line.elevation - rect.elevation) > elevationTolerance) {
|
package/dist/lib/line.d.ts
CHANGED
|
@@ -69,7 +69,7 @@ export declare class Line {
|
|
|
69
69
|
/**
|
|
70
70
|
* Points where this line crosses `rect`.
|
|
71
71
|
* @param rect - the rect (possibly rotated; a `Box` is accepted as an unrotated rect)
|
|
72
|
-
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to
|
|
72
|
+
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to {@link ELEVATION_TOLERANCE}
|
|
73
73
|
* @returns the intersection points
|
|
74
74
|
*/
|
|
75
75
|
intersectRect(rect: RectLike, elevationTolerance?: number): Point[];
|
|
@@ -87,7 +87,7 @@ export declare class Line {
|
|
|
87
87
|
/**
|
|
88
88
|
* Intersection with another segment `other` on the infinite lines.
|
|
89
89
|
* @param other - the other segment
|
|
90
|
-
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to
|
|
90
|
+
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to {@link ELEVATION_TOLERANCE}
|
|
91
91
|
* @returns the crossing data, or `null` when the lines are parallel or elevations are incompatible
|
|
92
92
|
*/
|
|
93
93
|
intersect(other: LineLike, elevationTolerance?: number): {
|
package/dist/lib/line.js
CHANGED
|
@@ -81,7 +81,7 @@ export class Line {
|
|
|
81
81
|
/**
|
|
82
82
|
* Points where this line crosses `rect`.
|
|
83
83
|
* @param rect - the rect (possibly rotated; a `Box` is accepted as an unrotated rect)
|
|
84
|
-
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to
|
|
84
|
+
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to {@link ELEVATION_TOLERANCE}
|
|
85
85
|
* @returns the intersection points
|
|
86
86
|
*/
|
|
87
87
|
intersectRect(rect, elevationTolerance) {
|
|
@@ -99,7 +99,7 @@ export class Line {
|
|
|
99
99
|
/**
|
|
100
100
|
* Intersection with another segment `other` on the infinite lines.
|
|
101
101
|
* @param other - the other segment
|
|
102
|
-
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to
|
|
102
|
+
* @param elevationTolerance - maximum elevation difference for a crossing to be reported; defaults to {@link ELEVATION_TOLERANCE}
|
|
103
103
|
* @returns the crossing data, or `null` when the lines are parallel or elevations are incompatible
|
|
104
104
|
*/
|
|
105
105
|
intersect(other, elevationTolerance) {
|
package/dist/lib/mesh.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export declare class Mesh {
|
|
|
24
24
|
private _indices;
|
|
25
25
|
/** Cached axis-aligned bounding box (xy only). */
|
|
26
26
|
private _bounds;
|
|
27
|
+
/** Cached planarity: whether every vertex sits on one plane in z. */
|
|
28
|
+
private _planar;
|
|
27
29
|
/**
|
|
28
30
|
* Constructs a mesh from a vertex list and triangle index triplets.
|
|
29
31
|
* @param vertices - source vertices; each is cloned into a {@link Point} (default `[]`)
|
|
@@ -45,6 +47,18 @@ export declare class Mesh {
|
|
|
45
47
|
* @returns the bounding box
|
|
46
48
|
*/
|
|
47
49
|
get bounds(): Box;
|
|
50
|
+
/**
|
|
51
|
+
* Whether this mesh is flat — every vertex sits on one plane in z, within
|
|
52
|
+
* {@link ELEVATION_TOLERANCE}, so it has no extent worth shading along that axis. An empty mesh is
|
|
53
|
+
* planar. Cached at {@link set}, so reading it is free.
|
|
54
|
+
*
|
|
55
|
+
* This is the predicate that separates a lifted-but-flat mesh from a volume: the former is placed
|
|
56
|
+
* correctly by draw order alone, while the latter needs depth testing and shading to read as solid.
|
|
57
|
+
* The comparison is deliberately approximate — see {@link meshIsPlanar} for why exact equality is
|
|
58
|
+
* the riskier choice.
|
|
59
|
+
* @returns true when the mesh has no meaningful z extent
|
|
60
|
+
*/
|
|
61
|
+
get isPlanar(): boolean;
|
|
48
62
|
/**
|
|
49
63
|
* Mutates this mesh in place, replacing vertices/indices and refreshing the cached bounds.
|
|
50
64
|
* Vertices are copied into fresh {@link Point}s — do not optimize this clone away; transform
|
package/dist/lib/mesh.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Box } from './box.js';
|
|
2
|
-
import { Point } from './point.js';
|
|
2
|
+
import { ELEVATION_TOLERANCE, Point } from './point.js';
|
|
3
3
|
/**
|
|
4
4
|
* A 3D triangle mesh: vertices carry an optional z coordinate, plus triangle index triplets.
|
|
5
5
|
* Immutable value object: readonly getters; transforms return a new instance unless a `target`
|
|
@@ -14,6 +14,8 @@ export class Mesh {
|
|
|
14
14
|
_indices = [];
|
|
15
15
|
/** Cached axis-aligned bounding box (xy only). */
|
|
16
16
|
_bounds = new Box();
|
|
17
|
+
/** Cached planarity: whether every vertex sits on one plane in z. */
|
|
18
|
+
_planar = true;
|
|
17
19
|
/**
|
|
18
20
|
* Constructs a mesh from a vertex list and triangle index triplets.
|
|
19
21
|
* @param vertices - source vertices; each is cloned into a {@link Point} (default `[]`)
|
|
@@ -43,6 +45,20 @@ export class Mesh {
|
|
|
43
45
|
get bounds() {
|
|
44
46
|
return this._bounds;
|
|
45
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Whether this mesh is flat — every vertex sits on one plane in z, within
|
|
50
|
+
* {@link ELEVATION_TOLERANCE}, so it has no extent worth shading along that axis. An empty mesh is
|
|
51
|
+
* planar. Cached at {@link set}, so reading it is free.
|
|
52
|
+
*
|
|
53
|
+
* This is the predicate that separates a lifted-but-flat mesh from a volume: the former is placed
|
|
54
|
+
* correctly by draw order alone, while the latter needs depth testing and shading to read as solid.
|
|
55
|
+
* The comparison is deliberately approximate — see {@link meshIsPlanar} for why exact equality is
|
|
56
|
+
* the riskier choice.
|
|
57
|
+
* @returns true when the mesh has no meaningful z extent
|
|
58
|
+
*/
|
|
59
|
+
get isPlanar() {
|
|
60
|
+
return this._planar;
|
|
61
|
+
}
|
|
46
62
|
/**
|
|
47
63
|
* Mutates this mesh in place, replacing vertices/indices and refreshing the cached bounds.
|
|
48
64
|
* Vertices are copied into fresh {@link Point}s — do not optimize this clone away; transform
|
|
@@ -54,6 +70,7 @@ export class Mesh {
|
|
|
54
70
|
set(vertices, indices) {
|
|
55
71
|
this._vertices = vertices.map((v) => new Point(v.x, v.y, v.z ?? 0));
|
|
56
72
|
this._indices = indices;
|
|
73
|
+
this._planar = meshIsPlanar(this._vertices);
|
|
57
74
|
this._bounds = meshBounds(this);
|
|
58
75
|
return this;
|
|
59
76
|
}
|
|
@@ -114,6 +131,33 @@ export function meshBounds(m) {
|
|
|
114
131
|
}
|
|
115
132
|
return Box.fromMinMax({ x: minX, y: minY }, { x: maxX, y: maxY });
|
|
116
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Whether every vertex sits on one plane in z, within {@link ELEVATION_TOLERANCE}.
|
|
136
|
+
*
|
|
137
|
+
* Approximate, not exact, and the asymmetry is why. Calling a flat mesh non-planar is the expensive
|
|
138
|
+
* mistake: it would be shaded and depth-tested as a volume, and a flat face whose computed normal
|
|
139
|
+
* points away from the camera renders black. Calling a mesh with sub-tolerance z extent planar costs
|
|
140
|
+
* nothing, because there is nothing there to shade. Exporters and float round-trips produce exactly
|
|
141
|
+
* that kind of noise — a `1e-17` where the author meant zero — and strict equality would take the
|
|
142
|
+
* expensive branch on it.
|
|
143
|
+
*
|
|
144
|
+
* Every vertex is compared against the *first* one rather than against its neighbor, so a mesh that
|
|
145
|
+
* ramps gently is correctly a volume even though no two adjacent vertices differ by much.
|
|
146
|
+
*
|
|
147
|
+
* Vertices have already been normalized to carry a z by {@link Mesh.set}, so no defaulting is needed.
|
|
148
|
+
* @param vertices - the mesh's vertices
|
|
149
|
+
* @returns true when the vertices have no meaningful z extent
|
|
150
|
+
*/
|
|
151
|
+
function meshIsPlanar(vertices) {
|
|
152
|
+
if (vertices.length === 0)
|
|
153
|
+
return true;
|
|
154
|
+
const z = vertices[0].z;
|
|
155
|
+
for (let i = 1; i < vertices.length; i++) {
|
|
156
|
+
if (Math.abs(vertices[i].z - z) > ELEVATION_TOLERANCE)
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
117
161
|
export function meshMerge(meshes, target) {
|
|
118
162
|
const vertices = [];
|
|
119
163
|
const indices = [];
|
package/dist/lib/point.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How far apart two `z` values may be and still count as the same plane, in plan units.
|
|
3
|
+
*
|
|
4
|
+
* Absolute rather than relative, because the question is about the plan's own scale: a thousandth of
|
|
5
|
+
* a unit is far below anything drawable, and far above the noise an exporter or a float round-trip
|
|
6
|
+
* introduces. Deliberate thinness is *not* noise — a slab authored half a unit thick is a volume and
|
|
7
|
+
* is treated as one.
|
|
8
|
+
*
|
|
9
|
+
* Shared so the package cannot answer "same plane?" two different ways: it gates elevation in
|
|
10
|
+
* {@link lineIntersection} and planarity in {@link Mesh.isPlanar}.
|
|
11
|
+
*/
|
|
12
|
+
export declare const ELEVATION_TOLERANCE = 0.1;
|
|
1
13
|
/** A 2D point/vector `{ x, y }`. */
|
|
2
14
|
export interface Point2Like {
|
|
3
15
|
/** Horizontal coordinate. */
|
package/dist/lib/point.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { fromPolar } from './angles.js';
|
|
2
|
+
/**
|
|
3
|
+
* How far apart two `z` values may be and still count as the same plane, in plan units.
|
|
4
|
+
*
|
|
5
|
+
* Absolute rather than relative, because the question is about the plan's own scale: a thousandth of
|
|
6
|
+
* a unit is far below anything drawable, and far above the noise an exporter or a float round-trip
|
|
7
|
+
* introduces. Deliberate thinness is *not* noise — a slab authored half a unit thick is a volume and
|
|
8
|
+
* is treated as one.
|
|
9
|
+
*
|
|
10
|
+
* Shared so the package cannot answer "same plane?" two different ways: it gates elevation in
|
|
11
|
+
* {@link lineIntersection} and planarity in {@link Mesh.isPlanar}.
|
|
12
|
+
*/
|
|
13
|
+
export const ELEVATION_TOLERANCE = 1e-1;
|
|
2
14
|
/**
|
|
3
15
|
* Mutable point/vector — the low-level building block other primitives and the point free functions
|
|
4
16
|
* write into. `width`/`height` alias `x`/`y` so a point used as a size reads naturally.
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expofp/geometry",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.27.1",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/expofp/efp-app.git",
|
|
7
|
+
"directory": "packages/geometry"
|
|
8
|
+
},
|
|
4
9
|
"type": "module",
|
|
5
10
|
"description": "ExpoFP SDK internal: shared geometry primitives",
|
|
6
11
|
"homepage": "https://developer.expofp.com/",
|