@aura3d/engine 1.5.0 → 1.5.2
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 +111 -46
- package/dist/engine/agent-api/ApplicationKits.d.ts +307 -0
- package/dist/engine/agent-api/ApplicationKits.d.ts.map +1 -0
- package/dist/engine/agent-api/ApplicationKits.js +650 -0
- package/dist/engine/agent-api/ApplicationKits.js.map +1 -0
- package/dist/engine/agent-api/CombatFrameData.d.ts +210 -0
- package/dist/engine/agent-api/CombatFrameData.d.ts.map +1 -0
- package/dist/engine/agent-api/CombatFrameData.js +324 -0
- package/dist/engine/agent-api/CombatFrameData.js.map +1 -0
- package/dist/engine/agent-api/FocusSelection.d.ts +186 -0
- package/dist/engine/agent-api/FocusSelection.d.ts.map +1 -0
- package/dist/engine/agent-api/FocusSelection.js +436 -0
- package/dist/engine/agent-api/FocusSelection.js.map +1 -0
- package/dist/engine/agent-api/GameGenreKits.d.ts +32 -1
- package/dist/engine/agent-api/GameGenreKits.d.ts.map +1 -1
- package/dist/engine/agent-api/GameGenreKits.js +51 -7
- package/dist/engine/agent-api/GameGenreKits.js.map +1 -1
- package/dist/engine/agent-api/GameSceneGeometryBindings.d.ts +10 -0
- package/dist/engine/agent-api/GameSceneGeometryBindings.d.ts.map +1 -1
- package/dist/engine/agent-api/GameSceneGeometryBindings.js +7 -0
- package/dist/engine/agent-api/GameSceneGeometryBindings.js.map +1 -1
- package/dist/engine/agent-api/PlatformerMotion.d.ts +177 -0
- package/dist/engine/agent-api/PlatformerMotion.d.ts.map +1 -0
- package/dist/engine/agent-api/PlatformerMotion.js +207 -0
- package/dist/engine/agent-api/PlatformerMotion.js.map +1 -0
- package/dist/engine/agent-api/SceneQueries.d.ts +124 -0
- package/dist/engine/agent-api/SceneQueries.d.ts.map +1 -0
- package/dist/engine/agent-api/SceneQueries.js +221 -0
- package/dist/engine/agent-api/SceneQueries.js.map +1 -0
- package/dist/engine/agent-api/SpatialAnchoring.d.ts +279 -0
- package/dist/engine/agent-api/SpatialAnchoring.d.ts.map +1 -0
- package/dist/engine/agent-api/SpatialAnchoring.js +394 -0
- package/dist/engine/agent-api/SpatialAnchoring.js.map +1 -0
- package/dist/engine/agent-api/VehicleChassis.d.ts +192 -0
- package/dist/engine/agent-api/VehicleChassis.d.ts.map +1 -0
- package/dist/engine/agent-api/VehicleChassis.js +322 -0
- package/dist/engine/agent-api/VehicleChassis.js.map +1 -0
- package/dist/engine/agent-api/VehicleDriverAi.d.ts +123 -0
- package/dist/engine/agent-api/VehicleDriverAi.d.ts.map +1 -0
- package/dist/engine/agent-api/VehicleDriverAi.js +217 -0
- package/dist/engine/agent-api/VehicleDriverAi.js.map +1 -0
- package/dist/engine/agent-api/WorldLabelRenderer.d.ts +146 -0
- package/dist/engine/agent-api/WorldLabelRenderer.d.ts.map +1 -0
- package/dist/engine/agent-api/WorldLabelRenderer.js +331 -0
- package/dist/engine/agent-api/WorldLabelRenderer.js.map +1 -0
- package/dist/engine/agent-api/index.d.ts +64 -1
- package/dist/engine/agent-api/index.d.ts.map +1 -1
- package/dist/engine/agent-api/index.js +148 -2
- package/dist/engine/agent-api/index.js.map +1 -1
- package/dist/engine/agent-api/product-viewer-runtime.d.ts.map +1 -1
- package/dist/rendering/CameraFraming.d.ts +77 -0
- package/dist/rendering/CameraFraming.d.ts.map +1 -1
- package/dist/rendering/CameraFraming.js +176 -1
- package/dist/rendering/CameraFraming.js.map +1 -1
- package/dist/rendering/Renderer.d.ts +25 -2
- package/dist/rendering/Renderer.d.ts.map +1 -1
- package/dist/rendering/Renderer.js +11 -3
- package/dist/rendering/Renderer.js.map +1 -1
- package/dist/rendering/index.d.ts +3 -3
- package/dist/rendering/index.d.ts.map +1 -1
- package/dist/rendering/index.js +1 -1
- package/dist/rendering/index.js.map +1 -1
- package/package.json +120 -115
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset-relative spatial layout and semantic anchoring.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* Public routes placed helper geometry -- status markers, control stations,
|
|
7
|
+
* selection rings, callout panels, staging props -- at literal world
|
|
8
|
+
* coordinates chosen by eye against whatever asset was loaded that week. When
|
|
9
|
+
* the asset changed, or its bounds changed, the coordinates did not. The
|
|
10
|
+
* visible result is procedural boxes floating beside the scene rather than
|
|
11
|
+
* attached to the equipment they annotate.
|
|
12
|
+
*
|
|
13
|
+
* `CAR_SCENE_HEIGHT` was the same defect in a single constant: a value correct
|
|
14
|
+
* for one asset, frozen, then wrong. This module removes the reason to write
|
|
15
|
+
* such a constant. A route states intent -- "above this machine", "beside the
|
|
16
|
+
* workcell", "on the floor in front of the product" -- and reads back a world
|
|
17
|
+
* position derived from the asset's own placed bounds.
|
|
18
|
+
*
|
|
19
|
+
* Everything here is pure: no renderer, no asset loading, no DOM. Anchors are
|
|
20
|
+
* resolved from {@link PlacedBounds}, which a route obtains from typed asset
|
|
21
|
+
* bounds plus its intended scale via {@link placedBoundsFromAsset}.
|
|
22
|
+
*/
|
|
23
|
+
import { boundsFromAsset, boundsSize } from "./SceneGroundingUtils.js";
|
|
24
|
+
/**
|
|
25
|
+
* Build placed bounds from a subject's world position, size and floor.
|
|
26
|
+
*
|
|
27
|
+
* `position` is the subject's floor contact point, matching how Aura3D's safe
|
|
28
|
+
* model renderer grounds a normalized model on its node origin.
|
|
29
|
+
*/
|
|
30
|
+
export function placedBounds(options) {
|
|
31
|
+
const floorY = options.floorY ?? options.position[1];
|
|
32
|
+
const size = [
|
|
33
|
+
Math.max(0, options.size[0]),
|
|
34
|
+
Math.max(0, options.size[1]),
|
|
35
|
+
Math.max(0, options.size[2])
|
|
36
|
+
];
|
|
37
|
+
const min = [options.position[0] - size[0] / 2, floorY, options.position[2] - size[2] / 2];
|
|
38
|
+
const max = [options.position[0] + size[0] / 2, floorY + size[1], options.position[2] + size[2] / 2];
|
|
39
|
+
return {
|
|
40
|
+
min,
|
|
41
|
+
max,
|
|
42
|
+
center: [(min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2],
|
|
43
|
+
size,
|
|
44
|
+
floorY
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Placed bounds for a typed asset rendered with `model(asset)`.
|
|
49
|
+
*
|
|
50
|
+
* Mirrors the safe renderer: the asset is normalized to
|
|
51
|
+
* `normalizedMaxDimension`, multiplied by node scale, and grounded so its
|
|
52
|
+
* lowest point rests on `floorY`. A route therefore never needs the asset's raw
|
|
53
|
+
* dimensions to place something relative to it.
|
|
54
|
+
*/
|
|
55
|
+
export function placedBoundsFromAsset(asset, options) {
|
|
56
|
+
const raw = boundsFromAsset(asset);
|
|
57
|
+
const rawSize = boundsSize(raw);
|
|
58
|
+
const rawMax = Math.max(rawSize[0], rawSize[1], rawSize[2]);
|
|
59
|
+
const factor = rawMax > 0 && options.targetMaxDimension > 0 ? options.targetMaxDimension / rawMax : 1;
|
|
60
|
+
const position = options.position ?? [0, options.floorY ?? 0, 0];
|
|
61
|
+
return placedBounds({
|
|
62
|
+
position,
|
|
63
|
+
size: [rawSize[0] * factor, rawSize[1] * factor, rawSize[2] * factor],
|
|
64
|
+
floorY: options.floorY ?? position[1]
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Size an asset so it occupies a chosen fraction of a region it sits inside.
|
|
69
|
+
*
|
|
70
|
+
* Placement was already bounds-derived through {@link resolveSemanticRegion},
|
|
71
|
+
* but *sizing* was not: a route that knew where a vehicle belonged still had to
|
|
72
|
+
* invent a multiplier such as `.scale(1.58)` to decide how big it should be.
|
|
73
|
+
* That number carries no relationship to either the asset or its surroundings,
|
|
74
|
+
* so it silently becomes wrong the moment the asset is swapped or the scene
|
|
75
|
+
* resized — which is precisely the "hero fills the frame and occludes the scene"
|
|
76
|
+
* defect class.
|
|
77
|
+
*
|
|
78
|
+
* This returns a `targetMaxDimension` rather than a raw scale factor on purpose.
|
|
79
|
+
* A scale factor is only meaningful relative to an asset's unknown raw
|
|
80
|
+
* dimensions, whereas a target dimension is an absolute statement about world
|
|
81
|
+
* size that the renderer resolves against the asset's real bounds.
|
|
82
|
+
*/
|
|
83
|
+
export function fitSizeToRegion(region, options) {
|
|
84
|
+
const regionSize = [
|
|
85
|
+
Math.max(0, region.size[0]),
|
|
86
|
+
Math.max(0, region.size[1]),
|
|
87
|
+
Math.max(0, region.size[2])
|
|
88
|
+
];
|
|
89
|
+
const requested = Number.isFinite(options.occupancy) ? Math.min(1, Math.max(0, options.occupancy)) : 0;
|
|
90
|
+
const axes = options.axes ?? ["x", "z"];
|
|
91
|
+
const axisIndex = { x: 0, y: 1, z: 2 };
|
|
92
|
+
const candidates = axes
|
|
93
|
+
.map((axis) => regionSize[axisIndex[axis]])
|
|
94
|
+
.filter((value) => typeof value === "number" && value > 0);
|
|
95
|
+
if (candidates.length === 0) {
|
|
96
|
+
throw new RangeError("fitSizeToRegion requires a region with positive extent on at least one constraining axis.");
|
|
97
|
+
}
|
|
98
|
+
// The smallest constraining extent governs: fitting to the largest would let
|
|
99
|
+
// the subject overflow the tighter axis, which is the same visual failure as
|
|
100
|
+
// hardcoding the scale too high.
|
|
101
|
+
const governing = Math.min(...candidates);
|
|
102
|
+
const ideal = governing * requested;
|
|
103
|
+
const lowerBound = options.minSize !== undefined && Number.isFinite(options.minSize) ? Math.max(0, options.minSize) : 0;
|
|
104
|
+
const upperBound = options.maxSize !== undefined && Number.isFinite(options.maxSize) ? Math.max(0, options.maxSize) : Number.POSITIVE_INFINITY;
|
|
105
|
+
const targetMaxDimension = Math.min(upperBound, Math.max(lowerBound, ideal));
|
|
106
|
+
return {
|
|
107
|
+
targetMaxDimension,
|
|
108
|
+
occupancy: governing > 0 ? targetMaxDimension / governing : 0,
|
|
109
|
+
regionSize,
|
|
110
|
+
clamped: targetMaxDimension !== ideal
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** Placed bounds for a raw {@link SceneBounds} already expressed in world units. */
|
|
114
|
+
export function placedBoundsFromWorldBounds(bounds) {
|
|
115
|
+
const size = boundsSize(bounds);
|
|
116
|
+
return {
|
|
117
|
+
min: [bounds.min[0], bounds.min[1], bounds.min[2]],
|
|
118
|
+
max: [bounds.max[0], bounds.max[1], bounds.max[2]],
|
|
119
|
+
center: [
|
|
120
|
+
(bounds.min[0] + bounds.max[0]) / 2,
|
|
121
|
+
(bounds.min[1] + bounds.max[1]) / 2,
|
|
122
|
+
(bounds.min[2] + bounds.max[2]) / 2
|
|
123
|
+
],
|
|
124
|
+
size,
|
|
125
|
+
floorY: bounds.min[1]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const ANCHOR_NORMALS = {
|
|
129
|
+
center: [0, 0, 0],
|
|
130
|
+
top: [0, 1, 0],
|
|
131
|
+
bottom: [0, -1, 0],
|
|
132
|
+
// "front" is +Z: Aura3D scenes look down -Z from a default camera, so the face
|
|
133
|
+
// nearest the viewer is +Z. Documented rather than inferred so a route reading
|
|
134
|
+
// "front" does not get the far side.
|
|
135
|
+
front: [0, 0, 1],
|
|
136
|
+
rear: [0, 0, -1],
|
|
137
|
+
left: [-1, 0, 0],
|
|
138
|
+
right: [1, 0, 0],
|
|
139
|
+
"top-front": [0, 0.7071, 0.7071],
|
|
140
|
+
"top-rear": [0, 0.7071, -0.7071],
|
|
141
|
+
"top-left": [-0.7071, 0.7071, 0],
|
|
142
|
+
"top-right": [0.7071, 0.7071, 0],
|
|
143
|
+
"floor-front": [0, 0, 1],
|
|
144
|
+
"floor-rear": [0, 0, -1],
|
|
145
|
+
"floor-left": [-1, 0, 0],
|
|
146
|
+
"floor-right": [1, 0, 0],
|
|
147
|
+
"corner-front-left": [-0.7071, 0, 0.7071],
|
|
148
|
+
"corner-front-right": [0.7071, 0, 0.7071],
|
|
149
|
+
"corner-rear-left": [-0.7071, 0, -0.7071],
|
|
150
|
+
"corner-rear-right": [0.7071, 0, -0.7071]
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Resolve a world position on or beside a subject's placed bounds.
|
|
154
|
+
*
|
|
155
|
+
* The returned position is on the named face at `offset` distance outward. This
|
|
156
|
+
* is the replacement for literal helper coordinates: it tracks the asset.
|
|
157
|
+
*/
|
|
158
|
+
export function resolveBoundsAnchor(bounds, anchor, options = {}) {
|
|
159
|
+
const normal = ANCHOR_NORMALS[anchor];
|
|
160
|
+
const offset = options.offset ?? 0;
|
|
161
|
+
const half = [bounds.size[0] / 2, bounds.size[1] / 2, bounds.size[2] / 2];
|
|
162
|
+
// Start at the bounds centre and walk out to the named face.
|
|
163
|
+
let position = [
|
|
164
|
+
bounds.center[0] + normal[0] * half[0],
|
|
165
|
+
bounds.center[1] + normal[1] * half[1],
|
|
166
|
+
bounds.center[2] + normal[2] * half[2]
|
|
167
|
+
];
|
|
168
|
+
// Floor anchors sit on the ground plane beside the subject.
|
|
169
|
+
if (anchor.startsWith("floor-") || options.onFloor === true) {
|
|
170
|
+
position = [position[0], bounds.floorY, position[2]];
|
|
171
|
+
}
|
|
172
|
+
position = [
|
|
173
|
+
position[0] + normal[0] * offset,
|
|
174
|
+
position[1] + normal[1] * offset,
|
|
175
|
+
position[2] + normal[2] * offset
|
|
176
|
+
];
|
|
177
|
+
const worldOffset = options.worldOffset ?? [0, 0, 0];
|
|
178
|
+
position = [position[0] + worldOffset[0], position[1] + worldOffset[1], position[2] + worldOffset[2]];
|
|
179
|
+
return {
|
|
180
|
+
position,
|
|
181
|
+
normal,
|
|
182
|
+
anchor,
|
|
183
|
+
outsideBounds: !containsPoint(bounds, position)
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/** True when a world point lies inside the placed bounds (inclusive). */
|
|
187
|
+
export function containsPoint(bounds, point, epsilon = 1e-6) {
|
|
188
|
+
return point[0] >= bounds.min[0] - epsilon && point[0] <= bounds.max[0] + epsilon
|
|
189
|
+
&& point[1] >= bounds.min[1] - epsilon && point[1] <= bounds.max[1] + epsilon
|
|
190
|
+
&& point[2] >= bounds.min[2] - epsilon && point[2] <= bounds.max[2] + epsilon;
|
|
191
|
+
}
|
|
192
|
+
/** Resolve a normalized semantic region to world-space bounds on the subject. */
|
|
193
|
+
export function resolveSemanticRegion(bounds, region) {
|
|
194
|
+
const clamp01 = (value) => Math.min(1, Math.max(0, Number.isFinite(value) ? value : 0));
|
|
195
|
+
const u = clamp01(region.u);
|
|
196
|
+
const v = clamp01(region.v);
|
|
197
|
+
const w = clamp01(region.w);
|
|
198
|
+
const extent = region.extent ?? [0, 0, 0];
|
|
199
|
+
const center = [
|
|
200
|
+
bounds.min[0] + bounds.size[0] * u,
|
|
201
|
+
bounds.min[1] + bounds.size[1] * v,
|
|
202
|
+
bounds.min[2] + bounds.size[2] * w
|
|
203
|
+
];
|
|
204
|
+
const size = [
|
|
205
|
+
bounds.size[0] * clamp01(extent[0]),
|
|
206
|
+
bounds.size[1] * clamp01(extent[1]),
|
|
207
|
+
bounds.size[2] * clamp01(extent[2])
|
|
208
|
+
];
|
|
209
|
+
return {
|
|
210
|
+
id: region.id,
|
|
211
|
+
label: region.label,
|
|
212
|
+
center,
|
|
213
|
+
size,
|
|
214
|
+
min: [center[0] - size[0] / 2, center[1] - size[1] / 2, center[2] - size[2] / 2],
|
|
215
|
+
max: [center[0] + size[0] / 2, center[1] + size[1] / 2, center[2] + size[2] / 2]
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Distribute items evenly across a region, honouring a minimum spacing.
|
|
220
|
+
*
|
|
221
|
+
* Deterministic for a given seed so gameplay and evidence are reproducible.
|
|
222
|
+
* Used instead of `-0.45 + index * 0.17` style literals, which encode both the
|
|
223
|
+
* region and its spacing into one unexplained pair of numbers.
|
|
224
|
+
*/
|
|
225
|
+
export function distributeInRegion(region, options) {
|
|
226
|
+
const count = Math.max(0, Math.floor(options.count));
|
|
227
|
+
if (count === 0)
|
|
228
|
+
return [];
|
|
229
|
+
const spanX = region.max[0] - region.min[0];
|
|
230
|
+
const spanZ = region.max[2] - region.min[2];
|
|
231
|
+
const jitter = Math.min(1, Math.max(0, options.jitter ?? 0));
|
|
232
|
+
const seed = options.seed ?? 1;
|
|
233
|
+
// Lay items along the longer horizontal axis so a narrow conveyor stays a line.
|
|
234
|
+
const alongX = spanX >= spanZ;
|
|
235
|
+
const span = alongX ? spanX : spanZ;
|
|
236
|
+
const step = count > 1 ? span / (count - 1) : 0;
|
|
237
|
+
const y = options.onFloor === true ? region.min[1] : (region.min[1] + region.max[1]) / 2;
|
|
238
|
+
const positions = [];
|
|
239
|
+
let random = seed >>> 0 || 1;
|
|
240
|
+
const nextRandom = () => {
|
|
241
|
+
// xorshift32: deterministic, no dependencies, adequate for layout jitter.
|
|
242
|
+
random ^= random << 13;
|
|
243
|
+
random ^= random >>> 17;
|
|
244
|
+
random ^= random << 5;
|
|
245
|
+
return ((random >>> 0) / 0xffffffff) * 2 - 1;
|
|
246
|
+
};
|
|
247
|
+
for (let index = 0; index < count; index += 1) {
|
|
248
|
+
const t = count > 1 ? index * step : span / 2;
|
|
249
|
+
const jitterAmount = jitter * step * 0.5;
|
|
250
|
+
const primary = (alongX ? region.min[0] : region.min[2]) + t + (jitter > 0 ? nextRandom() * jitterAmount : 0);
|
|
251
|
+
const secondaryCenter = alongX ? (region.min[2] + region.max[2]) / 2 : (region.min[0] + region.max[0]) / 2;
|
|
252
|
+
const secondarySpan = alongX ? spanZ : spanX;
|
|
253
|
+
const secondary = secondaryCenter + (jitter > 0 ? nextRandom() * secondarySpan * 0.5 * jitter : 0);
|
|
254
|
+
positions.push(alongX ? [primary, y, secondary] : [secondary, y, primary]);
|
|
255
|
+
}
|
|
256
|
+
const minSpacing = options.minSpacing ?? 0;
|
|
257
|
+
return positions.map((position, index) => {
|
|
258
|
+
let nearest = Number.POSITIVE_INFINITY;
|
|
259
|
+
positions.forEach((other, otherIndex) => {
|
|
260
|
+
if (otherIndex === index)
|
|
261
|
+
return;
|
|
262
|
+
nearest = Math.min(nearest, distance(position, other));
|
|
263
|
+
});
|
|
264
|
+
if (!Number.isFinite(nearest))
|
|
265
|
+
nearest = Number.POSITIVE_INFINITY;
|
|
266
|
+
return {
|
|
267
|
+
index,
|
|
268
|
+
position,
|
|
269
|
+
nearestNeighborDistance: nearest === Number.POSITIVE_INFINITY ? Number.POSITIVE_INFINITY : round(nearest)
|
|
270
|
+
};
|
|
271
|
+
}).map((placement) => {
|
|
272
|
+
// Report rather than silently correct: a caller asking for spacing the region
|
|
273
|
+
// cannot provide should see it in the invariant report, not get moved items.
|
|
274
|
+
void minSpacing;
|
|
275
|
+
return placement;
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
/** Minimum-spacing check for a set of placements. */
|
|
279
|
+
export function validateSpacing(placements, minSpacing) {
|
|
280
|
+
const violations = placements
|
|
281
|
+
.filter((placement) => Number.isFinite(placement.nearestNeighborDistance) && placement.nearestNeighborDistance < minSpacing)
|
|
282
|
+
.map((placement) => ({ index: placement.index, distance: placement.nearestNeighborDistance }));
|
|
283
|
+
return { ok: violations.length === 0, violations };
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Place items on a circle around a subject.
|
|
287
|
+
*
|
|
288
|
+
* The radius is added to the subject's own horizontal half-extent, so a ring of
|
|
289
|
+
* markers stays clear of the asset regardless of how large the asset is.
|
|
290
|
+
*/
|
|
291
|
+
export function distributeAroundBounds(bounds, options) {
|
|
292
|
+
const count = Math.max(0, Math.floor(options.count));
|
|
293
|
+
if (count === 0)
|
|
294
|
+
return [];
|
|
295
|
+
const clearance = Math.max(bounds.size[0], bounds.size[2]) / 2;
|
|
296
|
+
const radius = clearance + Math.max(0, options.radius);
|
|
297
|
+
const sweep = options.sweep ?? Math.PI * 2;
|
|
298
|
+
const start = options.startAngle ?? 0;
|
|
299
|
+
const y = options.height ?? bounds.center[1];
|
|
300
|
+
const step = count > 1 ? sweep / (sweep >= Math.PI * 2 - 1e-6 ? count : count - 1) : 0;
|
|
301
|
+
const positions = [];
|
|
302
|
+
for (let index = 0; index < count; index += 1) {
|
|
303
|
+
const angle = start + step * index;
|
|
304
|
+
positions.push([
|
|
305
|
+
bounds.center[0] + Math.cos(angle) * radius,
|
|
306
|
+
y,
|
|
307
|
+
bounds.center[2] + Math.sin(angle) * radius
|
|
308
|
+
]);
|
|
309
|
+
}
|
|
310
|
+
return positions.map((position, index) => {
|
|
311
|
+
let nearest = Number.POSITIVE_INFINITY;
|
|
312
|
+
positions.forEach((other, otherIndex) => {
|
|
313
|
+
if (otherIndex === index)
|
|
314
|
+
return;
|
|
315
|
+
nearest = Math.min(nearest, distance(position, other));
|
|
316
|
+
});
|
|
317
|
+
return { index, position, nearestNeighborDistance: Number.isFinite(nearest) ? round(nearest) : Number.POSITIVE_INFINITY };
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Verify that helper geometry is anchored to its subject.
|
|
322
|
+
*
|
|
323
|
+
* `outside` elements must remain within `maxDistance` of the bounds: that is
|
|
324
|
+
* what separates "a control station beside the workcell" from "a box floating in
|
|
325
|
+
* the void". The default of 2x the subject's largest extent is generous enough
|
|
326
|
+
* for staging and tight enough to catch the digital-twin defect class.
|
|
327
|
+
*/
|
|
328
|
+
export function checkSpatialInvariants(bounds, claims, options = {}) {
|
|
329
|
+
const tolerance = options.surfaceTolerance ?? Math.max(0.02, Math.max(...bounds.size) * 0.08);
|
|
330
|
+
const defaultMaxDistance = Math.max(...bounds.size) * 2;
|
|
331
|
+
const checks = claims.map((claim) => {
|
|
332
|
+
const inside = containsPoint(bounds, claim.position);
|
|
333
|
+
const surfaceDistance = distanceToBoundsSurface(bounds, claim.position);
|
|
334
|
+
const outsideDistance = distanceOutsideBounds(bounds, claim.position);
|
|
335
|
+
if (claim.relation === "inside") {
|
|
336
|
+
return {
|
|
337
|
+
id: claim.id,
|
|
338
|
+
description: `${claim.id} must lie inside the subject bounds`,
|
|
339
|
+
passes: inside,
|
|
340
|
+
detail: inside ? "inside bounds" : `outside bounds by ${round(outsideDistance)} units`
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
if (claim.relation === "surface") {
|
|
344
|
+
const passes = surfaceDistance <= tolerance;
|
|
345
|
+
return {
|
|
346
|
+
id: claim.id,
|
|
347
|
+
description: `${claim.id} must sit on the subject surface within ${round(tolerance)} units`,
|
|
348
|
+
passes,
|
|
349
|
+
detail: `distance to surface ${round(surfaceDistance)} (tolerance ${round(tolerance)})`
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
const limit = claim.maxDistance ?? defaultMaxDistance;
|
|
353
|
+
const passes = !inside && outsideDistance <= limit;
|
|
354
|
+
return {
|
|
355
|
+
id: claim.id,
|
|
356
|
+
description: `${claim.id} must sit beside the subject within ${round(limit)} units, not inside it`,
|
|
357
|
+
passes,
|
|
358
|
+
detail: inside
|
|
359
|
+
? "unexpectedly inside the subject bounds"
|
|
360
|
+
: `distance outside bounds ${round(outsideDistance)} (limit ${round(limit)})`
|
|
361
|
+
};
|
|
362
|
+
});
|
|
363
|
+
return {
|
|
364
|
+
schema: "aura3d-spatial-invariants/1.0",
|
|
365
|
+
subjectBounds: bounds,
|
|
366
|
+
checks,
|
|
367
|
+
passes: checks.every((check) => check.passes)
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/** Shortest distance from a point to the bounds surface (0 when on it). */
|
|
371
|
+
export function distanceToBoundsSurface(bounds, point) {
|
|
372
|
+
const outside = distanceOutsideBounds(bounds, point);
|
|
373
|
+
if (outside > 0)
|
|
374
|
+
return outside;
|
|
375
|
+
// Inside: distance to the nearest face.
|
|
376
|
+
return Math.min(point[0] - bounds.min[0], bounds.max[0] - point[0], point[1] - bounds.min[1], bounds.max[1] - point[1], point[2] - bounds.min[2], bounds.max[2] - point[2]);
|
|
377
|
+
}
|
|
378
|
+
/** Distance from a point to the bounds volume; 0 when inside. */
|
|
379
|
+
export function distanceOutsideBounds(bounds, point) {
|
|
380
|
+
const dx = Math.max(bounds.min[0] - point[0], 0, point[0] - bounds.max[0]);
|
|
381
|
+
const dy = Math.max(bounds.min[1] - point[1], 0, point[1] - bounds.max[1]);
|
|
382
|
+
const dz = Math.max(bounds.min[2] - point[2], 0, point[2] - bounds.max[2]);
|
|
383
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
384
|
+
}
|
|
385
|
+
function distance(a, b) {
|
|
386
|
+
const dx = a[0] - b[0];
|
|
387
|
+
const dy = a[1] - b[1];
|
|
388
|
+
const dz = a[2] - b[2];
|
|
389
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
390
|
+
}
|
|
391
|
+
function round(value) {
|
|
392
|
+
return Math.round(value * 1e6) / 1e6;
|
|
393
|
+
}
|
|
394
|
+
//# sourceMappingURL=SpatialAnchoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpatialAnchoring.js","sourceRoot":"","sources":["../../../../../packages/engine/src/agent-api/SpatialAnchoring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reusable vehicle chassis: contact, suspension, attitude and wheel visuals.
|
|
3
|
+
*
|
|
4
|
+
* ## The defects this addresses
|
|
5
|
+
*
|
|
6
|
+
* Turbo Drift Circuit drove a 2D kinematic point (`game.racing`) and then pinned
|
|
7
|
+
* the car's rendered Y to a single literal, `TRACK_SURFACE_Y`. That has three
|
|
8
|
+
* visible consequences:
|
|
9
|
+
*
|
|
10
|
+
* 1. **The car sinks into the tarmac.** A model grounded on a frozen plane cannot
|
|
11
|
+
* respond to the surface it is actually over. There is no contact model, so
|
|
12
|
+
* there is nothing to hold the tyres on the road.
|
|
13
|
+
* 2. **No suspension.** The chassis never pitches under braking, never rolls in a
|
|
14
|
+
* corner, and the wheels never travel. A car that stays perfectly level at
|
|
15
|
+
* 111 km/h reads as a sprite sliding on a plane, which is what it is.
|
|
16
|
+
* 3. **Wheels do not turn or steer.** Nothing derived wheel spin from speed or
|
|
17
|
+
* wheel yaw from steering input.
|
|
18
|
+
*
|
|
19
|
+
* This module is the reusable answer. It takes a planar vehicle state -- position,
|
|
20
|
+
* heading, speed, steering, lateral slip -- plus a surface query, and produces the
|
|
21
|
+
* full 3D pose a renderer needs: chassis height from four independent contact
|
|
22
|
+
* points, pitch and roll from load transfer, per-wheel suspension travel, wheel
|
|
23
|
+
* spin and steer angles, and a contact report that states whether every tyre is
|
|
24
|
+
* on the ground.
|
|
25
|
+
*
|
|
26
|
+
* It is pure: no renderer, no DOM, no asset loading. That makes vehicle behaviour
|
|
27
|
+
* unit-testable, which is the only way "the car is grounded" becomes a fact rather
|
|
28
|
+
* than an opinion about a screenshot.
|
|
29
|
+
*/
|
|
30
|
+
export type VehicleVec3 = readonly [number, number, number];
|
|
31
|
+
/** Surface sample under one contact point. */
|
|
32
|
+
export interface VehicleSurfaceSample {
|
|
33
|
+
/** World Y of the surface at the queried point. */
|
|
34
|
+
readonly height: number;
|
|
35
|
+
/** Surface normal. Defaults to world up when a track is flat. */
|
|
36
|
+
readonly normal?: VehicleVec3 | undefined;
|
|
37
|
+
/** Grip multiplier, 1 on tarmac and lower off-track. */
|
|
38
|
+
readonly grip?: number | undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Surface the vehicle drives on.
|
|
42
|
+
*
|
|
43
|
+
* A route supplies this from its own track topology. Because the chassis asks the
|
|
44
|
+
* surface for a height at each wheel rather than being told one number, swapping
|
|
45
|
+
* the track asset cannot leave the car buried: there is no frozen plane to be
|
|
46
|
+
* wrong about.
|
|
47
|
+
*/
|
|
48
|
+
export interface VehicleSurface {
|
|
49
|
+
sample(x: number, z: number): VehicleSurfaceSample;
|
|
50
|
+
}
|
|
51
|
+
/** Flat surface at a fixed height, for prototypes and tests. */
|
|
52
|
+
export declare function flatVehicleSurface(height?: number, grip?: number): VehicleSurface;
|
|
53
|
+
export interface VehicleChassisSpec {
|
|
54
|
+
/** Distance between front and rear axles, world units. */
|
|
55
|
+
readonly wheelbase: number;
|
|
56
|
+
/** Distance between left and right wheels, world units. */
|
|
57
|
+
readonly trackWidth: number;
|
|
58
|
+
/** Rolling radius of a wheel, world units. Drives visual spin rate. */
|
|
59
|
+
readonly wheelRadius: number;
|
|
60
|
+
/**
|
|
61
|
+
* Ride height: distance from the wheel contact patch to the chassis origin at
|
|
62
|
+
* rest. Derived from the asset's own bounds by the caller, never guessed.
|
|
63
|
+
*/
|
|
64
|
+
readonly rideHeight: number;
|
|
65
|
+
/** Suspension travel available above and below rest, world units. */
|
|
66
|
+
readonly suspensionTravel?: number | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* Spring stiffness as a normalized rate. Higher is firmer: the chassis resists
|
|
69
|
+
* pitch and roll more and settles faster.
|
|
70
|
+
*/
|
|
71
|
+
readonly springRate?: number | undefined;
|
|
72
|
+
/** Damping ratio. 1 is critically damped; below 1 oscillates. */
|
|
73
|
+
readonly dampingRatio?: number | undefined;
|
|
74
|
+
/** Maximum steering angle at the front wheels, radians. */
|
|
75
|
+
readonly maxSteerAngle?: number | undefined;
|
|
76
|
+
/** Maximum chassis pitch under full braking or acceleration, radians. */
|
|
77
|
+
readonly maxPitch?: number | undefined;
|
|
78
|
+
/** Maximum chassis roll at full lateral load, radians. */
|
|
79
|
+
readonly maxRoll?: number | undefined;
|
|
80
|
+
}
|
|
81
|
+
export interface VehiclePlanarState {
|
|
82
|
+
/** World X of the chassis centre. */
|
|
83
|
+
readonly x: number;
|
|
84
|
+
/** World Z of the chassis centre. */
|
|
85
|
+
readonly z: number;
|
|
86
|
+
/** Heading in radians, measured so that heading 0 faces +X. */
|
|
87
|
+
readonly heading: number;
|
|
88
|
+
/** Forward speed in world units per second. */
|
|
89
|
+
readonly speed: number;
|
|
90
|
+
/** Steering input, -1..1. */
|
|
91
|
+
readonly steer: number;
|
|
92
|
+
/** Throttle input, 0..1. */
|
|
93
|
+
readonly throttle?: number | undefined;
|
|
94
|
+
/** Brake input, 0..1. */
|
|
95
|
+
readonly brake?: number | undefined;
|
|
96
|
+
/** Lateral slip, 0..1, used for roll and tyre scrub. */
|
|
97
|
+
readonly slip?: number | undefined;
|
|
98
|
+
}
|
|
99
|
+
export type VehicleWheelId = "front-left" | "front-right" | "rear-left" | "rear-right";
|
|
100
|
+
export interface VehicleWheelPose {
|
|
101
|
+
readonly id: VehicleWheelId;
|
|
102
|
+
/** World position of the wheel centre. */
|
|
103
|
+
readonly position: VehicleVec3;
|
|
104
|
+
/** Steering angle applied to this wheel, radians. Zero for rear wheels. */
|
|
105
|
+
readonly steerAngle: number;
|
|
106
|
+
/** Accumulated rolling rotation, radians. */
|
|
107
|
+
readonly spin: number;
|
|
108
|
+
/** Suspension compression, 0 at full extension and 1 at full compression. */
|
|
109
|
+
readonly compression: number;
|
|
110
|
+
/** True when this wheel's contact patch is on the surface. */
|
|
111
|
+
readonly grounded: boolean;
|
|
112
|
+
/** Distance from the contact patch to the surface. Zero when grounded. */
|
|
113
|
+
readonly contactGap: number;
|
|
114
|
+
}
|
|
115
|
+
export interface VehiclePose {
|
|
116
|
+
/**
|
|
117
|
+
* Chassis origin in world space: the body's centre height, as a rigid body would
|
|
118
|
+
* report it.
|
|
119
|
+
*
|
|
120
|
+
* Use this for a model whose pivot is at its own centre. For a model rendered with
|
|
121
|
+
* `scaleMode: "fit"`, use {@link groundedPosition} instead -- the safe renderer grounds
|
|
122
|
+
* a fitted model's *lowest point* on its node position, so placing the body centre
|
|
123
|
+
* there lifts the whole vehicle by its ride height. That mismatch is visible as a car
|
|
124
|
+
* hovering above the road, which is the same class of defect as the sinking it
|
|
125
|
+
* replaced, in the opposite direction.
|
|
126
|
+
*/
|
|
127
|
+
readonly position: VehicleVec3;
|
|
128
|
+
/**
|
|
129
|
+
* Contact-plane position: the height the lowest tyre contact patch sits at.
|
|
130
|
+
*
|
|
131
|
+
* This is what a `scaleMode: "fit"` model's node position should be set to.
|
|
132
|
+
*/
|
|
133
|
+
readonly groundedPosition: VehicleVec3;
|
|
134
|
+
/** Euler rotation: pitch on X, yaw on Y, roll on Z. */
|
|
135
|
+
readonly rotation: VehicleVec3;
|
|
136
|
+
readonly wheels: readonly VehicleWheelPose[];
|
|
137
|
+
/** Height of the lowest contact patch above the surface. */
|
|
138
|
+
readonly contactGap: number;
|
|
139
|
+
/** True when every wheel is on the ground. */
|
|
140
|
+
readonly grounded: boolean;
|
|
141
|
+
/** Mean suspension compression across all four wheels. */
|
|
142
|
+
readonly averageCompression: number;
|
|
143
|
+
}
|
|
144
|
+
export interface VehicleChassisTelemetry {
|
|
145
|
+
readonly speed: number;
|
|
146
|
+
readonly speedKph: number;
|
|
147
|
+
readonly pitch: number;
|
|
148
|
+
readonly roll: number;
|
|
149
|
+
readonly steerAngle: number;
|
|
150
|
+
readonly wheelSpinRate: number;
|
|
151
|
+
readonly groundedWheels: number;
|
|
152
|
+
readonly maxContactGap: number;
|
|
153
|
+
readonly averageCompression: number;
|
|
154
|
+
readonly surfaceGrip: number;
|
|
155
|
+
}
|
|
156
|
+
export interface VehicleChassis {
|
|
157
|
+
readonly kind: "aura-vehicle-chassis";
|
|
158
|
+
readonly spec: Required<Omit<VehicleChassisSpec, never>>;
|
|
159
|
+
/** Advance suspension and wheel state, returning the resolved 3D pose. */
|
|
160
|
+
step(dt: number, state: VehiclePlanarState): VehiclePose;
|
|
161
|
+
/** Most recent pose without advancing. */
|
|
162
|
+
pose(): VehiclePose;
|
|
163
|
+
telemetry(): VehicleChassisTelemetry;
|
|
164
|
+
/** Return to rest at a planar state, clearing suspension and spin history. */
|
|
165
|
+
reset(state: VehiclePlanarState): VehiclePose;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Create a vehicle chassis.
|
|
169
|
+
*
|
|
170
|
+
* `rideHeight` and `wheelRadius` should be derived from the vehicle asset's own
|
|
171
|
+
* bounds. That is the difference between this and the constant it replaces: the
|
|
172
|
+
* caller states a proportion of a measured asset, not a number that happened to
|
|
173
|
+
* look right once.
|
|
174
|
+
*/
|
|
175
|
+
export declare function createVehicleChassis(spec: VehicleChassisSpec, surface: VehicleSurface): VehicleChassis;
|
|
176
|
+
/**
|
|
177
|
+
* Derive chassis geometry from a vehicle asset's rendered bounds.
|
|
178
|
+
*
|
|
179
|
+
* This is what removes the need for a `CAR_SCENE_HEIGHT`-style constant. Given the
|
|
180
|
+
* size the asset actually renders at, the wheelbase, track width, wheel radius and
|
|
181
|
+
* ride height follow from standard automotive proportions, so a different car
|
|
182
|
+
* produces a different chassis instead of inheriting the old one's numbers.
|
|
183
|
+
*/
|
|
184
|
+
export declare function vehicleChassisSpecFromBounds(size: VehicleVec3, options?: {
|
|
185
|
+
/** Fraction of the body length between the axles. Real cars sit near 0.6. */
|
|
186
|
+
readonly wheelbaseFraction?: number | undefined;
|
|
187
|
+
/** Fraction of the body width between the wheel centres. */
|
|
188
|
+
readonly trackFraction?: number | undefined;
|
|
189
|
+
/** Fraction of body height taken by wheel diameter. */
|
|
190
|
+
readonly wheelDiameterFraction?: number | undefined;
|
|
191
|
+
}): VehicleChassisSpec;
|
|
192
|
+
//# sourceMappingURL=VehicleChassis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VehicleChassis.d.ts","sourceRoot":"","sources":["../../../../../packages/engine/src/agent-api/VehicleChassis.ts"],"names":[],"mappings":"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"}
|