three-usd-robot 0.2.0 → 0.4.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.
@@ -507,26 +507,54 @@ function parsePrim(r) {
507
507
  if (r.is("ident")) typeName = r.expectIdent();
508
508
  const name = r.expect("string").value;
509
509
  const metadata = r.is("lparen") ? parseMetadataBlock(r) : {};
510
+ r.expect("lbrace");
511
+ const body = parseBody(r);
512
+ r.expect("rbrace");
513
+ const prim = {
514
+ specifier,
515
+ typeName,
516
+ name,
517
+ metadata,
518
+ properties: body.properties,
519
+ children: body.children,
520
+ line: head.line
521
+ };
522
+ if (body.variantSets) prim.variantSets = body.variantSets;
523
+ return prim;
524
+ }
525
+ function parseBody(r) {
510
526
  const properties = [];
511
527
  const children = [];
512
- r.expect("lbrace");
528
+ let variantSets;
513
529
  while (!r.is("rbrace") && !r.atEnd()) {
514
530
  if (r.is("ident") && SPECIFIERS.has(r.peek().value)) {
515
531
  children.push(parsePrim(r));
532
+ } else if (r.isIdent("variantSet")) {
533
+ const { setName, variants } = parseVariantSet(r);
534
+ variantSets ??= {};
535
+ variantSets[setName] = variants;
516
536
  } else {
517
537
  properties.push(parseProperty(r));
518
538
  }
519
539
  }
540
+ return variantSets ? { properties, children, variantSets } : { properties, children };
541
+ }
542
+ function parseVariantSet(r) {
543
+ r.expectIdent();
544
+ const setName = r.expect("string").value;
545
+ r.expect("equals");
546
+ r.expect("lbrace");
547
+ const variants = {};
548
+ while (!r.is("rbrace") && !r.atEnd()) {
549
+ const variantName = r.expect("string").value;
550
+ if (r.is("lparen")) parseMetadataBlock(r);
551
+ r.expect("lbrace");
552
+ const body = parseBody(r);
553
+ r.expect("rbrace");
554
+ variants[variantName] = { properties: body.properties, children: body.children };
555
+ }
520
556
  r.expect("rbrace");
521
- return {
522
- specifier,
523
- typeName,
524
- name,
525
- metadata,
526
- properties,
527
- children,
528
- line: head.line
529
- };
557
+ return { setName, variants };
530
558
  }
531
559
  function parseProperty(r) {
532
560
  const line = r.peek().line;
@@ -652,6 +680,10 @@ function parseMetadataValue(r) {
652
680
  if (raw.t === "asset") {
653
681
  return raw.v;
654
682
  }
683
+ if (raw.t === "path") {
684
+ const arc = { primPath: raw.v };
685
+ return arc;
686
+ }
655
687
  return rawToUsdValue(raw);
656
688
  }
657
689
  function parseMetadataList(r) {
@@ -666,6 +698,9 @@ function parseMetadataList(r) {
666
698
  } else {
667
699
  items.push(assetPath);
668
700
  }
701
+ } else if (r.is("path")) {
702
+ const arc = { primPath: r.next().value };
703
+ items.push(arc);
669
704
  } else {
670
705
  items.push(rawToUsdValue(parseLiteral(r)));
671
706
  }
@@ -1005,6 +1040,21 @@ var Stage = class _Stage {
1005
1040
  const v = this._layer.GetMetadata("metersPerUnit");
1006
1041
  return typeof v === "number" ? v : DEFAULT_METERS_PER_UNIT;
1007
1042
  }
1043
+ /** Animation start time code, if authored. */
1044
+ GetStartTimeCode() {
1045
+ const v = this._layer.GetMetadata("startTimeCode");
1046
+ return typeof v === "number" ? v : void 0;
1047
+ }
1048
+ /** Animation end time code, if authored. */
1049
+ GetEndTimeCode() {
1050
+ const v = this._layer.GetMetadata("endTimeCode");
1051
+ return typeof v === "number" ? v : void 0;
1052
+ }
1053
+ /** Time codes per second for playback; defaults to 24. */
1054
+ GetTimeCodesPerSecond() {
1055
+ const v = this._layer.GetMetadata("timeCodesPerSecond") ?? this._layer.GetMetadata("framesPerSecond");
1056
+ return typeof v === "number" && v > 0 ? v : 24;
1057
+ }
1008
1058
  };
1009
1059
 
1010
1060
  // src/usd/AssetResolver.ts
@@ -1028,14 +1078,21 @@ var DefaultAssetResolver = class {
1028
1078
  }
1029
1079
  };
1030
1080
  function createMemoryResolver(files) {
1081
+ const decoder = new TextDecoder();
1082
+ const encoder = new TextEncoder();
1031
1083
  return {
1032
1084
  resolve(assetPath, baseUrl) {
1033
1085
  return joinPosix(baseUrl, assetPath);
1034
1086
  },
1035
1087
  fetchText(url) {
1036
- const text = files[url];
1037
- if (text === void 0) return Promise.reject(new Error(`asset not found: ${url}`));
1038
- return Promise.resolve(text);
1088
+ const v = files[url];
1089
+ if (v === void 0) return Promise.reject(new Error(`asset not found: ${url}`));
1090
+ return Promise.resolve(typeof v === "string" ? v : decoder.decode(v));
1091
+ },
1092
+ fetchBytes(url) {
1093
+ const v = files[url];
1094
+ if (v === void 0) return Promise.reject(new Error(`asset not found: ${url}`));
1095
+ return Promise.resolve(typeof v === "string" ? encoder.encode(v) : v);
1039
1096
  }
1040
1097
  };
1041
1098
  }
