@math.gl/geometry 5.0.0-alpha.19 → 5.0.0-alpha.20
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/LICENSE +95 -54
- package/README.md +9 -0
- package/THIRD-PARTY-NOTICES.md +18 -0
- package/dist/culling/index.cjs +22 -30
- package/dist/culling/index.cjs.map +3 -3
- package/dist/culling/index.d.ts +1 -0
- package/dist/culling/index.d.ts.map +1 -1
- package/dist/culling/index.js +1 -0
- package/dist/culling/index.js.map +1 -1
- package/dist/culling/lib/algorithms/bounding-box-from-points.js +1 -1
- package/dist/culling/lib/algorithms/bounding-box-from-points.js.map +1 -1
- package/dist/culling/lib/algorithms/bounding-sphere-from-points.js +1 -1
- package/dist/culling/lib/algorithms/bounding-sphere-from-points.js.map +1 -1
- package/dist/culling/lib/algorithms/compute-eigen-decomposition.js +1 -1
- package/dist/culling/lib/algorithms/compute-eigen-decomposition.js.map +1 -1
- package/dist/culling/lib/algorithms/oriented-bounding-box-quaternion.d.ts +7 -0
- package/dist/culling/lib/algorithms/oriented-bounding-box-quaternion.d.ts.map +1 -0
- package/dist/culling/lib/algorithms/oriented-bounding-box-quaternion.js +26 -0
- package/dist/culling/lib/algorithms/oriented-bounding-box-quaternion.js.map +1 -0
- package/dist/culling/lib/bounding-volumes/bounding-sphere.js +1 -1
- package/dist/culling/lib/bounding-volumes/bounding-sphere.js.map +1 -1
- package/dist/culling/lib/bounding-volumes/oriented-bounding-box.d.ts +1 -7
- package/dist/culling/lib/bounding-volumes/oriented-bounding-box.d.ts.map +1 -1
- package/dist/culling/lib/bounding-volumes/oriented-bounding-box.js +1 -30
- package/dist/culling/lib/bounding-volumes/oriented-bounding-box.js.map +1 -1
- package/dist/culling/lib/culling-volume.js +1 -1
- package/dist/culling/lib/culling-volume.js.map +1 -1
- package/dist/culling/lib/perspective-off-center-frustum.js +1 -1
- package/dist/culling/lib/perspective-off-center-frustum.js.map +1 -1
- package/dist/culling/lib/plane.js +1 -1
- package/dist/culling/lib/plane.js.map +1 -1
- package/dist/culling/lib/ray.js +1 -1
- package/dist/culling/lib/ray.js.map +1 -1
- package/dist/index.cjs +126 -0
- package/dist/index.cjs.map +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/hinge-tree.d.ts +33 -0
- package/dist/lib/hinge-tree.d.ts.map +1 -0
- package/dist/lib/hinge-tree.js +144 -0
- package/dist/lib/hinge-tree.js.map +1 -0
- package/dist/polygon/index.cjs +34 -11
- package/dist/polygon/index.cjs.map +1 -1
- package/dist/polygon/index.d.ts +1 -1
- package/dist/polygon/index.d.ts.map +1 -1
- package/dist/polygon/index.js.map +1 -1
- package/dist/polygon/subdivide-polyline.d.ts +17 -3
- package/dist/polygon/subdivide-polyline.d.ts.map +1 -1
- package/dist/polygon/subdivide-polyline.js +44 -12
- package/dist/polygon/subdivide-polyline.js.map +1 -1
- package/package.json +6 -4
- package/src/culling/index.ts +5 -0
- package/src/culling/lib/algorithms/bounding-box-from-points.ts +1 -1
- package/src/culling/lib/algorithms/bounding-sphere-from-points.ts +1 -1
- package/src/culling/lib/algorithms/compute-eigen-decomposition.ts +1 -1
- package/src/culling/lib/algorithms/oriented-bounding-box-quaternion.ts +32 -0
- package/src/culling/lib/bounding-volumes/bounding-sphere.ts +1 -1
- package/src/culling/lib/bounding-volumes/oriented-bounding-box.ts +2 -37
- package/src/culling/lib/culling-volume.ts +1 -1
- package/src/culling/lib/perspective-frustum.ts +1 -1
- package/src/culling/lib/perspective-off-center-frustum.ts +1 -1
- package/src/culling/lib/plane.ts +1 -1
- package/src/culling/lib/ray.ts +1 -1
- package/src/index.ts +3 -0
- package/src/lib/hinge-tree.ts +170 -0
- package/src/polygon/index.ts +5 -1
- package/src/polygon/subdivide-polyline.ts +76 -13
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
/**
|
|
5
|
+
/** A constant-speed source edge, sampled at fractions of its declared length.
|
|
6
|
+
* Endpoints must match the factory inputs exactly. Samples are copied before use.
|
|
7
|
+
*/
|
|
8
|
+
export type PolylineEdgeSampler = {
|
|
9
|
+
readonly length: number;
|
|
10
|
+
sample: (fraction: number) => ArrayLike<number>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/** Configuration for sampling source edges through a nonlinear transform. */
|
|
6
14
|
export type SubdividePolylineOptions = {
|
|
7
15
|
/** Maps a source position to target coordinates; null or nonfinite output rejects the geometry.
|
|
8
16
|
* Must be deterministic. A fresh array is passed on every invocation.
|
|
@@ -10,11 +18,21 @@ export type SubdividePolylineOptions = {
|
|
|
10
18
|
transform: (position: readonly number[]) => ArrayLike<number> | null;
|
|
11
19
|
/** Positive error tolerance in target-coordinate units, measured across all output components. */
|
|
12
20
|
tolerance: number;
|
|
21
|
+
/** Optional factory, called once per original edge with independent endpoint copies.
|
|
22
|
+
* Without it, source coordinates interpolate linearly. Must be deterministic.
|
|
23
|
+
*/
|
|
24
|
+
createEdgeSampler?: (
|
|
25
|
+
start: readonly number[],
|
|
26
|
+
end: readonly number[],
|
|
27
|
+
segmentIndex: number
|
|
28
|
+
) => PolylineEdgeSampler;
|
|
13
29
|
/** Number of source components per vertex. Default 2. */
|
|
14
30
|
size?: 2 | 3;
|
|
15
31
|
/** Number of target components per vertex. Defaults to size. */
|
|
16
32
|
targetSize?: 2 | 3;
|
|
17
|
-
/**
|
|
33
|
+
/** Positive maximum leaf length: Euclidean source distance by default, or the
|
|
34
|
+
* custom sampler's declared length multiplied by its fraction interval.
|
|
35
|
+
*/
|
|
18
36
|
maxSegmentLength?: number;
|
|
19
37
|
/** Maximum bisection depth per input edge. Default 16; maximum 30. */
|
|
20
38
|
maxDepth?: number;
|
|
@@ -41,7 +59,8 @@ type Sample = {source: number[]; target: number[]; fraction: number};
|
|
|
41
59
|
/** Adaptively subdivides a flat polyline by sampling a supplied coordinate transform.
|
|
42
60
|
* Each leaf edge checks its quarter, midpoint and three-quarter positions against linear
|
|
43
61
|
* interpolation of its target endpoints. This is a sampled criterion, not a guaranteed
|
|
44
|
-
* bound for arbitrary transforms. Source
|
|
62
|
+
* bound for arbitrary transforms. Source components interpolate linearly unless a custom
|
|
63
|
+
* constant-speed source-edge sampler is supplied.
|
|
45
64
|
* Explicitly closed rings stay closed. Split seams and clip invalid domains before calling.
|
|
46
65
|
* @throws RangeError For invalid options/coordinates, rejected transform samples, or exhausted limits.
|
|
47
66
|
* Exceptions thrown by the transform itself propagate unchanged.
|
|
@@ -53,6 +72,7 @@ export function subdividePolyline(
|
|
|
53
72
|
const {
|
|
54
73
|
transform,
|
|
55
74
|
tolerance,
|
|
75
|
+
createEdgeSampler,
|
|
56
76
|
size = 2,
|
|
57
77
|
targetSize = size,
|
|
58
78
|
maxSegmentLength = Infinity,
|
|
@@ -61,6 +81,7 @@ export function subdividePolyline(
|
|
|
61
81
|
} = options;
|
|
62
82
|
if (
|
|
63
83
|
typeof transform !== 'function' ||
|
|
84
|
+
(createEdgeSampler !== undefined && typeof createEdgeSampler !== 'function') ||
|
|
64
85
|
!Number.isFinite(tolerance) ||
|
|
65
86
|
tolerance <= 0 ||
|
|
66
87
|
(size !== 2 && size !== 3) ||
|
|
@@ -113,24 +134,48 @@ export function subdividePolyline(
|
|
|
113
134
|
segmentFractions.push(point.fraction);
|
|
114
135
|
}
|
|
115
136
|
|
|
116
|
-
function
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
137
|
+
function sampledSource(sampler: PolylineEdgeSampler, fraction: number): number[] {
|
|
138
|
+
const value = sampler.sample(fraction);
|
|
139
|
+
if (!value || value.length !== size) throw new RangeError('Invalid source-edge sample size');
|
|
140
|
+
const source = Array.from(value);
|
|
141
|
+
if (!source.every(Number.isFinite)) throw new RangeError('Nonfinite source-edge sample');
|
|
142
|
+
return source;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function interpolate(
|
|
146
|
+
a: Sample,
|
|
147
|
+
b: Sample,
|
|
148
|
+
fraction: number,
|
|
149
|
+
sampler?: PolylineEdgeSampler
|
|
150
|
+
): Sample {
|
|
151
|
+
const originalFraction = a.fraction * (1 - fraction) + b.fraction * fraction;
|
|
152
|
+
// Always sample the original edge, never a new curve fitted to recursive endpoints.
|
|
153
|
+
const source = sampler
|
|
154
|
+
? sampledSource(sampler, originalFraction)
|
|
155
|
+
: a.source.map((value, i) => value * (1 - fraction) + b.source[i] * fraction);
|
|
156
|
+
return sample(source, originalFraction);
|
|
120
157
|
}
|
|
121
158
|
|
|
122
|
-
function subdivide(
|
|
159
|
+
function subdivide(
|
|
160
|
+
a: Sample,
|
|
161
|
+
b: Sample,
|
|
162
|
+
segment: number,
|
|
163
|
+
depth: number,
|
|
164
|
+
sampler?: PolylineEdgeSampler
|
|
165
|
+
): void {
|
|
123
166
|
let error = 0;
|
|
124
167
|
let midpoint: Sample;
|
|
125
168
|
for (const fraction of [0.25, 0.5, 0.75]) {
|
|
126
|
-
const point = interpolate(a, b, fraction);
|
|
169
|
+
const point = interpolate(a, b, fraction, sampler);
|
|
127
170
|
if (fraction === 0.5) midpoint = point;
|
|
128
171
|
const delta = point.target.map(
|
|
129
172
|
(value, i) => value - (a.target[i] * (1 - fraction) + b.target[i] * fraction)
|
|
130
173
|
);
|
|
131
174
|
error = Math.max(error, Math.hypot(...delta));
|
|
132
175
|
}
|
|
133
|
-
const sourceLength =
|
|
176
|
+
const sourceLength = sampler
|
|
177
|
+
? sampler.length * (b.fraction - a.fraction)
|
|
178
|
+
: Math.hypot(...a.source.map((value, i) => value - b.source[i]));
|
|
134
179
|
if (error <= tolerance && sourceLength <= maxSegmentLength) {
|
|
135
180
|
append(b, segment);
|
|
136
181
|
return;
|
|
@@ -138,8 +183,8 @@ export function subdividePolyline(
|
|
|
138
183
|
if (depth >= maxDepth) {
|
|
139
184
|
throw new RangeError(`Polyline subdivision exceeded maxDepth on input edge ${segment}`);
|
|
140
185
|
}
|
|
141
|
-
subdivide(a, midpoint!, segment, depth + 1);
|
|
142
|
-
subdivide(midpoint!, b, segment, depth + 1);
|
|
186
|
+
subdivide(a, midpoint!, segment, depth + 1, sampler);
|
|
187
|
+
subdivide(midpoint!, b, segment, depth + 1, sampler);
|
|
143
188
|
}
|
|
144
189
|
|
|
145
190
|
if (vertexCount) {
|
|
@@ -147,7 +192,25 @@ export function subdividePolyline(
|
|
|
147
192
|
append(previous, 0);
|
|
148
193
|
for (let i = 1; i < vertexCount; i++) {
|
|
149
194
|
const next = sample(sources.slice(i * size, (i + 1) * size), 1);
|
|
150
|
-
|
|
195
|
+
const start = {...previous, fraction: 0};
|
|
196
|
+
const sampler = createEdgeSampler?.(start.source.slice(), next.source.slice(), i - 1);
|
|
197
|
+
if (createEdgeSampler) {
|
|
198
|
+
if (
|
|
199
|
+
!sampler ||
|
|
200
|
+
typeof sampler.sample !== 'function' ||
|
|
201
|
+
!Number.isFinite(sampler.length) ||
|
|
202
|
+
sampler.length < 0
|
|
203
|
+
) {
|
|
204
|
+
throw new RangeError('Invalid source-edge sampler');
|
|
205
|
+
}
|
|
206
|
+
for (const endpoint of [start, next]) {
|
|
207
|
+
const sampled = sampledSource(sampler, endpoint.fraction);
|
|
208
|
+
if (sampled.some((value, component) => value !== endpoint.source[component])) {
|
|
209
|
+
throw new RangeError('Source-edge sampler endpoints must match input');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
subdivide(start, next, i - 1, 0, sampler);
|
|
151
214
|
previous = next;
|
|
152
215
|
}
|
|
153
216
|
}
|