@aura3d/animation 4.7.13 → 5.0.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.
Files changed (198) hide show
  1. package/dist/AnimationAction.d.ts +80 -1
  2. package/dist/AnimationAction.d.ts.map +1 -1
  3. package/dist/AnimationAction.js +450 -80
  4. package/dist/AnimationAction.js.map +1 -1
  5. package/dist/AnimationClip.d.ts.map +1 -1
  6. package/dist/AnimationClip.js.map +1 -1
  7. package/dist/AnimationClipEvents.d.ts.map +1 -1
  8. package/dist/AnimationClipEvents.js.map +1 -1
  9. package/dist/AnimationClipRegistry.d.ts.map +1 -1
  10. package/dist/AnimationClipRegistry.js.map +1 -1
  11. package/dist/AnimationController.d.ts.map +1 -1
  12. package/dist/AnimationController.js.map +1 -1
  13. package/dist/AnimationEvents.d.ts +23 -0
  14. package/dist/AnimationEvents.d.ts.map +1 -1
  15. package/dist/AnimationEvents.js +145 -36
  16. package/dist/AnimationEvents.js.map +1 -1
  17. package/dist/AnimationLayer.d.ts.map +1 -1
  18. package/dist/AnimationLayer.js.map +1 -1
  19. package/dist/AnimationMixer.d.ts +58 -3
  20. package/dist/AnimationMixer.d.ts.map +1 -1
  21. package/dist/AnimationMixer.js +366 -12
  22. package/dist/AnimationMixer.js.map +1 -1
  23. package/dist/AnimationObjectGroup.d.ts +36 -12
  24. package/dist/AnimationObjectGroup.d.ts.map +1 -1
  25. package/dist/AnimationObjectGroup.js +219 -208
  26. package/dist/AnimationObjectGroup.js.map +1 -1
  27. package/dist/AnimationStateGraph.d.ts.map +1 -1
  28. package/dist/AnimationStateGraph.js.map +1 -1
  29. package/dist/AnimationStateMachine.d.ts.map +1 -1
  30. package/dist/AnimationStateMachine.js.map +1 -1
  31. package/dist/AnimationTrack.d.ts +7 -1
  32. package/dist/AnimationTrack.d.ts.map +1 -1
  33. package/dist/AnimationTrack.js +87 -10
  34. package/dist/AnimationTrack.js.map +1 -1
  35. package/dist/AnimationUtils.d.ts +16 -14
  36. package/dist/AnimationUtils.d.ts.map +1 -1
  37. package/dist/AnimationUtils.js +148 -109
  38. package/dist/AnimationUtils.js.map +1 -1
  39. package/dist/BlendTree.d.ts.map +1 -1
  40. package/dist/BlendTree.js.map +1 -1
  41. package/dist/Bone.d.ts.map +1 -1
  42. package/dist/Bone.js.map +1 -1
  43. package/dist/ECSAnimationBridge.d.ts.map +1 -1
  44. package/dist/ECSAnimationBridge.js.map +1 -1
  45. package/dist/FighterAnimationAdapter.d.ts.map +1 -1
  46. package/dist/FighterAnimationAdapter.js.map +1 -1
  47. package/dist/FootIk.d.ts.map +1 -1
  48. package/dist/FootIk.js.map +1 -1
  49. package/dist/HumanoidBoneInference.d.ts.map +1 -1
  50. package/dist/HumanoidBoneInference.js.map +1 -1
  51. package/dist/HumanoidRetargeting.d.ts.map +1 -1
  52. package/dist/HumanoidRetargeting.js.map +1 -1
  53. package/dist/IK.d.ts.map +1 -1
  54. package/dist/IK.js.map +1 -1
  55. package/dist/Inertialization.d.ts.map +1 -1
  56. package/dist/Inertialization.js.map +1 -1
  57. package/dist/Keyframe.d.ts +15 -1
  58. package/dist/Keyframe.d.ts.map +1 -1
  59. package/dist/Keyframe.js +34 -1
  60. package/dist/Keyframe.js.map +1 -1
  61. package/dist/LocomotionController.d.ts.map +1 -1
  62. package/dist/LocomotionController.js.map +1 -1
  63. package/dist/LocomotionEvidence.d.ts.map +1 -1
  64. package/dist/LocomotionEvidence.js.map +1 -1
  65. package/dist/LocomotionKit.d.ts.map +1 -1
  66. package/dist/LocomotionKit.js.map +1 -1
  67. package/dist/MotionQuality.d.ts.map +1 -1
  68. package/dist/MotionQuality.js.map +1 -1
  69. package/dist/PropertyBinding.d.ts +42 -22
  70. package/dist/PropertyBinding.d.ts.map +1 -1
  71. package/dist/PropertyBinding.js +390 -293
  72. package/dist/PropertyBinding.js.map +1 -1
  73. package/dist/RigQuality.d.ts.map +1 -1
  74. package/dist/RigQuality.js.map +1 -1
  75. package/dist/RootMotion.d.ts.map +1 -1
  76. package/dist/RootMotion.js.map +1 -1
  77. package/dist/SceneAnimationBridge.d.ts +7 -0
  78. package/dist/SceneAnimationBridge.d.ts.map +1 -1
  79. package/dist/SceneAnimationBridge.js +48 -3
  80. package/dist/SceneAnimationBridge.js.map +1 -1
  81. package/dist/Skeleton.d.ts.map +1 -1
  82. package/dist/Skeleton.js.map +1 -1
  83. package/dist/Skinning.d.ts.map +1 -1
  84. package/dist/Skinning.js.map +1 -1
  85. package/dist/SpringBones.d.ts.map +1 -1
  86. package/dist/SpringBones.js.map +1 -1
  87. package/dist/browser-index.d.ts +1 -1
  88. package/dist/browser-index.d.ts.map +1 -1
  89. package/dist/browser-index.js +1 -1
  90. package/dist/browser-index.js.map +1 -1
  91. package/dist/contracts/index.d.ts.map +1 -1
  92. package/dist/contracts/index.js.map +1 -1
  93. package/dist/contracts/pose.d.ts.map +1 -1
  94. package/dist/contracts/pose.js.map +1 -1
  95. package/dist/experimental/CrowdAnimation.d.ts.map +1 -1
  96. package/dist/experimental/CrowdAnimation.js.map +1 -1
  97. package/dist/index.d.ts +1 -1
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +1 -1
  100. package/dist/index.js.map +1 -1
  101. package/dist/lanes/index.d.ts.map +1 -1
  102. package/dist/lanes/index.js.map +1 -1
  103. package/dist/lanes/prd01.d.ts.map +1 -1
  104. package/dist/lanes/prd01.js.map +1 -1
  105. package/dist/lanes/prd02.d.ts.map +1 -1
  106. package/dist/lanes/prd02.js.map +1 -1
  107. package/dist/lanes/prd03.d.ts.map +1 -1
  108. package/dist/lanes/prd03.js.map +1 -1
  109. package/dist/lanes/prd04.d.ts.map +1 -1
  110. package/dist/lanes/prd04.js.map +1 -1
  111. package/dist/lanes/prd05.d.ts.map +1 -1
  112. package/dist/lanes/prd05.js.map +1 -1
  113. package/dist/lanes/prd06.d.ts.map +1 -1
  114. package/dist/lanes/prd06.js.map +1 -1
  115. package/dist/lanes/prd07.d.ts.map +1 -1
  116. package/dist/lanes/prd07.js.map +1 -1
  117. package/dist/lanes/prd08.d.ts.map +1 -1
  118. package/dist/lanes/prd08.js.map +1 -1
  119. package/dist/lanes/prd09.d.ts.map +1 -1
  120. package/dist/lanes/prd09.js.map +1 -1
  121. package/dist/lanes/prd10.d.ts.map +1 -1
  122. package/dist/lanes/prd10.js.map +1 -1
  123. package/dist/lanes/prd11.d.ts.map +1 -1
  124. package/dist/lanes/prd11.js.map +1 -1
  125. package/dist/lanes/prd12.d.ts.map +1 -1
  126. package/dist/lanes/prd12.js.map +1 -1
  127. package/dist/lanes/prd13.d.ts.map +1 -1
  128. package/dist/lanes/prd13.js.map +1 -1
  129. package/dist/lanes/prd14.d.ts.map +1 -1
  130. package/dist/lanes/prd14.js.map +1 -1
  131. package/dist/lanes/prd15.d.ts.map +1 -1
  132. package/dist/lanes/prd15.js.map +1 -1
  133. package/dist/library/performanceStateGraph.d.ts.map +1 -1
  134. package/dist/library/performanceStateGraph.js.map +1 -1
  135. package/dist/library/standardHumanoidClips.d.ts.map +1 -1
  136. package/dist/library/standardHumanoidClips.js.map +1 -1
  137. package/dist/pose/BoneMask.d.ts.map +1 -1
  138. package/dist/pose/BoneMask.js.map +1 -1
  139. package/dist/pose/CcdIkConstraint.d.ts.map +1 -1
  140. package/dist/pose/CcdIkConstraint.js.map +1 -1
  141. package/dist/pose/CompiledClip.d.ts +15 -8
  142. package/dist/pose/CompiledClip.d.ts.map +1 -1
  143. package/dist/pose/CompiledClip.js +82 -50
  144. package/dist/pose/CompiledClip.js.map +1 -1
  145. package/dist/pose/LookAtConstraint.d.ts.map +1 -1
  146. package/dist/pose/LookAtConstraint.js.map +1 -1
  147. package/dist/pose/MotionMetrics.d.ts.map +1 -1
  148. package/dist/pose/MotionMetrics.js.map +1 -1
  149. package/dist/pose/PoseBuffer.d.ts.map +1 -1
  150. package/dist/pose/PoseBuffer.js.map +1 -1
  151. package/dist/pose/PoseInertializer.d.ts.map +1 -1
  152. package/dist/pose/PoseInertializer.js.map +1 -1
  153. package/dist/pose/PoseMixer.d.ts +196 -19
  154. package/dist/pose/PoseMixer.d.ts.map +1 -1
  155. package/dist/pose/PoseMixer.js +1209 -188
  156. package/dist/pose/PoseMixer.js.map +1 -1
  157. package/dist/pose/Retarget.d.ts +4 -3
  158. package/dist/pose/Retarget.d.ts.map +1 -1
  159. package/dist/pose/Retarget.js +32 -15
  160. package/dist/pose/Retarget.js.map +1 -1
  161. package/dist/pose/RetargetCache.d.ts +12 -6
  162. package/dist/pose/RetargetCache.d.ts.map +1 -1
  163. package/dist/pose/RetargetCache.js +253 -29
  164. package/dist/pose/RetargetCache.js.map +1 -1
  165. package/dist/pose/RetargetWorker.d.ts.map +1 -1
  166. package/dist/pose/RetargetWorker.js +5 -0
  167. package/dist/pose/RetargetWorker.js.map +1 -1
  168. package/dist/pose/SkeletonBinding.d.ts.map +1 -1
  169. package/dist/pose/SkeletonBinding.js.map +1 -1
  170. package/dist/pose/blendKernels.d.ts.map +1 -1
  171. package/dist/pose/blendKernels.js.map +1 -1
  172. package/dist/pose/makeClipAdditive.d.ts +7 -14
  173. package/dist/pose/makeClipAdditive.d.ts.map +1 -1
  174. package/dist/pose/makeClipAdditive.js +53 -85
  175. package/dist/pose/makeClipAdditive.js.map +1 -1
  176. package/dist/pose/poseMixerFlags.d.ts.map +1 -1
  177. package/dist/pose/poseMixerFlags.js.map +1 -1
  178. package/dist/pose/quatFlat.d.ts.map +1 -1
  179. package/dist/pose/quatFlat.js.map +1 -1
  180. package/dist/pose/retarget.worker.d.ts.map +1 -1
  181. package/dist/pose/retarget.worker.js.map +1 -1
  182. package/dist/threejs-compatibility/AnimationAction.d.ts.map +1 -1
  183. package/dist/threejs-compatibility/AnimationAction.js.map +1 -1
  184. package/dist/threejs-compatibility/AnimationClip.d.ts.map +1 -1
  185. package/dist/threejs-compatibility/AnimationClip.js.map +1 -1
  186. package/dist/threejs-compatibility/AnimationDiagnostics.d.ts.map +1 -1
  187. package/dist/threejs-compatibility/AnimationDiagnostics.js.map +1 -1
  188. package/dist/threejs-compatibility/AnimationMixer.d.ts.map +1 -1
  189. package/dist/threejs-compatibility/AnimationMixer.js.map +1 -1
  190. package/dist/threejs-compatibility/MorphTargetMixer.d.ts.map +1 -1
  191. package/dist/threejs-compatibility/MorphTargetMixer.js.map +1 -1
  192. package/dist/threejs-compatibility/Skeleton.d.ts.map +1 -1
  193. package/dist/threejs-compatibility/Skeleton.js.map +1 -1
  194. package/dist/threejs-compatibility/SkinnedMesh.d.ts.map +1 -1
  195. package/dist/threejs-compatibility/SkinnedMesh.js.map +1 -1
  196. package/dist/threejs-compatibility/index.d.ts.map +1 -1
  197. package/dist/threejs-compatibility/index.js.map +1 -1
  198. package/package.json +2 -2