@@ -1055,209 +1112,6 @@ function normalizePosix(path) {
1055
1112
  return (isAbsolute ? "/" : "") + out.join("/");
1056
1113
  }
1057
1114
 
1058
- // src/usd/composition.ts
1059
- var ARC_KEYS = ["references", "payload", "payloads"];
1060
- async function composeLayer(text, baseUrl, resolver, options = {}, stack = /* @__PURE__ */ new Set()) {
1061
- const warn = options.onWarn ?? (() => {
1062
- });
1063
- const file = parseUsda(text);
1064
- let weak = [];
1065
- const subLayers = toArcs(file.metadata.subLayers);
1066
- for (let i = subLayers.length - 1; i >= 0; i--) {
1067
- const sub = await loadComposedFile(subLayers[i], baseUrl, resolver, options, stack, warn);
1068
- if (sub) weak = mergePrimLists(weak, sub.prims);
1069
- }
1070
- const resolved = [];
1071
- for (const prim of file.prims) {
1072
- resolved.push(await resolvePrimArcs(prim, baseUrl, resolver, options, stack, warn));
1073
- }
1074
- const prims = weak.length > 0 ? mergePrimLists(weak, resolved) : resolved;
1075
- return { version: file.version, metadata: stripKeys(file.metadata, ["subLayers"]), prims };
1076
- }
1077
- async function resolvePrimArcs(spec, baseUrl, resolver, options, stack, warn) {
1078
- const children = [];
1079
- for (const child of spec.children) {
1080
- children.push(await resolvePrimArcs(child, baseUrl, resolver, options, stack, warn));
1081
- }
1082
- const local = { ...spec, children, metadata: stripKeys(spec.metadata, ARC_KEYS) };
1083
- const arcs = ARC_KEYS.flatMap((k) => toArcs(spec.metadata[k]));
1084
- if (arcs.length === 0) return local;
1085
- let base = null;
1086
- for (const arc of arcs) {
1087
- const target = await loadReferencedPrim(arc, baseUrl, resolver, options, stack, warn);
1088
- if (!target) continue;
1089
- base = base ? mergePrim(target, base) : target;
1090
- }
1091
- return base ? mergePrim(base, local) : local;
1092
- }
1093
- async function loadReferencedPrim(arc, baseUrl, resolver, options, stack, warn) {
1094
- if (!arc.assetPath) {
1095
- warn(`internal references (no asset path) are not supported yet: <${arc.primPath ?? "?"}>`);
1096
- return null;
1097
- }
1098
- const composed = await loadComposedFile(arc, baseUrl, resolver, options, stack, warn);
1099
- if (!composed) return null;
1100
- const target = arc.primPath ? findPrimByPath(composed, arc.primPath) : defaultPrim(composed, warn);
1101
- if (!target) {
1102
- warn(`reference target ${arc.primPath ?? "(defaultPrim)"} not found in ${arc.assetPath.path}`);
1103
- return null;
1104
- }
1105
- return target;
1106
- }
1107
- async function loadComposedFile(arc, baseUrl, resolver, options, stack, warn) {
1108
- if (!arc.assetPath) return null;
1109
- const url = resolver.resolve(arc.assetPath.path, baseUrl);
1110
- if (stack.has(url)) {
1111
- warn(`composition cycle detected at ${url}; skipping`);
1112
- return null;
1113
- }
1114
- if (stack.size >= (options.maxDepth ?? 64)) {
1115
- warn(`composition exceeded max depth at ${url}; skipping`);
1116
- return null;
1117
- }
1118
- let text;
1119
- try {
1120
- text = await resolver.fetchText(url);
1121
- } catch (err) {
1122
- warn(`cannot resolve "${arc.assetPath.path}" -> ${url}: ${err.message}`);
1123
- return null;
1124
- }
1125
- return composeLayer(text, url, resolver, options, /* @__PURE__ */ new Set([...stack, url]));
1126
- }
1127
- function mergePrim(base, over) {
1128
- return {
1129
- // `over` (def) wins; a pure `over` opinion keeps the base's specifier.
1130
- specifier: over.specifier === "over" ? base.specifier : over.specifier,
1131
- typeName: over.typeName || base.typeName,
1132
- name: over.name,
1133
- metadata: mergeMetadata(base.metadata, over.metadata),
1134
- properties: mergeProperties(base.properties, over.properties),
1135
- children: mergePrimLists(base.children, over.children),
1136
- line: over.line
1137
- };
1138
- }
1139
- function mergePrimLists(base, over) {
1140
- const byName = /* @__PURE__ */ new Map();
1141
- for (const p of base) byName.set(p.name, p);
1142
- for (const p of over) {
1143
- const existing = byName.get(p.name);
1144
- byName.set(p.name, existing ? mergePrim(existing, p) : p);
1145
- }
1146
- return [...byName.values()];
1147
- }
1148
- function mergeProperties(base, over) {
1149
- const byName = /* @__PURE__ */ new Map();
1150
- for (const p of base) byName.set(p.name, p);
1151
- for (const p of over) {
1152
- const existing = byName.get(p.name);
1153
- byName.set(p.name, existing ? mergeProperty(existing, p) : p);
1154
- }
1155
- return [...byName.values()];
1156
- }
1157
- function mergeProperty(base, over) {
1158
- if (base.kind !== over.kind) return over;
1159
- if (base.kind === "attribute" && over.kind === "attribute") {
1160
- const merged = {
1161
- ...over,
1162
- typeName: over.typeName || base.typeName,
1163
- metadata: mergeMetadata(base.metadata, over.metadata)
1164
- };
1165
- const value = over.value !== void 0 ? over.value : base.value;
1166
- if (value !== void 0) merged.value = value;
1167
- const timeSamples = over.timeSamples ?? base.timeSamples;
1168
- if (timeSamples) merged.timeSamples = timeSamples;
1169
- const connections = over.connections ?? base.connections;
1170
- if (connections) merged.connections = connections;
1171
- return merged;
1172
- }
1173
- if (base.kind === "relationship" && over.kind === "relationship") {
1174
- return {
1175
- ...over,
1176
- targets: over.targets.length > 0 ? over.targets : base.targets,
1177
- metadata: mergeMetadata(base.metadata, over.metadata)
1178
- };
1179
- }
1180
- return over;
1181
- }
1182
- function mergeMetadata(base, over) {
1183
- const merged = { ...base, ...over };
1184
- const a = base.apiSchemas;
1185
- const b = over.apiSchemas;
1186
- if (Array.isArray(a) || Array.isArray(b)) {
1187
- const seen = /* @__PURE__ */ new Set();
1188
- const union = [];
1189
- for (const v of [...Array.isArray(a) ? a : [], ...Array.isArray(b) ? b : []]) {
1190
- if (!seen.has(v)) {
1191
- seen.add(v);
1192
- union.push(v);
1193
- }
1194
- }
1195
- merged.apiSchemas = union;
1196
- }
1197
- return merged;
1198
- }
1199
- function findPrimByPath(file, path) {
1200
- const segments = path.split("/").filter(Boolean);
1201
- let level = file.prims;
1202
- let found = null;
1203
- for (const seg of segments) {
1204
- found = level.find((p) => p.name === seg) ?? null;
1205
- if (!found) return null;
1206
- level = found.children;
1207
- }
1208
- return found;
1209
- }
1210
- function defaultPrim(file, warn) {
1211
- const name = file.metadata.defaultPrim;
1212
- if (typeof name === "string") {
1213
- return file.prims.find((p) => p.name === name) ?? null;
1214
- }
1215
- const first = file.prims[0];
1216
- if (first) warn(`referenced layer has no defaultPrim; using first root prim "${first.name}"`);
1217
- return first ?? null;
1218
- }
1219
- function toArcs(value) {
1220
- if (value === void 0) return [];
1221
- const list = Array.isArray(value) ? value : [value];
1222
- const arcs = [];
1223
- for (const v of list) {
1224
- if (v instanceof AssetPath) arcs.push({ assetPath: v });
1225
- else if (v && typeof v === "object" && "assetPath" in v) arcs.push(v);
1226
- }
1227
- return arcs;
1228
- }
1229
- function stripKeys(meta, keys) {
1230
- const out = {};
1231
- for (const [k, v] of Object.entries(meta)) {
1232
- if (!keys.includes(k)) out[k] = v;
1233
- }
1234
- return out;
1235
- }
1236
- var USD_ENTRY = /\.(usda|usdc|usd)$/i;
1237
- function openUsdz(bytes) {
1238
- const entries = unzipSync(bytes);
1239
- const names = Object.keys(entries);
1240
- const rootEntry = names.find((n) => USD_ENTRY.test(n)) ?? names[0];
1241
- if (!rootEntry) throw new Error("usdz package contains no entries");
1242
- const decoder = new TextDecoder();
1243
- const resolver = {
1244
- resolve(assetPath, baseUrl) {
1245
- return joinPosix(baseUrl, assetPath);
1246
- },
1247
- fetchText(url) {
1248
- const data = entries[url] ?? entries[url.replace(/^\/+/, "")];
1249
- if (!data) return Promise.reject(new Error(`not found in usdz: ${url}`));
1250
- if (/\.usdc$/i.test(url)) {
1251
- return Promise.reject(
1252
- new Error(`USDC (binary crate) entries are not supported yet (M10): ${url}`)
1253
- );
1254
- }
1255
- return Promise.resolve(decoder.decode(data));
1256
- }
1257
- };
1258
- return { rootEntry, resolver };
1259
- }
1260
-
1261
1115
  // src/usd/crate/lz4.ts
