three-usd-robot 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 fabcowork
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # three-usd-robot
2
+
3
+ > **Kinematic OpenUSD robot loader for Three.js.**
4
+ > Load Isaac Sim / OpenUSD robot assets, extract joints and links, and control
5
+ > articulations directly in the browser — no physics engine required.
6
+
7
+ Think of it as a **USD version of [`urdf-loader`](https://www.npmjs.com/package/urdf-loader)**:
8
+ it reads the link / joint / xform / mesh structure out of `UsdPhysics` robot assets
9
+ and drives forward kinematics on a Three.js `Object3D` hierarchy.
10
+
11
+ > 🚧 **Status: v0.2 + USDC.** Loads **ASCII `.usda`**, **binary `.usdc` / `.usd`
12
+ > (crate)**, and **`.usdz`** robots — including multi-file assets via
13
+ > references/payloads/sublayers — drives forward kinematics with meshes,
14
+ > normalizes up-axis & units, and seeds the initial pose from joint drives. The
15
+ > crate reader is a from-scratch TypeScript implementation (no OpenUSD/WASM
16
+ > dependency). Not yet: variants, instancing, and time-sampled (animated)
17
+ > values (see [`MILESTONES.md`](./MILESTONES.md), M10–M11).
18
+
19
+ ```ts
20
+ // .usda / .usdc / binary .usd / .usdz are all auto-detected:
21
+ const robot = await new ThreeUsdRobotLoader().loadAsync("/assets/robot.usd");
22
+ // or from bytes you already have:
23
+ const robot = await new ThreeUsdRobotLoader().parseCrate(usdcBytes);
24
+ ```
25
+
26
+ ## Install
27
+
28
+ ```sh
29
+ npm install three-usd-robot three
30
+ ```
31
+
32
+ `three` is a **peer dependency** (`>=0.160.0`).
33
+
34
+ ## Usage
35
+
36
+ ```ts
37
+ import { ThreeUsdRobotLoader } from "three-usd-robot";
38
+
39
+ const robot = await new ThreeUsdRobotLoader().loadAsync("/assets/arm.usda");
40
+ scene.add(robot);
41
+
42
+ // Drive joints (revolute/continuous in radians, prismatic in stage units).
43
+ robot.setJointValues({ joint1: 0.4, joint2: -0.2 });
44
+
45
+ // Forward kinematics falls out of the Three.js scene graph.
46
+ const handMatrix = robot.getLinkWorldMatrix("tool0"); // THREE.Matrix4
47
+ const handPos = robot.getLinkWorldPosition("tool0"); // THREE.Vector3
48
+
49
+ robot.getJointNames(); // controllable joints
50
+ robot.getKinematicTree(); // root, ordering, loopJoints, ...
51
+ ```
52
+
53
+ ### Viewer toggles & helpers
54
+
55
+ ```ts
56
+ robot.showVisual = true;
57
+ robot.showCollision = false;
58
+ robot.showJointAxes = true; // built-in axes gizmos on each joint
59
+ robot.showLinkFrames = false;
60
+
61
+ import { addJointLimitHelpers } from "three-usd-robot/helpers";
62
+ addJointLimitHelpers(robot); // arc (revolute) / segment (prismatic) per joint
63
+ ```
64
+
65
+ ### Joint slider panel (lil-gui)
66
+
67
+ ```ts
68
+ import GUI from "lil-gui";
69
+ import { createJointSliderPanel } from "three-usd-robot/extras";
70
+
71
+ createJointSliderPanel(robot, new GUI()); // one slider per articulated joint
72
+ ```
73
+
74
+ The `extras` panel takes the GUI instance from you, so the library never bundles
75
+ `lil-gui`. See [`examples/`](./examples) for runnable Vite demos.
76
+
77
+ ### Inspect without Three.js
78
+
79
+ ```ts
80
+ import { parseUsda, Stage, extractRobotDescription } from "three-usd-robot/core";
81
+
82
+ const desc = extractRobotDescription(Stage.OpenFromString(usdaText));
83
+ console.log(desc.rootLink, Object.keys(desc.joints));
84
+ ```
85
+
86
+ ## Package entry points
87
+
88
+ | Import | Contents |
89
+ | --- | --- |
90
+ | `three-usd-robot` | Three.js runtime — `ThreeUsdRobotLoader`, `ThreeUsdRobot` |
91
+ | `three-usd-robot/core` | Three.js-independent USDA parser, robot IR, forward-kinematics math |
92
+ | `three-usd-robot/helpers` | Viewer helpers (joint axes, link frames, joint limits) |
93
+ | `three-usd-robot/extras` | Heavier convenience utilities (e.g. joint slider panel) |
94
+
95
+ ## Development
96
+
97
+ ```sh
98
+ npm install
99
+ npm run typecheck # tsc --noEmit
100
+ npm test # vitest
101
+ npm run build # tsup -> dist/
102
+ npm run check # biome lint + format (autofix)
103
+ ```
104
+
105
+ ## License
106
+
107
+ MIT
@@ -0,0 +1,121 @@
1
+ import * as THREE from 'three';
2
+ import { d as JointType, A as Axis, J as JointDescription, L as LinkDescription, R as RobotDescription, e as KinematicTree } from './buildKinematicTree-2fg6ZN8m.js';
3
+
4
+ /**
5
+ * The articulated "motion" node of a joint, inserted between the joint's two
6
+ * fixed frames (`jointFrame0` and `inverse(jointFrame1)`). {@link setValue}
7
+ * rotates it about the axis (revolute/continuous) or slides it along the axis
8
+ * (prismatic); fixed joints are inert.
9
+ */
10
+ declare class JointObject extends THREE.Object3D {
11
+ readonly isJointObject = true;
12
+ readonly jointName: string;
13
+ readonly jointType: JointType;
14
+ readonly axisToken: Axis;
15
+ readonly axis: THREE.Vector3;
16
+ readonly lower: number | undefined;
17
+ readonly upper: number | undefined;
18
+ private _value;
19
+ constructor(joint: JointDescription);
20
+ get value(): number;
21
+ get articulated(): boolean;
22
+ /**
23
+ * Set the joint value (radians for revolute/continuous, length for prismatic).
24
+ * Optionally clamps to authored limits. Returns the value actually applied.
25
+ */
26
+ setValue(value: number, clampToLimits?: boolean): number;
27
+ }
28
+
29
+ /**
30
+ * Three.js node for a robot link. Its frame is the USD link prim's frame;
31
+ * visual/collision meshes (M6) attach as children relative to it. Placement
32
+ * relative to the parent is supplied by the joint chain, so a link's own local
33
+ * matrix is identity (except the root, which carries the world-fixed placement).
34
+ */
35
+ declare class LinkObject extends THREE.Object3D {
36
+ readonly isLinkObject = true;
37
+ readonly linkName: string;
38
+ readonly primPath: string;
39
+ constructor(link: LinkDescription);
40
+ }
41
+
42
+ type ThreeUsdRobotOptions = {
43
+ /** Clamp `setJointValue` to authored limits (default `true`). */
44
+ clampJointLimits?: boolean;
45
+ /** Size of the built-in joint-axis / link-frame helpers (stage units, default `0.15`). */
46
+ helperSize?: number;
47
+ /**
48
+ * Up-axis correction applied at the robot root. `"auto"` rotates Z-up assets
49
+ * into the Three.js Y-up scene; `"Z"` forces it, `"Y"`/`"none"` leave the
50
+ * orientation as authored. Default `"none"` (the loader defaults to `"auto"`).
51
+ */
52
+ upAxisConversion?: "auto" | "Y" | "Z" | "none";
53
+ /** Extra uniform scale multiplied with the stage `metersPerUnit` (default `1`). */
54
+ unitScale?: number;
55
+ /** Seed joints from their authored initial value (drive target / joint state). Default `true`. */
56
+ applyInitialPose?: boolean;
57
+ };
58
+ /**
59
+ * A Three.js `Object3D` that realizes a {@link RobotDescription} as a kinematic
60
+ * hierarchy and drives forward kinematics via {@link setJointValue}.
61
+ *
62
+ * Per joint the hierarchy is
63
+ * `parentLink → jointFrame0 → jointMotion → jointFrame1⁻¹ → childLink`,
64
+ * where only `jointMotion` (a {@link JointObject}) changes with the joint value;
65
+ * world poses then fall out of Three.js's `updateMatrixWorld`.
66
+ */
67
+ declare class ThreeUsdRobot extends THREE.Object3D {
68
+ readonly isThreeUsdRobot = true;
69
+ readonly robot: RobotDescription;
70
+ readonly tree: KinematicTree;
71
+ readonly clampJointLimits: boolean;
72
+ private readonly linkObjects;
73
+ private readonly jointObjects;
74
+ private dirty;
75
+ private readonly helperSize;
76
+ private _showVisual;
77
+ private _showCollision;
78
+ private _showJointAxes;
79
+ private _showLinkFrames;
80
+ private jointAxesHelpers;
81
+ private linkFrameHelpers;
82
+ constructor(robot: RobotDescription, tree: KinematicTree, options?: ThreeUsdRobotOptions);
83
+ /** Orient (Z-up → Y-up) and scale (metersPerUnit × unitScale) the robot root. */
84
+ private applyStageNormalization;
85
+ /** Apply each joint's authored initial value, if any. */
86
+ private applyInitialPose;
87
+ private attachRoot;
88
+ private attachTreeEdges;
89
+ /** Build `parent → frame0 → motion → frame1⁻¹ → child` for one joint. */
90
+ private attachJointChain;
91
+ private attachIsolatedLinks;
92
+ /** Set one joint value. Unknown joints are ignored. Returns whether it applied. */
93
+ setJointValue(name: string, value: number): boolean;
94
+ /** Set several joint values at once (matrix update is coalesced). */
95
+ setJointValues(values: Record<string, number>): void;
96
+ getJointValue(name: string): number | undefined;
97
+ /** Recompute world matrices. Called lazily by the getters; safe to call directly. */
98
+ updateKinematics(): void;
99
+ private ensureUpdated;
100
+ getLinkWorldMatrix(name: string): THREE.Matrix4;
101
+ getLinkWorldPosition(name: string): THREE.Vector3;
102
+ getLinkObject(name: string): LinkObject | undefined;
103
+ getJointObject(name: string): JointObject | undefined;
104
+ getJoints(): JointDescription[];
105
+ getLinks(): LinkDescription[];
106
+ /** Names of the articulated (controllable) joints. */
107
+ getJointNames(): string[];
108
+ getLinkNames(): string[];
109
+ getKinematicTree(): KinematicTree;
110
+ get showVisual(): boolean;
111
+ set showVisual(v: boolean);
112
+ get showCollision(): boolean;
113
+ set showCollision(v: boolean);
114
+ get showJointAxes(): boolean;
115
+ set showJointAxes(v: boolean);
116
+ get showLinkFrames(): boolean;
117
+ set showLinkFrames(v: boolean);
118
+ private setKindVisibility;
119
+ }
120
+
121
+ export { JointObject as J, LinkObject as L, ThreeUsdRobot as T, type ThreeUsdRobotOptions as a };
@@ -0,0 +1,283 @@
1
+ /**
2
+ * AST and value types for the USDA (ASCII USD) format.
3
+ *
4
+ * The parser (`parseUsda`) produces a {@link UsdaFile}. The USD-conformant
5
+ * runtime layer (`usd/Stage.ts` etc.) wraps this AST to expose a pxr-USD-style
6
+ * API (`GetPrimAtPath`, `GetAttribute`, `Get`, `GetTargets`, ...).
7
+ */
8
+ /** Fixed-length numeric tuples, mirroring `GfVec2/3/4`. */
9
+ type Vec2 = [number, number];
10
+ type Vec3 = [number, number, number];
11
+ type Vec4 = [number, number, number, number];
12
+ /**
13
+ * A quaternion, mirroring `GfQuatf/d/h`.
14
+ *
15
+ * USDA authors quats as `(w, x, y, z)` — real part first. We keep the real and
16
+ * imaginary parts explicit so downstream code never has to guess the component
17
+ * order (Three.js, by contrast, uses `(x, y, z, w)`).
18
+ */
19
+ declare class Quat {
20
+ readonly real: number;
21
+ readonly imaginary: Vec3;
22
+ constructor(real: number, imaginary: Vec3);
23
+ /** Components in Three.js order `[x, y, z, w]`. */
24
+ toXYZW(): Vec4;
25
+ static identity(): Quat;
26
+ }
27
+ /**
28
+ * A square matrix (`GfMatrix3d`/`GfMatrix4d`), stored flat in **row-major**
29
+ * order exactly as authored in USD (USD matrices are row-major).
30
+ */
31
+ declare class UsdMatrix {
32
+ readonly values: number[];
33
+ readonly dim: 3 | 4;
34
+ constructor(values: number[], dim: 3 | 4);
35
+ static identity4(): UsdMatrix;
36
+ }
37
+ /** An asset reference (`SdfAssetPath`), e.g. `@./meshes/link0.usd@`. */
38
+ declare class AssetPath {
39
+ readonly path: string;
40
+ constructor(path: string);
41
+ }
42
+ /**
43
+ * A namespace path into a stage (`SdfPath`), e.g. `/World/robot/link0` or a
44
+ * property path `/World/robot.xformOp:translate`. Stored as the authored
45
+ * string; relationship/connection resolution happens in the runtime layer.
46
+ */
47
+ type SdfPath = string;
48
+ /** A reference / payload composition arc as authored (resolved in M8). */
49
+ type CompositionArc = {
50
+ assetPath?: AssetPath;
51
+ primPath?: SdfPath;
52
+ };
53
+ /** Any value an attribute or metadatum can hold. */
54
+ type UsdValue = number | boolean | string | null | Vec2 | Vec3 | Vec4 | Quat | UsdMatrix | AssetPath | SdfPath | CompositionArc | UsdValue[] | UsdDictionary;
55
+ /** A nested metadata dictionary (`customData`, `assetInfo`, ...). */
56
+ type UsdDictionary = {
57
+ [key: string]: UsdValue;
58
+ };
59
+ type Specifier = "def" | "over" | "class";
60
+ type Variability = "varying" | "uniform";
61
+ /** List-editing qualifier on relationships and list-op metadata. */
62
+ type ListOp = "explicit" | "prepend" | "append" | "add" | "delete" | "reorder";
63
+ type MetadataMap = {
64
+ [key: string]: UsdValue;
65
+ };
66
+ /** A parsed `.usda` layer. */
67
+ type UsdaFile = {
68
+ /** Version from the `#usda X.Y` magic line. */
69
+ version: string;
70
+ /** Layer-level metadata (`defaultPrim`, `upAxis`, `metersPerUnit`, ...). */
71
+ metadata: MetadataMap;
72
+ /** Root prims, in authored order. */
73
+ prims: PrimSpec[];
74
+ };
75
+ type PrimSpec = {
76
+ specifier: Specifier;
77
+ /** Schema type, e.g. `Xform`, `Mesh`, `PhysicsRevoluteJoint`. Empty if typeless. */
78
+ typeName: string;
79
+ name: string;
80
+ metadata: MetadataMap;
81
+ properties: PropertySpec[];
82
+ children: PrimSpec[];
83
+ /** 1-based source line of the prim declaration (for diagnostics). */
84
+ line: number;
85
+ };
86
+ type PropertySpec = AttributeSpec | RelationshipSpec;
87
+ type AttributeSpec = {
88
+ kind: "attribute";
89
+ name: string;
90
+ /** USD type name without the `[]` suffix, e.g. `float3`, `token`, `quatf`. */
91
+ typeName: string;
92
+ isArray: boolean;
93
+ variability: Variability;
94
+ custom: boolean;
95
+ /** Default (time-independent) value, if authored. */
96
+ value?: UsdValue;
97
+ /** Time samples keyed by time code, if authored as `{ t: v, ... }`. */
98
+ timeSamples?: Map<number, UsdValue>;
99
+ /** Connection target paths (`attr.connect = <path>`), if authored. */
100
+ connections?: SdfPath[];
101
+ metadata: MetadataMap;
102
+ line: number;
103
+ };
104
+ type RelationshipSpec = {
105
+ kind: "relationship";
106
+ name: string;
107
+ custom: boolean;
108
+ listOp: ListOp;
109
+ /** Target paths; empty when authored as `None`. */
110
+ targets: SdfPath[];
111
+ metadata: MetadataMap;
112
+ line: number;
113
+ };
114
+
115
+ /**
116
+ * Minimal 4×4 matrix math, Three.js-independent.
117
+ *
118
+ * ## Convention
119
+ *
120
+ * A {@link Mat4} is a flat 16-element array in **column-major** order — the
121
+ * exact layout of `THREE.Matrix4.elements`, so a `Mat4` can be handed to
122
+ * `matrix4.fromArray(m)` with no transposition. Transforms use the
123
+ * column-vector convention `v' = M · v`; the translation lives at indices
124
+ * `[12, 13, 14]`.
125
+ *
126
+ * Conveniently, OpenUSD stores `GfMatrix4d` **row-major** with a row-vector
127
+ * convention (`v' = v · M`). The two raw 16-number arrays for the *same*
128
+ * transform are therefore bit-identical, which is why {@link fromUsdMatrix} is
129
+ * a plain copy. See the module test for a worked example.
130
+ */
131
+
132
+ /** Column-major 4×4 matrix (Three.js `elements` layout). */
133
+ type Mat4 = number[];
134
+ declare const DEG2RAD: number;
135
+ declare const RAD2DEG: number;
136
+ declare function identity4(): Mat4;
137
+ /** Matrix product `a · b` (column-major), matching `THREE.Matrix4.multiplyMatrices`. */
138
+ declare function multiply(a: Mat4, b: Mat4): Mat4;
139
+ /** Product of a list of matrices, left-to-right (`m[0] · m[1] · …`). */
140
+ declare function multiplyAll(matrices: Mat4[]): Mat4;
141
+ /** Inverse (column-major), matching `THREE.Matrix4.invert`. Throws if singular. */
142
+ declare function invert(m: Mat4): Mat4;
143
+ declare function makeTranslation([x, y, z]: Vec3): Mat4;
144
+ declare function makeScale([x, y, z]: Vec3): Mat4;
145
+ declare function makeRotationX(rad: number): Mat4;
146
+ declare function makeRotationY(rad: number): Mat4;
147
+ declare function makeRotationZ(rad: number): Mat4;
148
+ /** Rotation matrix from a (normalized) quaternion, matching `THREE.Matrix4.compose`. */
149
+ declare function makeRotationFromQuat(q: Quat): Mat4;
150
+ /**
151
+ * Euler rotation matrix for the given axis order (e.g. `"XYZ"`), with per-axis
152
+ * angles in **radians** indexed by axis (`angles[0]`=X, `[1]`=Y, `[2]`=Z).
153
+ *
154
+ * Follows OpenUSD's `rotate<ORDER>` semantics: the first listed axis is applied
155
+ * first to the geometry, so `"XYZ"` yields `Rz · Ry · Rx` in column-vector form.
156
+ */
157
+ declare function makeEuler(angles: Vec3, order: string): Mat4;
158
+ /**
159
+ * Copy a USD matrix into a {@link Mat4}. USD's row-major storage is identical to
160
+ * Three.js column-major elements for the same transform (see module docs), so
161
+ * this is a straight copy after a dimension check.
162
+ */
163
+ declare function fromUsdMatrix(m: UsdMatrix): Mat4;
164
+ /** Extract the translation component `[x, y, z]` from a {@link Mat4}. */
165
+ declare function getTranslation(m: Mat4): Vec3;
166
+
167
+ /**
168
+ * Robot intermediate representation (IR) — Three.js-independent.
169
+ *
170
+ * The extractor (`RobotExtractor`) turns a USD {@link Stage} into a
171
+ * {@link RobotDescription}; the kinematics builder (M4) trees it; the Three.js
172
+ * runtime (M5) realizes it as an `Object3D` hierarchy. Keeping the IR free of
173
+ * Three.js lets the same data drive tooling, validation, and (later) IK.
174
+ *
175
+ * See concept.md §9 for the rationale behind each field.
176
+ */
177
+
178
+ type JointType = "fixed" | "revolute" | "continuous" | "prismatic";
179
+ /** Joint axis token as authored in USD (`physics:axis`); never a vector here. */
180
+ type Axis = "X" | "Y" | "Z";
181
+ type RobotDescription = {
182
+ name: string;
183
+ /** Key of the root link (see {@link LinkDescription} keys). */
184
+ rootLink: string;
185
+ links: Record<string, LinkDescription>;
186
+ joints: Record<string, JointDescription>;
187
+ /** Link keys whose prim carries `PhysicsArticulationRootAPI` (root hint for M4). */
188
+ articulationRoots?: string[];
189
+ /** Joints dropped from the spanning tree to break closed loops (filled in M4). */
190
+ loopJoints?: string[];
191
+ /** Stage up axis, for the M9 up-axis correction. */
192
+ upAxis: "Y" | "Z";
193
+ /** Stage linear unit, for the M9 scale normalization. */
194
+ metersPerUnit: number;
195
+ /** Non-fatal extraction diagnostics. */
196
+ warnings?: string[];
197
+ };
198
+ type LinkDescription = {
199
+ /** Display name (leaf prim name). */
200
+ name: string;
201
+ primPath: string;
202
+ /** Mesh prim paths to render. */
203
+ visualPrims: string[];
204
+ /** Mesh prim paths flagged with a collision API. */
205
+ collisionPrims?: string[];
206
+ };
207
+ type JointDescription = {
208
+ /** Display name (leaf prim name). */
209
+ name: string;
210
+ primPath: string;
211
+ type: JointType;
212
+ /** Parent link key (body0). Empty string `""` means fixed to the world. */
213
+ parent: string;
214
+ /** Child link key (body1). */
215
+ child: string;
216
+ axis: Axis;
217
+ lower?: number;
218
+ upper?: number;
219
+ effort?: number;
220
+ velocity?: number;
221
+ /** Joint frame in the parent (body0) local space: `(localPos0, localRot0)`. */
222
+ jointFrame0: Mat4;
223
+ /** Joint frame in the child (body1) local space: `(localPos1, localRot1)`. */
224
+ jointFrame1: Mat4;
225
+ /** Initial joint value (SI) from JointStateAPI or a drive target, if authored. */
226
+ initialValue?: number;
227
+ drive?: JointDriveDescription;
228
+ };
229
+ /** Authored joint drive parameters (`UsdPhysicsDriveAPI`), as read in M3. */
230
+ type JointDriveDescription = {
231
+ /** Target position in SI (radians for angular, linear units for linear). */
232
+ targetPosition?: number;
233
+ /** Gains/limits as authored (not unit-normalized). */
234
+ stiffness?: number;
235
+ damping?: number;
236
+ maxForce?: number;
237
+ };
238
+
239
+ /**
240
+ * Builds a kinematic spanning tree from a {@link RobotDescription}.
241
+ *
242
+ * USD physics joints form a directed graph (parent `body0` → child `body1`,
243
+ * with an empty `body0` meaning "fixed to world"). Three.js needs a tree, so we
244
+ * pick a root, breadth-first walk the real (link→link) edges, and drop any edge
245
+ * that would revisit a link — those are recorded as {@link KinematicTree.loopJoints}
246
+ * (closed chains / parallel mechanisms) rather than silently corrupting the tree.
247
+ */
248
+
249
+ type TreeEdge = {
250
+ /** Joint key connecting this node to the child. */
251
+ joint: string;
252
+ /** Child link key. */
253
+ child: string;
254
+ };
255
+ type KinematicNode = {
256
+ link: string;
257
+ /** Parent link key, or `null` for the root. */
258
+ parent: string | null;
259
+ /** Joint key to the parent, or `null` for the root. */
260
+ jointToParent: string | null;
261
+ children: TreeEdge[];
262
+ /** Distance from the root (root = 0). */
263
+ depth: number;
264
+ };
265
+ type KinematicTree = {
266
+ root: string;
267
+ /** World-fixed joint attaching the root to the world frame, if any. */
268
+ rootJoint: string | null;
269
+ nodes: Record<string, KinematicNode>;
270
+ /** Link keys in breadth-first order (parents before children). */
271
+ order: string[];
272
+ /** Joints dropped from the tree to break closed loops. */
273
+ loopJoints: string[];
274
+ /** Links not reachable from the root. */
275
+ isolatedLinks: string[];
276
+ warnings: string[];
277
+ };
278
+ type BuildTreeOptions = {
279
+ onWarn?: (message: string) => void;
280
+ };
281
+ declare function buildKinematicTree(robot: RobotDescription, options?: BuildTreeOptions): KinematicTree;
282
+
283
+ export { type Axis as A, type BuildTreeOptions as B, type CompositionArc as C, DEG2RAD as D, makeRotationZ as E, makeScale as F, makeTranslation as G, multiply as H, multiplyAll as I, type JointDescription as J, type KinematicNode as K, type LinkDescription as L, type Mat4 as M, type PrimSpec as P, Quat as Q, type RobotDescription as R, type SdfPath as S, type TreeEdge as T, type UsdDictionary as U, type Variability as V, AssetPath as a, type AttributeSpec as b, type JointDriveDescription as c, type JointType as d, type KinematicTree as e, type ListOp as f, type MetadataMap as g, type PropertySpec as h, RAD2DEG as i, type RelationshipSpec as j, type Specifier as k, UsdMatrix as l, type UsdValue as m, type UsdaFile as n, type Vec2 as o, type Vec3 as p, type Vec4 as q, buildKinematicTree as r, fromUsdMatrix as s, getTranslation as t, identity4 as u, invert as v, makeEuler as w, makeRotationFromQuat as x, makeRotationX as y, makeRotationY as z };