@@ -1,129 +1,240 @@
1
+ const OBJECT_SEGMENTS = new Set(["material", "materials", "bones", "map"]);
2
+ const UNSAFE_KEYS = new Set(["__proto__", "prototype", "constructor"]);
1
3
  /**
2
- * SA-03 — three-compatible `PropertyBinding` (`three@0.185.x
3
- * src/animation/PropertyBinding.js`) adapted to Aura's value-oriented track
4
- * API: `getValue()`/`setValue()` exchange `AnimationValue`s directly instead of
5
- * writing into a flat buffer at an offset. The track-name grammar, node lookup
6
- * (`findNode`), and `sanitizeNodeName` are exact ports, so paths like
7
- * `nodeName.property[index]`, `.morphTargetInfluences[name]`,
8
- * `uuid.materials[0].opacity`, `parent/child.position`, and
9
- * `.bones[Armature.DEF_cog].position` resolve exactly as they do upstream.
4
+ * Parses `node.property[index]` and `node.object[index].property` paths.
5
+ * Unescaped `.` separates selectors; `/` and `:` delimit a hierarchy prefix.
6
+ * Escape any delimiter, bracket, quote or backslash with `\\`. Accessors may
7
+ * also be quoted, e.g. `.weights["smile.right"]`. Names are Unicode strings.
8
+ * The prefix is retained for callers; legacy lookup uses its leaf node name.
10
9
  */