1262
1116
  var MAX_CHUNK_INPUT = 2113929216;
1263
1117
  function lz4DecompressBlock(src, dst) {
@@ -1372,12 +1226,14 @@ var CrateType = {
1372
1226
  Vec4f: 28,
1373
1227
  TokenListOp: 32,
1374
1228
  PathListOp: 34,
1229
+ ReferenceListOp: 35,
1375
1230
  IntListOp: 36,
1376
1231
  PathVector: 40,
1377
1232
  TokenVector: 41,
1378
1233
  Specifier: 42,
1379
1234
  Permission: 43,
1380
- Variability: 44};
1235
+ Variability: 44,
1236
+ PayloadListOp: 55};
1381
1237
  var ListOpBits = {
1382
1238
  HasExplicit: 1 << 1,
1383
1239
  HasAdded: 1 << 2,
@@ -1724,6 +1580,10 @@ var CrateReader = class {
1724
1580
  return this.readListOp(off, "token");
1725
1581
  case CrateType.PathListOp:
1726
1582
  return this.readListOp(off, "path");
1583
+ case CrateType.ReferenceListOp:
1584
+ return this.readArcListOp(off, true);
1585
+ case CrateType.PayloadListOp:
1586
+ return this.readArcListOp(off, false);
1727
1587
  case CrateType.TokenVector:
1728
1588
  return this.readIndexVector(off, "token").items;
1729
1589
  case CrateType.PathVector:
@@ -1827,6 +1687,41 @@ var CrateReader = class {
1827
1687
  if (bits & ListOpBits.HasOrdered) read([]);
1828
1688
  return [...explicit, ...prepended, ...added, ...appended];
1829
1689
  }
1690
+ /**
1691
+ * Read a Reference/Payload list-op into composition arcs. Each item is
1692
+ * `[assetPath: string-index][primPath: path-index][layerOffset: 2 doubles]`,
1693
+ * and a Reference additionally carries a (usually empty) customData dict.
1694
+ */
1695
+ readArcListOp(off, isReference) {
1696
+ const bits = this.bytes[off];
1697
+ let p = off + 1;
1698
+ const out = [];
1699
+ const readList = (collect) => {
1700
+ const count = this.u64(p);
1701
+ p += 8;
1702
+ for (let i = 0; i < count; i++) {
1703
+ const assetStrIndex = this.view.getUint32(p, true);
1704
+ p += 4;
1705
+ const primPathIndex = this.view.getInt32(p, true);
1706
+ p += 4;
1707
+ p += 16;
1708
+ if (isReference) p += 8;
1709
+ if (!collect) continue;
1710
+ const assetPath = this.getToken(this.getStrings()[assetStrIndex] ?? -1);
1711
+ const primPath = this.getPaths()[primPathIndex] ?? "";
1712
+ const arc = { assetPath: new AssetPath(assetPath) };
1713
+ if (primPath) arc.primPath = primPath;
1714
+ out.push(arc);
1715
+ }
1716
+ };
1717
+ if (bits & ListOpBits.HasExplicit) readList(true);
1718
+ if (bits & ListOpBits.HasAdded) readList(true);
1719
+ if (bits & ListOpBits.HasPrepended) readList(true);
1720
+ if (bits & ListOpBits.HasAppended) readList(true);
1721
+ if (bits & ListOpBits.HasDeleted) readList(false);
1722
+ if (bits & ListOpBits.HasOrdered) readList(false);
1723
+ return out;
1724
+ }
1830
1725
  };
1831
1726
  function appendElement(parent, elem, isProperty) {
1832
1727
  if (isProperty) return `${parent}.${elem}`;
@@ -1931,12 +1826,18 @@ function buildRelationship(crate, name, fm) {
1931
1826
  line: 0
1932
1827
  };
1933
1828
  }
1829
+ var ARC_FIELDS = ["references", "payload", "payloads", "inherits", "specializes"];
1934
1830
  function buildPrimMetadata(crate, fm) {
1935
1831
  const meta = {};
1936
1832
  const apiSchemas = fm.has("apiSchemas") ? crate.getValue(fm.get("apiSchemas")) : void 0;
1937
1833
  if (Array.isArray(apiSchemas)) meta.apiSchemas = apiSchemas;
1938
1834
  const kind = asString(crate, fm.get("kind"));
1939
1835
  if (kind !== void 0) meta.kind = kind;
1836
+ for (const key of ARC_FIELDS) {
1837
+ if (!fm.has(key)) continue;
1838
+ const arcs = crate.getValue(fm.get(key));
1839
+ if (Array.isArray(arcs) && arcs.length > 0) meta[key] = arcs;
1840
+ }
1940
1841
  return meta;
1941
1842
  }
1942
1843
  function buildLayerMetadata(crate, fm) {
@@ -1974,6 +1875,302 @@ function leaf(path) {
1974
1875
  return path.slice(path.lastIndexOf("/") + 1);
1975
1876
  }
1976
1877
 
1878
+ // src/usd/composition.ts
1879
+ var ARC_KEYS = ["references", "payload", "payloads", "inherits", "specializes"];
1880
+ var STRIP_KEYS = [...ARC_KEYS, "variants", "variantSets"];
1881
+ async function composeLayer(text, baseUrl, resolver, options = {}, stack = /* @__PURE__ */ new Set()) {
1882
+ return composeFile(parseUsda(text), baseUrl, resolver, options, stack);
1883
+ }
1884
+ async function composeFile(file, baseUrl, resolver, options = {}, stack = /* @__PURE__ */ new Set()) {
1885
+ const warn = options.onWarn ?? (() => {
1886
+ });
1887
+ let weak = [];
1888
+ const subLayers = toArcs(file.metadata.subLayers);
1889
+ for (let i = subLayers.length - 1; i >= 0; i--) {
1890
+ const sub = await loadExternalFile(subLayers[i], baseUrl, resolver, options, stack, warn);
1891
+ if (sub) weak = mergePrimLists(weak, sub.prims);
1892
+ }
1893
+ const ctx = {
1894
+ baseUrl,
1895
+ resolver,
1896
+ options,
1897
+ warn,
1898
+ stack,
1899
+ index: buildPathIndex(file.prims),
1900
+ resolving: /* @__PURE__ */ new Set()
1901
+ };
1902
+ const resolved = [];
1903
+ for (const prim of file.prims) resolved.push(await resolvePrim(prim, "", ctx));
1904
+ const prims = weak.length > 0 ? mergePrimLists(weak, resolved) : resolved;
1905
+ return { version: file.version, metadata: stripKeys(file.metadata, STRIP_KEYS), prims };
1906
+ }
1907
+ async function resolvePrim(spec, parentPath, ctx) {
1908
+ const path = `${parentPath}/${spec.name}`;
1909
+ let properties = spec.properties;
1910
+ let children = spec.children;
1911
+ const selection = spec.metadata.variants;
1912
+ if (spec.variantSets && isDictionary(selection)) {
1913
+ for (const [setName, variantName] of Object.entries(selection)) {
1914
+ const variant = spec.variantSets[setName]?.[String(variantName)];
1915
+ if (!variant) continue;
1916
+ properties = mergeProperties(variant.properties, properties);
1917
+ children = mergePrimLists(variant.children, children);
1918
+ }
1919
+ }
1920
+ const resolvedChildren = [];
1921
+ for (const child of children) resolvedChildren.push(await resolvePrim(child, path, ctx));
1922
+ const local = {
1923
+ specifier: spec.specifier,
1924
+ typeName: spec.typeName,
1925
+ name: spec.name,
1926
+ metadata: stripKeys(spec.metadata, STRIP_KEYS),
1927
+ properties,
1928
+ children: resolvedChildren,
1929
+ line: spec.line
1930
+ };
1931
+ const arcs = ARC_KEYS.flatMap((k) => toArcs(spec.metadata[k]));
1932
+ let base = null;
1933
+ for (const arc of arcs) {
1934
+ const target = await loadReferencedPrim(arc, ctx, path);
1935
+ if (target) base = base ? mergePrim(target, base) : target;
1936
+ }
1937
+ return base ? mergePrim(base, local) : local;
1938
+ }
1939
+ async function loadReferencedPrim(arc, ctx, destPath) {
1940
+ if (arc.assetPath) {
1941
+ const composed = await loadExternalFile(
1942
+ arc,
1943
+ ctx.baseUrl,
1944
+ ctx.resolver,
1945
+ ctx.options,
1946
+ ctx.stack,
1947
+ ctx.warn
1948
+ );
1949
+ if (!composed) return null;
1950
+ const target = arc.primPath ? findPrimByPath(composed, arc.primPath) : defaultPrim(composed, ctx.warn);
1951
+ if (!target) {
1952
+ ctx.warn(
1953
+ `reference target ${arc.primPath ?? "(defaultPrim)"} not found in ${arc.assetPath.path}`
1954
+ );
1955
+ return null;
1956
+ }
1957
+ const sourceRoot = arc.primPath ?? `/${target.name}`;
1958
+ return remapPaths(target, sourceRoot, destPath);
1959
+ }
1960
+ if (arc.primPath) {
1961
+ if (ctx.resolving.has(arc.primPath)) {
1962
+ ctx.warn(`internal composition cycle at ${arc.primPath}; skipping`);
1963
+ return null;
1964
+ }
1965
+ const target = ctx.index.get(arc.primPath);
1966
+ if (!target) {
1967
+ ctx.warn(`internal reference target ${arc.primPath} not found`);
1968
+ return null;
1969
+ }
1970
+ const parentPath = arc.primPath.slice(0, arc.primPath.lastIndexOf("/"));
1971
+ const composed = await resolvePrim(target, parentPath, {
1972
+ ...ctx,
1973
+ resolving: /* @__PURE__ */ new Set([...ctx.resolving, arc.primPath])
1974
+ });
1975
+ return remapPaths(composed, arc.primPath, destPath);
1976
+ }
1977
+ return null;
1978
+ }
1979
+ async function loadExternalFile(arc, baseUrl, resolver, options, stack, warn) {
1980
+ if (!arc.assetPath) return null;
1981
+ const url = resolver.resolve(arc.assetPath.path, baseUrl);
1982
+ if (stack.has(url)) {
1983
+ warn(`composition cycle detected at ${url}; skipping`);
1984
+ return null;
1985
+ }
1986
+ if (stack.size >= (options.maxDepth ?? 64)) {
1987
+ warn(`composition exceeded max depth at ${url}; skipping`);
1988
+ return null;
1989
+ }
1990
+ let bytes;
1991
+ try {
1992
+ bytes = await fetchLayerBytes(resolver, url);
1993
+ } catch (err) {
1994
+ warn(`cannot resolve "${arc.assetPath.path}" -> ${url}: ${err.message}`);
1995
+ return null;
1996
+ }
1997
+ const childStack = /* @__PURE__ */ new Set([...stack, url]);
1998
+ if (CrateReader.isCrate(bytes)) {
1999
+ return composeFile(crateToUsdaFile(new CrateReader(bytes)), url, resolver, options, childStack);
2000
+ }
2001
+ return composeLayer(new TextDecoder().decode(bytes), url, resolver, options, childStack);
2002
+ }
2003
+ async function fetchLayerBytes(resolver, url) {
2004
+ if (resolver.fetchBytes) return resolver.fetchBytes(url);
2005
+ return new TextEncoder().encode(await resolver.fetchText(url));
2006
+ }
2007
+ function remapPaths(prim, from, to) {
2008
+ if (from === to) return prim;
2009
+ return {
2010
+ ...prim,
2011
+ properties: prim.properties.map((p) => remapProperty(p, from, to)),
2012
+ children: prim.children.map((c) => remapPaths(c, from, to))
2013
+ };
2014
+ }
2015
+ function remapProperty(prop, from, to) {
2016
+ if (prop.kind === "relationship") {
2017
+ return { ...prop, targets: prop.targets.map((t) => remapPath(t, from, to)) };
2018
+ }
2019
+ if (prop.kind === "attribute" && prop.connections) {
2020
+ return { ...prop, connections: prop.connections.map((c) => remapPath(c, from, to)) };
2021
+ }
2022
+ return prop;
2023
+ }
2024
+ function remapPath(path, from, to) {
2025
+ if (path === from) return to;
2026
+ if (path.startsWith(from) && /[/.[{]/.test(path[from.length] ?? "")) {
2027
+ return to + path.slice(from.length);
2028
+ }
2029
+ return path;
2030
+ }
2031
+ function mergePrim(base, over) {
2032
+ return {
2033
+ specifier: over.specifier === "over" ? base.specifier : over.specifier,
2034
+ typeName: over.typeName || base.typeName,
2035
+ name: over.name,
2036
+ metadata: mergeMetadata(base.metadata, over.metadata),
2037
+ properties: mergeProperties(base.properties, over.properties),
2038
+ children: mergePrimLists(base.children, over.children),
2039
+ line: over.line
2040
+ };
2041
+ }
2042
+ function mergePrimLists(base, over) {
2043
+ const byName = /* @__PURE__ */ new Map();
2044
+ for (const p of base) byName.set(p.name, p);
2045
+ for (const p of over) {
2046
+ const existing = byName.get(p.name);
2047
+ byName.set(p.name, existing ? mergePrim(existing, p) : p);
2048
+ }
2049
+ return [...byName.values()];
2050
+ }
2051
+ function mergeProperties(base, over) {
2052
+ const byName = /* @__PURE__ */ new Map();
2053
+ for (const p of base) byName.set(p.name, p);
2054
+ for (const p of over) {
2055
+ const existing = byName.get(p.name);
2056
+ byName.set(p.name, existing ? mergeProperty(existing, p) : p);
2057
+ }
2058
+ return [...byName.values()];
2059
+ }
2060
+ function mergeProperty(base, over) {
2061
+ if (base.kind !== over.kind) return over;
2062
+ if (base.kind === "attribute" && over.kind === "attribute") {
2063
+ const merged = {
2064
+ ...over,
2065
+ typeName: over.typeName || base.typeName,
2066
+ metadata: mergeMetadata(base.metadata, over.metadata)
2067
+ };
2068
+ const value = over.value !== void 0 ? over.value : base.value;
2069
+ if (value !== void 0) merged.value = value;
2070
+ const timeSamples = over.timeSamples ?? base.timeSamples;
2071
+ if (timeSamples) merged.timeSamples = timeSamples;
2072
+ const connections = over.connections ?? base.connections;
2073
+ if (connections) merged.connections = connections;
2074
+ return merged;
2075
+ }
2076
+ if (base.kind === "relationship" && over.kind === "relationship") {
2077
+ return {
2078
+ ...over,
2079
+ targets: over.targets.length > 0 ? over.targets : base.targets,
2080
+ metadata: mergeMetadata(base.metadata, over.metadata)
2081
+ };
2082
+ }
2083
+ return over;
2084
+ }
2085
+ function mergeMetadata(base, over) {
2086
+ const merged = { ...base, ...over };
2087
+ const a = base.apiSchemas;
2088
+ const b = over.apiSchemas;
2089
+ if (Array.isArray(a) || Array.isArray(b)) {
2090
+ const seen = /* @__PURE__ */ new Set();
2091
+ const union = [];
2092
+ for (const v of [...Array.isArray(a) ? a : [], ...Array.isArray(b) ? b : []]) {
2093
+ if (!seen.has(v)) {
2094
+ seen.add(v);
2095
+ union.push(v);
2096
+ }
2097
+ }
2098
+ merged.apiSchemas = union;
2099
+ }
2100
+ return merged;
2101
+ }
2102
+ function buildPathIndex(prims, parent = "/", map = /* @__PURE__ */ new Map()) {
2103
+ for (const p of prims) {
2104
+ const path = parent === "/" ? `/${p.name}` : `${parent}/${p.name}`;
2105
+ map.set(path, p);
2106
+ buildPathIndex(p.children, path, map);
2107
+ }
2108
+ return map;
2109
+ }
2110
+ function findPrimByPath(file, path) {
2111
+ const segments = path.split("/").filter(Boolean);
2112
+ let level = file.prims;
2113
+ let found = null;
2114
+ for (const seg of segments) {
2115
+ found = level.find((p) => p.name === seg) ?? null;
2116
+ if (!found) return null;
2117
+ level = found.children;
2118
+ }
2119
+ return found;
2120
+ }
2121
+ function defaultPrim(file, warn) {
2122
+ const name = file.metadata.defaultPrim;
2123
+ if (typeof name === "string") return file.prims.find((p) => p.name === name) ?? null;
2124
+ const first = file.prims[0];
2125
+ if (first) warn(`referenced layer has no defaultPrim; using first root prim "${first.name}"`);
2126
+ return first ?? null;
2127
+ }
2128
+ function toArcs(value) {
2129
+ if (value === void 0) return [];
2130
+ const list = Array.isArray(value) ? value : [value];
2131
+ const arcs = [];
2132
+ for (const v of list) {
2133
+ if (v instanceof AssetPath) arcs.push({ assetPath: v });
2134
+ else if (v && typeof v === "object" && ("assetPath" in v || "primPath" in v))
2135
+ arcs.push(v);
2136
+ }
2137
+ return arcs;
2138
+ }
2139
+ function isDictionary(v) {
2140
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof Quat) && !(v instanceof UsdMatrix) && !(v instanceof AssetPath);
2141
+ }
2142
+ function stripKeys(meta, keys) {
2143
+ const out = {};
2144
+ for (const [k, v] of Object.entries(meta)) {
2145
+ if (!keys.includes(k)) out[k] = v;
2146
+ }
2147
+ return out;
2148
+ }
2149
+ var USD_ENTRY = /\.(usda|usdc|usd)$/i;
2150
+ function openUsdz(bytes) {
2151
+ const entries = unzipSync(bytes);
2152
+ const names = Object.keys(entries);
2153
+ const rootEntry = names.find((n) => USD_ENTRY.test(n)) ?? names[0];
2154
+ if (!rootEntry) throw new Error("usdz package contains no entries");
2155
+ const decoder = new TextDecoder();
2156
+ const resolver = {
2157
+ resolve(assetPath, baseUrl) {
2158
+ return joinPosix(baseUrl, assetPath);
2159
+ },
2160
+ fetchText(url) {
2161
+ const data = entries[url] ?? entries[url.replace(/^\/+/, "")];
2162
+ if (!data) return Promise.reject(new Error(`not found in usdz: ${url}`));
2163
+ if (/\.usdc$/i.test(url)) {
2164
+ return Promise.reject(
2165
+ new Error(`USDC (binary crate) entries are not supported yet (M10): ${url}`)
2166
+ );
2167
+ }
2168
+ return Promise.resolve(decoder.decode(data));
2169
+ }
2170
+ };
2171
+ return { rootEntry, resolver };
2172
+ }
2173
+
1977
2174
  // src/kinematics/transforms.ts
1978
2175
  var DEG2RAD = Math.PI / 180;
1979
2176
  var RAD2DEG = 180 / Math.PI;
@@ -2319,6 +2516,33 @@ function asMatrix(v, where) {
2319
2516
  throw new Error(`${where}: expected a matrix`);
2320
2517
  }
2321
2518
 
2519
+ // src/kinematics/sampling.ts
2520
+ function interpolate(channel, t) {
2521
+ const { times, values } = channel;
2522
+ const n = times.length;
2523
+ if (n === 0) return 0;
2524
+ if (t <= times[0]) return values[0];
2525
+ if (t >= times[n - 1]) return values[n - 1];
2526
+ let lo = 0;
2527
+ let hi = n - 1;
2528
+ while (lo < hi) {
2529
+ const mid = lo + hi >> 1;
2530
+ if (times[mid] <= t) lo = mid + 1;
2531
+ else hi = mid;
2532
+ }
2533
+ const i = lo - 1;
2534
+ const t0 = times[i];
2535
+ const t1 = times[i + 1];
2536
+ const span = t1 - t0;
2537
+ const f = span > 0 ? (t - t0) / span : 0;
2538
+ return values[i] + (values[i + 1] - values[i]) * f;
2539
+ }
2540
+ function channelFromSamples(samples) {
2541
+ const times = [...samples.keys()].sort((a, b) => a - b);
2542
+ const values = times.map((t) => samples.get(t) ?? 0);
2543
+ return { times, values };
2544
+ }
2545
+
2322
2546
  // src/schemas/usdGeom.ts
2323
2547
  function isXform(prim) {
2324
2548
  return prim.GetTypeName() === "Xform";
@@ -2596,8 +2820,13 @@ function extractRobotDescription(stage, options = {}) {
2596
2820
  joints,
2597
2821
  upAxis: stage.GetUpAxis(),
2598
2822
  metersPerUnit: stage.GetMetersPerUnit(),
2823
+ timeCodesPerSecond: stage.GetTimeCodesPerSecond(),
2599
2824
  ...articulationRoots.length ? { articulationRoots } : {}
2600
2825
  };
2826
+ const startTimeCode = stage.GetStartTimeCode();
2827
+ if (startTimeCode !== void 0) robot.startTimeCode = startTimeCode;
2828
+ const endTimeCode = stage.GetEndTimeCode();
2829
+ if (endTimeCode !== void 0) robot.endTimeCode = endTimeCode;
2601
2830
  const tree = buildKinematicTree(robot, { onWarn: warn });
2602
2831
  robot.rootLink = tree.root;
2603
2832
  if (tree.loopJoints.length) robot.loopJoints = tree.loopJoints;
@@ -2660,6 +2889,17 @@ function buildJoint(prim, linkKeyByPath, warn) {
2660
2889
  ...drive.maxForce !== void 0 ? { maxForce: drive.maxForce } : {}
2661
2890
  };
2662
2891
  if (Object.keys(driveDesc).length > 0) joint.drive = driveDesc;
2892
+ const stateSamples = prim.GetAttribute(`state:${kind}:physics:position`).GetTimeSamples();
2893
+ const driveSamples = prim.GetAttribute(`drive:${kind}:physics:targetPosition`).GetTimeSamples();
2894
+ const samples = stateSamples.size > 0 ? stateSamples : driveSamples;
2895
+ if (samples.size > 0) {
2896
+ const times = [...samples.keys()].sort((a, b) => a - b);
2897
+ const values = times.map((t) => {
2898
+ const v = samples.get(t);
2899
+ return typeof v === "number" ? jointValueToSI(angular, v) : 0;
2900
+ });
2901
+ joint.valueSamples = { times, values };
2902
+ }
2663
2903
  return joint;
2664
2904
  }
2665
2905
  function buildKeyMap(paths) {
@@ -2680,6 +2920,6 @@ function leafName(path) {
2680
2920
  return parts[parts.length - 1] ?? path;
2681
2921
  }
2682
2922
 
2683
- export { ARTICULATION_ROOT_API, AssetPath, Attribute, COLLISION_API, CrateReader, DEFAULT_METERS_PER_UNIT, DEG2RAD, DefaultAssetResolver, Layer, PACKAGE_NAME, ParseError, Prim, Quat, RAD2DEG, RIGID_BODY_API, Relationship, Stage, TokenizeError, UsdMatrix, VERSION, buildKinematicTree, composeLayer, computeLocalTransform, crateToUsdaFile, createMemoryResolver, driveKindFor, extractRobotDescription, fromUsdMatrix, gatherMeshDescendants, getJointAxis, getJointBodies, getJointDrive, getJointLimits, getJointLocalFrame, getJointStatePosition, getJointType, getTranslation, hasArticulationRootAPI, hasCollisionAPI, hasRigidBodyAPI, identity4, invert, isMesh, isScope, isXform, iterDescendants, joinPosix, jointValueToSI, makeEuler, makeRotationFromQuat, makeRotationX, makeRotationY, makeRotationZ, makeScale, makeTranslation, multiply, multiplyAll, normalizeJointLimits, openUsdz, parseOpType, parseUsda, refineJointType, tokenize };
2684
- //# sourceMappingURL=chunk-XCP5GZPY.js.map
2685
- //# sourceMappingURL=chunk-XCP5GZPY.js.map
2923
+ export { ARTICULATION_ROOT_API, AssetPath, Attribute, COLLISION_API, CrateReader, DEFAULT_METERS_PER_UNIT, DEG2RAD, DefaultAssetResolver, Layer, PACKAGE_NAME, ParseError, Prim, Quat, RAD2DEG, RIGID_BODY_API, Relationship, Stage, TokenizeError, UsdMatrix, VERSION, buildKinematicTree, channelFromSamples, composeFile, composeLayer, computeLocalTransform, crateToUsdaFile, createMemoryResolver, driveKindFor, extractRobotDescription, fromUsdMatrix, gatherMeshDescendants, getJointAxis, getJointBodies, getJointDrive, getJointLimits, getJointLocalFrame, getJointStatePosition, getJointType, getTranslation, hasArticulationRootAPI, hasCollisionAPI, hasRigidBodyAPI, identity4, interpolate, invert, isMesh, isScope, isXform, iterDescendants, joinPosix, jointValueToSI, makeEuler, makeRotationFromQuat, makeRotationX, makeRotationY, makeRotationZ, makeScale, makeTranslation, multiply, multiplyAll, normalizeJointLimits, openUsdz, parseOpType, parseUsda, refineJointType, tokenize };
2924
+ //# sourceMappingURL=chunk-FYVZ7YPW.js.map
2925
+ //# sourceMappingURL=chunk-FYVZ7YPW.js.map