11
- // Characters `[].:/` are reserved for track binding syntax.
12
- const RESERVED_CHARS_RE = "\\[\\]\\.:\\/";
13
- const RESERVED_RE = new RegExp("[" + RESERVED_CHARS_RE + "]", "g");
14
- // Attempts to allow node names from any language: match everything except the
15
- // reserved characters (three deliberately avoids `\w`).
16
- const WORD_CHAR = "[^" + RESERVED_CHARS_RE + "]";
17
- const WORD_CHAR_OR_DOT = "[^" + RESERVED_CHARS_RE.replace("\\.", "") + "]";
18
- // Parent directories, delimited by '/' or ':'. Currently unused, but must be
19
- // matched to parse the rest of the track name.
20
- const DIRECTORY_RE = /((?:WC+[\/:])*)/.source.replace(/WC/g, WORD_CHAR);
21
- // Target node. May contain word characters and '.' (e.g. 'foo.bar.baz').
22
- const NODE_RE = /(WCOD+)?/.source.replace(/WCOD/g, WORD_CHAR_OR_DOT);
23
- // Object on target node, and accessor (`materials[0]`).
24
- const OBJECT_RE = /(?:\.(WC+)(?:\[(.+)\])?)?/.source.replace(/WC/g, WORD_CHAR);
25
- // Property and accessor (`morphTargetInfluences[run]`, `position[x]`).
26
- const PROPERTY_RE = /\.(WC+)(?:\[(.+)\])?/.source.replace(/WC/g, WORD_CHAR);
27
- const TRACK_RE = new RegExp("^" + DIRECTORY_RE + NODE_RE + OBJECT_RE + PROPERTY_RE + "$");
28
- const SUPPORTED_OBJECT_NAMES = ["material", "materials", "bones", "map"];
29
10
  export function parseTrackName(trackName) {
30
- const matches = TRACK_RE.exec(trackName);
31
- if (matches === null) {
32
- throw new Error(`PropertyBinding: Cannot parse trackName: ${trackName}`);
11
+ const { dots, directories } = scanPath(trackName);
12
+ const directoryEnd = directories.length ? directories[directories.length - 1] + 1 : 0;
13
+ const propertyDot = dots[dots.length - 1];
14
+ if (propertyDot === undefined || propertyDot < directoryEnd)
15
+ throw invalidPath(trackName);
16
+ const property = parseSegment(trackName.slice(propertyDot + 1), trackName);
17
+ const prefix = trackName.slice(directoryEnd, propertyDot);
18
+ const precedingDot = dots.filter((position) => position >= directoryEnd && position < propertyDot).pop();
19
+ let node = prefix;
20
+ let object;
21
+ if (precedingDot !== undefined) {
22
+ const candidate = parseSegment(trackName.slice(precedingDot + 1, propertyDot), trackName);
23
+ if (candidate.index !== undefined || OBJECT_SEGMENTS.has(candidate.name)) {
24
+ object = candidate;
25
+ node = trackName.slice(directoryEnd, precedingDot);
26
+ }
33
27
  }
34
- const results = {
35
- directoryName: matches[1],
36
- nodeName: matches[2],
37
- objectName: matches[3],
38
- objectIndex: matches[4],
39
- propertyName: matches[5],
40
- propertyIndex: matches[6]
28
+ // No brackets may appear in the remaining node selector; literal brackets
29
+ // in a name must be escaped so a malformed object selector cannot be ignored.
30
+ if (hasUnescapedBracket(node))
31
+ throw invalidPath(trackName);
32
+ const result = {
33
+ directoryName: decodeEscapes(trackName.slice(0, directoryEnd), trackName),
34
+ nodeName: node ? decodeEscapes(node, trackName) : undefined,
35
+ objectName: object?.name,
36
+ objectIndex: object?.index,
37
+ propertyName: property.name,
38
+ propertyIndex: property.index
41
39
  };
42
- const lastDot = results.nodeName?.lastIndexOf(".");
43
- if (lastDot !== undefined && lastDot !== -1) {
44
- const objectName = results.nodeName.substring(lastDot + 1);
45
- // Object names must be checked against an allowlist. Otherwise there is no
46
- // way to parse 'foo.bar.baz': 'baz' must be a property, but 'bar' could be
47
- // the objectName, or part of a nodeName (which can include '.' characters).
48
- if (SUPPORTED_OBJECT_NAMES.indexOf(objectName) !== -1) {
49
- results.nodeName = results.nodeName.substring(0, lastDot);
50
- results.objectName = objectName;
40
+ assertSafePath(result, trackName);
41
+ return result;
42
+ }
43
+ function invalidPath(path) {
44
+ return new Error(`PropertyBinding: Cannot parse trackName: ${path}`);
45
+ }
46
+ function scanPath(path) {
47
+ if (!path)
48
+ throw invalidPath(path);
49
+ const dots = [];
50
+ const directories = [];
51
+ let bracket = -1;
52
+ let quote;
53
+ let quoteClosed = false;
54
+ let afterBracket = false;
55
+ for (let i = 0; i < path.length; i++) {
56
+ const char = path[i];
57
+ if (afterBracket && char !== ".")
58
+ throw invalidPath(path);
59
+ if (char === "\\") {
60
+ if (++i >= path.length)
61
+ throw invalidPath(path);
62
+ continue;
51
63
  }
64
+ if (bracket >= 0) {
65
+ if (quote) {
66
+ if (char === quote) {
67
+ quote = undefined;
68
+ quoteClosed = true;
69
+ }
70
+ }
71
+ else if (i === bracket + 1 && (char === '"' || char === "'")) {
72
+ quote = char;
73
+ }
74
+ else if (char === "]") {
75
+ bracket = -1;
76
+ quoteClosed = false;
77
+ afterBracket = true;
78
+ }
79
+ else if (char === "[" || quoteClosed) {
80
+ throw invalidPath(path);
81
+ }
82
+ continue;
83
+ }
84
+ if (char === "[")
85
+ bracket = i;
86
+ else if (char === "]")
87
+ throw invalidPath(path);
88
+ else if (char === ".") {
89
+ dots.push(i);
90
+ afterBracket = false;
91
+ }
92
+ else if (char === "/" || char === ":")
93
+ directories.push(i);
52
94
  }
53
- if (results.propertyName === null || results.propertyName === undefined || results.propertyName.length === 0) {
54
- throw new Error(`PropertyBinding: can not parse propertyName from trackName: ${trackName}`);
55
- }
56
- return results;
95
+ if (bracket >= 0 || quote)
96
+ throw invalidPath(path);
97
+ return { dots, directories };
57
98
  }
58
- export function sanitizeNodeName(name) {
59
- return name.replace(/\s/g, "_").replace(RESERVED_RE, "");
99
+ function decodeEscapes(text, path) {
100
+ let result = "";
101
+ for (let i = 0; i < text.length; i++) {
102
+ if (text[i] === "\\") {
103
+ if (++i >= text.length)
104
+ throw invalidPath(path);
105
+ }
106
+ result += text[i];
107
+ }
108
+ return result;
60
109
  }
61
- export function findNode(root, nodeName) {
62
- if (nodeName === undefined || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid) {
63
- return root;
110
+ function hasUnescapedBracket(text) {
111
+ for (let i = 0; i < text.length; i++) {
112
+ if (text[i] === "\\") {
113
+ i++;
114
+ continue;
115
+ }
116
+ if (text[i] === "[" || text[i] === "]")
117
+ return true;
64
118
  }
65
- // search into skeleton bones.
66
- if (root.skeleton) {
67
- const bone = root.skeleton.getBoneByName?.(String(nodeName));
68
- if (bone !== undefined) {
69
- return bone;
119
+ return false;
120
+ }
121
+ function parseSegment(raw, path) {
122
+ if (!raw)
123
+ throw invalidPath(path);
124
+ let open = -1;
125
+ for (let i = 0; i < raw.length; i++) {
126
+ if (raw[i] === "\\") {
127
+ i++;
128
+ continue;
70
129
  }
71
- if (root.skeleton.bones) {
72
- const named = root.skeleton.bones.find((candidate) => candidate.name === nodeName || candidate.uuid === nodeName);
73
- if (named) {
74
- return named;
75
- }
130
+ if (raw[i] === "[") {
131
+ open = i;
132
+ break;
76
133
  }
77
134
  }
78
- // search into node subtree.
79
- if (root.children) {
80
- const subTreeNode = searchNodeSubtree(root.children, nodeName);
81
- if (subTreeNode) {
82
- return subTreeNode;
83
- }
135
+ if (open < 0)
136
+ return { name: decodeEscapes(raw, path) };
137
+ if (open === 0 || raw[raw.length - 1] !== "]")
138
+ throw invalidPath(path);
139
+ const name = decodeEscapes(raw.slice(0, open), path);
140
+ let index = raw.slice(open + 1, -1);
141
+ if (!index)
142
+ throw invalidPath(path);
143
+ if (index[0] === '"' || index[0] === "'") {
144
+ if (index.length < 2 || index[index.length - 1] !== index[0])
145
+ throw invalidPath(path);
146
+ index = index.slice(1, -1);
147
+ }
148
+ index = decodeEscapes(index, path);
149
+ if (!index)
150
+ throw invalidPath(path);
151
+ return { name, index };
152
+ }
153
+ function assertSafePath(path, source) {
154
+ const keys = [path.objectName, path.objectIndex, path.propertyName, path.propertyIndex];
155
+ if (!path.propertyName || keys.some((key) => key !== undefined && UNSAFE_KEYS.has(String(key)))) {
156
+ throw new Error(`PropertyBinding: Unsafe property path: ${source}`);
84
157
  }
85
- return null;
86
158
  }
87
- function searchNodeSubtree(children, nodeName) {
88
- for (const childNode of children) {
89
- if (childNode.name === nodeName || childNode.uuid === nodeName) {
90
- return childNode;
159
+ export function sanitizeNodeName(name) {
160
+ return name.replace(/\s/g, "_").replace(/[\[\].:/]/g, "").replace(/\\/g, "");
161
+ }
162
+ /** UUIDs take precedence; duplicate names resolve in depth-first child order. */
163
+ export function findNode(root, nodeName) {
164
+ if (nodeName === undefined || nodeName === "" || nodeName === "." || nodeName === -1 || nodeName === root.uuid)
165
+ return root;
166
+ const bones = root.skeleton?.bones ?? [];
167
+ const nodes = [];
168
+ const pending = [...(root.children ?? [])].reverse();
169
+ const visited = new Set([root]);
170
+ while (pending.length) {
171
+ const candidate = pending.pop();
172
+ if (visited.has(candidate))
173
+ continue;
174
+ visited.add(candidate);
175
+ nodes.push(candidate);
176
+ if (candidate.children) {
177
+ for (let i = candidate.children.length - 1; i >= 0; i--)
178
+ pending.push(candidate.children[i]);
91
179
  }
92
- if (childNode.children) {
93
- const result = searchNodeSubtree(childNode.children, nodeName);
94
- if (result) {
95
- return result;
96
- }
180
+ }
181
+ const byUuid = [...bones, ...nodes].find((candidate) => candidate.uuid === nodeName);
182
+ if (byUuid)
183
+ return byUuid;
184
+ if (nodeName === root.name)
185
+ return root;
186
+ const bone = root.skeleton?.getBoneByName?.(String(nodeName));
187
+ return bone ?? bones.find((candidate) => candidate.name === nodeName)
188
+ ?? nodes.find((candidate) => candidate.name === nodeName) ?? null;
189
+ }
190
+ function isObject(value) {
191
+ return value !== null && (typeof value === "object" || typeof value === "function");
192
+ }
193
+ /** Own properties and declared class accessors are permitted; prototype data is not. */
194
+ function lookup(object, key) {
195
+ if (!isObject(object) || UNSAFE_KEYS.has(String(key)))
196
+ return { found: false };
197
+ const own = Object.getOwnPropertyDescriptor(object, key);
198
+ if (own)
199
+ return { found: true, value: Reflect.get(object, key) };
200
+ let prototype = Object.getPrototypeOf(object);
201
+ while (prototype && prototype !== Object.prototype && prototype !== Array.prototype) {
202
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, key);
203
+ if (descriptor) {
204
+ const constructor = Object.getOwnPropertyDescriptor(prototype, "constructor")?.value;
205
+ return (descriptor.get || descriptor.set) && typeof constructor === "function" && constructor.prototype === prototype
206
+ ? { found: true, value: Reflect.get(object, key) }
207
+ : { found: false };
97
208
  }
209
+ prototype = Object.getPrototypeOf(prototype);
98
210
  }
99
- return null;
211
+ return { found: false };
100
212
  }
101
- var BindingType;
102
- (function (BindingType) {
103
- BindingType[BindingType["Direct"] = 0] = "Direct";
104
- BindingType[BindingType["EntireArray"] = 1] = "EntireArray";
105
- BindingType[BindingType["ArrayElement"] = 2] = "ArrayElement";
106
- BindingType[BindingType["HasFromToArray"] = 3] = "HasFromToArray";
107
- })(BindingType || (BindingType = {}));
108
- var Versioning;
109
- (function (Versioning) {
110
- Versioning[Versioning["None"] = 0] = "None";
111
- Versioning[Versioning["NeedsUpdate"] = 1] = "NeedsUpdate";
112
- Versioning[Versioning["MatrixWorldNeedsUpdate"] = 2] = "MatrixWorldNeedsUpdate";
113
- })(Versioning || (Versioning = {}));
114
- /**
115
- * Binds a parsed track path to a concrete property on a node resolved from
116
- * `rootNode`. `getValue`/`setValue` read and write `AnimationValue`s; on
117
- * failure to bind they become no-ops (three's unbound state).
118
- */
213
+ function numericIndex(key) {
214
+ if (!/^(0|[1-9]\d*)$/.test(key))
215
+ return undefined;
216
+ const value = Number(key);
217
+ return Number.isSafeInteger(value) ? value : undefined;
218
+ }
219
+ function isNumericArray(value) {
220
+ return Array.isArray(value) || (ArrayBuffer.isView(value) && !(value instanceof DataView)
221
+ && !(value instanceof BigInt64Array) && !(value instanceof BigUint64Array));
222
+ }
223
+ function isArrayConvertible(value) {
224
+ const candidate = value;
225
+ return typeof candidate?.fromArray === "function" && typeof candidate?.toArray === "function";
226
+ }
227
+ // The group can revalidate membership after a lazy bind invokes a user getter.
228
+ // These operations stay internal to the module; the public binding methods
229
+ // retain their original signatures.
230
+ const readForGroup = Symbol("readForGroup");
231
+ const writeForGroup = Symbol("writeForGroup");
232
+ /** Resolves a track to an existing property; missing paths remain unbound. */
119
233
  export class PropertyBinding {
120
234
  static parseTrackName = parseTrackName;
121
235
  static sanitizeNodeName = sanitizeNodeName;
122
236
  static findNode = findNode;
123
- /** three's `PropertyBinding.Composite` (lazy so the class can be declared below). */
124
- static get Composite() {
125
- return PropertyBindingComposite;
126
- }
237
+ static get Composite() { return PropertyBindingComposite; }
127
238
  path;
128
239
  parsedPath;
129
240
  rootNode;
@@ -132,262 +243,248 @@ export class PropertyBinding {
132
243
  resolvedProperty;
133
244
  propertyName;
134
245
  propertyIndex;
246
+ kind = "direct";
135
247
  bound = false;
136
248
  constructor(rootNode, path, parsedPath) {
137
249
  this.path = path;
138
- this.parsedPath = parsedPath ?? parseTrackName(path);
250
+ // Copy caller-owned descriptors so later mutation cannot redirect a binding.
251
+ this.parsedPath = parsedPath ? { ...parsedPath } : parseTrackName(path);
252
+ assertSafePath(this.parsedPath, path);
139
253
  this.rootNode = rootNode;
140
254
  this.node = findNode(rootNode, this.parsedPath.nodeName);
141
255
  }
142
- /**
143
- * Factory matching three's `PropertyBinding.create`: a root marked
144
- * `isAnimationObjectGroup` produces a composite binding shared across the
145
- * group's objects.
146
- */
147
256
  static create(root, path, parsedPath) {
148
- if (!("isAnimationObjectGroup" in root && root.isAnimationObjectGroup)) {
149
- return new PropertyBinding(root, path, parsedPath);
150
- }
151
- return new PropertyBindingComposite(root, path, parsedPath);
257
+ if ("isAnimationObjectGroup" in root && root.isAnimationObjectGroup)
258
+ return new PropertyBindingComposite(root, path, parsedPath);
259
+ return new PropertyBinding(root, path, parsedPath);
152
260
  }
153
261
  bind() {
154
- let targetObject = this.node;
155
- const parsedPath = this.parsedPath;
156
- const objectName = parsedPath.objectName;
157
- const propertyName = parsedPath.propertyName;
158
- let propertyIndex = parsedPath.propertyIndex;
159
- if (!targetObject) {
160
- targetObject = findNode(this.rootNode, parsedPath.nodeName);
161
- this.node = targetObject;
162
- }
163
262
  this.bound = false;
164
- if (!targetObject) {
165
- console.warn(`PropertyBinding: No target node found for track: ${this.path}.`);
263
+ this.targetObject = undefined;
264
+ this.resolvedProperty = undefined;
265
+ this.propertyName = undefined;
266
+ this.propertyIndex = undefined;
267
+ assertSafePath(this.parsedPath, this.path);
268
+ const node = this.node ?? findNode(this.rootNode, this.parsedPath.nodeName);
269
+ this.node = node;
270
+ if (!node)
166
271
  return;
167
- }
272
+ let target = node;
273
+ const { objectName, propertyName } = this.parsedPath;
274
+ let objectIndex = this.parsedPath.objectIndex;
275
+ let propertyIndex = this.parsedPath.propertyIndex;
168
276
  if (objectName) {
169
- let objectIndex = parsedPath.objectIndex;
170
- switch (objectName) {
171
- case "materials": {
172
- const material = targetObject.material;
173
- if (!material) {
174
- console.error("PropertyBinding: Can not bind to material as node does not have a material.", this);
175
- return;
176
- }
177
- if (!material.materials) {
178
- console.error("PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.", this);
179
- return;
180
- }
181
- targetObject = material.materials;
182
- break;
183
- }
184
- case "bones": {
185
- if (!targetObject.skeleton) {
186
- console.error("PropertyBinding: Can not bind to bones as node does not have a skeleton.", this);
187
- return;
188
- }
189
- const bones = (targetObject.skeleton.bones ?? []);
190
- targetObject = bones;
191
- // support resolving bone names into indices.
192
- for (let i = 0; i < bones.length; i++) {
193
- if (bones[i].name === objectIndex) {
194
- objectIndex = i;
195
- break;
196
- }
197
- }
198
- break;
199
- }
200
- case "map": {
201
- if ("map" in targetObject) {
202
- targetObject = targetObject.map;
203
- break;
204
- }
205
- const material = targetObject.material;
206
- if (!material) {
207
- console.error("PropertyBinding: Can not bind to material as node does not have a material.", this);
208
- return;
209
- }
210
- if (!material.map) {
211
- console.error("PropertyBinding: Can not bind to material.map as node.material does not have a map.", this);
212
- return;
213
- }
214
- targetObject = material.map;
215
- break;
216
- }
217
- default: {
218
- if (targetObject[objectName] === undefined) {
219
- console.error("PropertyBinding: Can not bind to objectName of node undefined.", this);
220
- return;
221
- }
222
- targetObject = targetObject[objectName];
277
+ if (objectName === "materials") {
278
+ const material = lookup(node, "material");
279
+ const materials = material.found ? lookup(material.value, "materials") : { found: false };
280
+ target = material.found && Array.isArray(material.value) ? material.value : materials.found ? materials.value : undefined;
281
+ }
282
+ else if (objectName === "bones") {
283
+ const skeleton = lookup(node, "skeleton");
284
+ const bones = skeleton.found ? lookup(skeleton.value, "bones") : { found: false };
285
+ target = bones.found ? bones.value : undefined;
286
+ if (Array.isArray(target) && objectIndex !== undefined) {
287
+ let index = target.findIndex((bone) => bone?.uuid === objectIndex);
288
+ if (index < 0)
289
+ index = target.findIndex((bone) => bone?.name === objectIndex);
290
+ if (index >= 0)
291
+ objectIndex = String(index);
223
292
  }
224
293
  }
294
+ else if (objectName === "map") {
295
+ const direct = lookup(node, "map");
296
+ const material = lookup(node, "material");
297
+ const fallback = material.found ? lookup(material.value, "map") : { found: false };
298
+ target = direct.found && direct.value !== undefined ? direct.value : fallback.found ? fallback.value : undefined;
299
+ }
300
+ else {
301
+ const object = lookup(node, objectName);
302
+ target = object.found ? object.value : undefined;
303
+ }
225
304
  if (objectIndex !== undefined) {
226
- const indexed = targetObject[objectIndex];
227
- if (indexed === undefined) {
228
- console.error("PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.", this, targetObject);
229
- return;
230
- }
231
- targetObject = indexed;
305
+ const entry = lookup(target, objectIndex);
306
+ target = entry.found ? entry.value : undefined;
232
307
  }
233
308
  }
234
- // resolve property
235
- const nodeProperty = targetObject[propertyName];
236
- if (nodeProperty === undefined) {
237
- console.error(`PropertyBinding: Trying to update property for track: ${parsedPath.nodeName}.${propertyName} but it wasn't found.`, targetObject);
309
+ if (!isObject(target))
310
+ return;
311
+ const property = lookup(target, propertyName);
312
+ if (!property.found || property.value === undefined)
238
313
  return;
239
- }
240
- this.targetObject = targetObject;
241
314
  if (propertyIndex !== undefined) {
242
- // access a sub element of the property array (only primitives are supported right now)
243
315
  if (propertyName === "morphTargetInfluences") {
244
- // support resolving morphTarget names into indices.
245
- const dictionary = typeof targetObject.morphTargetDictionary === "function"
246
- ? targetObject.morphTargetDictionary()
247
- : targetObject.morphTargetDictionary;
248
- if (dictionary === undefined) {
249
- console.error("PropertyBinding: Can not bind to morphTargetInfluences because node does not have a morphTargetDictionary.", this);
250
- return;
251
- }
252
- if (dictionary[propertyIndex] !== undefined) {
253
- propertyIndex = dictionary[propertyIndex];
254
- }
255
- else if (!Number.isInteger(Number(propertyIndex))) {
256
- console.error(`PropertyBinding: Can not bind to morphTargetInfluences[${String(propertyIndex)}] because it is not a known morph target.`, this);
257
- return;
258
- }
316
+ // This is a fixed API hook, not a user-selected property path; class
317
+ // implementations may provide it as a prototype method.
318
+ const dictionarySource = target.morphTargetDictionary;
319
+ const dictionary = typeof dictionarySource === "function" ? dictionarySource.call(target) : dictionarySource;
320
+ const named = lookup(dictionary, propertyIndex);
321
+ if (named.found && typeof named.value === "number" && Number.isSafeInteger(named.value) && named.value >= 0)
322
+ propertyIndex = named.value;
259
323
  else {
260
- propertyIndex = Number(propertyIndex);
324
+ const index = numericIndex(propertyIndex);
325
+ if (index === undefined)
326
+ return;
327
+ propertyIndex = index;
261
328
  }
262
329
  }
263
- // Other properties keep the accessor verbatim: `[0]` and `[x]` both
264
- // index `resolvedProperty` (three supports named components).
265
- this.resolvedProperty = nodeProperty;
330
+ else if (typeof propertyIndex === "string" && isNumericArray(property.value)) {
331
+ const components = { x: 0, y: 1, z: 2, w: 3 };
332
+ const index = Object.hasOwn(components, propertyIndex) ? components[propertyIndex] : numericIndex(propertyIndex);
333
+ if (index === undefined)
334
+ return;
335
+ propertyIndex = index;
336
+ }
337
+ const entry = lookup(property.value, propertyIndex);
338
+ if (!entry.found || entry.value === undefined)
339
+ return;
340
+ this.kind = "element";
341
+ this.resolvedProperty = property.value;
266
342
  this.propertyIndex = propertyIndex;
267
- this.bindingType = BindingType.ArrayElement;
268
343
  }
269
- else if (isFromToArrayCapable(nodeProperty)) {
270
- this.resolvedProperty = nodeProperty;
271
- this.bindingType = BindingType.HasFromToArray;
272
- this.propertyName = undefined;
344
+ else if (isArrayConvertible(property.value)) {
345
+ this.kind = "convertible";
346
+ this.resolvedProperty = property.value;
273
347
  }
274
- else if (Array.isArray(nodeProperty)) {
275
- this.resolvedProperty = nodeProperty;
276
- this.bindingType = BindingType.EntireArray;
277
- this.propertyName = undefined;
348
+ else if (isNumericArray(property.value)) {
349
+ this.kind = "array";
350
+ this.resolvedProperty = property.value;
278
351
  }
279
352
  else {
353
+ this.kind = "direct";
280
354
  this.propertyName = propertyName;
281
- this.bindingType = BindingType.Direct;
282
355
  }
283
- this.versioning = targetObject.isMaterial === true
284
- ? Versioning.NeedsUpdate
285
- : targetObject.isObject3D === true
286
- ? Versioning.MatrixWorldNeedsUpdate
287
- : Versioning.None;
356
+ this.targetObject = target;
288
357
  this.bound = true;
289
358
  }
290
359
  unbind() {
291
360
  this.node = null;
361
+ this.targetObject = undefined;
362
+ this.resolvedProperty = undefined;
363
+ this.propertyName = undefined;
364
+ this.propertyIndex = undefined;
292
365
  this.bound = false;
293
366
  }
294
367
  getValue() {
295
- if (!this.bound) {
368
+ return this[readForGroup]();
369
+ }
370
+ [readForGroup](isActive) {
371
+ if (!this.bound)
296
372
  this.bind();
297
- if (!this.bound) {
298
- return undefined;
299
- }
300
- }
301
- const target = this.targetObject;
302
- switch (this.bindingType) {
303
- case BindingType.ArrayElement:
304
- return this.resolvedProperty[this.propertyIndex];
305
- case BindingType.EntireArray:
306
- return [...this.resolvedProperty];
307
- case BindingType.HasFromToArray: {
308
- const source = this.resolvedProperty.toArray();
309
- return Array.from(source);
310
- }
311
- default:
312
- return target[this.propertyName];
313
- }
373
+ if (!this.bound || (isActive && !isActive()))
374
+ return undefined;
375
+ if (this.kind === "element")
376
+ return this.resolvedProperty[this.propertyIndex];
377
+ if (this.kind === "array")
378
+ return Array.from(this.resolvedProperty);
379
+ if (this.kind === "convertible")
380
+ return Array.from(this.resolvedProperty.toArray());
381
+ return this.targetObject[this.propertyName];
314
382
  }
315
383
  setValue(value) {
316
- if (!this.bound) {
384
+ this[writeForGroup](value);
385
+ }
386
+ [writeForGroup](value, isActive) {
387
+ if (!this.bound)
317
388
  this.bind();
318
- if (!this.bound) {
319
- return;
320
- }
321
- }
389
+ if (!this.bound || (isActive && !isActive()))
390
+ return;
391
+ // A setter can uncache or dispose its own group, which unbinds this
392
+ // binding while the assignment is in progress. Keep the actual target
393
+ // that received the write so invalidation remains safe and accurate.
322
394
  const target = this.targetObject;
323
- switch (this.bindingType) {
324
- case BindingType.ArrayElement:
325
- this.resolvedProperty[this.propertyIndex] = value;
326
- break;
327
- case BindingType.EntireArray: {
328
- const dest = this.resolvedProperty;
329
- const source = value;
330
- for (let i = 0, n = dest.length; i !== n && i < source.length; i++) {
331
- dest[i] = source[i];
332
- }
333
- break;
334
- }
335
- case BindingType.HasFromToArray:
336
- this.resolvedProperty.fromArray(value);
337
- break;
338
- default:
339
- target[this.propertyName] = value;
395
+ if (this.kind === "element")
396
+ this.resolvedProperty[this.propertyIndex] = value;
397
+ else if (this.kind === "array") {
398
+ if (!Array.isArray(value))
399
+ throw new Error(`PropertyBinding: Expected an array for ${this.path}.`);
400
+ const destination = this.resolvedProperty;
401
+ for (let i = 0; i < Math.min(destination.length, value.length); i++)
402
+ destination[i] = value[i];
340
403
  }
341
- if (this.versioning === Versioning.NeedsUpdate) {
342
- target.needsUpdate = true;
404
+ else if (this.kind === "convertible") {
405
+ if (!Array.isArray(value))
406
+ throw new Error(`PropertyBinding: Expected an array for ${this.path}.`);
407
+ this.resolvedProperty.fromArray(value);
343
408
  }
344
- else if (this.versioning === Versioning.MatrixWorldNeedsUpdate) {
409
+ else
410
+ target[this.propertyName] = value;
411
+ if (target.isMaterial === true)
412
+ target.needsUpdate = true;
413
+ else if (target.isObject3D === true) {
345
414
  target.matrixWorldNeedsUpdate = true;
415
+ // Aura3D scene nodes cache transforms separately from their object fields.
416
+ // Updating a vector in place must invalidate that cache as well.
417
+ const transform = target.transform;
418
+ transform?.markDirty?.();
346
419
  }
347
420
  }
348
- bindingType = BindingType.Direct;
349
- versioning = Versioning.None;
350
- }
351
- function isFromToArrayCapable(value) {
352
- const candidate = value;
353
- return typeof candidate?.fromArray === "function" && typeof candidate?.toArray === "function";
354
421
  }
422
+ // Capture the original dispatch methods once so a consumer's later prototype
423
+ // override continues to receive composite calls through its public method.
424
+ const defaultGetValue = PropertyBinding.prototype.getValue;
425
+ const defaultSetValue = PropertyBinding.prototype.setValue;
355
426
  /**
356
- * three's `PropertyBinding.Composite`: fans a single track path out over every
357
- * object in an {@link AnimationObjectGroup}-compatible group. `getValue` reads
358
- * the first valid object; `setValue` writes all valid objects.
427
+ * Fans a path out over active group members. Each operation snapshots the
428
+ * active binding identities at its start. New members wait for the next
429
+ * operation; members removed, uncached, unsubscribed or disposed before their
430
+ * turn are skipped. A member removed and reactivated before its turn remains
431
+ * eligible if it still has the same binding. Nested calls take new snapshots.
359
432
  */
360
433
  export class PropertyBindingComposite {
361
434
  targetGroup;
362
435
  bindings;
436
+ path;
363
437
  constructor(targetGroup, path, optionalParsedPath) {
364
438
  const parsedPath = optionalParsedPath ?? parseTrackName(path);
439
+ assertSafePath(parsedPath, path);
365
440
  this.targetGroup = targetGroup;
441
+ this.path = path;
366
442
  this.bindings = targetGroup.subscribe_(path, parsedPath);
367
443
  }
444
+ activeSnapshot() {
445
+ return [...new Set(this.bindings.slice(this.targetGroup.nCachedObjects_).filter((binding) => binding != null))];
446
+ }
447
+ isActive(binding) {
448
+ const check = this.targetGroup.isBindingActive_;
449
+ if (typeof check === "function")
450
+ return check.call(this.targetGroup, this.path, binding);
451
+ // Support custom group-like implementations that predate the optional
452
+ // membership method, using their live array and cached boundary.
453
+ return this.bindings.indexOf(binding, this.targetGroup.nCachedObjects_) !== -1;
454
+ }
368
455
  getValue() {
369
- this.bind();
370
- const firstValidIndex = this.targetGroup.nCachedObjects_;
371
- const binding = this.bindings[firstValidIndex];
372
- return binding?.getValue();
456
+ for (const binding of this.activeSnapshot()) {
457
+ if (!this.isActive(binding))
458
+ continue;
459
+ const value = binding instanceof PropertyBinding && binding.getValue === defaultGetValue
460
+ ? binding[readForGroup](() => this.isActive(binding))
461
+ : binding.getValue();
462
+ if (value !== undefined && this.isActive(binding))
463
+ return value;
464
+ }
465
+ return undefined;
373
466
  }
374
467
  setValue(value) {
375
- const bindings = this.bindings;
376
- for (let i = this.targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) {
377
- bindings[i].setValue(value);
468
+ for (const binding of this.activeSnapshot()) {
469
+ if (!this.isActive(binding))
470
+ continue;
471
+ if (binding instanceof PropertyBinding && binding.setValue === defaultSetValue) {
472
+ binding[writeForGroup](value, () => this.isActive(binding));
473
+ }
474
+ else {
475
+ binding.setValue(value);
476
+ }
378
477
  }
379
478
  }
380
479
  bind() {
381
- const bindings = this.bindings;
382
- for (let i = this.targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) {
383
- bindings[i].bind();
384
- }
480
+ for (const binding of this.activeSnapshot())
481
+ if (this.isActive(binding))
482
+ binding.bind();
385
483
  }
386
484
  unbind() {
387
- const bindings = this.bindings;
388
- for (let i = this.targetGroup.nCachedObjects_, n = bindings.length; i !== n; ++i) {
389
- bindings[i].unbind();
390
- }
485
+ for (const binding of this.activeSnapshot())
486
+ if (this.isActive(binding))
487
+ binding.unbind();
391
488
  }
392
489
  }
393
490
  //# sourceMappingURL=PropertyBinding.js.map