topview-3d-cli 0.1.0__py3-none-any.whl

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 (332) hide show
  1. topview_3d_cli/__init__.py +3 -0
  2. topview_3d_cli/__main__.py +5 -0
  3. topview_3d_cli/_runtime/builtin-assets/LICENSE +396 -0
  4. topview_3d_cli/_runtime/builtin-assets/NOTICE +24 -0
  5. topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_30ec53c93eae4173946b38639691ebe0.glb +0 -0
  6. topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_461b4f4b76ae4b198f439fa4f6f743b6.glb +0 -0
  7. topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_7d220ac359844c80a8faaf0e25478477.glb +0 -0
  8. topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_de37873a79244fbe8a0660ea08d1b4ab.glb +0 -0
  9. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-attack-jab.webp +0 -0
  10. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-attack-lunge.webp +0 -0
  11. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-attack-rear.webp +0 -0
  12. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-crawling-forward-reaching-with-le.webp +0 -0
  13. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-exaggerated-punch-with-right-hand.webp +0 -0
  14. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-examine-fwd.webp +0 -0
  15. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-holding-bow.webp +0 -0
  16. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-jab-with-right-hand.webp +0 -0
  17. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-on-hands-and-toes-right-leg-forwa.webp +0 -0
  18. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-on-right-leg.webp +0 -0
  19. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-push-up.webp +0 -0
  20. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-pushing-backwards.webp +0 -0
  21. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-pushing-with-both-hands.webp +0 -0
  22. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-pushing-with-left-side-of-body.webp +0 -0
  23. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-sliding.webp +0 -0
  24. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-arms-spread-looking-upwards.webp +0 -0
  25. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-examine-fwd.webp +0 -0
  26. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-lean-fwd-right-hand-supporting.webp +0 -0
  27. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-leaning-against-object-look-l.webp +0 -0
  28. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-back.webp +0 -0
  29. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-down-back.webp +0 -0
  30. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-fwd-f.webp +0 -0
  31. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-fwd-m.webp +0 -0
  32. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-on-knees-arms-up.webp +0 -0
  33. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-sneaking.webp +0 -0
  34. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-l-toe-top-of-right-foot.webp +0 -0
  35. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-legs-spread-lean-l.webp +0 -0
  36. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-legs-spread-lean-r.webp +0 -0
  37. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-legs-x.webp +0 -0
  38. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-r-foot-arms-extended-backwards.webp +0 -0
  39. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-r-toe-arms-up.webp +0 -0
  40. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-r-toe-l-leg-up.webp +0 -0
  41. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-toes-arch.webp +0 -0
  42. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-toes-reach.webp +0 -0
  43. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-bent-forwards.webp +0 -0
  44. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-left-leg-bent-hands-spread.webp +0 -0
  45. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-legs-bent-and-arms-spread.webp +0 -0
  46. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-legs-raised-arms-extended-u.webp +0 -0
  47. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-l-toe-look-l.webp +0 -0
  48. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-leaping-forward-arms-extended-backwa.webp +0 -0
  49. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dynamic-float-arch.webp +0 -0
  50. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dynamic-float-dive.webp +0 -0
  51. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-back-arched-arms-supp.webp +0 -0
  52. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-legs-to-side-arms-up.webp +0 -0
  53. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-legs-x-arms-supp.webp +0 -0
  54. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-back-left-leg-bent-hands-head.webp +0 -0
  55. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-back-left-leg-bent.webp +0 -0
  56. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-back-legs-x-hands-head.webp +0 -0
  57. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-left-side-left-arm-supporting-hea.webp +0 -0
  58. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-left-side-left-arm-supporting.webp +0 -0
  59. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-right-side-right-arm-supporting.webp +0 -0
  60. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-right-side-right-hand-supporting.webp +0 -0
  61. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-stomach-elbows-supporting.webp +0 -0
  62. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-right-arm-supporting-l-arm-up.webp +0 -0
  63. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-right-leg-forward-arms-supp.webp +0 -0
  64. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-run-fwd.webp +0 -0
  65. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-run-reach.webp +0 -0
  66. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-sneaking-forward.webp +0 -0
  67. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-sprinting.webp +0 -0
  68. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-stepping-forward-look-l.webp +0 -0
  69. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walk-fwd-look-r.webp +0 -0
  70. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walk-fwd.webp +0 -0
  71. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walking-arms-on-hips.webp +0 -0
  72. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walking-right-arm-on-hip.webp +0 -0
  73. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walking.webp +0 -0
  74. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-holding-left-knee-reclining.webp +0 -0
  75. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-holding-right-knee.webp +0 -0
  76. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-resting-on-knees.webp +0 -0
  77. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-supp.webp +0 -0
  78. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-up.webp +0 -0
  79. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-elbows-on-legs-lean-fwd.webp +0 -0
  80. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-elbows-on-legs-lean-l.webp +0 -0
  81. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-knees-up-hold.webp +0 -0
  82. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-lean-back-arms-supp.webp +0 -0
  83. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-lean-fwd-elbows-on-knees.webp +0 -0
  84. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-lean-fwd-hands-in-front.webp +0 -0
  85. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-arms-x.webp +0 -0
  86. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-hands-head.webp +0 -0
  87. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-hold.webp +0 -0
  88. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-reclining.webp +0 -0
  89. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-right-arm-over-knee.webp +0 -0
  90. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reaching-hands-towards-toes.webp +0 -0
  91. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reaching-left-hand-towards-right-toe.webp +0 -0
  92. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-facepalm.webp +0 -0
  93. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-hands-head.webp +0 -0
  94. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-left-leg-crossed-hands-head.webp +0 -0
  95. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-left-leg-crossed.webp +0 -0
  96. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-look-down.webp +0 -0
  97. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-right-leg-crossed-hands-hea.webp +0 -0
  98. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-rev-sit-hands-grasping.webp +0 -0
  99. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-rev-sit-hands-relaxed.webp +0 -0
  100. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-arms-behind-back.webp +0 -0
  101. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-arms-down.webp +0 -0
  102. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-arms-slightly-spread.webp +0 -0
  103. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-bending-forward-arms-x-over-knees.webp +0 -0
  104. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-hands-on-hips-look-back.webp +0 -0
  105. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-l-hip-f.webp +0 -0
  106. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-l-hip-m.webp +0 -0
  107. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-l-leg-r-hip-l-arm-up.webp +0 -0
  108. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-lean-back-arms-behind.webp +0 -0
  109. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-lean-back-l-hip.webp +0 -0
  110. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-left-elbow-resting-obj.webp +0 -0
  111. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-left-leg-obj-left-hand-on-knee-r-h.webp +0 -0
  112. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-left-leg-obj-left-hand-on-knee.webp +0 -0
  113. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-arms-up.webp +0 -0
  114. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-l-hip.webp +0 -0
  115. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-leaning-obj.webp +0 -0
  116. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-left-arm-obj-r-hip.webp +0 -0
  117. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-down-r.webp +0 -0
  118. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-fwd.webp +0 -0
  119. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-l.webp +0 -0
  120. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-r.webp +0 -0
  121. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-arm-up-l-hip.webp +0 -0
  122. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-foot-l-leg-up.webp +0 -0
  123. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-foot-up.webp +0 -0
  124. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-hip-l-arm-up.webp +0 -0
  125. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-hip.webp +0 -0
  126. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-right-hand-behind-head.webp +0 -0
  127. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-right-leg-obj-arms-x.webp +0 -0
  128. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-right-leg-obj-l-hip-right-hand-on.webp +0 -0
  129. topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_t-pose.webp +0 -0
  130. topview_3d_cli/_runtime/builtin-assets/manifest.json +2372 -0
  131. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-attack-jab.json +1 -0
  132. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-attack-lunge.json +1 -0
  133. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-attack-rear.json +1 -0
  134. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-crawling-forward-reaching-with-le.json +1 -0
  135. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-exaggerated-punch-with-right-hand.json +1 -0
  136. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-examine-fwd.json +1 -0
  137. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-holding-bow.json +1 -0
  138. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-jab-with-right-hand.json +1 -0
  139. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-on-hands-and-toes-right-leg-forwa.json +1 -0
  140. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-on-right-leg.json +1 -0
  141. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-push-up.json +1 -0
  142. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-pushing-backwards.json +1 -0
  143. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-pushing-with-both-hands.json +1 -0
  144. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-pushing-with-left-side-of-body.json +1 -0
  145. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-sliding.json +1 -0
  146. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-arms-spread-looking-upwards.json +1 -0
  147. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-examine-fwd.json +1 -0
  148. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-lean-fwd-right-hand-supporting.json +1 -0
  149. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-leaning-against-object-look-l.json +1 -0
  150. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-back.json +1 -0
  151. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-down-back.json +1 -0
  152. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-fwd-f.json +1 -0
  153. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-fwd-m.json +1 -0
  154. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-on-knees-arms-up.json +1 -0
  155. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-sneaking.json +1 -0
  156. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-l-toe-top-of-right-foot.json +1 -0
  157. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-legs-spread-lean-l.json +1 -0
  158. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-legs-spread-lean-r.json +1 -0
  159. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-legs-x.json +1 -0
  160. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-r-foot-arms-extended-backwards.json +1 -0
  161. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-r-toe-arms-up.json +1 -0
  162. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-r-toe-l-leg-up.json +1 -0
  163. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-toes-arch.json +1 -0
  164. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-toes-reach.json +1 -0
  165. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-bent-forwards.json +1 -0
  166. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-left-leg-bent-hands-spread.json +1 -0
  167. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-legs-bent-and-arms-spread.json +1 -0
  168. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-legs-raised-arms-extended-u.json +1 -0
  169. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-l-toe-look-l.json +1 -0
  170. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-leaping-forward-arms-extended-backwa.json +1 -0
  171. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dynamic-float-arch.json +1 -0
  172. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dynamic-float-dive.json +1 -0
  173. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-back-arched-arms-supp.json +1 -0
  174. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-legs-to-side-arms-up.json +1 -0
  175. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-legs-x-arms-supp.json +1 -0
  176. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-back-left-leg-bent-hands-head.json +1 -0
  177. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-back-left-leg-bent.json +1 -0
  178. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-back-legs-x-hands-head.json +1 -0
  179. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-left-side-left-arm-supporting-hea.json +1 -0
  180. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-left-side-left-arm-supporting.json +1 -0
  181. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-right-side-right-arm-supporting.json +1 -0
  182. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-right-side-right-hand-supporting.json +1 -0
  183. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-stomach-elbows-supporting.json +1 -0
  184. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-right-arm-supporting-l-arm-up.json +1 -0
  185. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-right-leg-forward-arms-supp.json +1 -0
  186. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-run-fwd.json +1 -0
  187. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-run-reach.json +1 -0
  188. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-sneaking-forward.json +1 -0
  189. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-sprinting.json +1 -0
  190. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-stepping-forward-look-l.json +1 -0
  191. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walk-fwd-look-r.json +1 -0
  192. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walk-fwd.json +1 -0
  193. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walking-arms-on-hips.json +1 -0
  194. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walking-right-arm-on-hip.json +1 -0
  195. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walking.json +1 -0
  196. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-holding-left-knee-reclining.json +1 -0
  197. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-holding-right-knee.json +1 -0
  198. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-resting-on-knees.json +1 -0
  199. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-supp.json +1 -0
  200. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-up.json +1 -0
  201. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-elbows-on-legs-lean-fwd.json +1 -0
  202. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-elbows-on-legs-lean-l.json +1 -0
  203. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-knees-up-hold.json +1 -0
  204. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-lean-back-arms-supp.json +1 -0
  205. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-lean-fwd-elbows-on-knees.json +1 -0
  206. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-lean-fwd-hands-in-front.json +1 -0
  207. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-arms-x.json +1 -0
  208. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-hands-head.json +1 -0
  209. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-hold.json +1 -0
  210. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-reclining.json +1 -0
  211. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-right-arm-over-knee.json +1 -0
  212. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reaching-hands-towards-toes.json +1 -0
  213. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reaching-left-hand-towards-right-toe.json +1 -0
  214. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-facepalm.json +1 -0
  215. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-hands-head.json +1 -0
  216. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-left-leg-crossed-hands-head.json +1 -0
  217. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-left-leg-crossed.json +1 -0
  218. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-look-down.json +1 -0
  219. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-right-leg-crossed-hands-hea.json +1 -0
  220. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-rev-sit-hands-grasping.json +1 -0
  221. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-rev-sit-hands-relaxed.json +1 -0
  222. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-arms-behind-back.json +1 -0
  223. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-arms-down.json +1 -0
  224. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-arms-slightly-spread.json +1 -0
  225. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-bending-forward-arms-x-over-knees.json +1 -0
  226. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-hands-on-hips-look-back.json +1 -0
  227. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-l-hip-f.json +1 -0
  228. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-l-hip-m.json +1 -0
  229. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-l-leg-r-hip-l-arm-up.json +1 -0
  230. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-lean-back-arms-behind.json +1 -0
  231. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-lean-back-l-hip.json +1 -0
  232. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-left-elbow-resting-obj.json +1 -0
  233. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-left-leg-obj-left-hand-on-knee-r-h.json +1 -0
  234. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-left-leg-obj-left-hand-on-knee.json +1 -0
  235. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-arms-up.json +1 -0
  236. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-l-hip.json +1 -0
  237. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-leaning-obj.json +1 -0
  238. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-left-arm-obj-r-hip.json +1 -0
  239. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-down-r.json +1 -0
  240. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-fwd.json +1 -0
  241. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-l.json +1 -0
  242. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-r.json +1 -0
  243. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-arm-up-l-hip.json +1 -0
  244. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-foot-l-leg-up.json +1 -0
  245. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-foot-up.json +1 -0
  246. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-hip-l-arm-up.json +1 -0
  247. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-hip.json +1 -0
  248. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-right-hand-behind-head.json +1 -0
  249. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-right-leg-obj-arms-x.json +1 -0
  250. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-right-leg-obj-l-hip-right-hand-on.json +1 -0
  251. topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_t-pose.json +1 -0
  252. topview_3d_cli/_runtime/director-cli/applyIntent.mjs +35 -0
  253. topview_3d_cli/_runtime/director-cli/browser.mjs +47 -0
  254. topview_3d_cli/_runtime/director-cli/cli.mjs +56 -0
  255. topview_3d_cli/_runtime/director-cli/editSequence.mjs +125 -0
  256. topview_3d_cli/_runtime/director-cli/evaluate.mjs +254 -0
  257. topview_3d_cli/_runtime/director-cli/mediaUrl.mjs +47 -0
  258. topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/draco/draco_decoder.js +34 -0
  259. topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/draco/draco_decoder.wasm +0 -0
  260. topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/draco/draco_wasm_wrapper.js +117 -0
  261. topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/engine/index.mjs +22219 -0
  262. topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/evaluate/index.mjs +4229 -0
  263. topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/headless/index.mjs +22832 -0
  264. topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/package.json +75 -0
  265. topview_3d_cli/_runtime/director-cli/node_modules/three/LICENSE +21 -0
  266. topview_3d_cli/_runtime/director-cli/node_modules/three/build/three.core.js +59732 -0
  267. topview_3d_cli/_runtime/director-cli/node_modules/three/build/three.module.js +19552 -0
  268. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/controls/OrbitControls.js +1963 -0
  269. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/curves/NURBSCurve.js +155 -0
  270. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/curves/NURBSUtils.js +532 -0
  271. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/draco/draco_decoder.js +34 -0
  272. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/draco/draco_decoder.wasm +0 -0
  273. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/draco/draco_wasm_wrapper.js +117 -0
  274. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/fflate.module.js +2672 -0
  275. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/lines/LineMaterial.js +703 -0
  276. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/lines/LineSegments2.js +426 -0
  277. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/lines/LineSegmentsGeometry.js +298 -0
  278. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/loaders/DRACOLoader.js +739 -0
  279. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/loaders/FBXLoader.js +4580 -0
  280. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/loaders/GLTFLoader.js +4860 -0
  281. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/utils/BufferGeometryUtils.js +1434 -0
  282. topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/utils/SkeletonUtils.js +496 -0
  283. topview_3d_cli/_runtime/director-cli/node_modules/three/package.json +123 -0
  284. topview_3d_cli/_runtime/director-cli/node_modules/zod/LICENSE +21 -0
  285. topview_3d_cli/_runtime/director-cli/node_modules/zod/index.js +4 -0
  286. topview_3d_cli/_runtime/director-cli/node_modules/zod/package.json +118 -0
  287. topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/ZodError.js +133 -0
  288. topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/errors.js +9 -0
  289. topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/external.js +6 -0
  290. topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/errorUtil.js +6 -0
  291. topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/parseUtil.js +109 -0
  292. topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/typeAliases.js +1 -0
  293. topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/util.js +133 -0
  294. topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/locales/en.js +109 -0
  295. topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/types.js +3693 -0
  296. topview_3d_cli/_runtime/director-cli/package.json +6 -0
  297. topview_3d_cli/_runtime/director-cli/playwright.mjs +26 -0
  298. topview_3d_cli/_runtime/director-cli/render.mjs +336 -0
  299. topview_3d_cli/_runtime/director-cli/static/headless.html +124 -0
  300. topview_3d_cli/_runtime/director-cli/static/measure.mjs +61 -0
  301. topview_3d_cli/_runtime/director-cli/static/pose.mjs +71 -0
  302. topview_3d_cli/_runtime/director-cli/staticIntent.mjs +135 -0
  303. topview_3d_cli/_runtime/runtime.json +6 -0
  304. topview_3d_cli/bom.py +326 -0
  305. topview_3d_cli/contracts/director-v1/schema/director-document.schema.json +1097 -0
  306. topview_3d_cli/contracts/director-v1/schema/fcurves-compact-v1.schema.json +105 -0
  307. topview_3d_cli/director_camera_view.py +54 -0
  308. topview_3d_cli/director_document.py +793 -0
  309. topview_3d_cli/director_geometry.py +66 -0
  310. topview_3d_cli/director_operations.py +679 -0
  311. topview_3d_cli/director_static.py +134 -0
  312. topview_3d_cli/local_assets.py +248 -0
  313. topview_3d_cli/local_bom.py +108 -0
  314. topview_3d_cli/local_catalog.py +156 -0
  315. topview_3d_cli/local_cli.py +493 -0
  316. topview_3d_cli/local_edit.py +350 -0
  317. topview_3d_cli/local_errors.py +104 -0
  318. topview_3d_cli/local_inspect.py +302 -0
  319. topview_3d_cli/local_project.py +280 -0
  320. topview_3d_cli/local_read.py +139 -0
  321. topview_3d_cli/local_render.py +97 -0
  322. topview_3d_cli/node_batch.py +299 -0
  323. topview_3d_cli/renderer.py +75 -0
  324. topview_3d_cli/runtime.py +107 -0
  325. topview_3d_cli-0.1.0.dist-info/METADATA +48 -0
  326. topview_3d_cli-0.1.0.dist-info/RECORD +332 -0
  327. topview_3d_cli-0.1.0.dist-info/WHEEL +5 -0
  328. topview_3d_cli-0.1.0.dist-info/entry_points.txt +2 -0
  329. topview_3d_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
  330. topview_3d_cli-0.1.0.dist-info/licenses/LICENSE-ASSETS +396 -0
  331. topview_3d_cli-0.1.0.dist-info/licenses/NOTICE +18 -0
  332. topview_3d_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,4229 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/evaluate/FrameSnapshot.ts
4
+ function vec3(x = 0, y = 0, z2 = 0) {
5
+ return { x, y, z: z2 };
6
+ }
7
+ var CMD_POOL = Symbol("cmdPool");
8
+ function cmdPoolOf(out) {
9
+ const snap = out;
10
+ let pool = snap[CMD_POOL];
11
+ if (!pool) {
12
+ pool = [];
13
+ snap[CMD_POOL] = pool;
14
+ }
15
+ return pool;
16
+ }
17
+ function createFrameSnapshot() {
18
+ const out = {
19
+ transforms: /* @__PURE__ */ new Map(),
20
+ poses: /* @__PURE__ */ new Map(),
21
+ motionPlayback: [],
22
+ camera: { position: vec3(), lookAt: vec3(0, 1.2, 0), fov: 50 }
23
+ };
24
+ out[CMD_POOL] = [];
25
+ return out;
26
+ }
27
+ function prepareFrameSnapshot(out, nodeIds) {
28
+ for (const id of nodeIds) {
29
+ if (!out.transforms.has(id)) {
30
+ out.transforms.set(id, {
31
+ position: vec3(),
32
+ rotation: vec3(),
33
+ scale: vec3(1, 1, 1),
34
+ lookAt: vec3(0, 1.2, 0),
35
+ fov: 50
36
+ });
37
+ }
38
+ if (!out.poses.has(id))
39
+ out.poses.set(id, []);
40
+ }
41
+ }
42
+ function resetFrameSnapshot(out) {
43
+ out.motionPlayback.length = 0;
44
+ for (const poses of out.poses.values())
45
+ poses.length = 0;
46
+ }
47
+ function copyVec3(dst, src) {
48
+ dst.x = src.x;
49
+ dst.y = src.y;
50
+ dst.z = src.z;
51
+ }
52
+ function ensureTransform(out, nodeId) {
53
+ let xf = out.transforms.get(nodeId);
54
+ if (xf)
55
+ return xf;
56
+ xf = {
57
+ position: vec3(),
58
+ rotation: vec3(),
59
+ scale: vec3(1, 1, 1),
60
+ lookAt: vec3(0, 1.2, 0),
61
+ fov: 50
62
+ };
63
+ out.transforms.set(nodeId, xf);
64
+ return xf;
65
+ }
66
+
67
+ // src/evaluate/camera/cameraAim.ts
68
+ var D2R = Math.PI / 180;
69
+ function lookDistance(pos, lookAt) {
70
+ if (!lookAt)
71
+ return 2;
72
+ return Math.hypot(lookAt.x - pos.x, lookAt.y - pos.y, lookAt.z - pos.z) || 2;
73
+ }
74
+ function lookAtFromEulerDeg(pos, rotDeg, dist) {
75
+ const x = rotDeg.x * D2R;
76
+ const y = rotDeg.y * D2R;
77
+ const z2 = rotDeg.z * D2R;
78
+ const cx = Math.cos(x);
79
+ const sx = Math.sin(x);
80
+ const cy = Math.cos(y);
81
+ const sy = Math.sin(y);
82
+ const cz = Math.cos(z2);
83
+ const sz = Math.sin(z2);
84
+ const iy = sx;
85
+ const iz = -cx;
86
+ const jx = sy * iz;
87
+ const jy = iy;
88
+ const jz = cy * iz;
89
+ const fx = cz * jx - sz * jy;
90
+ const fy = sz * jx + cz * jy;
91
+ const fz = jz;
92
+ const len = Math.hypot(fx, fy, fz) || 1;
93
+ const d = dist > 1e-4 ? dist : 2;
94
+ return {
95
+ x: pos.x + fx / len * d,
96
+ y: pos.y + fy / len * d,
97
+ z: pos.z + fz / len * d
98
+ };
99
+ }
100
+
101
+ // src/evaluate/curves/BakedCurve.ts
102
+ function sampleCurve(curve, timeMs) {
103
+ const kfs = curve.keyframes;
104
+ if (kfs.length === 0)
105
+ return 0;
106
+ if (kfs.length === 1 || timeMs <= kfs[0].time)
107
+ return kfs[0].value;
108
+ const last = kfs[kfs.length - 1];
109
+ if (timeMs >= last.time)
110
+ return last.value;
111
+ let lo = 0;
112
+ let hi = kfs.length - 1;
113
+ while (lo + 1 < hi) {
114
+ const mid = lo + hi >> 1;
115
+ if (kfs[mid].time <= timeMs)
116
+ lo = mid;
117
+ else
118
+ hi = mid;
119
+ }
120
+ const a = kfs[lo];
121
+ const b = kfs[hi];
122
+ const span = b.time - a.time;
123
+ let t = span > 0 ? (timeMs - a.time) / span : 0;
124
+ if (a.interpolation === "bezier") {
125
+ t = t * t * (3 - 2 * t);
126
+ }
127
+ return a.value + (b.value - a.value) * t;
128
+ }
129
+
130
+ // src/evaluate/camera/CameraRig.ts
131
+ var _rawOut = { position: [0, 0, 0], lookAt: [0, 0, 0], fov: 50, clipId: null };
132
+ var curveMapCache = /* @__PURE__ */ new WeakMap();
133
+ function curveMap(clip) {
134
+ let m = curveMapCache.get(clip);
135
+ if (!m) {
136
+ m = /* @__PURE__ */ new Map();
137
+ for (const c of clip.motion.curves)
138
+ m.set(`${c.group}:${c.arrayIndex}`, c);
139
+ curveMapCache.set(clip, m);
140
+ }
141
+ return m;
142
+ }
143
+ function clipProgress(clip, frame) {
144
+ const span = clip.frameEnd - clip.frameStart;
145
+ const p = span > 0 ? (frame - clip.frameStart) / span : 0;
146
+ const dur = clip.motion.durationMs;
147
+ if (!(dur > 0))
148
+ return Math.min(Math.max(p, 0), 1);
149
+ const ms = clip.trimStartMs + p * (clip.trimEndMs - clip.trimStartMs);
150
+ return Math.min(Math.max(ms / dur, 0), 1);
151
+ }
152
+ function applyEasing(easing, t) {
153
+ switch (easing) {
154
+ case "ease-in-out":
155
+ return t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t);
156
+ case "ease-in":
157
+ return t * t;
158
+ case "ease-out":
159
+ return 1 - (1 - t) * (1 - t);
160
+ default:
161
+ return t;
162
+ }
163
+ }
164
+ var D2R2 = Math.PI / 180;
165
+ function proceduralOrbitPose(node, clip, frame) {
166
+ const src = clip.motion.source;
167
+ if (!src || src.type !== "orbit" || !src.startPosition || !src.pivot)
168
+ return false;
169
+ const e = applyEasing(src.easing, clipProgress(clip, frame));
170
+ const axis = src.axis ?? { x: 0, y: 1, z: 0 };
171
+ const al = Math.hypot(axis.x, axis.y, axis.z) || 1;
172
+ const ax = { x: axis.x / al, y: axis.y / al, z: axis.z / al };
173
+ const v = {
174
+ x: src.startPosition.x - src.pivot.x,
175
+ y: src.startPosition.y - src.pivot.y,
176
+ z: src.startPosition.z - src.pivot.z
177
+ };
178
+ const dot = v.x * ax.x + v.y * ax.y + v.z * ax.z;
179
+ const par = { x: ax.x * dot, y: ax.y * dot, z: ax.z * dot };
180
+ const perp = { x: v.x - par.x, y: v.y - par.y, z: v.z - par.z };
181
+ const ang = (src.yawDeg ?? 0) * e * D2R2;
182
+ const cos = Math.cos(ang);
183
+ const sin = Math.sin(ang);
184
+ const cross = {
185
+ x: ax.y * perp.z - ax.z * perp.y,
186
+ y: ax.z * perp.x - ax.x * perp.z,
187
+ z: ax.x * perp.y - ax.y * perp.x
188
+ };
189
+ let rp = {
190
+ x: perp.x * cos + cross.x * sin,
191
+ y: perp.y * cos + cross.y * sin,
192
+ z: perp.z * cos + cross.z * sin
193
+ };
194
+ const r0 = Math.hypot(perp.x, perp.y, perp.z);
195
+ const rd = src.radiusDelta ?? 0;
196
+ if (r0 > 1e-9 && rd !== 0) {
197
+ const k = (r0 + rd * e) / r0;
198
+ rp = { x: rp.x * k, y: rp.y * k, z: rp.z * k };
199
+ }
200
+ const hd = (src.heightDelta ?? 0) * e;
201
+ _rawOut.position[0] = src.pivot.x + par.x + ax.x * hd + rp.x;
202
+ _rawOut.position[1] = src.pivot.y + par.y + ax.y * hd + rp.y;
203
+ _rawOut.position[2] = src.pivot.z + par.z + ax.z * hd + rp.z;
204
+ const la0 = src.startLookAt ?? { x: 0, y: 1.2, z: 0 };
205
+ const la1 = src.lookAtTarget ?? la0;
206
+ _rawOut.lookAt[0] = la0.x + (la1.x - la0.x) * e;
207
+ _rawOut.lookAt[1] = la0.y + (la1.y - la0.y) * e;
208
+ _rawOut.lookAt[2] = la0.z + (la1.z - la0.z) * e;
209
+ _rawOut.fov = src.fov ?? node.camera?.fov ?? 50;
210
+ _rawOut.clipId = clip.id;
211
+ return true;
212
+ }
213
+ function staticPose(node) {
214
+ const t = node.transform;
215
+ const la = node.camera?.lookAt ?? { x: 0, y: 1.2, z: 0 };
216
+ return {
217
+ position: [t.position.x, t.position.y, t.position.z],
218
+ lookAt: [la.x, la.y, la.z],
219
+ fov: node.camera?.fov ?? 50,
220
+ clipId: null
221
+ };
222
+ }
223
+ function writeStaticPose(node, out) {
224
+ const t = node.transform;
225
+ const la = node.camera?.lookAt ?? { x: 0, y: 1.2, z: 0 };
226
+ out.position[0] = t.position.x;
227
+ out.position[1] = t.position.y;
228
+ out.position[2] = t.position.z;
229
+ out.lookAt[0] = la.x;
230
+ out.lookAt[1] = la.y;
231
+ out.lookAt[2] = la.z;
232
+ out.fov = node.camera?.fov ?? 50;
233
+ out.clipId = null;
234
+ }
235
+ function writeRawClipPose(node, clip, frame, _fps, out) {
236
+ if (proceduralOrbitPose(node, clip, frame)) {
237
+ out.position[0] = _rawOut.position[0];
238
+ out.position[1] = _rawOut.position[1];
239
+ out.position[2] = _rawOut.position[2];
240
+ out.lookAt[0] = _rawOut.lookAt[0];
241
+ out.lookAt[1] = _rawOut.lookAt[1];
242
+ out.lookAt[2] = _rawOut.lookAt[2];
243
+ out.fov = _rawOut.fov;
244
+ out.clipId = _rawOut.clipId;
245
+ return;
246
+ }
247
+ const speed = clip.playback?.speed ?? 1;
248
+ const span = clip.frameEnd - clip.frameStart;
249
+ const p = span > 0 ? (frame - clip.frameStart) / span : 0;
250
+ let ms = clip.trimStartMs + p * (clip.trimEndMs - clip.trimStartMs) * speed;
251
+ const trimSpan = clip.trimEndMs - clip.trimStartMs;
252
+ if (clip.playback?.loop && trimSpan > 0) {
253
+ ms = clip.trimStartMs + ((ms - clip.trimStartMs) % trimSpan + trimSpan) % trimSpan;
254
+ }
255
+ ms = Math.min(Math.max(ms, clip.trimStartMs), clip.trimEndMs);
256
+ const m = curveMap(clip);
257
+ const s = (group, idx, fallback) => {
258
+ const c = m.get(`${group}:${idx}`);
259
+ return c ? sampleCurve(c, ms) : fallback;
260
+ };
261
+ const t = node.transform;
262
+ const staticLa = node.camera?.lookAt ?? { x: 0, y: 1.2, z: 0 };
263
+ out.position[0] = s("position", 0, t.position.x);
264
+ out.position[1] = s("position", 1, t.position.y);
265
+ out.position[2] = s("position", 2, t.position.z);
266
+ out.lookAt[0] = s("lookAt", 0, staticLa.x);
267
+ out.lookAt[1] = s("lookAt", 1, staticLa.y);
268
+ out.lookAt[2] = s("lookAt", 2, staticLa.z);
269
+ out.fov = s("lens", 0, node.camera?.fov ?? 50);
270
+ out.clipId = clip.id;
271
+ }
272
+ function rawClipPose(node, clip, frame, fps) {
273
+ writeRawClipPose(node, clip, frame, fps, _rawOut);
274
+ return {
275
+ position: [_rawOut.position[0], _rawOut.position[1], _rawOut.position[2]],
276
+ lookAt: [_rawOut.lookAt[0], _rawOut.lookAt[1], _rawOut.lookAt[2]],
277
+ fov: _rawOut.fov,
278
+ clipId: _rawOut.clipId
279
+ };
280
+ }
281
+ var chainCache = /* @__PURE__ */ new WeakMap();
282
+ function buildChain(node, clips, fps) {
283
+ const segs = clips.filter((c) => c.target.nodeId === node.id).sort((a, b) => a.frameStart - b.frameStart);
284
+ let prev = staticPose(node);
285
+ let first = true;
286
+ return segs.map((clip) => {
287
+ const rawFirst = rawClipPose(node, clip, clip.frameStart, fps);
288
+ const rawLast = rawClipPose(node, clip, clip.frameEnd, fps);
289
+ const offset = first ? { position: [0, 0, 0], lookAt: [0, 0, 0], fov: 0 } : {
290
+ position: [
291
+ prev.position[0] - rawFirst.position[0],
292
+ prev.position[1] - rawFirst.position[1],
293
+ prev.position[2] - rawFirst.position[2]
294
+ ],
295
+ lookAt: [
296
+ prev.lookAt[0] - rawFirst.lookAt[0],
297
+ prev.lookAt[1] - rawFirst.lookAt[1],
298
+ prev.lookAt[2] - rawFirst.lookAt[2]
299
+ ],
300
+ fov: prev.fov - rawFirst.fov
301
+ };
302
+ first = false;
303
+ const end = {
304
+ position: [
305
+ rawLast.position[0] + offset.position[0],
306
+ rawLast.position[1] + offset.position[1],
307
+ rawLast.position[2] + offset.position[2]
308
+ ],
309
+ lookAt: [
310
+ rawLast.lookAt[0] + offset.lookAt[0],
311
+ rawLast.lookAt[1] + offset.lookAt[1],
312
+ rawLast.lookAt[2] + offset.lookAt[2]
313
+ ],
314
+ fov: rawLast.fov + offset.fov
315
+ };
316
+ prev = { ...end, clipId: null };
317
+ return { clip, offset, end };
318
+ });
319
+ }
320
+ function getChain(node, clips, fps) {
321
+ let byNode = chainCache.get(clips);
322
+ if (!byNode) {
323
+ byNode = /* @__PURE__ */ new WeakMap();
324
+ chainCache.set(clips, byNode);
325
+ }
326
+ let chain = byNode.get(node);
327
+ if (!chain) {
328
+ chain = buildChain(node, clips, fps);
329
+ byNode.set(node, chain);
330
+ }
331
+ return chain;
332
+ }
333
+ function evaluateCameraPoseInto(node, clips, frame, fps, chained, out) {
334
+ const clip = clips.find(
335
+ (c) => c.target.nodeId === node.id && frame >= c.frameStart && frame <= c.frameEnd
336
+ );
337
+ if (!node.camera) {
338
+ writeStaticPose(node, out);
339
+ return;
340
+ }
341
+ if (!chained) {
342
+ if (!clip)
343
+ writeStaticPose(node, out);
344
+ else
345
+ writeRawClipPose(node, clip, frame, fps, out);
346
+ return;
347
+ }
348
+ const chain = getChain(node, clips, fps);
349
+ if (chain.length === 0) {
350
+ writeStaticPose(node, out);
351
+ return;
352
+ }
353
+ if (clip) {
354
+ const seg = chain.find((s) => s.clip === clip);
355
+ writeRawClipPose(node, clip, frame, fps, out);
356
+ out.position[0] += seg.offset.position[0];
357
+ out.position[1] += seg.offset.position[1];
358
+ out.position[2] += seg.offset.position[2];
359
+ out.lookAt[0] += seg.offset.lookAt[0];
360
+ out.lookAt[1] += seg.offset.lookAt[1];
361
+ out.lookAt[2] += seg.offset.lookAt[2];
362
+ out.fov += seg.offset.fov;
363
+ out.clipId = clip.id;
364
+ return;
365
+ }
366
+ if (frame < chain[0].clip.frameStart) {
367
+ writeStaticPose(node, out);
368
+ return;
369
+ }
370
+ let prev = chain[0];
371
+ for (const s of chain) {
372
+ if (s.clip.frameEnd < frame)
373
+ prev = s;
374
+ else
375
+ break;
376
+ }
377
+ out.position[0] = prev.end.position[0];
378
+ out.position[1] = prev.end.position[1];
379
+ out.position[2] = prev.end.position[2];
380
+ out.lookAt[0] = prev.end.lookAt[0];
381
+ out.lookAt[1] = prev.end.lookAt[1];
382
+ out.lookAt[2] = prev.end.lookAt[2];
383
+ out.fov = prev.end.fov;
384
+ out.clipId = null;
385
+ }
386
+
387
+ // src/evaluate/camera/subjectBinding.ts
388
+ var DEG2RAD = Math.PI / 180;
389
+ function subjectYawDeg(rotation) {
390
+ const x = rotation.x * DEG2RAD;
391
+ const y = rotation.y * DEG2RAD;
392
+ const forwardX = Math.sin(y);
393
+ const forwardZ = Math.cos(x) * Math.cos(y);
394
+ if (Math.abs(forwardX) < 1e-9 && Math.abs(forwardZ) < 1e-9)
395
+ return rotation.y;
396
+ return Math.atan2(forwardX, forwardZ) / DEG2RAD;
397
+ }
398
+ function subjectLocalToWorld(local, subjectPos, yawDeg) {
399
+ const yaw = yawDeg * DEG2RAD;
400
+ const cos = Math.cos(yaw);
401
+ const sin = Math.sin(yaw);
402
+ return {
403
+ x: subjectPos.x + local.x * cos + local.z * sin,
404
+ y: subjectPos.y + local.y,
405
+ z: subjectPos.z - local.x * sin + local.z * cos
406
+ };
407
+ }
408
+ function subjectWorldToLocal(world, subjectPos, yawDeg) {
409
+ const yaw = yawDeg * DEG2RAD;
410
+ const cos = Math.cos(yaw);
411
+ const sin = Math.sin(yaw);
412
+ const dx = world.x - subjectPos.x;
413
+ const dz = world.z - subjectPos.z;
414
+ return {
415
+ x: dx * cos - dz * sin,
416
+ y: world.y - subjectPos.y,
417
+ z: dx * sin + dz * cos
418
+ };
419
+ }
420
+ function subjectOverridesMotion(binding) {
421
+ return binding.overridesMotion !== false;
422
+ }
423
+ var CHARACTER_LOOK_AT_HEIGHT = 1.2;
424
+ function characterLookAtPoint(subjectPos, offset) {
425
+ if (offset) {
426
+ return {
427
+ x: subjectPos.x + offset.x,
428
+ y: subjectPos.y + offset.y,
429
+ z: subjectPos.z + offset.z
430
+ };
431
+ }
432
+ return {
433
+ x: subjectPos.x,
434
+ y: subjectPos.y + CHARACTER_LOOK_AT_HEIGHT,
435
+ z: subjectPos.z
436
+ };
437
+ }
438
+ function resolveSubjectCameraPose(binding, subjectPos, subjectRotation) {
439
+ const yawDeg = binding.followRotation ? subjectYawDeg(subjectRotation) : 0;
440
+ return {
441
+ position: subjectLocalToWorld(binding.offset, subjectPos, yawDeg),
442
+ lookAt: subjectLocalToWorld(binding.lookAtOffset, subjectPos, yawDeg)
443
+ };
444
+ }
445
+
446
+ // src/evaluate/clips.ts
447
+ function motionClipAt(clips, nodeId, frame) {
448
+ for (const clip of clips) {
449
+ if (clip.target.nodeId === nodeId && frame >= clip.frameStart && frame <= clip.frameEnd) {
450
+ return clip;
451
+ }
452
+ }
453
+ return null;
454
+ }
455
+ function motionTimeSeconds(clip, frame, fps) {
456
+ const speed = clip.playback.speed ?? 1;
457
+ const t = (Math.max(clip.frameStart, frame) - clip.frameStart) / fps * speed;
458
+ if (clip.playback.loop && clip.sourceDuration > 0) {
459
+ return clip.motion.time + t % clip.sourceDuration;
460
+ }
461
+ return Math.min(clip.motion.time + t, clip.sourceDuration);
462
+ }
463
+
464
+ // src/evaluate/curves/KeyframeTrack.ts
465
+ function applyInterpolation(interp, t) {
466
+ switch (interp) {
467
+ case "smooth":
468
+ return t * t * (3 - 2 * t);
469
+ case "ease-in-out":
470
+ return t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t);
471
+ case "ease-in":
472
+ return t * t;
473
+ case "ease-out":
474
+ return 1 - (1 - t) * (1 - t);
475
+ case "linear":
476
+ default:
477
+ return t;
478
+ }
479
+ }
480
+ var _sortedCache = /* @__PURE__ */ new WeakMap();
481
+ function sortedKeyframes(kfs) {
482
+ let sorted = _sortedCache.get(kfs);
483
+ if (sorted)
484
+ return sorted;
485
+ sorted = kfs.slice().sort((a, b) => a.frame - b.frame);
486
+ _sortedCache.set(kfs, sorted);
487
+ return sorted;
488
+ }
489
+ function sampleKeyframesInto(kfs, frame, out, extrapolate = true) {
490
+ if (kfs.length === 0)
491
+ return false;
492
+ const sorted = sortedKeyframes(kfs);
493
+ const write = (src) => {
494
+ const n = Math.max(src.length, out.length);
495
+ for (let i2 = 0; i2 < n; i2++)
496
+ out[i2] = src[i2] ?? 0;
497
+ };
498
+ if (frame <= sorted[0].frame) {
499
+ if (extrapolate !== true && frame < sorted[0].frame)
500
+ return false;
501
+ write(sorted[0].value);
502
+ return true;
503
+ }
504
+ const last = sorted[sorted.length - 1];
505
+ if (frame >= last.frame) {
506
+ write(last.value);
507
+ return true;
508
+ }
509
+ let i = 0;
510
+ while (i < sorted.length - 2 && sorted[i + 1].frame < frame)
511
+ i++;
512
+ const a = sorted[i];
513
+ const b = sorted[i + 1];
514
+ const span = b.frame - a.frame;
515
+ let t = span > 0 ? (frame - a.frame) / span : 0;
516
+ t = applyInterpolation(a.interpolation, t);
517
+ const dims = Math.max(a.value.length, b.value.length, out.length);
518
+ for (let j = 0; j < dims; j++) {
519
+ const av = a.value[j] ?? 0;
520
+ out[j] = av + ((b.value[j] ?? av) - av) * t;
521
+ }
522
+ return true;
523
+ }
524
+
525
+ // src/evaluate/path/catmullRom.ts
526
+ var ARC_DIVISIONS = 200;
527
+ var curveCache = /* @__PURE__ */ new WeakMap();
528
+ function distSq(a, b) {
529
+ const dx = a.x - b.x;
530
+ const dy = a.y - b.y;
531
+ const dz = a.z - b.z;
532
+ return dx * dx + dy * dy + dz * dz;
533
+ }
534
+ function interpolate(p0, p1, p2, p3, dt0, dt1, dt2, t, out) {
535
+ const axis = (a, b, c, d) => {
536
+ const t1 = (b - a) / dt0 - (c - a) / (dt0 + dt1) + (c - b) / dt1;
537
+ const t2 = (c - b) / dt1 - (d - b) / (dt1 + dt2) + (d - c) / dt2;
538
+ const c0 = b;
539
+ const c1 = dt1 * t1;
540
+ const c2 = -3 * b + 3 * c - 2 * dt1 * t1 - dt1 * t2;
541
+ const c3 = 2 * b - 2 * c + dt1 * t1 + dt1 * t2;
542
+ return c0 + c1 * t + c2 * t * t + c3 * t * t * t;
543
+ };
544
+ out.x = axis(p0.x, p1.x, p2.x, p3.x);
545
+ out.y = axis(p0.y, p1.y, p2.y, p3.y);
546
+ out.z = axis(p0.z, p1.z, p2.z, p3.z);
547
+ }
548
+ var _p0 = { x: 0, y: 0, z: 0 };
549
+ var _p3 = { x: 0, y: 0, z: 0 };
550
+ var _tmp = { x: 0, y: 0, z: 0 };
551
+ function getPoint(cache, t, out) {
552
+ const points = cache.pts;
553
+ const l = points.length;
554
+ const closed = cache.closed;
555
+ const p = (l - (closed ? 0 : 1)) * t;
556
+ let intPoint = Math.floor(p);
557
+ let weight = p - intPoint;
558
+ if (closed) {
559
+ intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / l) + 1) * l;
560
+ } else if (weight === 0 && intPoint === l - 1) {
561
+ intPoint = l - 2;
562
+ weight = 1;
563
+ }
564
+ let p0;
565
+ if (closed || intPoint > 0) {
566
+ p0 = points[(intPoint - 1 + l) % l];
567
+ } else {
568
+ _p0.x = points[0].x * 2 - points[1].x;
569
+ _p0.y = points[0].y * 2 - points[1].y;
570
+ _p0.z = points[0].z * 2 - points[1].z;
571
+ p0 = _p0;
572
+ }
573
+ const p1 = points[intPoint % l];
574
+ const p2 = points[(intPoint + 1) % l];
575
+ let p3;
576
+ if (closed || intPoint + 2 < l) {
577
+ p3 = points[(intPoint + 2) % l];
578
+ } else {
579
+ const last = points[l - 1];
580
+ const prev = points[l - 2];
581
+ _p3.x = last.x * 2 - prev.x;
582
+ _p3.y = last.y * 2 - prev.y;
583
+ _p3.z = last.z * 2 - prev.z;
584
+ p3 = _p3;
585
+ }
586
+ let dt0 = Math.pow(distSq(p0, p1), 0.25);
587
+ let dt1 = Math.pow(distSq(p1, p2), 0.25);
588
+ let dt2 = Math.pow(distSq(p2, p3), 0.25);
589
+ if (dt1 < 1e-4)
590
+ dt1 = 1;
591
+ if (dt0 < 1e-4)
592
+ dt0 = dt1;
593
+ if (dt2 < 1e-4)
594
+ dt2 = dt1;
595
+ interpolate(p0, p1, p2, p3, dt0, dt1, dt2, weight, out);
596
+ }
597
+ function buildCache(points, closed) {
598
+ const pts = points.map((p) => ({ x: p.position.x, y: p.position.y, z: p.position.z }));
599
+ const cache = { pts, closed, lengths: [0], total: 0 };
600
+ let sum = 0;
601
+ const a = { x: 0, y: 0, z: 0 };
602
+ const b = { x: 0, y: 0, z: 0 };
603
+ getPoint(cache, 0, a);
604
+ for (let i = 1; i <= ARC_DIVISIONS; i++) {
605
+ getPoint(cache, i / ARC_DIVISIONS, b);
606
+ sum += Math.hypot(b.x - a.x, b.y - a.y, b.z - a.z);
607
+ cache.lengths.push(sum);
608
+ a.x = b.x;
609
+ a.y = b.y;
610
+ a.z = b.z;
611
+ }
612
+ cache.total = sum;
613
+ return cache;
614
+ }
615
+ function cacheOf(points, closed) {
616
+ const key = points;
617
+ const hit = curveCache.get(key);
618
+ if (hit && hit.closed === closed)
619
+ return hit;
620
+ const next = buildCache(points, closed);
621
+ curveCache.set(key, next);
622
+ return next;
623
+ }
624
+ function uToT(cache, u) {
625
+ const target = Math.min(Math.max(u, 0), 1) * cache.total;
626
+ const lengths = cache.lengths;
627
+ let lo = 0;
628
+ let hi = ARC_DIVISIONS;
629
+ while (hi - lo > 1) {
630
+ const mid = lo + hi >> 1;
631
+ if (lengths[mid] < target)
632
+ lo = mid;
633
+ else
634
+ hi = mid;
635
+ }
636
+ const span = lengths[hi] - lengths[lo];
637
+ const t = span > 0 ? (target - lengths[lo]) / span : 0;
638
+ return (lo + t) / ARC_DIVISIONS;
639
+ }
640
+ function catmullRomPointAt(points, closed, u, out) {
641
+ const cache = cacheOf(points, closed);
642
+ getPoint(cache, uToT(cache, u), out);
643
+ }
644
+ function catmullRomTangentAt(points, closed, u, out) {
645
+ const cache = cacheOf(points, closed);
646
+ const t = uToT(cache, u);
647
+ const delta = 1e-4;
648
+ let t1 = t - delta;
649
+ let t2 = t + delta;
650
+ if (t1 < 0)
651
+ t1 = closed ? t1 + 1 : 0;
652
+ if (t2 > 1)
653
+ t2 = closed ? t2 - 1 : 1;
654
+ getPoint(cache, t1, _tmp);
655
+ getPoint(cache, t2, out);
656
+ out.x -= _tmp.x;
657
+ out.y -= _tmp.y;
658
+ out.z -= _tmp.z;
659
+ }
660
+ function catmullRomLength(points, closed) {
661
+ return cacheOf(points, closed).total;
662
+ }
663
+ function catmullRomSpacedPoints(points, closed, divisions) {
664
+ const out = [];
665
+ for (let i = 0; i <= divisions; i++) {
666
+ const p = { x: 0, y: 0, z: 0 };
667
+ catmullRomPointAt(points, closed, i / divisions, p);
668
+ out.push(p);
669
+ }
670
+ return out;
671
+ }
672
+
673
+ // src/evaluate/path/samplePath.ts
674
+ var _pts = [];
675
+ var _eval = { position: { x: 0, y: 0, z: 0 }, yaw: 0 };
676
+ var _pos = { x: 0, y: 0, z: 0 };
677
+ var _tan = { x: 0, y: 0, z: 0 };
678
+ var PATH_WALK_SPEED = 2;
679
+ var PATH_CLIP_MAX_SECONDS = 15;
680
+ var D2R3 = Math.PI / 180;
681
+ function pathControlCentroid(pathNode) {
682
+ const pts = pathNode.path?.points;
683
+ if (!pts || pts.length === 0)
684
+ return { x: 0, y: 0, z: 0 };
685
+ let x = 0;
686
+ let y = 0;
687
+ let z2 = 0;
688
+ for (const p of pts) {
689
+ x += p.position.x;
690
+ y += p.position.y;
691
+ z2 += p.position.z;
692
+ }
693
+ const n = pts.length;
694
+ return { x: x / n, y: y / n, z: z2 / n };
695
+ }
696
+ function rotateEulerXYZ(x, y, z2, rotDeg) {
697
+ const rx = rotDeg.x * D2R3;
698
+ const ry = rotDeg.y * D2R3;
699
+ const rz = rotDeg.z * D2R3;
700
+ const cx = Math.cos(rx);
701
+ const sx = Math.sin(rx);
702
+ const cy = Math.cos(ry);
703
+ const sy = Math.sin(ry);
704
+ const cz = Math.cos(rz);
705
+ const sz = Math.sin(rz);
706
+ const x1 = x;
707
+ const y1 = cx * y - sx * z2;
708
+ const z1 = sx * y + cx * z2;
709
+ const x2 = cy * x1 + sy * z1;
710
+ const y2 = y1;
711
+ const z22 = -sy * x1 + cy * z1;
712
+ return [cz * x2 - sz * y2, sz * x2 + cz * y2, z22];
713
+ }
714
+ function transformPathLocalPoint(local, transform, center) {
715
+ const dx = (local.x - center.x) * transform.scale.x;
716
+ const dy = (local.y - center.y) * transform.scale.y;
717
+ const dz = (local.z - center.z) * transform.scale.z;
718
+ const [rx, ry, rz] = rotateEulerXYZ(dx, dy, dz, transform.rotation);
719
+ return {
720
+ x: rx + center.x + transform.position.x,
721
+ y: ry + center.y + transform.position.y,
722
+ z: rz + center.z + transform.position.z
723
+ };
724
+ }
725
+ function transformPathDirection(dir, transform) {
726
+ const dx = dir.x * transform.scale.x;
727
+ const dy = dir.y * transform.scale.y;
728
+ const dz = dir.z * transform.scale.z;
729
+ const [rx, ry, rz] = rotateEulerXYZ(dx, dy, dz, transform.rotation);
730
+ return { x: rx, y: ry, z: rz };
731
+ }
732
+ function pathArcLength(pathNode) {
733
+ const path = pathNode.path;
734
+ if (!path || path.points.length < 2)
735
+ return 0;
736
+ if (path.parameterization === "arc-length")
737
+ return catmullRomLength(path.points, path.closed);
738
+ let len = 0;
739
+ for (let i = 1; i < path.points.length; i++) {
740
+ const a = path.points[i - 1].position;
741
+ const b = path.points[i].position;
742
+ len += Math.hypot(b.x - a.x, b.y - a.y, b.z - a.z);
743
+ }
744
+ return len;
745
+ }
746
+ function pathClipDurationFrames(pathNode, fps) {
747
+ const seconds = Math.min(pathArcLength(pathNode) / PATH_WALK_SPEED, PATH_CLIP_MAX_SECONDS);
748
+ return Math.max(1, Math.round(seconds * fps));
749
+ }
750
+ function samplePathPoints(pathNode, divisions = 120) {
751
+ const path = pathNode.path;
752
+ if (!path || path.points.length < 2)
753
+ return [];
754
+ const center = pathControlCentroid(pathNode);
755
+ const xf = pathNode.transform;
756
+ const toWorld = (p) => transformPathLocalPoint(p, xf, center);
757
+ if (path.parameterization === "arc-length") {
758
+ return catmullRomSpacedPoints(path.points, path.closed, divisions).map(toWorld);
759
+ }
760
+ return [...path.points].sort((a, b) => (a.timeRatio ?? 0) - (b.timeRatio ?? 0)).map((p) => toWorld(p.position));
761
+ }
762
+ function evaluatePathMotionInto(pathNode, clip, frame, out) {
763
+ const path = pathNode.path;
764
+ if (!path || path.points.length < 2)
765
+ return false;
766
+ const span = clip.frameEnd - clip.frameStart;
767
+ const progress = span > 0 ? Math.min(Math.max((frame - clip.frameStart) / span, 0), 1) : 0;
768
+ const a = clip.pathStartPercent / 100;
769
+ const b = clip.pathEndPercent / 100;
770
+ const u = clip.direction === "backward" ? b - progress * (b - a) : a + progress * (b - a);
771
+ const center = pathControlCentroid(pathNode);
772
+ const xf = pathNode.transform;
773
+ let tanX;
774
+ let tanZ;
775
+ if (path.parameterization === "arc-length") {
776
+ const uc = Math.min(Math.max(u, 0), 1);
777
+ catmullRomPointAt(path.points, path.closed, uc, _pos);
778
+ catmullRomTangentAt(path.points, path.closed, uc, _tan);
779
+ const world = transformPathLocalPoint(_pos, xf, center);
780
+ out.position.x = world.x;
781
+ out.position.y = world.y;
782
+ out.position.z = world.z;
783
+ const dir = transformPathDirection(_tan, xf);
784
+ tanX = dir.x;
785
+ tanZ = dir.z;
786
+ } else {
787
+ _pts.length = 0;
788
+ for (const point of path.points)
789
+ _pts.push(point);
790
+ _pts.sort((x, y) => (x.timeRatio ?? 0) - (y.timeRatio ?? 0));
791
+ let i = 0;
792
+ while (i < _pts.length - 2 && (_pts[i + 1].timeRatio ?? 0) < u)
793
+ i++;
794
+ const p0 = _pts[i];
795
+ const p1 = _pts[i + 1];
796
+ const segSpan = (p1.timeRatio ?? 0) - (p0.timeRatio ?? 0);
797
+ const t = segSpan > 1e-6 ? Math.min(Math.max((u - (p0.timeRatio ?? 0)) / segSpan, 0), 1) : 0;
798
+ const local = {
799
+ x: p0.position.x + (p1.position.x - p0.position.x) * t,
800
+ y: p0.position.y + (p1.position.y - p0.position.y) * t,
801
+ z: p0.position.z + (p1.position.z - p0.position.z) * t
802
+ };
803
+ const world = transformPathLocalPoint(local, xf, center);
804
+ out.position.x = world.x;
805
+ out.position.y = world.y;
806
+ out.position.z = world.z;
807
+ const dir = transformPathDirection(
808
+ {
809
+ x: p1.position.x - p0.position.x,
810
+ y: p1.position.y - p0.position.y,
811
+ z: p1.position.z - p0.position.z
812
+ },
813
+ xf
814
+ );
815
+ tanX = dir.x;
816
+ tanZ = dir.z;
817
+ }
818
+ out.yaw = tanX * tanX + tanZ * tanZ > 1e-12 ? Math.atan2(tanX, tanZ) : 0;
819
+ return true;
820
+ }
821
+ function evaluatePathMotion(pathNode, clip, frame) {
822
+ if (!evaluatePathMotionInto(pathNode, clip, frame, _eval))
823
+ return null;
824
+ return {
825
+ position: { x: _eval.position.x, y: _eval.position.y, z: _eval.position.z },
826
+ yaw: _eval.yaw
827
+ };
828
+ }
829
+
830
+ // src/evaluate/evaluateSteps.ts
831
+ var RAD2DEG = 180 / Math.PI;
832
+ var AXES = ["x", "y", "z"];
833
+ var POSE_KEYS = [
834
+ "bodyBend",
835
+ "bodyTilt",
836
+ "bodyTurn",
837
+ "torsoBend",
838
+ "torsoTilt",
839
+ "torsoTurn",
840
+ "headNod",
841
+ "headTilt",
842
+ "headTurn",
843
+ "lArmRaise",
844
+ "lArmStraddle",
845
+ "lArmTurn",
846
+ "lElbowBend",
847
+ "rArmRaise",
848
+ "rArmStraddle",
849
+ "rArmTurn",
850
+ "rElbowBend",
851
+ "lLegRaise",
852
+ "lLegStraddle",
853
+ "lLegTurn",
854
+ "lKneeBend",
855
+ "rLegRaise",
856
+ "rLegStraddle",
857
+ "rLegTurn",
858
+ "rKneeBend"
859
+ ];
860
+ var _sample = [0, 0, 0];
861
+ var _path = { position: { x: 0, y: 0, z: 0 }, yaw: 0 };
862
+ var _cam = { position: [0, 0, 0], lookAt: [0, 0, 0], fov: 50, clipId: null };
863
+ var _nodeById = /* @__PURE__ */ new Map();
864
+ function indexSceneNodes(nodes) {
865
+ _nodeById.clear();
866
+ for (const node of nodes)
867
+ _nodeById.set(node.id, node);
868
+ }
869
+ function writeStaticTransform(xf, t) {
870
+ copyVec3(xf.position, t.position);
871
+ copyVec3(xf.rotation, t.rotation);
872
+ copyVec3(xf.scale, t.scale);
873
+ }
874
+ function applyFcurvesToTransform(xf, fcurves, nodeId, frame) {
875
+ for (let i = 0; i < 3; i++) {
876
+ const p = fcurves.evalScalar(nodeId, "transform.position", i, frame);
877
+ if (p !== null)
878
+ xf.position[AXES[i]] = p;
879
+ const r = fcurves.evalScalar(nodeId, "transform.rotation", i, frame);
880
+ if (r !== null)
881
+ xf.rotation[AXES[i]] = r;
882
+ const s = fcurves.evalScalar(nodeId, "transform.scale", i, frame);
883
+ if (s !== null)
884
+ xf.scale[AXES[i]] = s;
885
+ }
886
+ }
887
+ function emitPlayback(out, nodeId, clipId, timeSeconds) {
888
+ const i = out.motionPlayback.length;
889
+ const pool = cmdPoolOf(out);
890
+ let cmd = pool[i];
891
+ if (!cmd) {
892
+ cmd = { nodeId, clipId, timeSeconds, weight: 1 };
893
+ pool[i] = cmd;
894
+ } else {
895
+ cmd.nodeId = nodeId;
896
+ cmd.clipId = clipId;
897
+ cmd.timeSeconds = timeSeconds;
898
+ cmd.weight = 1;
899
+ }
900
+ out.motionPlayback.push(cmd);
901
+ }
902
+ function writePoseKnobs(out, nodeId, values) {
903
+ let arr = out.poses.get(nodeId);
904
+ if (!arr) {
905
+ arr = [];
906
+ out.poses.set(nodeId, arr);
907
+ }
908
+ let n = 0;
909
+ for (const key of POSE_KEYS) {
910
+ const value = values[key];
911
+ if (value === void 0)
912
+ continue;
913
+ let pose = arr[n];
914
+ if (!pose) {
915
+ pose = { key, value };
916
+ arr[n] = pose;
917
+ } else {
918
+ pose.key = key;
919
+ pose.value = value;
920
+ }
921
+ n += 1;
922
+ }
923
+ arr.length = n;
924
+ }
925
+ function evaluateCharacters(scene, frame, out) {
926
+ const clips = scene.timeline.animation.motionClips;
927
+ const fps = scene.meta.fps;
928
+ const fcurves = scene.fcurves;
929
+ for (const node of scene.nodes) {
930
+ if (node.type !== "character")
931
+ continue;
932
+ const xf = ensureTransform(out, node.id);
933
+ writeStaticTransform(xf, node.transform);
934
+ const clip = motionClipAt(clips, node.id, frame);
935
+ if (clip) {
936
+ emitPlayback(out, node.id, clip.id, motionTimeSeconds(clip, frame, fps));
937
+ } else if (node.character?.animation.mode === "pose") {
938
+ writePoseKnobs(out, node.id, node.character.animation.controlValues ?? {});
939
+ }
940
+ if (fcurves) {
941
+ for (let i = 0; i < 3; i++) {
942
+ const p = fcurves.evalScalar(node.id, "transform.position", i, frame);
943
+ if (p !== null)
944
+ xf.position[AXES[i]] = p;
945
+ if (!clip) {
946
+ const r = fcurves.evalScalar(node.id, "transform.rotation", i, frame);
947
+ if (r !== null)
948
+ xf.rotation[AXES[i]] = r;
949
+ }
950
+ const s = fcurves.evalScalar(node.id, "transform.scale", i, frame);
951
+ if (s !== null)
952
+ xf.scale[AXES[i]] = s;
953
+ }
954
+ }
955
+ }
956
+ }
957
+ function evaluatePaths(scene, frame, out) {
958
+ const fcurves = scene.fcurves;
959
+ for (const clip of scene.timeline.animation.pathMotionClips) {
960
+ const derivedRange = clip.lockedReason === "derived-from-keyframes";
961
+ if (frame < clip.frameStart)
962
+ continue;
963
+ if (derivedRange ? frame >= clip.frameEnd : frame > clip.frameEnd)
964
+ continue;
965
+ const pathNode = _nodeById.get(clip.pathNodeId);
966
+ if (!pathNode || !evaluatePathMotionInto(pathNode, clip, frame, _path))
967
+ continue;
968
+ const target = _nodeById.get(clip.target.nodeId);
969
+ if (target?.type === "camera")
970
+ continue;
971
+ const xf = out.transforms.get(clip.target.nodeId);
972
+ if (!xf)
973
+ continue;
974
+ const derived = clip.lockedReason === "derived-from-keyframes";
975
+ const fcPos = derived && !!(fcurves?.hasTrack(clip.target.nodeId, "transform.position") || scene.userKeys?.[clip.target.nodeId]?.position?.length);
976
+ if (!fcPos)
977
+ copyVec3(xf.position, _path.position);
978
+ if (clip.facing !== "path-tangent")
979
+ continue;
980
+ const yawDeg = _path.yaw * RAD2DEG;
981
+ xf.rotation.x = 0;
982
+ xf.rotation.y = yawDeg;
983
+ xf.rotation.z = 0;
984
+ }
985
+ }
986
+ function evaluateStaticNodes(scene, frame, out) {
987
+ const fcurves = scene.fcurves;
988
+ for (const node of scene.nodes) {
989
+ if (node.type !== "prop" && node.type !== "group" && node.type !== "primitive")
990
+ continue;
991
+ const xf = ensureTransform(out, node.id);
992
+ writeStaticTransform(xf, node.transform);
993
+ if (fcurves)
994
+ applyFcurvesToTransform(xf, fcurves, node.id, frame);
995
+ }
996
+ }
997
+ function applyUserProp(xf, prop, keys, frame) {
998
+ if (!keys || !sampleKeyframesInto(keys, frame, _sample, false))
999
+ return;
1000
+ if (prop === "position") {
1001
+ xf.position.x = _sample[0];
1002
+ xf.position.y = _sample[1];
1003
+ xf.position.z = _sample[2];
1004
+ return;
1005
+ }
1006
+ if (prop === "rotation") {
1007
+ xf.rotation.x = _sample[0];
1008
+ xf.rotation.y = _sample[1];
1009
+ xf.rotation.z = _sample[2];
1010
+ return;
1011
+ }
1012
+ if (prop === "scale") {
1013
+ xf.scale.x = _sample[0];
1014
+ xf.scale.y = _sample[1];
1015
+ xf.scale.z = _sample[2];
1016
+ }
1017
+ }
1018
+ function derivedFacingAt(scene, nodeId, frame) {
1019
+ for (const clip of scene.timeline.animation.pathMotionClips) {
1020
+ if (clip.lockedReason !== "derived-from-keyframes")
1021
+ continue;
1022
+ if (clip.facing !== "path-tangent")
1023
+ continue;
1024
+ if (clip.target?.nodeId !== nodeId)
1025
+ continue;
1026
+ if (frame >= clip.frameStart && frame < clip.frameEnd)
1027
+ return true;
1028
+ }
1029
+ return false;
1030
+ }
1031
+ function evaluateUserKeys(scene, frame, out) {
1032
+ if (!scene.userKeysEnabled || !scene.userKeys)
1033
+ return;
1034
+ for (const node of scene.nodes) {
1035
+ if (node.type === "camera" || node.type === "path")
1036
+ continue;
1037
+ const uk = scene.userKeys[node.id];
1038
+ if (!uk)
1039
+ continue;
1040
+ const xf = out.transforms.get(node.id);
1041
+ if (!xf)
1042
+ continue;
1043
+ applyUserProp(xf, "position", uk.position, frame);
1044
+ const rotationLockedByPath = node.type === "character" && derivedFacingAt(scene, node.id, frame);
1045
+ if (!rotationLockedByPath)
1046
+ applyUserProp(xf, "rotation", uk.rotation, frame);
1047
+ applyUserProp(xf, "scale", uk.scale, frame);
1048
+ }
1049
+ }
1050
+ function writeCameraSlot(xf, pose) {
1051
+ xf.position.x = pose.position[0];
1052
+ xf.position.y = pose.position[1];
1053
+ xf.position.z = pose.position[2];
1054
+ if (!xf.lookAt)
1055
+ xf.lookAt = { x: 0, y: 0, z: 0 };
1056
+ xf.lookAt.x = pose.lookAt[0];
1057
+ xf.lookAt.y = pose.lookAt[1];
1058
+ xf.lookAt.z = pose.lookAt[2];
1059
+ xf.fov = pose.fov;
1060
+ xf.rotation.x = 0;
1061
+ xf.rotation.y = 0;
1062
+ xf.rotation.z = 0;
1063
+ xf.useEuler = false;
1064
+ }
1065
+ function moveCameraKeepingAim(xf, next) {
1066
+ const look = xf.lookAt;
1067
+ if (look) {
1068
+ look.x += next.x - xf.position.x;
1069
+ look.y += next.y - xf.position.y;
1070
+ look.z += next.z - xf.position.z;
1071
+ }
1072
+ copyVec3(xf.position, next);
1073
+ }
1074
+ function overlayCameraFcurves(xf, fcurves, nodeId, frame) {
1075
+ const look = xf.lookAt;
1076
+ if (!look)
1077
+ return;
1078
+ let hitRotation = false;
1079
+ for (let i = 0; i < 3; i++) {
1080
+ const pv = fcurves.evalScalar(nodeId, "transform.position", i, frame);
1081
+ const lv = fcurves.evalScalar(nodeId, "camera.lookAt", i, frame);
1082
+ if (pv !== null) {
1083
+ if (lv === null)
1084
+ look[AXES[i]] += pv - xf.position[AXES[i]];
1085
+ xf.position[AXES[i]] = pv;
1086
+ }
1087
+ if (lv !== null)
1088
+ look[AXES[i]] = lv;
1089
+ const rv = fcurves.evalScalar(nodeId, "transform.rotation", i, frame);
1090
+ if (rv !== null) {
1091
+ xf.rotation[AXES[i]] = rv;
1092
+ hitRotation = true;
1093
+ }
1094
+ }
1095
+ if (hitRotation)
1096
+ applyRotationToLookAt(xf);
1097
+ const fv = fcurves.evalScalar(nodeId, "camera.fov", 0, frame);
1098
+ if (fv !== null)
1099
+ xf.fov = fv;
1100
+ }
1101
+ function overlayCameraUserKeys(xf, uk, frame) {
1102
+ if (uk.position && sampleKeyframesInto(uk.position, frame, _sample)) {
1103
+ moveCameraKeepingAim(xf, { x: _sample[0], y: _sample[1], z: _sample[2] });
1104
+ }
1105
+ if (uk.lookAt && xf.lookAt && sampleKeyframesInto(uk.lookAt, frame, _sample)) {
1106
+ xf.lookAt.x = _sample[0];
1107
+ xf.lookAt.y = _sample[1];
1108
+ xf.lookAt.z = _sample[2];
1109
+ }
1110
+ if (uk.rotation && sampleKeyframesInto(uk.rotation, frame, _sample)) {
1111
+ xf.rotation.x = _sample[0];
1112
+ xf.rotation.y = _sample[1];
1113
+ xf.rotation.z = _sample[2];
1114
+ applyRotationToLookAt(xf);
1115
+ }
1116
+ if (uk.fov && sampleKeyframesInto(uk.fov, frame, _sample))
1117
+ xf.fov = _sample[0];
1118
+ }
1119
+ function applyRotationToLookAt(xf) {
1120
+ if (!xf.lookAt)
1121
+ xf.lookAt = { x: 0, y: 0, z: 0 };
1122
+ const dist = lookDistance(xf.position, xf.lookAt);
1123
+ copyVec3(xf.lookAt, lookAtFromEulerDeg(xf.position, xf.rotation, dist));
1124
+ xf.useEuler = true;
1125
+ }
1126
+ function writeActiveCamera(out, xf) {
1127
+ if (!out.camera) {
1128
+ out.camera = {
1129
+ position: { x: 0, y: 0, z: 0 },
1130
+ lookAt: { x: 0, y: 1.2, z: 0 },
1131
+ fov: 50
1132
+ };
1133
+ }
1134
+ copyVec3(out.camera.position, xf.position);
1135
+ if (xf.lookAt)
1136
+ copyVec3(out.camera.lookAt, xf.lookAt);
1137
+ out.camera.fov = xf.fov ?? 50;
1138
+ }
1139
+ function evaluateCameras(scene, frame, out) {
1140
+ const clips = scene.timeline.animation.cameraMotionClips;
1141
+ const fps = scene.meta.fps;
1142
+ const chained = scene.chainCameraMotion === true;
1143
+ const fcurves = scene.fcurves;
1144
+ const userKeys = scene.userKeysEnabled ? scene.userKeys : void 0;
1145
+ let primary = null;
1146
+ let firstCam = null;
1147
+ for (const node of scene.nodes) {
1148
+ if (node.type !== "camera")
1149
+ continue;
1150
+ evaluateCameraPoseInto(node, clips, frame, fps, chained, _cam);
1151
+ const xf = ensureTransform(out, node.id);
1152
+ writeCameraSlot(xf, _cam);
1153
+ const motionOwnsKeyframes = clips.some((c) => c.target.nodeId === node.id);
1154
+ const motionActive = clips.some(
1155
+ (c) => c.target.nodeId === node.id && frame >= c.frameStart && frame <= c.frameEnd
1156
+ );
1157
+ if (fcurves && _cam.clipId === null && !motionOwnsKeyframes)
1158
+ overlayCameraFcurves(xf, fcurves, node.id, frame);
1159
+ const uk = userKeys?.[node.id];
1160
+ if (uk && !motionOwnsKeyframes)
1161
+ overlayCameraUserKeys(xf, uk, frame);
1162
+ if (!motionActive) {
1163
+ const pathClip = scene.timeline.animation.pathMotionClips.find((c) => {
1164
+ if (c.target.nodeId !== node.id)
1165
+ return false;
1166
+ const derivedRange = c.lockedReason === "derived-from-keyframes";
1167
+ if (derivedRange) {
1168
+ if (motionOwnsKeyframes)
1169
+ return false;
1170
+ if (fcurves?.hasTrack(node.id, "transform.position") || uk?.position?.length)
1171
+ return false;
1172
+ }
1173
+ if (frame < c.frameStart)
1174
+ return false;
1175
+ return derivedRange ? frame < c.frameEnd : frame <= c.frameEnd;
1176
+ });
1177
+ if (pathClip) {
1178
+ const pathNode = _nodeById.get(pathClip.pathNodeId);
1179
+ if (pathNode && evaluatePathMotionInto(pathNode, pathClip, frame, _path)) {
1180
+ moveCameraKeepingAim(xf, _path.position);
1181
+ }
1182
+ }
1183
+ }
1184
+ const binding = node.camera?.subject;
1185
+ const tp = binding?.follow ? out.transforms.get(binding.nodeId) : void 0;
1186
+ const followOwns = Boolean(binding && tp && (_cam.clipId === null || subjectOverridesMotion(binding)));
1187
+ if (followOwns && binding && tp) {
1188
+ const pose = resolveSubjectCameraPose(binding, tp.position, tp.rotation);
1189
+ copyVec3(xf.position, pose.position);
1190
+ if (xf.lookAt)
1191
+ copyVec3(xf.lookAt, pose.lookAt);
1192
+ else
1193
+ xf.lookAt = pose.lookAt;
1194
+ xf.useEuler = false;
1195
+ }
1196
+ const lookAtTarget = node.camera?.lookAtTarget;
1197
+ const clipOwnsLookAt = _cam.clipId !== null && !followOwns;
1198
+ if (lookAtTarget?.nodeId && !clipOwnsLookAt) {
1199
+ const targetXf = out.transforms.get(lookAtTarget.nodeId);
1200
+ if (targetXf) {
1201
+ const aim = characterLookAtPoint(targetXf.position, lookAtTarget.offset);
1202
+ if (xf.lookAt)
1203
+ copyVec3(xf.lookAt, aim);
1204
+ else
1205
+ xf.lookAt = aim;
1206
+ xf.useEuler = false;
1207
+ }
1208
+ }
1209
+ if (!firstCam)
1210
+ firstCam = xf;
1211
+ if (node.camera?.isPrimary)
1212
+ primary = xf;
1213
+ }
1214
+ const active = primary ?? firstCam;
1215
+ if (active)
1216
+ writeActiveCamera(out, active);
1217
+ else
1218
+ out.camera = null;
1219
+ }
1220
+
1221
+ // src/evaluate/evaluateFrame.ts
1222
+ function evaluateFrame(scene, frame, out) {
1223
+ resetFrameSnapshot(out);
1224
+ indexSceneNodes(scene.nodes);
1225
+ evaluateCharacters(scene, frame, out);
1226
+ evaluateStaticNodes(scene, frame, out);
1227
+ evaluatePaths(scene, frame, out);
1228
+ evaluateUserKeys(scene, frame, out);
1229
+ evaluateCameras(scene, frame, out);
1230
+ }
1231
+
1232
+ // src/evaluate/sceneFromDocument.ts
1233
+ function sceneFromDocument(doc, overlays) {
1234
+ const tl = doc.content.timeline;
1235
+ return {
1236
+ meta: { fps: tl.fps, frameStart: tl.frameStart, frameEnd: tl.frameEnd },
1237
+ nodes: doc.content.nodes,
1238
+ timeline: tl,
1239
+ fcurves: overlays?.fcurves ?? null,
1240
+ userKeys: overlays?.userKeys,
1241
+ userKeysEnabled: overlays?.userKeysEnabled,
1242
+ chainCameraMotion: overlays?.chainCameraMotion
1243
+ };
1244
+ }
1245
+
1246
+ // src/evaluate/curves/FCurveSet.ts
1247
+ var USER_KEY_PROPS = ["position", "rotation", "scale", "lookAt", "fov"];
1248
+ var _autoOut = { x: 0, y: 0 };
1249
+ var _autoIn = { x: 0, y: 0 };
1250
+ var _tmpA = { frame: 0, value: 0, interp: "", id: "", inHandle: null, outHandle: null };
1251
+ var _tmpB = { frame: 0, value: 0, interp: "", id: "", inHandle: null, outHandle: null };
1252
+ function evalBezierSegment(a, b, frame) {
1253
+ const p0x = a.frame;
1254
+ const p0y = a.value;
1255
+ const p1x = a.outHandle.x;
1256
+ const p1y = a.outHandle.y;
1257
+ const p2x = b.inHandle.x;
1258
+ const p2y = b.inHandle.y;
1259
+ const p3x = b.frame;
1260
+ const p3y = b.value;
1261
+ const xAt = (t2) => {
1262
+ const u = 1 - t2;
1263
+ return u * u * u * p0x + 3 * u * u * t2 * p1x + 3 * u * t2 * t2 * p2x + t2 * t2 * t2 * p3x;
1264
+ };
1265
+ const yAt = (t2) => {
1266
+ const u = 1 - t2;
1267
+ return u * u * u * p0y + 3 * u * u * t2 * p1y + 3 * u * t2 * t2 * p2y + t2 * t2 * t2 * p3y;
1268
+ };
1269
+ const dxAt = (t2) => {
1270
+ const u = 1 - t2;
1271
+ return 3 * u * u * (p1x - p0x) + 6 * u * t2 * (p2x - p1x) + 3 * t2 * t2 * (p3x - p2x);
1272
+ };
1273
+ let t = p3x !== p0x ? (frame - p0x) / (p3x - p0x) : 0.5;
1274
+ t = Math.min(Math.max(t, 0), 1);
1275
+ let ok2 = false;
1276
+ for (let i = 0; i < 8; i++) {
1277
+ const err = xAt(t) - frame;
1278
+ if (Math.abs(err) < 1e-7) {
1279
+ ok2 = true;
1280
+ break;
1281
+ }
1282
+ const d = dxAt(t);
1283
+ if (Math.abs(d) < 1e-12)
1284
+ break;
1285
+ const nt = t - err / d;
1286
+ if (nt < 0 || nt > 1)
1287
+ break;
1288
+ t = nt;
1289
+ }
1290
+ if (!ok2) {
1291
+ let lo = 0;
1292
+ let hi = 1;
1293
+ for (let i = 0; i < 40; i++) {
1294
+ const mid = (lo + hi) / 2;
1295
+ if (xAt(mid) < frame)
1296
+ lo = mid;
1297
+ else
1298
+ hi = mid;
1299
+ }
1300
+ t = (lo + hi) / 2;
1301
+ }
1302
+ return yAt(t);
1303
+ }
1304
+ function genKeyId() {
1305
+ return `kf_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
1306
+ }
1307
+ var FCurveSet = class _FCurveSet {
1308
+ constructor(curves) {
1309
+ /** nodeId → propPath → 分量序号 → 曲线 */
1310
+ this.byNode = /* @__PURE__ */ new Map();
1311
+ this.curveCount = curves.length;
1312
+ for (const c of curves) {
1313
+ let byProp = this.byNode.get(c.nodeId);
1314
+ if (!byProp) {
1315
+ byProp = /* @__PURE__ */ new Map();
1316
+ this.byNode.set(c.nodeId, byProp);
1317
+ }
1318
+ let byIndex = byProp.get(c.propPath);
1319
+ if (!byIndex) {
1320
+ byIndex = /* @__PURE__ */ new Map();
1321
+ byProp.set(c.propPath, byIndex);
1322
+ }
1323
+ byIndex.set(c.index, c);
1324
+ }
1325
+ }
1326
+ static parse(json) {
1327
+ if (!json || json.encoding !== "compact-v1" || !Array.isArray(json.fcurves)) {
1328
+ throw new Error("\u4E0D\u662F compact-v1 \u683C\u5F0F\u7684 fcurves \u6587\u4EF6");
1329
+ }
1330
+ const curves = json.fcurves.map((raw) => {
1331
+ const nodeId = Array.isArray(raw.t) ? String(raw.t[1]) : String(raw.t);
1332
+ const keys = raw.k.map((k) => ({
1333
+ frame: Number(k[0]),
1334
+ value: Number(k[1]),
1335
+ interp: String(k[2] ?? "linear"),
1336
+ id: String(k[3] ?? ""),
1337
+ inHandle: k[4] ? { x: Number(k[4].x), y: Number(k[4].y) } : null,
1338
+ outHandle: k[5] ? { x: Number(k[5].x), y: Number(k[5].y) } : null
1339
+ }));
1340
+ keys.sort((a, b) => a.frame - b.frame);
1341
+ return {
1342
+ id: String(raw.id),
1343
+ nodeId,
1344
+ propPath: String(raw.p),
1345
+ index: Number(raw.i ?? 0),
1346
+ keys
1347
+ };
1348
+ });
1349
+ return new _FCurveSet(curves);
1350
+ }
1351
+ findCurve(nodeId, propPath, index) {
1352
+ return this.byNode.get(nodeId)?.get(propPath)?.get(index) ?? null;
1353
+ }
1354
+ /** 空集合(无 fcurves 的草稿首次提交拖拽时用) */
1355
+ static empty() {
1356
+ return new _FCurveSet([]);
1357
+ }
1358
+ /**
1359
+ * upsert 一个关键帧:曲线不存在则建(id `fc_node_<nodeId>_<propPath点转下划线>_<分量>`),
1360
+ * 键按 frame 升序插入;同帧(严格相等)替换值并保留原键 id/插值。
1361
+ * 新键 interp='bezier'、手柄 null(运行时等价 smoothstep)。
1362
+ */
1363
+ upsertKey(nodeId, propPath, index, frame, value, keyId) {
1364
+ let byProp = this.byNode.get(nodeId);
1365
+ if (!byProp) {
1366
+ byProp = /* @__PURE__ */ new Map();
1367
+ this.byNode.set(nodeId, byProp);
1368
+ }
1369
+ let byIndex = byProp.get(propPath);
1370
+ if (!byIndex) {
1371
+ byIndex = /* @__PURE__ */ new Map();
1372
+ byProp.set(propPath, byIndex);
1373
+ }
1374
+ let curve = byIndex.get(index);
1375
+ if (!curve) {
1376
+ curve = {
1377
+ id: `fc_node_${nodeId}_${propPath.replace(/\./g, "_")}_${index}`,
1378
+ nodeId,
1379
+ propPath,
1380
+ index,
1381
+ keys: []
1382
+ };
1383
+ byIndex.set(index, curve);
1384
+ this.curveCount++;
1385
+ }
1386
+ const keys = curve.keys;
1387
+ let lo = 0;
1388
+ let hi = keys.length;
1389
+ while (lo < hi) {
1390
+ const mid = lo + hi >> 1;
1391
+ if (keys[mid].frame < frame)
1392
+ lo = mid + 1;
1393
+ else
1394
+ hi = mid;
1395
+ }
1396
+ if (lo < keys.length && keys[lo].frame === frame) {
1397
+ keys[lo].value = value;
1398
+ return keys[lo];
1399
+ }
1400
+ const key = {
1401
+ frame,
1402
+ value,
1403
+ interp: "bezier",
1404
+ id: keyId ?? genKeyId(),
1405
+ inHandle: null,
1406
+ outHandle: null
1407
+ };
1408
+ keys.splice(lo, 0, key);
1409
+ return key;
1410
+ }
1411
+ /** 某分量曲线在某帧(严格相等)是否已有键;有则返回该键 */
1412
+ getKeyAt(nodeId, propPath, index, frame) {
1413
+ const curve = this.findCurve(nodeId, propPath, index);
1414
+ if (!curve)
1415
+ return null;
1416
+ let lo = 0;
1417
+ let hi = curve.keys.length;
1418
+ while (lo < hi) {
1419
+ const mid = lo + hi >> 1;
1420
+ if (curve.keys[mid].frame < frame)
1421
+ lo = mid + 1;
1422
+ else
1423
+ hi = mid;
1424
+ }
1425
+ const k = curve.keys[lo];
1426
+ return k && k.frame === frame ? k : null;
1427
+ }
1428
+ /** 某分量曲线 id(派生 clip 的 derivedSource.curveIds 用);无曲线返回 null */
1429
+ curveId(nodeId, propPath, index) {
1430
+ return this.findCurve(nodeId, propPath, index)?.id ?? null;
1431
+ }
1432
+ /**
1433
+ * 节点 position 曲线的关键帧列表(按帧升序):三分量键总是一起写,
1434
+ * 按 x 曲线的键列表取帧,y/z 取同帧值(缺任一分量则跳过该帧)。
1435
+ * 返回值同时携带三条曲线上的键 id(派生 clip 的 derivedSource.keyframeIds 用)。
1436
+ */
1437
+ positionKeys(nodeId) {
1438
+ const cx = this.findCurve(nodeId, "transform.position", 0);
1439
+ const cy = this.findCurve(nodeId, "transform.position", 1);
1440
+ const cz = this.findCurve(nodeId, "transform.position", 2);
1441
+ if (!cx || !cy || !cz)
1442
+ return [];
1443
+ const byFrameY = new Map(cy.keys.map((k) => [k.frame, k]));
1444
+ const byFrameZ = new Map(cz.keys.map((k) => [k.frame, k]));
1445
+ const out = [];
1446
+ for (const kx of cx.keys) {
1447
+ const ky = byFrameY.get(kx.frame);
1448
+ const kz = byFrameZ.get(kx.frame);
1449
+ if (!ky || !kz)
1450
+ continue;
1451
+ out.push({
1452
+ frame: kx.frame,
1453
+ value: [kx.value, ky.value, kz.value],
1454
+ keyIds: [kx.id, ky.id, kz.id]
1455
+ });
1456
+ }
1457
+ return out;
1458
+ }
1459
+ /** 节点某条属性路径是否有曲线(任一分量存在即算有) */
1460
+ hasTrack(nodeId, propPath) {
1461
+ return (this.byNode.get(nodeId)?.get(propPath)?.size ?? 0) > 0;
1462
+ }
1463
+ /** 单分量求值;无曲线返回 null。范围外 constant 外推。 */
1464
+ evalScalar(nodeId, propPath, index, frame) {
1465
+ const curve = this.findCurve(nodeId, propPath, index);
1466
+ if (!curve || curve.keys.length === 0)
1467
+ return null;
1468
+ const keys = curve.keys;
1469
+ const first = keys[0];
1470
+ const last = keys[keys.length - 1];
1471
+ if (frame <= first.frame)
1472
+ return first.value;
1473
+ if (frame >= last.frame)
1474
+ return last.value;
1475
+ let lo = 0;
1476
+ let hi = keys.length - 1;
1477
+ while (hi - lo > 1) {
1478
+ const mid = lo + hi >> 1;
1479
+ if (keys[mid].frame <= frame)
1480
+ lo = mid;
1481
+ else
1482
+ hi = mid;
1483
+ }
1484
+ const a = keys[lo];
1485
+ const b = keys[hi];
1486
+ if (a.interp === "bezier") {
1487
+ const span2 = b.frame - a.frame;
1488
+ _autoOut.x = a.frame + span2 / 3;
1489
+ _autoOut.y = a.value;
1490
+ _autoIn.x = b.frame - span2 / 3;
1491
+ _autoIn.y = b.value;
1492
+ _tmpA.frame = a.frame;
1493
+ _tmpA.value = a.value;
1494
+ _tmpA.interp = a.interp;
1495
+ _tmpA.id = a.id;
1496
+ _tmpA.inHandle = a.inHandle;
1497
+ _tmpA.outHandle = a.outHandle ?? _autoOut;
1498
+ _tmpB.frame = b.frame;
1499
+ _tmpB.value = b.value;
1500
+ _tmpB.interp = b.interp;
1501
+ _tmpB.id = b.id;
1502
+ _tmpB.inHandle = b.inHandle ?? _autoIn;
1503
+ _tmpB.outHandle = b.outHandle;
1504
+ return evalBezierSegment(_tmpA, _tmpB, frame);
1505
+ }
1506
+ const span = b.frame - a.frame;
1507
+ const t = span > 0 ? (frame - a.frame) / span : 0;
1508
+ return a.value + (b.value - a.value) * t;
1509
+ }
1510
+ /**
1511
+ * 把 fromId 的全部曲线拷到 toId:新曲线 id、新 key id。
1512
+ * mapKey 可在拷贝时改值(例如套变换增量)。不改源曲线。
1513
+ */
1514
+ copyNode(fromId, toId, mapKey) {
1515
+ const source = this.byNode.get(fromId);
1516
+ if (!source || fromId === toId)
1517
+ return;
1518
+ let destProps = this.byNode.get(toId);
1519
+ if (!destProps) {
1520
+ destProps = /* @__PURE__ */ new Map();
1521
+ this.byNode.set(toId, destProps);
1522
+ }
1523
+ for (const [propPath, byIndex] of source) {
1524
+ let destIndex = destProps.get(propPath);
1525
+ if (!destIndex) {
1526
+ destIndex = /* @__PURE__ */ new Map();
1527
+ destProps.set(propPath, destIndex);
1528
+ }
1529
+ for (const [index, curve] of byIndex) {
1530
+ if (destIndex.has(index)) {
1531
+ this.curveCount = Math.max(0, this.curveCount - 1);
1532
+ }
1533
+ destIndex.set(index, {
1534
+ id: `fc_node_${toId}_${propPath.replace(/\./g, "_")}_${index}`,
1535
+ nodeId: toId,
1536
+ propPath,
1537
+ index,
1538
+ keys: curve.keys.map((key) => {
1539
+ const cloned = {
1540
+ frame: key.frame,
1541
+ value: key.value,
1542
+ interp: key.interp,
1543
+ id: genKeyId(),
1544
+ inHandle: key.inHandle ? { ...key.inHandle } : null,
1545
+ outHandle: key.outHandle ? { ...key.outHandle } : null
1546
+ };
1547
+ return mapKey ? mapKey(cloned, curve) : cloned;
1548
+ })
1549
+ });
1550
+ this.curveCount++;
1551
+ }
1552
+ }
1553
+ }
1554
+ /** 去掉某节点的全部曲线(删角色/机位时清轨道,curveCount 同步减) */
1555
+ removeNode(nodeId) {
1556
+ const byProp = this.byNode.get(nodeId);
1557
+ if (!byProp)
1558
+ return;
1559
+ let n = 0;
1560
+ for (const byIndex of byProp.values())
1561
+ n += byIndex.size;
1562
+ this.byNode.delete(nodeId);
1563
+ this.curveCount = Math.max(0, this.curveCount - n);
1564
+ }
1565
+ /** 去掉某节点若干属性路径上的曲线(点机位时清旧 position/lookAt/fov,避免 overlay 把新机位拽回去) */
1566
+ removeTracks(nodeId, propPaths) {
1567
+ const byProp = this.byNode.get(nodeId);
1568
+ if (!byProp)
1569
+ return;
1570
+ for (const path of propPaths) {
1571
+ const byIndex = byProp.get(path);
1572
+ if (!byIndex)
1573
+ continue;
1574
+ this.curveCount = Math.max(0, this.curveCount - byIndex.size);
1575
+ byProp.delete(path);
1576
+ }
1577
+ if (byProp.size === 0)
1578
+ this.byNode.delete(nodeId);
1579
+ }
1580
+ /**
1581
+ * 删除某属性路径在指定帧(严格相等)的关键帧:所有分量曲线同帧一起删,
1582
+ * 返回删除的键数。删空的曲线/属性路径/节点壳一并清掉,避免落盘留下 k:[]。
1583
+ */
1584
+ removeKeysAtFrame(nodeId, propPath, frame) {
1585
+ const byProp = this.byNode.get(nodeId);
1586
+ const byIndex = byProp?.get(propPath);
1587
+ if (!byProp || !byIndex)
1588
+ return 0;
1589
+ let removed = 0;
1590
+ for (const [index, curve] of byIndex) {
1591
+ const before = curve.keys.length;
1592
+ curve.keys = curve.keys.filter((k) => k.frame !== frame);
1593
+ removed += before - curve.keys.length;
1594
+ if (curve.keys.length === 0) {
1595
+ byIndex.delete(index);
1596
+ this.curveCount = Math.max(0, this.curveCount - 1);
1597
+ }
1598
+ }
1599
+ if (byIndex.size === 0) {
1600
+ byProp.delete(propPath);
1601
+ if (byProp.size === 0)
1602
+ this.byNode.delete(nodeId);
1603
+ }
1604
+ return removed;
1605
+ }
1606
+ /** 节点的全部曲线(UI 列轨道用) */
1607
+ tracksForNode(nodeId) {
1608
+ const out = [];
1609
+ for (const byIndex of this.byNode.get(nodeId)?.values() ?? []) {
1610
+ for (const c of byIndex.values())
1611
+ out.push(c);
1612
+ }
1613
+ return out;
1614
+ }
1615
+ /** 某轨道(单分量)的关键帧列表(UI 画菱形用) */
1616
+ keyframesForTrack(nodeId, propPath, index) {
1617
+ const curve = this.findCurve(nodeId, propPath, index);
1618
+ if (!curve)
1619
+ return [];
1620
+ return curve.keys.map((k) => ({ frame: k.frame, value: k.value }));
1621
+ }
1622
+ /**
1623
+ * 某属性路径的所有分量曲线是否全程无真实变化(平台)。
1624
+ * 用于判断派生路径的 path-tangent yaw 是否还让位给 fcurve。
1625
+ */
1626
+ trackIsFlat(nodeId, propPath, eps = 1e-3) {
1627
+ const byIndex = this.byNode.get(nodeId)?.get(propPath);
1628
+ if (!byIndex || byIndex.size === 0)
1629
+ return false;
1630
+ for (const c of byIndex.values()) {
1631
+ let min = Infinity;
1632
+ let max = -Infinity;
1633
+ for (const k of c.keys) {
1634
+ if (k.value < min)
1635
+ min = k.value;
1636
+ if (k.value > max)
1637
+ max = k.value;
1638
+ }
1639
+ if (max - min > eps)
1640
+ return false;
1641
+ }
1642
+ return true;
1643
+ }
1644
+ encode() {
1645
+ const fcurves = [];
1646
+ for (const byProp of this.byNode.values()) {
1647
+ for (const byIndex of byProp.values()) {
1648
+ for (const curve of byIndex.values()) {
1649
+ fcurves.push({
1650
+ id: curve.id,
1651
+ t: ["node", curve.nodeId],
1652
+ p: curve.propPath,
1653
+ i: curve.index,
1654
+ k: curve.keys.map((key) => {
1655
+ const row = [key.frame, key.value, key.interp, key.id];
1656
+ if (key.inHandle || key.outHandle) {
1657
+ row.push(key.inHandle);
1658
+ row.push(key.outHandle);
1659
+ }
1660
+ return row;
1661
+ })
1662
+ });
1663
+ }
1664
+ }
1665
+ }
1666
+ return { version: 1, encoding: "compact-v1", fcurves };
1667
+ }
1668
+ /**
1669
+ * 落盘:官方 compact-v1 曲线原样保留(不把用户键烘焙进去,避免改 walk / 删键无法撤回)。
1670
+ * 时间线「位移 / 旋转 / 缩放」读的是 userKeys 覆写层,非空时挂在 sidecar,加载后再灌回去。
1671
+ */
1672
+ static persist(fcurves, userKeys) {
1673
+ const overlay = _FCurveSet.parseUserKeys({ userKeys });
1674
+ const encoded = fcurves && fcurves.curveCount > 0 ? fcurves.encode() : { version: 1, encoding: "compact-v1", fcurves: [] };
1675
+ if (Object.keys(overlay).length === 0)
1676
+ return encoded;
1677
+ return { ...encoded, userKeys: overlay };
1678
+ }
1679
+ /** 从 compact-v1 JSON 取出时间线覆写层(缺省 / 损坏则空对象)。 */
1680
+ static parseUserKeys(json) {
1681
+ if (!json || typeof json !== "object" || Array.isArray(json))
1682
+ return {};
1683
+ const raw = json.userKeys;
1684
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
1685
+ return {};
1686
+ const out = {};
1687
+ for (const [nodeId, props] of Object.entries(raw)) {
1688
+ if (!nodeId || !props || typeof props !== "object" || Array.isArray(props))
1689
+ continue;
1690
+ const node = {};
1691
+ for (const prop of USER_KEY_PROPS) {
1692
+ const keys = props[prop];
1693
+ if (!Array.isArray(keys))
1694
+ continue;
1695
+ const parsed = [];
1696
+ for (const item of keys) {
1697
+ if (!item || typeof item !== "object" || Array.isArray(item))
1698
+ continue;
1699
+ const rec = item;
1700
+ if (!Array.isArray(rec.value))
1701
+ continue;
1702
+ const frame = Math.round(Number(rec.frame));
1703
+ if (!Number.isFinite(frame))
1704
+ continue;
1705
+ parsed.push({
1706
+ id: String(rec.id ?? ""),
1707
+ frame,
1708
+ value: rec.value.map((v) => {
1709
+ const n = Number(v);
1710
+ return Number.isFinite(n) ? n : 0;
1711
+ }),
1712
+ interpolation: (() => {
1713
+ const raw2 = String(rec.interpolation ?? "linear");
1714
+ if (raw2 === "smooth" || raw2 === "ease-in-out" || raw2 === "ease-in" || raw2 === "ease-out" || raw2 === "linear") {
1715
+ return raw2;
1716
+ }
1717
+ return "linear";
1718
+ })()
1719
+ });
1720
+ }
1721
+ if (parsed.length)
1722
+ node[prop] = parsed;
1723
+ }
1724
+ if (Object.keys(node).length)
1725
+ out[nodeId] = node;
1726
+ }
1727
+ return out;
1728
+ }
1729
+ /**
1730
+ * 用户关键帧层(userKeys)→ 官方 compact-v1 fcurves JSON。
1731
+ * 格式:
1732
+ * 曲线 id `fc_node_<nodeId>_<propPath 点转下划线>_<分量>`;
1733
+ * 关键帧 [帧, 值, 插值, kfId, 入手柄?, 出手柄?],手柄缺省 = 运行时自动水平切线;
1734
+ * 我们的 smooth(smoothstep)与官方「bezier 无手柄」运行时行为一致 → 映射为 bezier 无手柄;
1735
+ * linear → linear。rotation 单位保持「度」(与官方一致,由消费方转弧度)。
1736
+ */
1737
+ static encodeUserKeys(userKeys) {
1738
+ const PROP_PATH = {
1739
+ position: "transform.position",
1740
+ rotation: "transform.rotation",
1741
+ scale: "transform.scale",
1742
+ lookAt: "camera.lookAt",
1743
+ fov: "camera.fov"
1744
+ };
1745
+ const kfId2 = () => `kf_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
1746
+ const fcurves = [];
1747
+ for (const [nodeId, props] of Object.entries(userKeys)) {
1748
+ for (const [prop, keys] of Object.entries(props)) {
1749
+ if (!keys || keys.length === 0)
1750
+ continue;
1751
+ const p = PROP_PATH[prop];
1752
+ const components = prop === "fov" ? 1 : 3;
1753
+ const sorted = [...keys].sort((a, b) => a.frame - b.frame);
1754
+ for (let i = 0; i < components; i++) {
1755
+ fcurves.push({
1756
+ id: `fc_node_${nodeId}_${p.replace(/\./g, "_")}_${i}`,
1757
+ t: ["node", nodeId],
1758
+ p,
1759
+ i,
1760
+ k: sorted.map((k) => [
1761
+ k.frame,
1762
+ k.value[i] ?? 0,
1763
+ k.interpolation === "linear" ? "linear" : "bezier",
1764
+ kfId2()
1765
+ ])
1766
+ });
1767
+ }
1768
+ }
1769
+ }
1770
+ return { version: 1, encoding: "compact-v1", fcurves };
1771
+ }
1772
+ };
1773
+
1774
+ // src/evaluate/camera/bakeMotion.ts
1775
+ var UP = { x: 0, y: 1, z: 0 };
1776
+ var vAdd = (a, b) => ({ x: a.x + b.x, y: a.y + b.y, z: a.z + b.z });
1777
+ var vSub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y, z: a.z - b.z });
1778
+ var vScale = (a, s) => ({ x: a.x * s, y: a.y * s, z: a.z * s });
1779
+ var vDot = (a, b) => a.x * b.x + a.y * b.y + a.z * b.z;
1780
+ var vCross = (a, b) => ({
1781
+ x: a.y * b.z - a.z * b.y,
1782
+ y: a.z * b.x - a.x * b.z,
1783
+ z: a.x * b.y - a.y * b.x
1784
+ });
1785
+ var vLen = (a) => Math.hypot(a.x, a.y, a.z);
1786
+ var vNorm = (a) => {
1787
+ const l = vLen(a);
1788
+ return l > 1e-9 ? vScale(a, 1 / l) : { x: 0, y: 0, z: 0 };
1789
+ };
1790
+ var vDist = (a, b) => vLen(vSub(a, b));
1791
+ var vLerp = (a, b, t) => ({
1792
+ x: a.x + (b.x - a.x) * t,
1793
+ y: a.y + (b.y - a.y) * t,
1794
+ z: a.z + (b.z - a.z) * t
1795
+ });
1796
+ var lerp = (a, b, t) => a + (b - a) * t;
1797
+ var clamp = (v, lo, hi) => Math.min(Math.max(v, lo), hi);
1798
+ function vRotateAxis(v, axis, deg) {
1799
+ const k = vNorm(axis);
1800
+ const th = deg * Math.PI / 180;
1801
+ const cos = Math.cos(th);
1802
+ const sin = Math.sin(th);
1803
+ const term1 = vScale(v, cos);
1804
+ const term2 = vScale(vCross(k, v), sin);
1805
+ const term3 = vScale(k, vDot(k, v) * (1 - cos));
1806
+ return vAdd(vAdd(term1, term2), term3);
1807
+ }
1808
+ function cameraBasis(position, lookAt, sceneUp = UP) {
1809
+ const forward = vNorm(vSub(lookAt, position));
1810
+ let right = vNorm(vCross(forward, sceneUp));
1811
+ if (vLen(right) < 1e-6)
1812
+ right = { x: 1, y: 0, z: 0 };
1813
+ const up = vCross(right, forward);
1814
+ return { forward, right, up };
1815
+ }
1816
+ function localToWorld(local, b) {
1817
+ return vAdd(vAdd(vScale(b.right, local.x), vScale(b.up, local.y)), vScale(b.forward, local.z));
1818
+ }
1819
+ function easeValue(easing, u) {
1820
+ const t = clamp(u, 0, 1);
1821
+ switch (easing) {
1822
+ case "linear":
1823
+ return t;
1824
+ case "ease-in":
1825
+ return t * t * t;
1826
+ case "ease-out":
1827
+ return 1 - Math.pow(1 - t, 3);
1828
+ case "ease-in-out":
1829
+ default:
1830
+ return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
1831
+ }
1832
+ }
1833
+ function resolveCameraState(ctx) {
1834
+ const n = ctx.cameraNode;
1835
+ return {
1836
+ timeMs: 0,
1837
+ position: { ...n.transform.position },
1838
+ lookAt: { ...n.camera?.lookAt ?? { x: 0, y: 1.2, z: 0 } },
1839
+ fov: clamp(n.camera?.fov ?? 50, 18, 85),
1840
+ rollDeg: void 0
1841
+ };
1842
+ }
1843
+ function targetPoint(ctx) {
1844
+ if (ctx.targetNode) {
1845
+ const p = ctx.targetNode.transform.position;
1846
+ return { x: p.x, y: p.y + 1.2, z: p.z };
1847
+ }
1848
+ return ctx.cameraNode.camera?.lookAt ?? { x: 0, y: 1.2, z: 0 };
1849
+ }
1850
+ function contextScale(_ctx) {
1851
+ return 4;
1852
+ }
1853
+ function hashRand(seed, i, channel) {
1854
+ const s = Math.sin((seed + 1) * 12.9898 + (i + 1) * 78.233 + 37.719 * channel);
1855
+ return s - Math.trunc(s);
1856
+ }
1857
+ function lerpPoses(a, b, cfg) {
1858
+ const count = Math.max(2, Math.round(cfg.keyframeCount ?? 2));
1859
+ return Array.from({ length: count }, (_, i) => {
1860
+ const u = count <= 1 ? 0 : i / (count - 1);
1861
+ const s = easeValue(cfg.easing, u);
1862
+ const hasRoll = a.rollDeg !== void 0 || b.rollDeg !== void 0;
1863
+ return {
1864
+ timeMs: lerp(0, cfg.durationMs, u),
1865
+ position: vLerp(a.position, b.position, s),
1866
+ lookAt: vLerp(a.lookAt, b.lookAt, s),
1867
+ fov: lerp(a.fov, b.fov, s),
1868
+ rollDeg: hasRoll ? lerp(a.rollDeg ?? 0, b.rollDeg ?? 0, s) : void 0
1869
+ };
1870
+ });
1871
+ }
1872
+ function translatePose(state, ctx, localDir, dist, lockLookAt) {
1873
+ const b = cameraBasis(state.position, state.lookAt);
1874
+ const offset = vScale(localToWorld(localDir, b), dist);
1875
+ return {
1876
+ ...state,
1877
+ position: vAdd(state.position, offset),
1878
+ lookAt: targetPoint(ctx)
1879
+ };
1880
+ }
1881
+ function bakeTranslate(recipe, ctx, cfg) {
1882
+ const start = resolveCameraState(ctx);
1883
+ const scale = contextScale();
1884
+ const n = Math.max(cfg.distance ?? 1, 0) * scale;
1885
+ const off = recipe.localOffset ?? { x: 0, y: 0, z: 1 };
1886
+ const dir = recipe.targetSpace === "world" ? vScale(vNorm(off), 1) : localToWorld(off, cameraBasis(start.position, start.lookAt));
1887
+ const offset = vScale(dir, n);
1888
+ const end = {
1889
+ ...start,
1890
+ position: vAdd(start.position, offset),
1891
+ lookAt: recipe.lockTarget ? targetPoint(ctx) : vAdd(start.lookAt, offset)
1892
+ };
1893
+ return lerpPoses(start, end, cfg);
1894
+ }
1895
+ function bakeLinePath(a, b, cfg) {
1896
+ const N_SAMPLE = 64;
1897
+ return Array.from({ length: N_SAMPLE }, (_, i) => {
1898
+ const u = i / (N_SAMPLE - 1);
1899
+ const s = easeValue(cfg.easing, u);
1900
+ const hasRoll = a.rollDeg !== void 0 || b.rollDeg !== void 0;
1901
+ return {
1902
+ timeMs: lerp(0, cfg.durationMs, u),
1903
+ position: vLerp(a.position, b.position, s),
1904
+ lookAt: vLerp(a.lookAt, b.lookAt, s),
1905
+ fov: lerp(a.fov, b.fov, s),
1906
+ rollDeg: hasRoll ? lerp(a.rollDeg ?? 0, b.rollDeg ?? 0, s) : void 0
1907
+ };
1908
+ });
1909
+ }
1910
+ function linePathSource(a, b, cfg) {
1911
+ return {
1912
+ easing: cfg.easing,
1913
+ fovEnd: b.fov,
1914
+ fovStart: a.fov,
1915
+ lookAt: { path: pathObj([a.lookAt, b.lookAt]), type: "curve" },
1916
+ positionPath: pathObj([a.position, b.position]),
1917
+ rollDeg: a.rollDeg ?? b.rollDeg,
1918
+ type: "path"
1919
+ };
1920
+ }
1921
+ function bakeOrbit(recipe, ctx, cfg) {
1922
+ const start = resolveCameraState(ctx);
1923
+ const pivot = start.lookAt;
1924
+ const scale = contextScale();
1925
+ const yawDeg = recipe.yawDeg ?? 0;
1926
+ const heightDelta = (cfg.height ?? recipe.heightDelta ?? 0) * scale;
1927
+ const radiusDelta = (recipe.radiusDelta ?? 0) * scale;
1928
+ const startOffset = vSub(start.position, pivot);
1929
+ const startRadius = Math.max(vLen(startOffset), 1e-3);
1930
+ const N_SAMPLE = 96;
1931
+ const poses = Array.from({ length: N_SAMPLE }, (_, i) => {
1932
+ const u = i / (N_SAMPLE - 1);
1933
+ const s = easeValue(cfg.easing, u);
1934
+ const rotated = vRotateAxis(startOffset, UP, yawDeg * s);
1935
+ const radius = startRadius + radiusDelta * s;
1936
+ const scaled = vScale(vNorm(rotated), radius);
1937
+ const pos = vAdd(vAdd(pivot, scaled), vScale(UP, heightDelta * s));
1938
+ return {
1939
+ timeMs: lerp(0, cfg.durationMs, u),
1940
+ position: pos,
1941
+ lookAt: { ...pivot },
1942
+ fov: start.fov,
1943
+ rollDeg: start.rollDeg
1944
+ };
1945
+ });
1946
+ const source = {
1947
+ axis: { ...UP },
1948
+ easing: cfg.easing,
1949
+ fov: start.fov,
1950
+ heightDelta,
1951
+ lookAtTarget: { ...pivot },
1952
+ pivot: { ...pivot },
1953
+ radiusDelta,
1954
+ rollDeg: start.rollDeg,
1955
+ startLookAt: { ...start.lookAt },
1956
+ startPosition: { ...start.position },
1957
+ type: "orbit",
1958
+ yawDeg
1959
+ };
1960
+ return { poses, source };
1961
+ }
1962
+ function bakeShake(recipe, ctx, cfg) {
1963
+ const start = resolveCameraState(ctx);
1964
+ const count = Math.max(8, Math.round(cfg.keyframeCount ?? 12));
1965
+ const Q = (cfg.shakeAmplitude ?? recipe.amplitude ?? 0.04) * contextScale();
1966
+ const seed = 1;
1967
+ return Array.from({ length: count }, (_, i) => {
1968
+ const u = count <= 1 ? 0 : i / (count - 1);
1969
+ const n1 = hashRand(seed, i, 1) * Q;
1970
+ const n2 = hashRand(seed, i, 2) * Q * 0.5;
1971
+ const n3 = hashRand(seed, i, 3) * Q * 0.35;
1972
+ const n4 = hashRand(seed, i, 4) * Q * 0.4;
1973
+ return {
1974
+ timeMs: lerp(0, cfg.durationMs, u),
1975
+ position: vAdd(start.position, { x: n1, y: n2, z: n3 }),
1976
+ lookAt: vAdd(start.lookAt, { x: n4, y: -0.3 * n4, z: 0 }),
1977
+ fov: start.fov,
1978
+ rollDeg: hashRand(seed, i, 5) * (cfg.rollDeg ?? 1.2)
1979
+ };
1980
+ });
1981
+ }
1982
+ function makeTargetSampler(ctx, frameStart, durationMs) {
1983
+ const tn = ctx.targetNode;
1984
+ if (!tn)
1985
+ return null;
1986
+ const anim = ctx.doc.content.timeline.animation;
1987
+ const fps = ctx.doc.content.timeline.fps;
1988
+ const frameEnd = frameStart + Math.round(durationMs * fps / 1e3);
1989
+ const clip = anim.pathMotionClips.find(
1990
+ (c) => c.target.nodeId === tn.id && c.frameEnd >= frameStart && c.frameStart <= frameEnd
1991
+ );
1992
+ const pathNode = clip ? ctx.doc.content.nodes.find((n) => n.id === clip.pathNodeId) : void 0;
1993
+ const base = tn.transform.position;
1994
+ if (!clip || !pathNode) {
1995
+ const p = { x: base.x, y: base.y + 1.2, z: base.z };
1996
+ return () => ({ ...p });
1997
+ }
1998
+ return (u) => {
1999
+ const f = frameStart + u * (frameEnd - frameStart);
2000
+ const res = evaluatePathMotion(pathNode, clip, f);
2001
+ const p = res?.position ?? base;
2002
+ return { x: p.x, y: p.y + 1.2, z: p.z };
2003
+ };
2004
+ }
2005
+ function pathObj(points, preferCurve = false) {
2006
+ let curve = "line";
2007
+ if (preferCurve && points.length >= 3)
2008
+ curve = "catmullRom";
2009
+ else if (points.length > 2)
2010
+ curve = "polyline";
2011
+ return { closed: void 0, curve, points: points.map((p) => ({ ...p })), tension: 0.5 };
2012
+ }
2013
+ function targetPathPoints(ctx, frameStart, durationMs) {
2014
+ const tn = ctx.targetNode;
2015
+ if (!tn)
2016
+ return null;
2017
+ const anim = ctx.doc.content.timeline.animation;
2018
+ const fps = ctx.doc.content.timeline.fps;
2019
+ const frameEnd = frameStart + Math.round(durationMs * fps / 1e3);
2020
+ const clip = anim.pathMotionClips.find(
2021
+ (c) => c.target.nodeId === tn.id && c.frameEnd >= frameStart && c.frameStart <= frameEnd
2022
+ );
2023
+ const pathNode = clip ? ctx.doc.content.nodes.find((n) => n.id === clip.pathNodeId) : void 0;
2024
+ const pts = pathNode?.path?.points;
2025
+ return clip && pathNode && pts && pts.length >= 2 ? pts : null;
2026
+ }
2027
+ function bakeTracking(pathKind, ctx, cfg, frameStart) {
2028
+ const start = resolveCameraState(ctx);
2029
+ const target0 = targetPoint(ctx);
2030
+ const scale = contextScale();
2031
+ const m = Math.max(vDist(start.position, target0), Math.max(cfg.distance ?? 1, 1e-3) * scale);
2032
+ const h = Number.isFinite(start.position.y - target0.y) ? start.position.y - target0.y : 0;
2033
+ const offset = pathKind === "leading" ? { x: 0, y: h, z: m } : pathKind === "profile" ? { x: m, y: h, z: 0 } : { x: 0, y: h, z: -m };
2034
+ const pathPts = targetPathPoints(ctx, frameStart, cfg.durationMs);
2035
+ const rawFwd = pathPts ? vSub(pathPts[pathPts.length - 1].position, pathPts[0].position) : vSub(target0, start.position);
2036
+ const fwd = vLen(rawFwd) > 1e-9 ? vNorm(rawFwd) : { x: 0, y: 0, z: -1 };
2037
+ const basis = cameraBasis({ x: 0, y: 0, z: 0 }, fwd);
2038
+ const sampler = makeTargetSampler(ctx, frameStart, cfg.durationMs);
2039
+ const lookOffsetWorld = vSub(start.lookAt, target0);
2040
+ const lookOffsetLocal = {
2041
+ x: vDot(lookOffsetWorld, basis.right),
2042
+ y: vDot(lookOffsetWorld, basis.up),
2043
+ z: vDot(lookOffsetWorld, basis.forward)
2044
+ };
2045
+ const N_SAMPLE = 96;
2046
+ const poses = Array.from({ length: N_SAMPLE }, (_, i) => {
2047
+ const u = i / (N_SAMPLE - 1);
2048
+ const s = easeValue(cfg.easing, u);
2049
+ const tp = sampler ? sampler(s) : target0;
2050
+ const pos = vAdd(tp, localToWorld(offset, basis));
2051
+ const look = vAdd(tp, localToWorld(lookOffsetLocal, basis));
2052
+ return { timeMs: lerp(0, cfg.durationMs, u), position: pos, lookAt: look, fov: start.fov };
2053
+ });
2054
+ const mode = pathKind === "leading" ? "leading" : pathKind === "profile" ? "profile" : "follow";
2055
+ const target = ctx.targetNode && ctx.targetNode.id ? { type: "node", nodeId: ctx.targetNode.id } : { type: "point", point: target0 };
2056
+ const source = {
2057
+ basisFallback: { forward: fwd, up: { ...UP } },
2058
+ fov: start.fov,
2059
+ lookAtOffset: lookOffsetLocal,
2060
+ mode,
2061
+ offset,
2062
+ rollDeg: start.rollDeg,
2063
+ target,
2064
+ targetFallback: { forward: fwd, position: target0, up: { ...UP } },
2065
+ type: "target-tracking"
2066
+ };
2067
+ return { poses, source };
2068
+ }
2069
+ function bakeFpv(ctx, cfg, frameStart) {
2070
+ const pts = targetPathPoints(ctx, frameStart, cfg.durationMs);
2071
+ if (!pts)
2072
+ return null;
2073
+ const start = resolveCameraState(ctx);
2074
+ const sorted = [...pts].sort((a, b) => (a.timeRatio ?? 0) - (b.timeRatio ?? 0));
2075
+ const N_SAMPLE = 96;
2076
+ const posAt = (u) => {
2077
+ const i = Math.min(sorted.length - 2, Math.floor(u * (sorted.length - 1)));
2078
+ const t = u * (sorted.length - 1) - i;
2079
+ return vLerp(sorted[i].position, sorted[i + 1].position, clamp(t, 0, 1));
2080
+ };
2081
+ const ahead = 1 / Math.max(4 * sorted.length, 12);
2082
+ const poses = Array.from({ length: N_SAMPLE }, (_, i) => {
2083
+ const u = i / (N_SAMPLE - 1);
2084
+ const s = easeValue(cfg.easing, u);
2085
+ const position = posAt(s);
2086
+ const lookAt = posAt(clamp(s + ahead, 0, 1));
2087
+ return { timeMs: lerp(0, cfg.durationMs, u), position, lookAt, fov: start.fov };
2088
+ });
2089
+ const source = {
2090
+ easing: cfg.easing,
2091
+ fovStart: start.fov,
2092
+ lookAt: {
2093
+ fallbackOffset: vSub(start.lookAt, start.position),
2094
+ progressOffset: ahead,
2095
+ type: "path-ahead"
2096
+ },
2097
+ positionPath: pathObj(sorted.map((p) => p.position), true),
2098
+ rollDeg: start.rollDeg,
2099
+ type: "path"
2100
+ };
2101
+ return { poses, source };
2102
+ }
2103
+ function kfId(prefix, i) {
2104
+ return `${prefix}:${i}`;
2105
+ }
2106
+ function toCurves(poses, preset, cfg, approximated) {
2107
+ const interpolation = cfg.easing === "linear" ? "linear" : "bezier";
2108
+ const interp2 = poses.length > 8 ? "linear" : interpolation;
2109
+ const build = (suffix, group, dataPath, arrayIndex, pick) => ({
2110
+ id: `${preset.id}:${suffix}`,
2111
+ group,
2112
+ dataPath,
2113
+ arrayIndex,
2114
+ extrapolation: "constant",
2115
+ keyframes: poses.map((p, i) => ({
2116
+ id: kfId(`${preset.id}:${suffix}`, i),
2117
+ time: p.timeMs,
2118
+ value: pick(p),
2119
+ interpolation: interp2
2120
+ }))
2121
+ });
2122
+ const curves = [
2123
+ build("position-x", "position", "camera.position", 0, (p) => p.position.x),
2124
+ build("position-y", "position", "camera.position", 1, (p) => p.position.y),
2125
+ build("position-z", "position", "camera.position", 2, (p) => p.position.z),
2126
+ build("look-at-x", "lookAt", "camera.lookAt", 0, (p) => p.lookAt.x),
2127
+ build("look-at-y", "lookAt", "camera.lookAt", 1, (p) => p.lookAt.y),
2128
+ build("look-at-z", "lookAt", "camera.lookAt", 2, (p) => p.lookAt.z),
2129
+ build("fov", "lens", "camera.lens.fov", 0, (p) => p.fov)
2130
+ ];
2131
+ if (poses.some((p) => p.rollDeg !== void 0 && p.rollDeg !== 0)) {
2132
+ curves.push({
2133
+ id: `${preset.id}:roll`,
2134
+ group: "rotation",
2135
+ dataPath: "camera.rollDeg",
2136
+ arrayIndex: 0,
2137
+ extrapolation: "constant",
2138
+ keyframes: poses.map((p, i) => ({
2139
+ id: kfId(`${preset.id}:roll`, i),
2140
+ time: p.timeMs,
2141
+ value: p.rollDeg ?? 0,
2142
+ interpolation: interp2
2143
+ }))
2144
+ });
2145
+ }
2146
+ const warnings = [];
2147
+ if (approximated)
2148
+ warnings.push("advanced-motion-approximated");
2149
+ if (poses.some((p) => p.fov <= 18 || p.fov >= 85))
2150
+ warnings.push("fov-clamped");
2151
+ return { curves, warnings: warnings.length ? warnings : void 0 };
2152
+ }
2153
+ var clipSeq = 1;
2154
+ function rand6() {
2155
+ return Math.random().toString(36).slice(2, 8);
2156
+ }
2157
+ function checkTargetPolicy(preset, ctx) {
2158
+ switch (preset.targetPolicy) {
2159
+ case "required-look-at":
2160
+ case "required-focus-target":
2161
+ return ctx.targetNode ? null : "missing-target";
2162
+ default:
2163
+ return null;
2164
+ }
2165
+ }
2166
+ function sampleCameraMotionPoses(preset, ctx, frameStart, userConfig) {
2167
+ const reason = checkTargetPolicy(preset, ctx);
2168
+ if (reason)
2169
+ return { ok: false, reason };
2170
+ const cfg = {
2171
+ ...preset.defaultConfig,
2172
+ ...userConfig,
2173
+ strength: clamp(userConfig?.strength ?? preset.defaultConfig.strength, 0, 1)
2174
+ };
2175
+ if (!Number.isFinite(cfg.durationMs) || cfg.durationMs <= 0)
2176
+ return { ok: false, reason: "invalid-config" };
2177
+ const recipe = preset.recipe;
2178
+ let poses;
2179
+ let approximated = false;
2180
+ let source;
2181
+ const start = resolveCameraState(ctx);
2182
+ switch (recipe.type) {
2183
+ case "static": {
2184
+ poses = [start, { ...start, timeMs: cfg.durationMs }];
2185
+ break;
2186
+ }
2187
+ case "pan-tilt": {
2188
+ const dist = Math.max(vDist(start.position, start.lookAt), 1e-3);
2189
+ const b = cameraBasis(start.position, start.lookAt);
2190
+ const yaw = recipe.yawDeg ?? 0;
2191
+ const pitch = recipe.pitchDeg ?? 0;
2192
+ let fwd = vRotateAxis(b.forward, UP, yaw);
2193
+ fwd = vRotateAxis(fwd, b.right, pitch);
2194
+ const end = { ...start, lookAt: vAdd(start.position, vScale(fwd, dist)) };
2195
+ poses = lerpPoses(start, end, cfg);
2196
+ break;
2197
+ }
2198
+ case "translate": {
2199
+ poses = bakeTranslate(recipe, ctx, cfg);
2200
+ break;
2201
+ }
2202
+ case "zoom": {
2203
+ const delta = cfg.fovDelta ?? recipe.fovDelta ?? 0;
2204
+ const end = { ...start, fov: clamp(start.fov + delta, 18, 85) };
2205
+ poses = lerpPoses(start, end, cfg);
2206
+ break;
2207
+ }
2208
+ case "orbit": {
2209
+ const r = bakeOrbit(recipe, ctx, cfg);
2210
+ poses = r.poses;
2211
+ source = r.source;
2212
+ break;
2213
+ }
2214
+ case "shake": {
2215
+ poses = bakeShake(recipe, ctx, cfg);
2216
+ approximated = true;
2217
+ break;
2218
+ }
2219
+ case "focus-shift": {
2220
+ const end = { ...start, lookAt: targetPoint(ctx) };
2221
+ poses = lerpPoses(start, end, cfg);
2222
+ approximated = true;
2223
+ break;
2224
+ }
2225
+ case "path": {
2226
+ const kind = recipe.pathKind ?? "slider-reveal";
2227
+ if (kind === "follow" || kind === "leading" || kind === "profile") {
2228
+ const r = bakeTracking(kind, ctx, cfg, frameStart);
2229
+ poses = r.poses;
2230
+ source = r.source;
2231
+ break;
2232
+ }
2233
+ if (kind === "fpv") {
2234
+ const fpv = bakeFpv(ctx, cfg, frameStart);
2235
+ if (fpv) {
2236
+ poses = fpv.poses;
2237
+ source = fpv.source;
2238
+ break;
2239
+ }
2240
+ const end2 = translatePose(start, ctx, { x: 0, y: 0, z: 1 }, 1.4 * contextScale());
2241
+ poses = bakeLinePath(start, end2, cfg);
2242
+ source = linePathSource(start, end2, cfg);
2243
+ approximated = true;
2244
+ break;
2245
+ }
2246
+ const scale = contextScale();
2247
+ let end;
2248
+ switch (kind) {
2249
+ case "slider-reveal":
2250
+ end = translatePose(start, ctx, { x: 1, y: 0, z: 0 }, scale);
2251
+ break;
2252
+ case "foreground-wipe":
2253
+ end = translatePose(start, ctx, { x: 1, y: 0, z: 0 }, 0.8 * scale);
2254
+ break;
2255
+ case "push-through":
2256
+ end = translatePose(start, ctx, { x: 0, y: 0, z: 1 }, 1.4 * scale);
2257
+ break;
2258
+ case "aerial":
2259
+ end = {
2260
+ ...translatePose(start, ctx, { x: 0.6, y: 0.5, z: -0.25 }, scale),
2261
+ fov: clamp(start.fov + (cfg.fovDelta ?? 6), 18, 85)
2262
+ };
2263
+ break;
2264
+ case "dive":
2265
+ end = translatePose(start, ctx, { x: 0, y: -0.7, z: 0.8 }, scale);
2266
+ break;
2267
+ case "pull-away":
2268
+ end = {
2269
+ ...translatePose(start, ctx, { x: 0, y: 0.7, z: -1 }, scale),
2270
+ fov: clamp(start.fov + (cfg.fovDelta ?? 8), 18, 85)
2271
+ };
2272
+ break;
2273
+ default:
2274
+ return { ok: false, reason: `unsupported-path-kind:${kind}` };
2275
+ }
2276
+ poses = bakeLinePath(start, end, cfg);
2277
+ source = linePathSource(start, end, cfg);
2278
+ if (kind === "aerial" || kind === "pull-away" || kind === "push-through" || kind === "slider-reveal" || kind === "foreground-wipe" || kind === "dive") {
2279
+ approximated = true;
2280
+ }
2281
+ break;
2282
+ }
2283
+ default:
2284
+ return { ok: false, reason: `unsupported-recipe:${recipe.type}` };
2285
+ }
2286
+ return { ok: true, poses, cfg, approximated, source };
2287
+ }
2288
+ function bakeCameraMotionPoses(preset, ctx, frameStart, userConfig) {
2289
+ const sampled = sampleCameraMotionPoses(preset, ctx, frameStart, userConfig);
2290
+ if (!sampled.ok || !sampled.poses)
2291
+ return { ok: false, reason: sampled.reason };
2292
+ return { ok: true, poses: sampled.poses };
2293
+ }
2294
+ function bakeCameraMotionClip(preset, ctx, frameStart, userConfig) {
2295
+ const sampled = sampleCameraMotionPoses(preset, ctx, frameStart, userConfig);
2296
+ if (!sampled.ok || !sampled.poses || !sampled.cfg)
2297
+ return { ok: false, reason: sampled.reason };
2298
+ const { poses, cfg, approximated = false, source } = sampled;
2299
+ const { curves, warnings } = toCurves(poses, preset, cfg, approximated);
2300
+ const fps = ctx.doc.content.timeline.fps;
2301
+ const spanFrames = Math.max(1, Math.round(cfg.durationMs * fps / 1e3));
2302
+ const outerId = `camera_motion_clip_${Date.now().toString(36)}_${rand6()}`;
2303
+ const motionId = `camera_motion_clip_${clipSeq++}`;
2304
+ const clip = {
2305
+ id: outerId,
2306
+ target: { type: "node", nodeId: ctx.cameraNode.id },
2307
+ focusTarget: { type: "center" },
2308
+ frameStart,
2309
+ frameEnd: frameStart + spanFrames,
2310
+ trimStartMs: 0,
2311
+ trimEndMs: cfg.durationMs,
2312
+ playback: {
2313
+ version: 1,
2314
+ speed: 1,
2315
+ loop: false,
2316
+ loopMode: "ping-pong",
2317
+ baseDurationFrames: spanFrames
2318
+ },
2319
+ motion: {
2320
+ id: motionId,
2321
+ version: 1,
2322
+ presetId: preset.id,
2323
+ label: preset.name,
2324
+ timeUnit: "millisecond",
2325
+ durationMs: cfg.durationMs,
2326
+ metadata: {
2327
+ generatedAt: Date.now(),
2328
+ cameraNodeId: ctx.cameraNode.id,
2329
+ targetNodeId: ctx.targetNode?.id,
2330
+ // 本次烘焙用的合并参数(defaultConfig + 用户调参),重烘焙时叠加修改
2331
+ // —— 注意:官方 metadata 只有 generatedAt/cameraNodeId/targetNodeId/
2332
+ // secondaryTargetNodeId/occluderNodeId,config 是我们为重烘焙额外加的
2333
+ config: cfg
2334
+ },
2335
+ // 原版:确定性 recipe(C())无 source;采样类(k()/I())写 source 描述对象
2336
+ source,
2337
+ // 原版:无 warning 时整个字段省略(undefined),不写 null
2338
+ warnings,
2339
+ curves
2340
+ }
2341
+ };
2342
+ return { ok: true, clip };
2343
+ }
2344
+ function applyCameraPresetToNode(node, preset) {
2345
+ node.transform.position = { ...preset.position };
2346
+ node.transform.rotation = { ...preset.rotation };
2347
+ if (node.camera) {
2348
+ node.camera.fov = preset.fov;
2349
+ node.camera.lookAt = { ...preset.lookAt };
2350
+ delete node.camera.subject;
2351
+ }
2352
+ }
2353
+ function applyCameraPresetToSubject(node, preset, subject) {
2354
+ const offset = {
2355
+ x: preset.position.x - preset.lookAt.x,
2356
+ y: preset.position.y,
2357
+ z: preset.position.z - preset.lookAt.z
2358
+ };
2359
+ const lookAtOffset = { x: 0, y: preset.lookAt.y, z: 0 };
2360
+ const binding = {
2361
+ nodeId: subject.id,
2362
+ distance: Math.hypot(offset.x, offset.z) || 1,
2363
+ offset,
2364
+ lookAtOffset,
2365
+ // 默认不跟随:机位只按人物当前姿态摆一次,之后可以自由拖。
2366
+ // 用户在 Inspector 勾上「跟随人物」后才会跟着走。
2367
+ follow: false,
2368
+ followRotation: true,
2369
+ overridesMotion: true
2370
+ };
2371
+ const pose = resolveSubjectCameraPose(binding, subject.transform.position, subject.transform.rotation);
2372
+ node.transform.position = pose.position;
2373
+ node.transform.rotation = {
2374
+ ...preset.rotation,
2375
+ y: preset.rotation.y + subjectYawDeg(subject.transform.rotation)
2376
+ };
2377
+ if (node.camera) {
2378
+ node.camera.fov = preset.fov;
2379
+ node.camera.lookAt = pose.lookAt;
2380
+ node.camera.subject = binding;
2381
+ }
2382
+ }
2383
+
2384
+ // src/evaluate/camera/cameraMotionRange.ts
2385
+ function defaultVideoExportEndFrame(clips, cameraId, timelineEnd) {
2386
+ const scoped = cameraId ? clips.filter((clip) => clip.target.nodeId === cameraId) : clips;
2387
+ if (scoped.length === 0)
2388
+ return timelineEnd;
2389
+ return Math.min(timelineEnd, Math.max(...scoped.map((clip) => clip.frameEnd)));
2390
+ }
2391
+
2392
+ // src/evaluate/editSequence.ts
2393
+ function emptyEditorial() {
2394
+ return {
2395
+ version: 1,
2396
+ activeSequenceId: "sequence_1",
2397
+ sequences: [{ id: "sequence_1", clips: [] }]
2398
+ };
2399
+ }
2400
+ function getEditorial(document) {
2401
+ return document.content.editorial ?? emptyEditorial();
2402
+ }
2403
+ function getEditSequence(document, sequenceId) {
2404
+ return getEditorial(document).sequences.find((sequence) => sequence.id === sequenceId) ?? null;
2405
+ }
2406
+ function getClipDurationFrames(clip) {
2407
+ return clip.sourceFrameEnd - clip.sourceFrameStart + 1;
2408
+ }
2409
+ function getEditSequenceDurationFrames(clips) {
2410
+ let total = 0;
2411
+ for (const clip of clips) {
2412
+ const duration = getClipDurationFrames(clip);
2413
+ if (duration > 0)
2414
+ total += duration;
2415
+ }
2416
+ return total;
2417
+ }
2418
+ function resolveEditSequenceFrame(clips, sequenceFrame) {
2419
+ if (!Number.isInteger(sequenceFrame) || sequenceFrame < 0)
2420
+ return null;
2421
+ let cursor = 0;
2422
+ for (let i = 0; i < clips.length; i++) {
2423
+ const clip = clips[i];
2424
+ const duration = getClipDurationFrames(clip);
2425
+ if (duration <= 0)
2426
+ continue;
2427
+ if (sequenceFrame >= cursor && sequenceFrame < cursor + duration) {
2428
+ const clipLocalFrame = sequenceFrame - cursor;
2429
+ return {
2430
+ clipId: clip.id,
2431
+ cameraNodeId: clip.cameraNodeId,
2432
+ sourceFrame: clip.sourceFrameStart + clipLocalFrame,
2433
+ clipIndex: i,
2434
+ clipLocalFrame
2435
+ };
2436
+ }
2437
+ cursor += duration;
2438
+ }
2439
+ return null;
2440
+ }
2441
+ function collectCameraIds(nodes) {
2442
+ const ids = /* @__PURE__ */ new Set();
2443
+ for (const node of nodes) {
2444
+ if (node.type === "camera")
2445
+ ids.add(node.id);
2446
+ }
2447
+ return ids;
2448
+ }
2449
+ function validateClip(document, sequenceId, clip, cameraIds) {
2450
+ const timeline = document.content.timeline;
2451
+ const issues = [];
2452
+ const details = { sequenceId, clipId: clip.id };
2453
+ if (!Number.isInteger(clip.sourceFrameStart) || !Number.isInteger(clip.sourceFrameEnd)) {
2454
+ issues.push({
2455
+ code: "non-integer-frame",
2456
+ ...details,
2457
+ message: `clip ${clip.id} source frames must be integers`
2458
+ });
2459
+ }
2460
+ if (clip.sourceFrameEnd < clip.sourceFrameStart) {
2461
+ issues.push({
2462
+ code: "inverted-range",
2463
+ ...details,
2464
+ message: `clip ${clip.id} sourceFrameEnd < sourceFrameStart`
2465
+ });
2466
+ }
2467
+ if (clip.sourceFrameStart < timeline.frameStart || clip.sourceFrameEnd > timeline.frameEnd) {
2468
+ issues.push({
2469
+ code: "out-of-timeline",
2470
+ ...details,
2471
+ message: `clip ${clip.id} source range is outside timeline.frameStart..frameEnd`
2472
+ });
2473
+ }
2474
+ if (!cameraIds.has(clip.cameraNodeId)) {
2475
+ issues.push({
2476
+ code: "missing-camera",
2477
+ ...details,
2478
+ message: `clip ${clip.id} references missing camera ${clip.cameraNodeId}`
2479
+ });
2480
+ }
2481
+ return issues;
2482
+ }
2483
+ function validateEditorial(document, options = {}) {
2484
+ const editorial = getEditorial(document);
2485
+ const issues = [];
2486
+ if (editorial.sequences.length === 0) {
2487
+ return [{ code: "empty-editorial", message: "editorial.sequences is empty" }];
2488
+ }
2489
+ const sequenceIds = /* @__PURE__ */ new Set();
2490
+ for (const sequence of editorial.sequences) {
2491
+ if (sequenceIds.has(sequence.id)) {
2492
+ issues.push({
2493
+ code: "duplicate-sequence-id",
2494
+ sequenceId: sequence.id,
2495
+ message: `duplicate sequence id ${sequence.id}`
2496
+ });
2497
+ }
2498
+ sequenceIds.add(sequence.id);
2499
+ }
2500
+ if (!sequenceIds.has(editorial.activeSequenceId)) {
2501
+ issues.push({
2502
+ code: "active-sequence-missing",
2503
+ sequenceId: editorial.activeSequenceId,
2504
+ message: `active sequence ${editorial.activeSequenceId} does not exist`
2505
+ });
2506
+ }
2507
+ const clipIds = /* @__PURE__ */ new Set();
2508
+ for (const sequence of editorial.sequences) {
2509
+ for (const clip of sequence.clips) {
2510
+ if (clipIds.has(clip.id)) {
2511
+ issues.push({
2512
+ code: "duplicate-clip-id",
2513
+ sequenceId: sequence.id,
2514
+ clipId: clip.id,
2515
+ message: `duplicate clip id ${clip.id}`
2516
+ });
2517
+ }
2518
+ clipIds.add(clip.id);
2519
+ }
2520
+ }
2521
+ const sequences = options.sequenceId ? editorial.sequences.filter((sequence) => sequence.id === options.sequenceId) : editorial.sequences;
2522
+ if (options.sequenceId && sequences.length === 0) {
2523
+ issues.push({
2524
+ code: "missing-sequence",
2525
+ sequenceId: options.sequenceId,
2526
+ message: `sequence ${options.sequenceId} does not exist`
2527
+ });
2528
+ return issues;
2529
+ }
2530
+ const cameraIds = collectCameraIds(document.content.nodes);
2531
+ for (const sequence of sequences) {
2532
+ if (options.requireClips && sequence.clips.length === 0) {
2533
+ issues.push({
2534
+ code: "empty-sequence",
2535
+ sequenceId: sequence.id,
2536
+ message: `sequence ${sequence.id} has no clips`
2537
+ });
2538
+ }
2539
+ for (const clip of sequence.clips) {
2540
+ issues.push(...validateClip(document, sequence.id, clip, cameraIds));
2541
+ }
2542
+ }
2543
+ return issues;
2544
+ }
2545
+
2546
+ // src/evaluate/editSequenceOps.ts
2547
+ var PLAYBACK_BLOCKING = /* @__PURE__ */ new Set([
2548
+ "empty-editorial",
2549
+ "missing-sequence",
2550
+ "empty-sequence",
2551
+ "duplicate-sequence-id",
2552
+ "duplicate-clip-id",
2553
+ "missing-camera",
2554
+ "non-integer-frame",
2555
+ "inverted-range",
2556
+ "out-of-timeline"
2557
+ ]);
2558
+ function cloneEditorial(editorial) {
2559
+ return JSON.parse(JSON.stringify(editorial));
2560
+ }
2561
+ function emptyEditorial2() {
2562
+ return {
2563
+ version: 1,
2564
+ activeSequenceId: "sequence_1",
2565
+ sequences: [{ id: "sequence_1", clips: [] }]
2566
+ };
2567
+ }
2568
+ function materializeEditorial(document) {
2569
+ return document.content.editorial ? cloneEditorial(document.content.editorial) : emptyEditorial2();
2570
+ }
2571
+ function rand62() {
2572
+ return Math.random().toString(36).slice(2, 8).padEnd(6, "0");
2573
+ }
2574
+ function uniqueId(used, prefix) {
2575
+ for (let i = 0; i < 32; i++) {
2576
+ const id = `${prefix}_${Date.now().toString(36)}_${rand62()}`;
2577
+ if (!used.has(id))
2578
+ return id;
2579
+ }
2580
+ throw new Error(`unable to allocate unique id with prefix ${prefix}`);
2581
+ }
2582
+ function collectIds(editorial) {
2583
+ const sequences = /* @__PURE__ */ new Set();
2584
+ const clips = /* @__PURE__ */ new Set();
2585
+ for (const sequence of editorial.sequences) {
2586
+ sequences.add(sequence.id);
2587
+ for (const clip of sequence.clips)
2588
+ clips.add(clip.id);
2589
+ }
2590
+ return { sequences, clips };
2591
+ }
2592
+ function hasDuplicateIds(editorial) {
2593
+ const sequences = /* @__PURE__ */ new Set();
2594
+ const clips = /* @__PURE__ */ new Set();
2595
+ for (const sequence of editorial.sequences) {
2596
+ if (sequences.has(sequence.id))
2597
+ return true;
2598
+ sequences.add(sequence.id);
2599
+ for (const clip of sequence.clips) {
2600
+ if (clips.has(clip.id))
2601
+ return true;
2602
+ clips.add(clip.id);
2603
+ }
2604
+ }
2605
+ return false;
2606
+ }
2607
+ function allocateSequenceId(editorial) {
2608
+ return uniqueId(collectIds(editorial).sequences, "sequence");
2609
+ }
2610
+ function allocateClipId(editorial) {
2611
+ return uniqueId(collectIds(editorial).clips, "edit_clip");
2612
+ }
2613
+ function findEditClip(editorial, clipId) {
2614
+ for (let sequenceIndex = 0; sequenceIndex < editorial.sequences.length; sequenceIndex++) {
2615
+ const sequence = editorial.sequences[sequenceIndex];
2616
+ const clipIndex = sequence.clips.findIndex((clip) => clip.id === clipId);
2617
+ if (clipIndex >= 0)
2618
+ return { sequence, sequenceIndex, clipIndex };
2619
+ }
2620
+ return null;
2621
+ }
2622
+ function getActiveEditSequence(document) {
2623
+ const editorial = getEditorial(document);
2624
+ return editorial.sequences.find((sequence) => sequence.id === editorial.activeSequenceId) ?? null;
2625
+ }
2626
+ function clipSequenceSpan(clips, index) {
2627
+ if (index < 0 || index >= clips.length)
2628
+ return null;
2629
+ let sequenceStart = 0;
2630
+ for (let i = 0; i < index; i++) {
2631
+ const duration = getClipDurationFrames(clips[i]);
2632
+ if (duration > 0)
2633
+ sequenceStart += duration;
2634
+ }
2635
+ return { sequenceStart, duration: Math.max(0, getClipDurationFrames(clips[index])) };
2636
+ }
2637
+ function playbackIssues(document, sequenceId) {
2638
+ return validateEditorial(document, { sequenceId, requireClips: true }).filter(
2639
+ (issue) => issue.code !== "active-sequence-missing" && PLAYBACK_BLOCKING.has(issue.code)
2640
+ );
2641
+ }
2642
+ function fail(error, issues) {
2643
+ return issues ? { ok: false, error, issues } : { ok: false, error };
2644
+ }
2645
+ function ok(editorial, createdId) {
2646
+ return createdId ? { ok: true, editorial, createdId } : { ok: true, editorial };
2647
+ }
2648
+ function requireEditorial(document) {
2649
+ if (!document)
2650
+ return fail("no-document");
2651
+ const editorial = materializeEditorial(document);
2652
+ if (hasDuplicateIds(editorial))
2653
+ return fail("duplicate-id");
2654
+ return editorial;
2655
+ }
2656
+ function normalizeName(name) {
2657
+ if (name == null)
2658
+ return void 0;
2659
+ const trimmed = name.trim();
2660
+ return trimmed || void 0;
2661
+ }
2662
+ function isCamera(document, cameraNodeId) {
2663
+ return document.content.nodes.some((node) => node.id === cameraNodeId && node.type === "camera");
2664
+ }
2665
+ function normalizeRange(document, start, end) {
2666
+ const sourceFrameStart = Math.round(start);
2667
+ const sourceFrameEnd = Math.round(end);
2668
+ const { frameStart, frameEnd } = document.content.timeline;
2669
+ if (!Number.isInteger(sourceFrameStart) || !Number.isInteger(sourceFrameEnd) || sourceFrameEnd < sourceFrameStart || sourceFrameStart < frameStart || sourceFrameEnd > frameEnd) {
2670
+ return null;
2671
+ }
2672
+ return { sourceFrameStart, sourceFrameEnd };
2673
+ }
2674
+ function createSequence(document, input = {}) {
2675
+ const editorial = requireEditorial(document);
2676
+ if (!("sequences" in editorial))
2677
+ return editorial;
2678
+ const name = normalizeName(input.name);
2679
+ const id = allocateSequenceId(editorial);
2680
+ const sequence = { id, clips: [] };
2681
+ if (typeof name === "string")
2682
+ sequence.name = name;
2683
+ editorial.sequences.push(sequence);
2684
+ if (input.activate !== false)
2685
+ editorial.activeSequenceId = id;
2686
+ return ok(editorial, id);
2687
+ }
2688
+ function renameSequence(document, sequenceId, name) {
2689
+ const editorial = requireEditorial(document);
2690
+ if (!("sequences" in editorial))
2691
+ return editorial;
2692
+ const sequence = editorial.sequences.find((item) => item.id === sequenceId);
2693
+ if (!sequence)
2694
+ return fail("missing-sequence");
2695
+ const next = normalizeName(name);
2696
+ if (typeof next === "string")
2697
+ sequence.name = next;
2698
+ else
2699
+ delete sequence.name;
2700
+ return ok(editorial);
2701
+ }
2702
+ function duplicateSequence(document, sequenceId, input = {}) {
2703
+ const editorial = requireEditorial(document);
2704
+ if (!("sequences" in editorial))
2705
+ return editorial;
2706
+ const index = editorial.sequences.findIndex((item) => item.id === sequenceId);
2707
+ if (index < 0)
2708
+ return fail("missing-sequence");
2709
+ const source = editorial.sequences[index];
2710
+ const copy = JSON.parse(JSON.stringify(source));
2711
+ copy.id = allocateSequenceId(editorial);
2712
+ editorial.sequences.splice(index + 1, 0, copy);
2713
+ copy.clips = source.clips.map((clip) => ({
2714
+ ...JSON.parse(JSON.stringify(clip)),
2715
+ id: allocateClipId(editorial)
2716
+ }));
2717
+ if (input.activate !== false)
2718
+ editorial.activeSequenceId = copy.id;
2719
+ return ok(editorial, copy.id);
2720
+ }
2721
+ function deleteSequence(document, sequenceId) {
2722
+ const editorial = requireEditorial(document);
2723
+ if (!("sequences" in editorial))
2724
+ return editorial;
2725
+ const index = editorial.sequences.findIndex((item) => item.id === sequenceId);
2726
+ if (index < 0)
2727
+ return fail("missing-sequence");
2728
+ if (editorial.sequences.length === 1)
2729
+ return fail("last-sequence");
2730
+ editorial.sequences.splice(index, 1);
2731
+ if (editorial.activeSequenceId === sequenceId) {
2732
+ const neighbor = editorial.sequences[Math.max(0, index - 1)];
2733
+ editorial.activeSequenceId = neighbor.id;
2734
+ }
2735
+ return ok(editorial);
2736
+ }
2737
+ function activateSequence(document, sequenceId) {
2738
+ const editorial = requireEditorial(document);
2739
+ if (!("sequences" in editorial))
2740
+ return editorial;
2741
+ if (!editorial.sequences.some((item) => item.id === sequenceId)) {
2742
+ return fail("missing-sequence");
2743
+ }
2744
+ editorial.activeSequenceId = sequenceId;
2745
+ return ok(editorial);
2746
+ }
2747
+ function clearSequenceClips(document, sequenceId) {
2748
+ const editorial = requireEditorial(document);
2749
+ if (!("sequences" in editorial))
2750
+ return editorial;
2751
+ const sequence = editorial.sequences.find((item) => item.id === sequenceId);
2752
+ if (!sequence)
2753
+ return fail("missing-sequence");
2754
+ sequence.clips = [];
2755
+ return ok(editorial);
2756
+ }
2757
+ function insertEditClip(document, input) {
2758
+ const editorial = requireEditorial(document);
2759
+ if (!("sequences" in editorial))
2760
+ return editorial;
2761
+ const sequence = editorial.sequences.find((item) => item.id === input.sequenceId);
2762
+ if (!sequence)
2763
+ return fail("missing-sequence");
2764
+ if (!isCamera(document, input.cameraNodeId))
2765
+ return fail("invalid-camera");
2766
+ const range = normalizeRange(document, input.sourceFrameStart, input.sourceFrameEnd);
2767
+ if (!range)
2768
+ return fail("invalid-range");
2769
+ const index = input.index ?? sequence.clips.length;
2770
+ if (!Number.isInteger(index) || index < 0 || index > sequence.clips.length) {
2771
+ return fail("invalid-index");
2772
+ }
2773
+ const clip = {
2774
+ id: allocateClipId(editorial),
2775
+ cameraNodeId: input.cameraNodeId,
2776
+ ...range
2777
+ };
2778
+ sequence.clips.splice(index, 0, clip);
2779
+ return ok(editorial, clip.id);
2780
+ }
2781
+ function updateEditClip(document, clipId, patch) {
2782
+ const editorial = requireEditorial(document);
2783
+ if (!("sequences" in editorial))
2784
+ return editorial;
2785
+ const found = findEditClip(editorial, clipId);
2786
+ if (!found)
2787
+ return fail("missing-clip");
2788
+ const clip = found.sequence.clips[found.clipIndex];
2789
+ const cameraNodeId = patch.cameraNodeId ?? clip.cameraNodeId;
2790
+ if (patch.cameraNodeId !== void 0 && !isCamera(document, cameraNodeId)) {
2791
+ return fail("invalid-camera");
2792
+ }
2793
+ const range = normalizeRange(
2794
+ document,
2795
+ patch.sourceFrameStart ?? clip.sourceFrameStart,
2796
+ patch.sourceFrameEnd ?? clip.sourceFrameEnd
2797
+ );
2798
+ if (!range)
2799
+ return fail("invalid-range");
2800
+ found.sequence.clips[found.clipIndex] = { ...clip, cameraNodeId, ...range };
2801
+ return ok(editorial);
2802
+ }
2803
+ function moveEditClip(document, clipId, toIndex) {
2804
+ const editorial = requireEditorial(document);
2805
+ if (!("sequences" in editorial))
2806
+ return editorial;
2807
+ const found = findEditClip(editorial, clipId);
2808
+ if (!found)
2809
+ return fail("missing-clip");
2810
+ const clips = found.sequence.clips;
2811
+ if (!Number.isInteger(toIndex) || toIndex < 0 || toIndex >= clips.length) {
2812
+ return fail("invalid-index");
2813
+ }
2814
+ if (toIndex === found.clipIndex)
2815
+ return ok(editorial);
2816
+ const [moved] = clips.splice(found.clipIndex, 1);
2817
+ clips.splice(toIndex, 0, moved);
2818
+ return ok(editorial);
2819
+ }
2820
+ function duplicateEditClip(document, clipId, input = {}) {
2821
+ const editorial = requireEditorial(document);
2822
+ if (!("sequences" in editorial))
2823
+ return editorial;
2824
+ const found = findEditClip(editorial, clipId);
2825
+ if (!found)
2826
+ return fail("missing-clip");
2827
+ const source = found.sequence.clips[found.clipIndex];
2828
+ const index = input.index ?? found.clipIndex + 1;
2829
+ if (!Number.isInteger(index) || index < 0 || index > found.sequence.clips.length) {
2830
+ return fail("invalid-index");
2831
+ }
2832
+ const copy = {
2833
+ ...JSON.parse(JSON.stringify(source)),
2834
+ id: allocateClipId(editorial)
2835
+ };
2836
+ found.sequence.clips.splice(index, 0, copy);
2837
+ return ok(editorial, copy.id);
2838
+ }
2839
+ function deleteEditClip(document, clipId) {
2840
+ const editorial = requireEditorial(document);
2841
+ if (!("sequences" in editorial))
2842
+ return editorial;
2843
+ const found = findEditClip(editorial, clipId);
2844
+ if (!found)
2845
+ return fail("missing-clip");
2846
+ found.sequence.clips.splice(found.clipIndex, 1);
2847
+ return ok(editorial);
2848
+ }
2849
+ function defaultInsertRange(document, sourceFrame, spanFrames = 60) {
2850
+ const { frameStart, frameEnd } = document.content.timeline;
2851
+ const span = Math.max(1, Math.round(spanFrames));
2852
+ const preferredStart = Math.round(sourceFrame);
2853
+ const preferredEnd = preferredStart + span - 1;
2854
+ if (preferredStart >= frameStart && preferredEnd <= frameEnd) {
2855
+ return { sourceFrameStart: preferredStart, sourceFrameEnd: preferredEnd };
2856
+ }
2857
+ if (preferredEnd > frameEnd) {
2858
+ return {
2859
+ sourceFrameStart: Math.max(frameStart, frameEnd - span + 1),
2860
+ sourceFrameEnd: frameEnd
2861
+ };
2862
+ }
2863
+ return {
2864
+ sourceFrameStart: frameStart,
2865
+ sourceFrameEnd: Math.min(frameStart + span - 1, frameEnd)
2866
+ };
2867
+ }
2868
+
2869
+ // src/evaluate/timecode.ts
2870
+ function safeFps(fps) {
2871
+ return Math.max(1, Math.round(fps));
2872
+ }
2873
+ function framesToSeconds(frame, fps) {
2874
+ return frame / safeFps(fps);
2875
+ }
2876
+ function secondsToFrame(seconds, fps) {
2877
+ return Math.round(seconds * safeFps(fps));
2878
+ }
2879
+ function formatTimecode(frame, fps) {
2880
+ const rate = safeFps(fps);
2881
+ const total = Math.max(0, Math.round(frame));
2882
+ const ff = total % rate;
2883
+ const seconds = Math.floor(total / rate);
2884
+ const ss = seconds % 60;
2885
+ const mm = Math.floor(seconds / 60) % 60;
2886
+ const hh = Math.floor(seconds / 3600);
2887
+ return `${pad2(hh)}:${pad2(mm)}:${pad2(ss)}:${pad2(ff)}`;
2888
+ }
2889
+ function parseTimecode(value, fps) {
2890
+ const match = value.trim().match(/^(\d+):(\d{2}):(\d{2}):(\d{2})$/);
2891
+ if (!match)
2892
+ return null;
2893
+ const rate = safeFps(fps);
2894
+ const hh = Number(match[1]);
2895
+ const mm = Number(match[2]);
2896
+ const ss = Number(match[3]);
2897
+ const ff = Number(match[4]);
2898
+ if (mm > 59 || ss > 59 || ff >= rate)
2899
+ return null;
2900
+ return ((hh * 60 + mm) * 60 + ss) * rate + ff;
2901
+ }
2902
+ function pad2(value) {
2903
+ return String(value).padStart(2, "0");
2904
+ }
2905
+
2906
+ // src/contract/skyColor.ts
2907
+ var DEFAULT_SKY_COLOR = "#1e1e1e";
2908
+ var LEGACY_DEFAULT_SKY_COLOR = "#060608";
2909
+ var HEX6 = /^#[0-9a-fA-F]{6}$/;
2910
+ var HEX3 = /^#[0-9a-fA-F]{3}$/;
2911
+ function expandHex3(raw) {
2912
+ const r = raw[1];
2913
+ const g = raw[2];
2914
+ const b = raw[3];
2915
+ return `#${r}${r}${g}${g}${b}${b}`;
2916
+ }
2917
+ function normalizeSkyHex(value) {
2918
+ if (typeof value !== "string")
2919
+ return null;
2920
+ const raw = value.trim();
2921
+ if (HEX6.test(raw))
2922
+ return raw;
2923
+ if (HEX3.test(raw))
2924
+ return expandHex3(raw);
2925
+ return null;
2926
+ }
2927
+ function isLeakedBrowserSkyColor(value) {
2928
+ const raw = value.trim().toLowerCase();
2929
+ return raw === "#0000ff" || raw === "#00f" || raw === "blue";
2930
+ }
2931
+ function isEmptyDirectorGenesis(nodes) {
2932
+ if (!Array.isArray(nodes))
2933
+ return true;
2934
+ return nodes.length <= 1;
2935
+ }
2936
+ function resolveSkyColor(value, emptyGenesis) {
2937
+ const normalized = normalizeSkyHex(value);
2938
+ if (!normalized || normalized.toLowerCase() === LEGACY_DEFAULT_SKY_COLOR)
2939
+ return DEFAULT_SKY_COLOR;
2940
+ if (emptyGenesis && isLeakedBrowserSkyColor(value ?? ""))
2941
+ return DEFAULT_SKY_COLOR;
2942
+ return normalized;
2943
+ }
2944
+ function hydrateSkyColor(doc) {
2945
+ const background = doc.content.environment?.background;
2946
+ if (!background)
2947
+ return doc;
2948
+ const next = resolveSkyColor(background.skyColor, isEmptyDirectorGenesis(doc.content.nodes));
2949
+ if (background.skyColor === next)
2950
+ return doc;
2951
+ background.skyColor = next;
2952
+ return doc;
2953
+ }
2954
+
2955
+ // src/contract/validate.ts
2956
+ var vec3Schema = z.object({
2957
+ x: z.number(),
2958
+ y: z.number(),
2959
+ z: z.number()
2960
+ });
2961
+ var transformSchema = z.object({
2962
+ position: vec3Schema,
2963
+ rotation: vec3Schema,
2964
+ scale: vec3Schema
2965
+ });
2966
+ var nodeRefSchema = z.object({
2967
+ type: z.string(),
2968
+ nodeId: z.string()
2969
+ }).passthrough();
2970
+ var playbackSchema = z.object({
2971
+ version: z.number(),
2972
+ speed: z.number(),
2973
+ loop: z.boolean(),
2974
+ loopMode: z.string(),
2975
+ baseDurationFrames: z.number().optional()
2976
+ }).passthrough();
2977
+ var curveKeyframeSchema = z.object({
2978
+ id: z.string(),
2979
+ time: z.number(),
2980
+ value: z.number(),
2981
+ interpolation: z.enum(["linear", "bezier"])
2982
+ }).passthrough();
2983
+ var bakedCurveSchema = z.object({
2984
+ id: z.string(),
2985
+ group: z.enum(["position", "lookAt", "lens", "rotation"]),
2986
+ dataPath: z.string(),
2987
+ arrayIndex: z.number(),
2988
+ extrapolation: z.string(),
2989
+ keyframes: z.array(curveKeyframeSchema)
2990
+ }).passthrough();
2991
+ var cameraMotionClipSchema = z.object({
2992
+ id: z.string(),
2993
+ target: nodeRefSchema,
2994
+ focusTarget: z.object({ type: z.string() }).passthrough().optional(),
2995
+ frameStart: z.number(),
2996
+ frameEnd: z.number(),
2997
+ trimStartMs: z.number(),
2998
+ trimEndMs: z.number(),
2999
+ playback: playbackSchema,
3000
+ motion: z.object({
3001
+ id: z.string(),
3002
+ version: z.number(),
3003
+ presetId: z.string(),
3004
+ label: z.string(),
3005
+ timeUnit: z.string(),
3006
+ durationMs: z.number(),
3007
+ metadata: z.unknown().optional(),
3008
+ source: z.unknown().optional(),
3009
+ warnings: z.array(z.string()).nullable().optional(),
3010
+ curves: z.array(bakedCurveSchema)
3011
+ }).passthrough()
3012
+ }).passthrough();
3013
+ var motionClipSchema = z.object({
3014
+ id: z.string(),
3015
+ source: z.string(),
3016
+ sourceDuration: z.number(),
3017
+ frameStart: z.number(),
3018
+ frameEnd: z.number(),
3019
+ target: nodeRefSchema,
3020
+ playback: playbackSchema,
3021
+ motion: z.object({
3022
+ assetId: z.string(),
3023
+ name: z.string(),
3024
+ source: z.string(),
3025
+ sourceRig: z.string(),
3026
+ url: z.string(),
3027
+ inPlace: z.boolean(),
3028
+ loop: z.boolean(),
3029
+ speed: z.number(),
3030
+ time: z.number()
3031
+ }).passthrough()
3032
+ }).passthrough();
3033
+ var pathMotionClipSchema = z.object({
3034
+ id: z.string(),
3035
+ status: z.string(),
3036
+ locked: z.boolean(),
3037
+ lockedReason: z.string().optional(),
3038
+ source: z.string(),
3039
+ target: nodeRefSchema,
3040
+ pathNodeId: z.string(),
3041
+ pathName: z.string(),
3042
+ pathLength: z.number(),
3043
+ pathStartPercent: z.number(),
3044
+ pathEndPercent: z.number(),
3045
+ direction: z.string(),
3046
+ facing: z.string(),
3047
+ frameStart: z.number(),
3048
+ frameEnd: z.number(),
3049
+ playback: playbackSchema,
3050
+ derivedSource: z.unknown().optional()
3051
+ }).passthrough();
3052
+ var editSequenceClipSchema = z.object({
3053
+ id: z.string().min(1),
3054
+ cameraNodeId: z.string().min(1),
3055
+ sourceFrameStart: z.number().int(),
3056
+ sourceFrameEnd: z.number().int()
3057
+ }).passthrough().refine(
3058
+ (clip) => clip.sourceFrameEnd >= clip.sourceFrameStart,
3059
+ { message: "sourceFrameEnd must be >= sourceFrameStart", path: ["sourceFrameEnd"] }
3060
+ );
3061
+ var editSequenceSchema = z.object({
3062
+ id: z.string().min(1),
3063
+ name: z.string().refine((name) => name.trim().length > 0, {
3064
+ message: "name must not be blank"
3065
+ }).optional(),
3066
+ clips: z.array(editSequenceClipSchema)
3067
+ }).passthrough();
3068
+ var editorialSchema = z.object({
3069
+ version: z.literal(1),
3070
+ activeSequenceId: z.string().min(1),
3071
+ sequences: z.array(editSequenceSchema).min(1)
3072
+ }).passthrough();
3073
+ var timelineSchema = z.object({
3074
+ version: z.number(),
3075
+ fps: z.number(),
3076
+ frameStart: z.number(),
3077
+ frameEnd: z.number(),
3078
+ usePreviewRange: z.boolean(),
3079
+ animation: z.object({
3080
+ fcurves: z.array(z.unknown()),
3081
+ fcurvesRef: z.unknown().optional(),
3082
+ cameraMotionClips: z.array(cameraMotionClipSchema),
3083
+ motionTransitions: z.array(z.unknown()).optional(),
3084
+ motionClips: z.array(motionClipSchema),
3085
+ pathMotionClips: z.array(pathMotionClipSchema)
3086
+ }).passthrough()
3087
+ }).passthrough();
3088
+ var draftNodeSchema = z.object({
3089
+ id: z.string(),
3090
+ type: z.enum(["camera", "character", "prop", "path", "group", "primitive"]),
3091
+ name: z.string(),
3092
+ visible: z.boolean(),
3093
+ locked: z.boolean(),
3094
+ transform: transformSchema,
3095
+ metadata: z.unknown().optional(),
3096
+ parentId: z.string().optional(),
3097
+ children: z.array(z.string()).optional(),
3098
+ group: z.object({
3099
+ appearance: z.object({ color: z.string() }).passthrough(),
3100
+ kind: z.string(),
3101
+ label: z.object({
3102
+ showLabel: z.boolean(),
3103
+ scale: z.number(),
3104
+ yOffset: z.number()
3105
+ }).passthrough().optional(),
3106
+ layout: z.record(z.unknown()).optional()
3107
+ }).passthrough().optional(),
3108
+ primitive: z.object({
3109
+ kind: z.string(),
3110
+ parameters: z.record(z.unknown()),
3111
+ appearance: z.object({ color: z.string() }).passthrough().optional(),
3112
+ label: z.object({ showLabel: z.boolean() }).passthrough().optional()
3113
+ }).passthrough().optional(),
3114
+ camera: z.object({
3115
+ projection: z.string(),
3116
+ fov: z.number(),
3117
+ fovAxis: z.string(),
3118
+ near: z.number(),
3119
+ far: z.number(),
3120
+ isPrimary: z.boolean(),
3121
+ lookAt: vec3Schema
3122
+ }).passthrough().optional(),
3123
+ character: z.object({
3124
+ placeholder: z.boolean(),
3125
+ gender: z.string(),
3126
+ motionId: z.string().nullable().optional(),
3127
+ appearance: z.object({ color: z.string() }).passthrough(),
3128
+ label: z.object({
3129
+ showLabel: z.boolean(),
3130
+ scale: z.number(),
3131
+ yOffset: z.number()
3132
+ }).passthrough(),
3133
+ animation: z.object({
3134
+ mode: z.string(),
3135
+ controlValues: z.record(z.number())
3136
+ }).passthrough()
3137
+ }).passthrough().optional(),
3138
+ prop: z.object({
3139
+ category: z.string(),
3140
+ appearance: z.object({ color: z.string() }).passthrough().optional(),
3141
+ label: z.object({ showLabel: z.boolean() }).passthrough().optional()
3142
+ }).passthrough().optional(),
3143
+ path: z.object({
3144
+ source: z.string(),
3145
+ curve: z.string(),
3146
+ closed: z.boolean(),
3147
+ groundSnap: z.boolean(),
3148
+ parameterization: z.string(),
3149
+ smoothing: z.number(),
3150
+ points: z.array(z.object({
3151
+ id: z.string(),
3152
+ position: vec3Schema,
3153
+ timeRatio: z.number().optional()
3154
+ }).passthrough())
3155
+ }).passthrough().optional()
3156
+ }).passthrough();
3157
+ var draftContentSchema = z.object({
3158
+ version: z.number(),
3159
+ aspectRatio: z.string(),
3160
+ activeShotCameraNodeId: z.string(),
3161
+ generate: z.string().optional(),
3162
+ scenePlan: z.unknown().optional(),
3163
+ settings: z.unknown().optional(),
3164
+ environment: z.object({
3165
+ background: z.object({
3166
+ mode: z.string(),
3167
+ skyColor: z.string()
3168
+ }).passthrough(),
3169
+ display: z.object({
3170
+ characterLabelsVisible: z.boolean(),
3171
+ groundVisible: z.boolean(),
3172
+ groundHeight: z.number(),
3173
+ groundOpacity: z.number()
3174
+ }).passthrough(),
3175
+ sphere: z.unknown().optional(),
3176
+ transform: transformSchema.optional()
3177
+ }).passthrough(),
3178
+ asset: z.object({
3179
+ motionPath: z.array(z.object({
3180
+ id: z.string(),
3181
+ path: z.string()
3182
+ }).passthrough())
3183
+ }).passthrough(),
3184
+ nodes: z.array(draftNodeSchema),
3185
+ physicalConstraints: z.array(z.unknown()),
3186
+ timeline: timelineSchema,
3187
+ editorial: editorialSchema.optional()
3188
+ }).passthrough();
3189
+ var directorDocumentSchema = z.object({
3190
+ type: z.string(),
3191
+ pippitAssetId: z.string(),
3192
+ extra: z.unknown().optional(),
3193
+ content: draftContentSchema
3194
+ }).passthrough();
3195
+ function parseDirectorDocument(json) {
3196
+ return hydrateSkyColor(directorDocumentSchema.parse(json));
3197
+ }
3198
+ var fcurveHandleSchema = z.object({
3199
+ x: z.number(),
3200
+ y: z.number()
3201
+ }).passthrough();
3202
+ var compactKeyframeSchema = z.array(
3203
+ z.union([z.number(), z.string(), fcurveHandleSchema, z.null()])
3204
+ ).min(3);
3205
+ var compactCurveSchema = z.object({
3206
+ id: z.string(),
3207
+ t: z.union([z.tuple([z.string(), z.string()]), z.string()]),
3208
+ p: z.string(),
3209
+ i: z.number().optional(),
3210
+ k: z.array(compactKeyframeSchema)
3211
+ }).passthrough();
3212
+ var fcurvesCompactV1Schema = z.object({
3213
+ version: z.literal(1),
3214
+ encoding: z.literal("compact-v1"),
3215
+ fcurves: z.array(compactCurveSchema)
3216
+ }).passthrough();
3217
+
3218
+ // src/contract/aspectRatio.ts
3219
+ var AUTO_ASPECT_RATIO = "auto";
3220
+ var DEFAULT_ASPECT_RATIO = AUTO_ASPECT_RATIO;
3221
+
3222
+ // src/contract/emptyDraft.ts
3223
+ function makeEmptyDraft(name, fps, totalFrames, camera) {
3224
+ const nodes = [];
3225
+ if (camera) {
3226
+ nodes.push({
3227
+ id: "camera_1",
3228
+ type: "camera",
3229
+ name: "Main Camera",
3230
+ visible: true,
3231
+ locked: false,
3232
+ transform: {
3233
+ position: { ...camera.position },
3234
+ rotation: { ...camera.rotation },
3235
+ scale: { x: 1, y: 1, z: 1 }
3236
+ },
3237
+ camera: {
3238
+ projection: "perspective",
3239
+ fov: camera.fov,
3240
+ fovAxis: "vertical",
3241
+ near: 0.1,
3242
+ far: 2e3,
3243
+ isPrimary: true,
3244
+ lookAt: { ...camera.lookAt }
3245
+ }
3246
+ });
3247
+ }
3248
+ return {
3249
+ type: "biz/scene3d-director-document",
3250
+ pippitAssetId: "",
3251
+ extra: { customDraftName: name },
3252
+ content: {
3253
+ version: 1,
3254
+ aspectRatio: DEFAULT_ASPECT_RATIO,
3255
+ activeShotCameraNodeId: camera ? "camera_1" : "",
3256
+ environment: {
3257
+ background: { mode: "color", skyColor: DEFAULT_SKY_COLOR },
3258
+ display: {
3259
+ characterLabelsVisible: true,
3260
+ groundVisible: true,
3261
+ groundHeight: 0,
3262
+ groundOpacity: 1
3263
+ }
3264
+ },
3265
+ asset: { motionPath: [] },
3266
+ nodes,
3267
+ physicalConstraints: [],
3268
+ editorial: {
3269
+ version: 1,
3270
+ activeSequenceId: "sequence_1",
3271
+ sequences: [{ id: "sequence_1", clips: [] }]
3272
+ },
3273
+ timeline: {
3274
+ version: 1,
3275
+ fps,
3276
+ frameStart: 0,
3277
+ frameEnd: Math.max(1, Math.round(totalFrames)),
3278
+ usePreviewRange: false,
3279
+ animation: {
3280
+ fcurves: [],
3281
+ cameraMotionClips: [],
3282
+ motionTransitions: [],
3283
+ motionClips: [],
3284
+ pathMotionClips: []
3285
+ }
3286
+ }
3287
+ }
3288
+ };
3289
+ }
3290
+
3291
+ // src/data/defaultCharacter.ts
3292
+ var DEFAULT_CHARACTER = {
3293
+ name: "Female",
3294
+ gender: "female",
3295
+ color: "#bababa",
3296
+ posePresetId: "stand"
3297
+ };
3298
+ var INITIAL_DRAFT_SEED = "default-character-v1";
3299
+
3300
+ // src/data/cameraLibrary.ts
3301
+ var CAMERA_PRESETS = [{ "id": "current", "name": "\u5F53\u524D\u89C6\u89D2", "fov": 50, "position": { "x": 0, "y": 1.6246041243470328, "z": 4.246041243470324 }, "rotation": { "x": 0, "y": 0, "z": 0 }, "lookAt": { "x": 0, "y": 1.2, "z": 0 } }, { "id": "front-medium", "name": "\u6B63\u9762\u4E2D\u666F", "fov": 50, "position": { "x": 0, "y": 1.8, "z": 5 }, "rotation": { "x": -8, "y": 0, "z": 0 }, "lookAt": { "x": 0, "y": 1.2, "z": 0 } }, { "id": "front-close", "name": "\u6B63\u9762\u7279\u5199", "fov": 35, "position": { "x": 0, "y": 1.6, "z": 2.6 }, "rotation": { "x": -6, "y": 0, "z": 0 }, "lookAt": { "x": 0, "y": 1.35, "z": 0 } }, { "id": "front-wide", "name": "\u6B63\u9762\u5168\u666F", "fov": 70, "position": { "x": 0, "y": 2.4, "z": 7.5 }, "rotation": { "x": -14, "y": 0, "z": 0 }, "lookAt": { "x": 0, "y": 1.1, "z": 0 } }, { "id": "side-follow", "name": "\u4FA7\u9762\u8DDF\u62CD", "fov": 50, "position": { "x": 4.8, "y": 1.8, "z": 0.8 }, "rotation": { "x": -8, "y": 78, "z": 0 }, "lookAt": { "x": 0, "y": 1.2, "z": 0 } }, { "id": "side-close", "name": "\u4FA7\u9762\u8FD1\u666F", "fov": 42, "position": { "x": 3, "y": 1.7, "z": 0.4 }, "rotation": { "x": -7, "y": 85, "z": 0 }, "lookAt": { "x": 0, "y": 1.25, "z": 0 } }, { "id": "back-medium", "name": "\u80CC\u9762\u4E2D\u666F", "fov": 50, "position": { "x": 0, "y": 1.8, "z": -5 }, "rotation": { "x": -8, "y": 180, "z": 0 }, "lookAt": { "x": 0, "y": 1.2, "z": 0 } }, { "id": "top-wide", "name": "\u4FEF\u62CD\u5168\u666F", "fov": 70, "position": { "x": 0, "y": 6.5, "z": 4.2 }, "rotation": { "x": -58, "y": 0, "z": 0 }, "lookAt": { "x": 0, "y": 1, "z": 0 } }, { "id": "forty-five", "name": "45\xB0 \u4FEF\u62CD", "fov": 50, "position": { "x": 4.2, "y": 4.6, "z": 4.2 }, "rotation": { "x": -40, "y": 40, "z": 0 }, "lookAt": { "x": 0, "y": 1.2, "z": 0 } }, { "id": "low-angle", "name": "\u4F4E\u89D2\u5EA6\u4EF0\u62CD", "fov": 45, "position": { "x": 0, "y": 0.55, "z": 3.2 }, "rotation": { "x": 16, "y": 0, "z": 0 }, "lookAt": { "x": 0, "y": 1.7, "z": 0 } }, { "id": "low-wide", "name": "\u4F4E\u89D2\u5EA6\u5E7F\u89D2", "fov": 90, "position": { "x": 0, "y": 0.45, "z": 2.8 }, "rotation": { "x": 18, "y": 0, "z": 0 }, "lookAt": { "x": 0, "y": 1.6, "z": 0 } }, { "id": "over-shoulder", "name": "\u8FC7\u80A9\u955C\u5934", "fov": 45, "position": { "x": -1.4, "y": 1.6, "z": 2.8 }, "rotation": { "x": -6, "y": -22, "z": 0 }, "lookAt": { "x": 0.4, "y": 1.35, "z": 0 } }, { "id": "over-shoulder-right", "name": "\u8FC7\u80A9\u955C\u5934\uFF08\u53F3\uFF09", "fov": 45, "position": { "x": 1.4, "y": 1.6, "z": 2.8 }, "rotation": { "x": -6, "y": 22, "z": 0 }, "lookAt": { "x": -0.4, "y": 1.35, "z": 0 } }, { "id": "bird-eye", "name": "\u9E1F\u77B0", "fov": 80, "position": { "x": 0, "y": 8, "z": 0.2 }, "rotation": { "x": -88, "y": 0, "z": 0 }, "lookAt": { "x": 0, "y": 0, "z": 0 } }, { "id": "dutch", "name": "\u8377\u5170\u89D2", "fov": 50, "position": { "x": 2.8, "y": 1.7, "z": 4.2 }, "rotation": { "x": -10, "y": 32, "z": 12 }, "lookAt": { "x": 0, "y": 1.2, "z": 0 } }];
3302
+ var CAMERA_MOTIONS = [{ "id": "static_shot", "name": "\u56FA\u5B9A\u955C\u5934", "subtitle": "\u673A\u4F4D\u548C\u65B9\u5411\u4E0D\u53D8", "description": "\u8FD0\u955C\uFF1A\u56FA\u5B9A\u673A\u4F4D\uFF0C\u955C\u5934\u4F4D\u7F6E\u548C\u62CD\u6444\u65B9\u5411\u90FD\u4FDD\u6301\u4E0D\u53D8\uFF0C\u753B\u9762\u6784\u56FE\u7A33\u5B9A\uFF0C\u7531\u4E3B\u4F53\u52A8\u4F5C\u6216\u753B\u9762\u5185\u90E8\u53D8\u5316\u5F62\u6210\u8FD0\u52A8\u611F\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6 }, "recipe": { "type": "static" } }, { "id": "tilt_up", "name": "\u955C\u5934\u4E0A\u6447", "subtitle": "\u56FA\u5B9A\u673A\u4F4D\u5411\u4E0A\u8F6C\u52A8", "description": "\u8FD0\u955C\uFF1A\u56FA\u5B9A\u673A\u4F4D\u4E0A\u6447\uFF0C\u955C\u5934\u4F4D\u7F6E\u4E0D\u79FB\u52A8\uFF0C\u53EA\u6CBF\u5782\u76F4\u65B9\u5411\u7F13\u6162\u5411\u4E0A\u8F6C\u52A8\uFF0C\u753B\u9762\u91CD\u5FC3\u4ECE\u4E0B\u65B9\u9010\u6B65\u79FB\u5411\u4E0A\u65B9\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "angleDeg": 12 }, "recipe": { "type": "pan-tilt", "pitchDeg": 12 } }, { "id": "tilt_down", "name": "\u955C\u5934\u4E0B\u6447", "subtitle": "\u56FA\u5B9A\u673A\u4F4D\u5411\u4E0B\u8F6C\u52A8", "description": "\u8FD0\u955C\uFF1A\u56FA\u5B9A\u673A\u4F4D\u4E0B\u6447\uFF0C\u955C\u5934\u4F4D\u7F6E\u4E0D\u79FB\u52A8\uFF0C\u53EA\u6CBF\u5782\u76F4\u65B9\u5411\u7F13\u6162\u5411\u4E0B\u8F6C\u52A8\uFF0C\u753B\u9762\u91CD\u5FC3\u4ECE\u4E0A\u65B9\u9010\u6B65\u79FB\u5411\u4E0B\u65B9\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "angleDeg": 12 }, "recipe": { "type": "pan-tilt", "pitchDeg": -12 } }, { "id": "pan_left", "name": "\u955C\u5934\u5DE6\u6447", "subtitle": "\u56FA\u5B9A\u673A\u4F4D\u5411\u5DE6\u8F6C\u52A8", "description": "\u8FD0\u955C\uFF1A\u56FA\u5B9A\u673A\u4F4D\u5DE6\u6447\uFF0C\u955C\u5934\u4F4D\u7F6E\u4E0D\u79FB\u52A8\uFF0C\u53EA\u6CBF\u6C34\u5E73\u65B9\u5411\u7F13\u6162\u5411\u5DE6\u8F6C\u52A8\uFF0C\u8BA9\u753B\u9762\u5185\u5BB9\u4ECE\u53F3\u5411\u5DE6\u8FDE\u7EED\u53D8\u5316\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "angleDeg": 12 }, "recipe": { "type": "pan-tilt", "yawDeg": 12 } }, { "id": "pan_right", "name": "\u955C\u5934\u53F3\u6447", "subtitle": "\u56FA\u5B9A\u673A\u4F4D\u5411\u53F3\u8F6C\u52A8", "description": "\u8FD0\u955C\uFF1A\u56FA\u5B9A\u673A\u4F4D\u53F3\u6447\uFF0C\u955C\u5934\u4F4D\u7F6E\u4E0D\u79FB\u52A8\uFF0C\u53EA\u6CBF\u6C34\u5E73\u65B9\u5411\u7F13\u6162\u5411\u53F3\u8F6C\u52A8\uFF0C\u8BA9\u753B\u9762\u5185\u5BB9\u4ECE\u5DE6\u5411\u53F3\u8FDE\u7EED\u53D8\u5316\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "angleDeg": 12 }, "recipe": { "type": "pan-tilt", "yawDeg": -12 } }, { "id": "crane_up", "name": "\u955C\u5934\u4E0A\u5347", "subtitle": "\u5782\u76F4\u4E0A\u5347\u5E76\u4FDD\u6301\u65B9\u5411", "description": "\u8FD0\u955C\uFF1A\u955C\u5934\u5782\u76F4\u4E0A\u5347\uFF0C\u62CD\u6444\u65B9\u5411\u57FA\u672C\u4FDD\u6301\u7A33\u5B9A\uFF0C\u673A\u4F4D\u9AD8\u5EA6\u4ECE\u4F4E\u5230\u9AD8\u53D8\u5316\uFF0C\u753B\u9762\u89C6\u89D2\u968F\u9AD8\u5EA6\u4EA7\u751F\u53D8\u5316\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "distance": 0.8 }, "recipe": { "type": "translate", "localOffset": { "x": 0, "y": 1, "z": 0 }, "targetSpace": "world" } }, { "id": "crane_down", "name": "\u955C\u5934\u4E0B\u964D", "subtitle": "\u5782\u76F4\u4E0B\u964D\u5E76\u4FDD\u6301\u65B9\u5411", "description": "\u8FD0\u955C\uFF1A\u955C\u5934\u5782\u76F4\u4E0B\u964D\uFF0C\u62CD\u6444\u65B9\u5411\u57FA\u672C\u4FDD\u6301\u7A33\u5B9A\uFF0C\u673A\u4F4D\u9AD8\u5EA6\u4ECE\u9AD8\u5230\u4F4E\u53D8\u5316\uFF0C\u753B\u9762\u89C6\u89D2\u968F\u9AD8\u5EA6\u4EA7\u751F\u53D8\u5316\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "distance": 0.8 }, "recipe": { "type": "translate", "localOffset": { "x": 0, "y": -1, "z": 0 }, "targetSpace": "world" } }, { "id": "truck_left", "name": "\u955C\u5934\u5DE6\u79FB", "subtitle": "\u6A2A\u5411\u5DE6\u79FB\u5E76\u4FDD\u6301\u65B9\u5411", "description": "\u8FD0\u955C\uFF1A\u955C\u5934\u5DE6\u79FB\uFF0C\u673A\u4F4D\u6574\u4F53\u5411\u5DE6\u6A2A\u5411\u5E73\u79FB\uFF0C\u62CD\u6444\u65B9\u5411\u57FA\u672C\u4E0D\u53D8\uFF0C\u524D\u666F\u4E0E\u80CC\u666F\u5F62\u6210\u81EA\u7136\u89C6\u5DEE\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "distance": 0.8 }, "recipe": { "type": "translate", "localOffset": { "x": -1, "y": 0, "z": 0 } } }, { "id": "truck_right", "name": "\u955C\u5934\u53F3\u79FB", "subtitle": "\u6A2A\u5411\u53F3\u79FB\u5E76\u4FDD\u6301\u65B9\u5411", "description": "\u8FD0\u955C\uFF1A\u955C\u5934\u53F3\u79FB\uFF0C\u673A\u4F4D\u6574\u4F53\u5411\u53F3\u6A2A\u5411\u5E73\u79FB\uFF0C\u62CD\u6444\u65B9\u5411\u57FA\u672C\u4E0D\u53D8\uFF0C\u524D\u666F\u4E0E\u80CC\u666F\u5F62\u6210\u81EA\u7136\u89C6\u5DEE\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "distance": 0.8 }, "recipe": { "type": "translate", "localOffset": { "x": 1, "y": 0, "z": 0 } } }, { "id": "dolly_in", "name": "\u955C\u5934\u524D\u63A8", "subtitle": "\u6CBF\u955C\u5934\u65B9\u5411\u524D\u63A8", "description": "\u8FD0\u955C\uFF1A\u955C\u5934\u7F13\u6162\u524D\u63A8\uFF0C\u673A\u4F4D\u6CBF\u955C\u5934\u671D\u5411\u5411\u524D\u79FB\u52A8\uFF0C\u4E3B\u4F53\u5728\u753B\u9762\u4E2D\u9010\u6E10\u53D8\u5927\uFF0C\u7A7A\u95F4\u5C42\u6B21\u4EA7\u751F\u81EA\u7136\u89C6\u5DEE\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "distance": 0.8 }, "recipe": { "type": "translate", "localOffset": { "x": 0, "y": 0, "z": 1 }, "lockTarget": true } }, { "id": "dolly_out", "name": "\u955C\u5934\u540E\u79FB", "subtitle": "\u6CBF\u955C\u5934\u65B9\u5411\u540E\u62C9", "description": "\u8FD0\u955C\uFF1A\u955C\u5934\u7F13\u6162\u540E\u62C9\uFF0C\u673A\u4F4D\u6CBF\u955C\u5934\u671D\u5411\u5411\u540E\u79FB\u52A8\uFF0C\u4E3B\u4F53\u5728\u753B\u9762\u4E2D\u9010\u6E10\u53D8\u5C0F\uFF0C\u753B\u9762\u8303\u56F4\u9010\u6B65\u53D8\u5BBD\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "distance": 0.8 }, "recipe": { "type": "translate", "localOffset": { "x": 0, "y": 0, "z": -1 }, "lockTarget": true } }, { "id": "zoom_in", "name": "\u53D8\u7126\u63A8\u8FDB", "subtitle": "\u673A\u4F4D\u4E0D\u52A8\u753B\u9762\u6536\u7D27", "description": "\u8FD0\u955C\uFF1A\u7F13\u6162\u53D8\u7126\u63A8\u8FDB\uFF0C\u673A\u4F4D\u4FDD\u6301\u4E0D\u52A8\uFF0C\u53EA\u901A\u8FC7\u7126\u8DDD\u53D8\u5316\u8BA9\u753B\u9762\u8303\u56F4\u9010\u6E10\u6536\u7D27\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "fovDelta": -12 }, "recipe": { "type": "zoom", "fovDelta": -12 } }, { "id": "zoom_out", "name": "\u53D8\u7126\u62C9\u8FDC", "subtitle": "\u673A\u4F4D\u4E0D\u52A8\u753B\u9762\u653E\u5BBD", "description": "\u8FD0\u955C\uFF1A\u7F13\u6162\u53D8\u7126\u62C9\u8FDC\uFF0C\u673A\u4F4D\u4FDD\u6301\u4E0D\u52A8\uFF0C\u53EA\u901A\u8FC7\u7126\u8DDD\u53D8\u5316\u8BA9\u753B\u9762\u8303\u56F4\u9010\u6E10\u653E\u5BBD\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "fovDelta": 12 }, "recipe": { "type": "zoom", "fovDelta": 12 } }, { "id": "handheld_shot", "name": "\u624B\u6301\u62CD\u6444", "subtitle": "\u8F7B\u5FAE\u81EA\u7136\u6643\u52A8", "description": "\u8FD0\u955C\uFF1A\u624B\u6301\u62CD\u6444\uFF0C\u955C\u5934\u5E26\u8F7B\u5FAE\u81EA\u7136\u6643\u52A8\u548C\u8FD1\u8DDD\u79BB\u4E34\u573A\u611F\uFF0C\u53EF\u968F\u4E3B\u4F53\u52A8\u4F5C\u4EA7\u751F\u5C0F\u5E45\u8C03\u6574\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "keyframeCount": 12, "shakeAmplitude": 0.04 }, "recipe": { "type": "shake", "amplitude": 0.04, "frequency": 3 } }, { "id": "slider_reveal", "name": "\u6A2A\u6ED1\u63ED\u793A", "subtitle": "\u6A2A\u5411\u6ED1\u51FA\u906E\u6321", "description": "\u8FD0\u955C\uFF1A\u6A2A\u6ED1\u63ED\u793A\uFF0C\u955C\u5934\u4ECE\u906E\u6321\u5173\u7CFB\u4E2D\u6A2A\u5411\u6ED1\u51FA\uFF0C\u9010\u6B65\u9732\u51FA\u88AB\u906E\u6321\u7684\u4E3B\u4F53\u6216\u5173\u952E\u4FE1\u606F\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6, "distance": 1 }, "recipe": { "type": "path", "pathKind": "slider-reveal" } }, { "id": "foreground_wipe", "name": "\u524D\u666F\u64E6\u8FC7", "subtitle": "\u8FD1\u666F\u906E\u6321\u64E6\u8FC7\u753B\u9762", "description": "\u8FD0\u955C\uFF1A\u524D\u666F\u64E6\u8FC7\uFF0C\u8FD1\u5904\u906E\u6321\u7269\u4ECE\u955C\u5934\u524D\u63A0\u8FC7\u5E76\u77ED\u6682\u8986\u76D6\u753B\u9762\uFF0C\u968F\u540E\u9732\u51FA\u65B0\u7684\u753B\u9762\u5185\u5BB9\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "required-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6 }, "recipe": { "type": "path", "pathKind": "foreground-wipe" } }, { "id": "push_through", "name": "\u7A7F\u8D8A\u955C\u5934", "subtitle": "\u5FEB\u901F\u524D\u63A8\u8FDB\u5165\u540E\u666F", "description": "\u8FD0\u955C\uFF1A\u7A7F\u8D8A\u955C\u5934\uFF0C\u955C\u5934\u6301\u7EED\u5FEB\u901F\u5411\u524D\u63A8\u8FDB\uFF0C\u5E76\u4EA7\u751F\u77ED\u6682\u8FD0\u52A8\u6A21\u7CCA\uFF0C\u7A7F\u8FC7\u660E\u786E\u7684\u524D\u666F\u8FB9\u754C\u6216\u7A7A\u95F4\u5C42\u6B21\uFF0C\u8FDB\u5165\u540E\u65B9\u753B\u9762\uFF0C\u6700\u540E\u843D\u5230\u65B0\u7684\u6784\u56FE\u6216\u76EE\u6807\u4E0A\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 1200, "easing": "ease-in", "strength": 0.6, "distance": 1.4 }, "recipe": { "type": "path", "pathKind": "push-through" } }, { "id": "whip_pan", "name": "\u7529\u6447", "subtitle": "\u5FEB\u901F\u6C34\u5E73\u7529\u5230\u65B0\u6784\u56FE", "description": "\u8FD0\u955C\uFF1A\u7529\u6447\uFF0C\u955C\u5934\u5FEB\u901F\u6C34\u5E73\u8F6C\u52A8\u5E76\u4EA7\u751F\u77ED\u6682\u8FD0\u52A8\u6A21\u7CCA\uFF0C\u6700\u540E\u843D\u5230\u65B0\u7684\u6784\u56FE\u6216\u76EE\u6807\u4E0A\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 700, "easing": "ease-out", "strength": 0.6, "angleDeg": 52 }, "recipe": { "type": "pan-tilt", "yawDeg": -52 } }, { "id": "rack_focus", "name": "\u7126\u70B9\u8F6C\u79FB", "subtitle": "\u7126\u70B9\u5728\u5C42\u6B21\u95F4\u5207\u6362", "description": "\u8FD0\u955C\uFF1A\u7126\u70B9\u8F6C\u79FB\uFF0C\u955C\u5934\u4F4D\u7F6E\u53EF\u4EE5\u4E0D\u53D8\uFF0C\u7126\u70B9\u4ECE\u4E00\u4E2A\u753B\u9762\u5C42\u6B21\u5E73\u6ED1\u5207\u6362\u5230\u53E6\u4E00\u4E2A\u753B\u9762\u5C42\u6B21\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "required-focus-target", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2e3, "easing": "ease-in-out", "strength": 0.6 }, "recipe": { "type": "focus-shift" } }, { "id": "crash_zoom", "name": "\u6025\u901F\u53D8\u7126", "subtitle": "\u5FEB\u901F\u6536\u7D27\u5230\u76EE\u6807\u533A\u57DF", "description": "\u8FD0\u955C\uFF1A\u6025\u901F\u53D8\u7126\u63A8\u8FDB\uFF0C\u673A\u4F4D\u4FDD\u6301\u4E0D\u52A8\uFF0C\u753B\u9762\u8303\u56F4\u5FEB\u901F\u6536\u7D27\u5230\u76EE\u6807\u533A\u57DF\uFF0C\u5F62\u6210\u7A81\u7136\u5F3A\u8C03\u3002", "categoryId": "basic", "categoryName": "\u57FA\u7840\u63A7\u5236", "targetPolicy": "none", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 700, "easing": "ease-out", "strength": 0.6, "fovDelta": -24 }, "recipe": { "type": "zoom", "fovDelta": -24 } }, { "id": "follow_tracking", "name": "\u8DDF\u968F\u62CD\u6444", "subtitle": "\u8DDF\u968F\u4E3B\u4F53\u4FDD\u6301\u6784\u56FE", "description": "\u8FD0\u955C\uFF1A\u8DDF\u968F\u62CD\u6444\uFF0C\u955C\u5934\u8DDF\u968F\u4E3B\u4F53\u8FD0\u52A8\u65B9\u5411\u79FB\u52A8\uFF0C\u4E0E\u4E3B\u4F53\u4FDD\u6301\u76F8\u5BF9\u7A33\u5B9A\u7684\u8DDD\u79BB\u548C\u6784\u56FE\u3002", "categoryId": "character", "categoryName": "\u4EBA\u7269\u8DDF\u62CD", "targetPolicy": "required-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2400, "easing": "ease-in-out", "strength": 0.6, "distance": 1, "keyframeCount": 8 }, "recipe": { "type": "path", "pathKind": "follow" } }, { "id": "leading_tracking", "name": "\u8FCE\u9762\u8DDF\u62CD", "subtitle": "\u4E3B\u4F53\u524D\u65B9\u540C\u6B65\u540E\u9000", "description": "\u8FD0\u955C\uFF1A\u8FCE\u9762\u8DDF\u62CD\uFF0C\u955C\u5934\u4F4D\u4E8E\u4E3B\u4F53\u524D\u65B9\u5E76\u968F\u4E3B\u4F53\u8FD0\u52A8\u540C\u6B65\u540E\u9000\uFF0C\u4FDD\u6301\u4E3B\u4F53\u6B63\u9762\u4FE1\u606F\u7A33\u5B9A\u53EF\u89C1\u3002", "categoryId": "character", "categoryName": "\u4EBA\u7269\u8DDF\u62CD", "targetPolicy": "required-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2400, "easing": "ease-in-out", "strength": 0.6, "distance": 1, "keyframeCount": 8 }, "recipe": { "type": "path", "pathKind": "leading" } }, { "id": "profile_tracking", "name": "\u4FA7\u9762\u8DDF\u62CD", "subtitle": "\u4E3B\u4F53\u4FA7\u65B9\u5E73\u884C\u79FB\u52A8", "description": "\u8FD0\u955C\uFF1A\u4FA7\u9762\u8DDF\u62CD\uFF0C\u955C\u5934\u5728\u4E3B\u4F53\u4FA7\u65B9\u4E0E\u5176\u5E73\u884C\u79FB\u52A8\uFF0C\u4FDD\u6301\u4FA7\u5411\u6784\u56FE\u7A33\u5B9A\uFF0C\u5E76\u5F62\u6210\u6A2A\u5411\u7A7A\u95F4\u89C6\u5DEE\u3002", "categoryId": "character", "categoryName": "\u4EBA\u7269\u8DDF\u62CD", "targetPolicy": "required-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2400, "easing": "ease-in-out", "strength": 0.6, "distance": 1, "keyframeCount": 8 }, "recipe": { "type": "path", "pathKind": "profile" } }, { "id": "orbit_180", "name": "\u73AF\u7ED5\u62CD\u6444", "subtitle": "\u56F4\u7ED5\u4E3B\u4F53\u65CB\u8F6C 180\xB0 \u6216 360\xB0", "description": "\u8FD0\u955C\uFF1A\u73AF\u7ED5\u62CD\u6444\uFF0C\u955C\u5934\u56F4\u7ED5\u4E3B\u4F53\u5E73\u7A33\u65CB\u8F6C\uFF0C\u652F\u6301 180\xB0 \u6216 360\xB0\uFF0C\u4E3B\u4F53\u4FDD\u6301\u5728\u753B\u9762\u6838\u5FC3\u4F4D\u7F6E\uFF0C\u80CC\u666F\u4EA7\u751F\u8FDE\u7EED\u65CB\u8F6C\u89C6\u5DEE\u3002", "categoryId": "character", "categoryName": "\u4EBA\u7269\u8DDF\u62CD", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2600, "easing": "ease-in-out", "strength": 0.6, "angleDeg": 180 }, "recipe": { "type": "orbit", "yawDeg": 180 } }, { "id": "ascending_orbit", "name": "\u76D8\u65CB\u62AC\u5347", "subtitle": "\u73AF\u7ED5\u4E3B\u4F53\u5E76\u9010\u6E10\u5347\u9AD8", "description": "\u8FD0\u955C\uFF1A\u76D8\u65CB\u62AC\u5347\uFF0C\u955C\u5934\u56F4\u7ED5\u4E3B\u4F53\u65CB\u8F6C\u7684\u540C\u65F6\u9010\u6E10\u5347\u9AD8\uFF0C\u4E3B\u4F53\u4FDD\u6301\u9501\u5B9A\uFF0C\u673A\u4F4D\u9AD8\u5EA6\u6301\u7EED\u53D8\u5316\u3002", "categoryId": "character", "categoryName": "\u4EBA\u7269\u8DDF\u62CD", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2600, "easing": "ease-in-out", "strength": 0.6, "angleDeg": 150, "height": 0.7 }, "recipe": { "type": "orbit", "yawDeg": 150, "heightDelta": 0.7 } }, { "id": "descending_orbit", "name": "\u76D8\u65CB\u4E0B\u964D", "subtitle": "\u73AF\u7ED5\u4E3B\u4F53\u5E76\u9010\u6E10\u964D\u4F4E", "description": "\u8FD0\u955C\uFF1A\u76D8\u65CB\u4E0B\u964D\uFF0C\u955C\u5934\u56F4\u7ED5\u4E3B\u4F53\u65CB\u8F6C\u7684\u540C\u65F6\u9010\u6E10\u964D\u4F4E\uFF0C\u4E3B\u4F53\u4FDD\u6301\u9501\u5B9A\uFF0C\u673A\u4F4D\u9AD8\u5EA6\u6301\u7EED\u53D8\u5316\u3002", "categoryId": "character", "categoryName": "\u4EBA\u7269\u8DDF\u62CD", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2600, "easing": "ease-in-out", "strength": 0.6, "angleDeg": 150, "height": -0.7 }, "recipe": { "type": "orbit", "yawDeg": 150, "heightDelta": -0.7 } }, { "id": "aerial_establishing", "name": "\u9AD8\u7A7A\u822A\u62CD", "subtitle": "\u9AD8\u89D2\u5EA6\u7A33\u5B9A\u6ED1\u884C", "description": "\u8FD0\u955C\uFF1A\u9AD8\u7A7A\u822A\u62CD\uFF0C\u955C\u5934\u4EE5\u9AD8\u89D2\u5EA6\u4FEF\u89C6\u753B\u9762\uFF0C\u5E76\u4FDD\u6301\u7A33\u5B9A\u6ED1\u884C\uFF0C\u7528\u4E8E\u5448\u73B0\u7A7A\u95F4\u5173\u7CFB\u548C\u6574\u4F53\u5E03\u5C40\u3002", "categoryId": "space", "categoryName": "\u7A7A\u95F4\u822A\u62CD", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2600, "easing": "ease-in-out", "strength": 0.6, "distance": 1.2, "height": 0.8, "fovDelta": 6 }, "recipe": { "type": "path", "pathKind": "aerial" } }, { "id": "fpv_flythrough", "name": "\u7A7F\u8D8A\u673A\u8FD0\u955C", "subtitle": "\u7B2C\u4E00\u89C6\u89D2\u9AD8\u901F\u7A7F\u884C", "description": "\u8FD0\u955C\uFF1A\u7A7F\u8D8A\u673A\u8FD0\u955C\uFF0C\u955C\u5934\u4EE5\u7B2C\u4E00\u89C6\u89D2\u9AD8\u901F\u7A7F\u884C\u7A7A\u95F4\uFF0C\u8D34\u8FD1\u573A\u666F\u8FB9\u754C\u548C\u4E3B\u4F53\u7075\u6D3B\u98DE\u884C\uFF0C\u5E26\u660E\u663E\u901F\u5EA6\u611F\u4E0E\u6EDA\u8F6C\u4FEF\u4EF0\u53D8\u5316\u3002", "categoryId": "space", "categoryName": "\u7A7A\u95F4\u822A\u62CD", "targetPolicy": "required-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2600, "easing": "ease-in-out", "strength": 0.6, "keyframeCount": 12 }, "recipe": { "type": "path", "pathKind": "fpv" } }, { "id": "drone_dive", "name": "\u4FEF\u51B2\u4E0B\u964D", "subtitle": "\u4ECE\u9AD8\u5904\u4FEF\u51B2\u9760\u8FD1", "description": "\u8FD0\u955C\uFF1A\u4FEF\u51B2\u4E0B\u964D\uFF0C\u955C\u5934\u4ECE\u9AD8\u5904\u5411\u76EE\u6807\u65B9\u5411\u5FEB\u901F\u4E0B\u964D\u5E76\u9760\u8FD1\uFF0C\u5F62\u6210\u660E\u663E\u901F\u5EA6\u611F\u548C\u538B\u8FEB\u611F\u3002", "categoryId": "space", "categoryName": "\u7A7A\u95F4\u822A\u62CD", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 1800, "easing": "ease-in-out", "strength": 0.6, "distance": 1.2, "height": -1 }, "recipe": { "type": "path", "pathKind": "dive" } }, { "id": "pull_away", "name": "\u62C9\u5347\u79BB\u573A", "subtitle": "\u5411\u540E\u5E76\u5411\u4E0A\u79BB\u573A", "description": "\u8FD0\u955C\uFF1A\u62C9\u5347\u79BB\u573A\uFF0C\u955C\u5934\u4ECE\u4E3B\u4F53\u9644\u8FD1\u5411\u540E\u5E76\u5411\u4E0A\u79FB\u52A8\uFF0C\u4E3B\u4F53\u5728\u753B\u9762\u4E2D\u9010\u6E10\u53D8\u5C0F\uFF0C\u753B\u9762\u8303\u56F4\u9010\u6B65\u6269\u5927\u3002", "categoryId": "space", "categoryName": "\u7A7A\u95F4\u822A\u62CD", "targetPolicy": "optional-look-at", "parameterSchema": { "durationMs": { "min": 400, "max": 6e3, "step": 100 }, "strength": { "min": 0, "max": 1, "step": 0.05 } }, "defaultConfig": { "durationMs": 2600, "easing": "ease-in-out", "strength": 0.6, "distance": 1.4, "height": 1, "fovDelta": 8 }, "recipe": { "type": "path", "pathKind": "pull-away" } }];
3303
+ var CAMERA_FOV_MIN = 12;
3304
+ var CAMERA_FOV_MAX = 120;
3305
+
3306
+ // src/data/poseLibraryBank.ts
3307
+ var POSE_LIBRARY_TAG_ORDER = ["stand", "sit", "lie", "move", "action"];
3308
+ var PoseLibraryBank = class {
3309
+ constructor() {
3310
+ this.poses = [];
3311
+ this.byId = {};
3312
+ }
3313
+ set(items) {
3314
+ const previous = this.byId;
3315
+ this.poses = [...items].map((item) => {
3316
+ const old = previous[item.id];
3317
+ if (old?.bonesLoaded) {
3318
+ return {
3319
+ ...item,
3320
+ hips: old.hips,
3321
+ bones: old.bones,
3322
+ bonesLoaded: true,
3323
+ modelUrl: item.modelUrl || old.modelUrl
3324
+ };
3325
+ }
3326
+ return item;
3327
+ }).sort((a, b) => a.rank - b.rank || a.id.localeCompare(b.id));
3328
+ this.byId = Object.fromEntries(this.poses.map((pose) => [pose.id, pose]));
3329
+ }
3330
+ applyBones(id, hips, bones) {
3331
+ const current = this.byId[id];
3332
+ const next = current ? { ...current, hips, bones, bonesLoaded: true } : {
3333
+ id,
3334
+ name: id,
3335
+ nameZh: id,
3336
+ tag: "",
3337
+ tags: [],
3338
+ rank: 100,
3339
+ hips,
3340
+ bones,
3341
+ bonesLoaded: true
3342
+ };
3343
+ this.byId[id] = next;
3344
+ this.poses = current ? this.poses.map((pose) => pose.id === id ? next : pose) : [...this.poses, next].sort((a, b) => a.rank - b.rank || a.id.localeCompare(b.id));
3345
+ return next;
3346
+ }
3347
+ get(id) {
3348
+ if (!id)
3349
+ return void 0;
3350
+ return this.byId[id];
3351
+ }
3352
+ has(id) {
3353
+ return !!this.get(id);
3354
+ }
3355
+ list(tag) {
3356
+ if (!tag || tag === "all")
3357
+ return [...this.poses];
3358
+ return this.poses.filter((pose) => pose.tag === tag || pose.tags.includes(tag));
3359
+ }
3360
+ listTags() {
3361
+ const seen = /* @__PURE__ */ new Set();
3362
+ for (const pose of this.poses) {
3363
+ if (pose.tag)
3364
+ seen.add(pose.tag);
3365
+ for (const item of pose.tags) {
3366
+ if (item)
3367
+ seen.add(item);
3368
+ }
3369
+ }
3370
+ const known = POSE_LIBRARY_TAG_ORDER.filter((item) => seen.has(item));
3371
+ const rest = [...seen].filter((item) => !POSE_LIBRARY_TAG_ORDER.includes(item)).sort();
3372
+ return [...known, ...rest];
3373
+ }
3374
+ defaultId() {
3375
+ return this.poses[0]?.id;
3376
+ }
3377
+ rootOffset(poseId) {
3378
+ const pose = this.get(poseId);
3379
+ if (!pose || !pose.bonesLoaded)
3380
+ return null;
3381
+ const baseId = this.defaultId();
3382
+ const basePose = baseId ? this.get(baseId) : void 0;
3383
+ const base = basePose?.bonesLoaded ? basePose.hips : [0, 0, 0];
3384
+ return {
3385
+ x: pose.hips[0] - base[0],
3386
+ y: pose.hips[1] - base[1],
3387
+ z: pose.hips[2] - base[2]
3388
+ };
3389
+ }
3390
+ };
3391
+ var defaultPoseLibraryBank = new PoseLibraryBank();
3392
+
3393
+ // src/data/poseControls.ts
3394
+ var POSE_CONTROLS = [
3395
+ { key: "bodyBend", label: "\u8EAB\u4F53\u5F2F\u66F2", group: "body", min: -90, max: 90 },
3396
+ { key: "bodyTilt", label: "\u8EAB\u4F53\u4FA7\u503E", group: "body", min: -45, max: 45 },
3397
+ { key: "bodyTurn", label: "\u8EAB\u4F53\u626D\u8F6C", group: "body", min: -90, max: 90 },
3398
+ { key: "torsoBend", label: "\u8EAF\u5E72\u5F2F\u66F2", group: "torso", min: -45, max: 45 },
3399
+ { key: "torsoTilt", label: "\u8EAF\u5E72\u4FA7\u503E", group: "torso", min: -30, max: 30 },
3400
+ { key: "torsoTurn", label: "\u8EAF\u5E72\u626D\u8F6C", group: "torso", min: -45, max: 45 },
3401
+ { key: "headNod", label: "\u70B9\u5934", group: "head", min: -60, max: 60 },
3402
+ { key: "headTilt", label: "\u5934\u90E8\u4FA7\u503E", group: "head", min: -30, max: 30 },
3403
+ { key: "headTurn", label: "\u8F6C\u5934", group: "head", min: -90, max: 90 },
3404
+ { key: "lArmRaise", label: "\u5DE6\u81C2\u62AC\u5347", group: "leftArm", min: -90, max: 180 },
3405
+ { key: "lArmStraddle", label: "\u5DE6\u81C2\u5C55\u5F00", group: "leftArm", min: -10, max: 90 },
3406
+ { key: "lArmTurn", label: "\u5DE6\u81C2\u626D\u8F6C", group: "leftArm", min: -90, max: 90 },
3407
+ { key: "lElbowBend", label: "\u5DE6\u8098\u5F2F\u66F2", group: "leftArm", min: 0, max: 150 },
3408
+ { key: "rArmRaise", label: "\u53F3\u81C2\u62AC\u5347", group: "rightArm", min: -90, max: 180 },
3409
+ { key: "rArmStraddle", label: "\u53F3\u81C2\u5C55\u5F00", group: "rightArm", min: -10, max: 90 },
3410
+ { key: "rArmTurn", label: "\u53F3\u81C2\u626D\u8F6C", group: "rightArm", min: -90, max: 90 },
3411
+ { key: "rElbowBend", label: "\u53F3\u8098\u5F2F\u66F2", group: "rightArm", min: 0, max: 150 },
3412
+ { key: "lLegRaise", label: "\u5DE6\u817F\u62AC\u5347", group: "leftLeg", min: -90, max: 90 },
3413
+ { key: "lLegStraddle", label: "\u5DE6\u817F\u5916\u5C55", group: "leftLeg", min: -30, max: 60 },
3414
+ { key: "lLegTurn", label: "\u5DE6\u817F\u626D\u8F6C", group: "leftLeg", min: -45, max: 45 },
3415
+ { key: "lKneeBend", label: "\u5DE6\u819D\u5F2F\u66F2", group: "leftLeg", min: 0, max: 150 },
3416
+ { key: "rLegRaise", label: "\u53F3\u817F\u62AC\u5347", group: "rightLeg", min: -90, max: 90 },
3417
+ { key: "rLegStraddle", label: "\u53F3\u817F\u5916\u5C55", group: "rightLeg", min: -30, max: 60 },
3418
+ { key: "rLegTurn", label: "\u53F3\u817F\u626D\u8F6C", group: "rightLeg", min: -45, max: 45 },
3419
+ { key: "rKneeBend", label: "\u53F3\u819D\u5F2F\u66F2", group: "rightLeg", min: 0, max: 150 }
3420
+ ];
3421
+
3422
+ // src/data/posePresets.ts
3423
+ var POSE_PRESETS = [
3424
+ {
3425
+ id: "stand",
3426
+ name: "\u7AD9\u7ACB",
3427
+ controlValues: {
3428
+ torsoBend: 2,
3429
+ headNod: -10,
3430
+ lArmRaise: -5,
3431
+ lArmStraddle: 12,
3432
+ rArmRaise: -5,
3433
+ rArmStraddle: 14,
3434
+ lElbowBend: 15,
3435
+ rElbowBend: 15
3436
+ }
3437
+ },
3438
+ {
3439
+ id: "tpose",
3440
+ name: "T\u578B",
3441
+ controlValues: {}
3442
+ },
3443
+ {
3444
+ id: "walk",
3445
+ name: "\u884C\u8D70",
3446
+ controlValues: {
3447
+ torsoBend: 3,
3448
+ torsoTurn: 5,
3449
+ headNod: -5,
3450
+ lArmRaise: 40,
3451
+ lArmStraddle: 9,
3452
+ rArmRaise: -8,
3453
+ rArmStraddle: 27,
3454
+ rArmTurn: -4,
3455
+ lElbowBend: 37,
3456
+ rElbowBend: 26,
3457
+ lLegRaise: -10,
3458
+ rLegRaise: 36,
3459
+ rLegStraddle: -2,
3460
+ lKneeBend: 22,
3461
+ rKneeBend: 41
3462
+ }
3463
+ },
3464
+ {
3465
+ id: "run",
3466
+ name: "\u8DD1\u6B65",
3467
+ controlValues: {
3468
+ bodyBend: -2,
3469
+ torsoBend: 3,
3470
+ torsoTurn: 5,
3471
+ headNod: -10,
3472
+ lArmRaise: -26,
3473
+ lArmStraddle: 23,
3474
+ lArmTurn: 14,
3475
+ rArmRaise: 23,
3476
+ rArmStraddle: 14,
3477
+ rArmTurn: 19,
3478
+ lElbowBend: 60,
3479
+ rElbowBend: 89,
3480
+ lLegRaise: 50,
3481
+ rLegRaise: -20,
3482
+ lKneeBend: 18,
3483
+ rKneeBend: 36
3484
+ }
3485
+ },
3486
+ {
3487
+ id: "sit",
3488
+ name: "\u5750\u59FF",
3489
+ controlValues: {
3490
+ bodyBend: -5,
3491
+ torsoBend: 5,
3492
+ headNod: -5,
3493
+ lArmStraddle: 8,
3494
+ lArmTurn: 6,
3495
+ rArmStraddle: 15,
3496
+ rArmTurn: 16,
3497
+ lElbowBend: 80,
3498
+ rElbowBend: 80,
3499
+ lLegRaise: 90,
3500
+ lLegStraddle: 5,
3501
+ rLegRaise: 90,
3502
+ rLegStraddle: 5,
3503
+ lKneeBend: 90,
3504
+ rKneeBend: 90
3505
+ },
3506
+ rootOffsetY: -0.28
3507
+ },
3508
+ {
3509
+ id: "crouch",
3510
+ name: "\u8E72\u4E0B",
3511
+ controlValues: {
3512
+ bodyBend: 20,
3513
+ bodyTurn: 4,
3514
+ bodyTilt: 1,
3515
+ torsoBend: 32,
3516
+ torsoTilt: -3,
3517
+ headNod: -24,
3518
+ headTurn: -6,
3519
+ headTilt: 1,
3520
+ lArmRaise: 34,
3521
+ lArmStraddle: 18,
3522
+ lArmTurn: 18,
3523
+ rArmRaise: 34,
3524
+ rArmStraddle: 18,
3525
+ rArmTurn: -18,
3526
+ lElbowBend: 86,
3527
+ rElbowBend: 86,
3528
+ lLegRaise: 88,
3529
+ lLegStraddle: 12,
3530
+ lLegTurn: -3,
3531
+ rLegRaise: 88,
3532
+ rLegStraddle: 8,
3533
+ rLegTurn: 2,
3534
+ lKneeBend: 146,
3535
+ rKneeBend: 146
3536
+ },
3537
+ rootOffsetY: -0.58
3538
+ },
3539
+ {
3540
+ id: "one_knee",
3541
+ name: "\u5355\u819D\u8DEA",
3542
+ controlValues: {
3543
+ bodyBend: -4,
3544
+ bodyTurn: -2,
3545
+ bodyTilt: 6,
3546
+ torsoBend: 24,
3547
+ torsoTurn: -6,
3548
+ torsoTilt: -3,
3549
+ headNod: -10,
3550
+ headTurn: 3,
3551
+ headTilt: -2,
3552
+ lArmRaise: 14,
3553
+ lArmStraddle: 18,
3554
+ lArmTurn: 6,
3555
+ rArmRaise: 8,
3556
+ rArmStraddle: 18,
3557
+ rArmTurn: -4,
3558
+ lElbowBend: 70,
3559
+ rElbowBend: 42,
3560
+ lLegRaise: 82,
3561
+ lLegStraddle: 6,
3562
+ lLegTurn: 4,
3563
+ rLegRaise: -5,
3564
+ rLegStraddle: 6,
3565
+ rLegTurn: -8,
3566
+ lKneeBend: 86,
3567
+ rKneeBend: 130
3568
+ },
3569
+ rootOffsetY: -0.54
3570
+ },
3571
+ {
3572
+ id: "two_knees",
3573
+ name: "\u53CC\u819D\u8DEA",
3574
+ controlValues: {
3575
+ bodyBend: 8,
3576
+ torsoBend: 6,
3577
+ headNod: -5,
3578
+ lArmRaise: -5,
3579
+ lArmStraddle: 7,
3580
+ rArmRaise: -5,
3581
+ rArmStraddle: 10,
3582
+ lElbowBend: 15,
3583
+ rElbowBend: 15,
3584
+ lLegRaise: -10,
3585
+ rLegRaise: -9,
3586
+ lKneeBend: 108,
3587
+ rKneeBend: 104
3588
+ },
3589
+ rootOffsetY: -0.26
3590
+ },
3591
+ {
3592
+ id: "lean",
3593
+ name: "\u501A\u9760",
3594
+ controlValues: {
3595
+ bodyTilt: -5,
3596
+ torsoBend: -3,
3597
+ torsoTurn: 5,
3598
+ torsoTilt: -5,
3599
+ headNod: -10,
3600
+ headTurn: 10,
3601
+ headTilt: -5,
3602
+ lArmRaise: -5,
3603
+ lArmStraddle: 13,
3604
+ rArmRaise: -10,
3605
+ rArmStraddle: 16,
3606
+ lElbowBend: 15,
3607
+ rElbowBend: 20,
3608
+ lLegStraddle: 5,
3609
+ rLegRaise: 20,
3610
+ rLegTurn: 10,
3611
+ rKneeBend: 40
3612
+ }
3613
+ },
3614
+ {
3615
+ id: "bow",
3616
+ name: "\u97A0\u8EAC",
3617
+ controlValues: {
3618
+ bodyBend: 24,
3619
+ bodyTurn: 1,
3620
+ torsoBend: 30,
3621
+ torsoTilt: -1,
3622
+ headNod: 18,
3623
+ headTurn: -3,
3624
+ headTilt: 1,
3625
+ lArmRaise: -8,
3626
+ lArmStraddle: 8,
3627
+ rArmRaise: -8,
3628
+ rArmStraddle: 8,
3629
+ lElbowBend: 4,
3630
+ rElbowBend: 4,
3631
+ lKneeBend: 4,
3632
+ rKneeBend: 4
3633
+ }
3634
+ },
3635
+ {
3636
+ id: "think",
3637
+ name: "\u601D\u8003",
3638
+ controlValues: {
3639
+ bodyBend: -2,
3640
+ bodyTurn: 2,
3641
+ bodyTilt: 1,
3642
+ torsoBend: 7,
3643
+ torsoTurn: 2,
3644
+ torsoTilt: 1,
3645
+ headNod: 14,
3646
+ headTurn: 8,
3647
+ headTilt: -3,
3648
+ lArmRaise: 42,
3649
+ lArmStraddle: -8,
3650
+ lArmTurn: -10,
3651
+ rArmRaise: 8,
3652
+ rArmStraddle: 22,
3653
+ rArmTurn: 39,
3654
+ lElbowBend: 98,
3655
+ rElbowBend: 107,
3656
+ lLegStraddle: 4,
3657
+ rLegStraddle: 4
3658
+ }
3659
+ },
3660
+ {
3661
+ id: "fight",
3662
+ name: "\u683C\u6597",
3663
+ controlValues: {
3664
+ bodyBend: 10,
3665
+ bodyTurn: -24,
3666
+ bodyTilt: 8,
3667
+ torsoBend: 18,
3668
+ torsoTilt: -4,
3669
+ headNod: -7,
3670
+ headTurn: 24,
3671
+ headTilt: -10,
3672
+ lArmRaise: 78,
3673
+ lArmStraddle: 24,
3674
+ lArmTurn: 8,
3675
+ rArmRaise: 82,
3676
+ rArmStraddle: 48,
3677
+ rArmTurn: 45,
3678
+ lElbowBend: 84,
3679
+ rElbowBend: 124,
3680
+ lLegRaise: 46,
3681
+ lLegStraddle: 18,
3682
+ lLegTurn: 8,
3683
+ rLegRaise: 34,
3684
+ rLegStraddle: 14,
3685
+ rLegTurn: -2,
3686
+ lKneeBend: 55,
3687
+ rKneeBend: 78
3688
+ }
3689
+ },
3690
+ {
3691
+ id: "kick",
3692
+ name: "\u8E22\u7403",
3693
+ controlValues: {
3694
+ bodyBend: -8,
3695
+ bodyTurn: -12,
3696
+ bodyTilt: -5,
3697
+ torsoBend: -10,
3698
+ torsoTurn: -16,
3699
+ torsoTilt: 8,
3700
+ headNod: 8,
3701
+ headTurn: -12,
3702
+ headTilt: 2,
3703
+ lArmRaise: -34,
3704
+ lArmStraddle: 34,
3705
+ lArmTurn: -8,
3706
+ rArmRaise: 42,
3707
+ rArmStraddle: 28,
3708
+ rArmTurn: 12,
3709
+ lElbowBend: 34,
3710
+ rElbowBend: 44,
3711
+ lLegRaise: -8,
3712
+ lLegStraddle: 3,
3713
+ lLegTurn: -4,
3714
+ rLegRaise: 104,
3715
+ rLegStraddle: 3,
3716
+ lKneeBend: 16,
3717
+ rKneeBend: 10
3718
+ }
3719
+ },
3720
+ {
3721
+ id: "throw",
3722
+ name: "\u6295\u63B7",
3723
+ controlValues: {
3724
+ bodyBend: 5,
3725
+ bodyTurn: -2,
3726
+ torsoBend: 4,
3727
+ torsoTurn: 15,
3728
+ torsoTilt: -5,
3729
+ headNod: -5,
3730
+ headTurn: -15,
3731
+ lArmRaise: 7,
3732
+ lArmStraddle: 17,
3733
+ rArmRaise: 77,
3734
+ rArmTurn: 90,
3735
+ lElbowBend: 88,
3736
+ rElbowBend: 96,
3737
+ lLegRaise: 15,
3738
+ lLegStraddle: 5,
3739
+ rLegRaise: -10,
3740
+ rLegStraddle: 5,
3741
+ lKneeBend: 26,
3742
+ rKneeBend: 36
3743
+ }
3744
+ },
3745
+ {
3746
+ id: "push",
3747
+ name: "\u63A8\u8FDB",
3748
+ controlValues: {
3749
+ bodyBend: -1,
3750
+ torsoBend: 19,
3751
+ headNod: -5,
3752
+ lArmRaise: 100,
3753
+ lArmStraddle: 6,
3754
+ lArmTurn: 5,
3755
+ rArmRaise: 101,
3756
+ rArmStraddle: 11,
3757
+ lElbowBend: 14,
3758
+ lLegRaise: 41,
3759
+ rLegRaise: -15,
3760
+ lKneeBend: 35,
3761
+ rKneeBend: 10
3762
+ }
3763
+ },
3764
+ {
3765
+ id: "wave",
3766
+ name: "\u62DB\u624B",
3767
+ controlValues: {
3768
+ torsoBend: 2,
3769
+ torsoTurn: -10,
3770
+ headNod: -5,
3771
+ headTurn: 15,
3772
+ headTilt: 5,
3773
+ lArmStraddle: 6,
3774
+ rArmRaise: 62,
3775
+ rArmStraddle: -10,
3776
+ rArmTurn: 90,
3777
+ lElbowBend: 62,
3778
+ rElbowBend: 94
3779
+ }
3780
+ },
3781
+ // 以下 3 个在预设表里但 UI 网格未展示(hidden)。
3782
+ {
3783
+ id: "stretch",
3784
+ name: "\u4F38\u624B",
3785
+ hidden: true,
3786
+ controlValues: {
3787
+ bodyBend: -5,
3788
+ torsoBend: -16,
3789
+ torsoTilt: 3,
3790
+ headNod: -25,
3791
+ lArmRaise: 115,
3792
+ lArmStraddle: -10,
3793
+ lArmTurn: 10,
3794
+ rArmRaise: 133,
3795
+ rArmStraddle: -10,
3796
+ rArmTurn: -10,
3797
+ lElbowBend: 59,
3798
+ rElbowBend: 53,
3799
+ lLegStraddle: 10,
3800
+ rLegStraddle: 10
3801
+ }
3802
+ },
3803
+ {
3804
+ id: "arms_crossed",
3805
+ name: "\u62B1\u81C2",
3806
+ hidden: true,
3807
+ controlValues: {
3808
+ torsoTurn: 2,
3809
+ headNod: -8,
3810
+ lArmRaise: 7,
3811
+ lArmStraddle: 22,
3812
+ lArmTurn: 17,
3813
+ rArmRaise: 8,
3814
+ rArmStraddle: 22,
3815
+ rArmTurn: 39,
3816
+ lElbowBend: 107,
3817
+ rElbowBend: 107,
3818
+ lLegStraddle: 5,
3819
+ rLegStraddle: 5
3820
+ }
3821
+ },
3822
+ {
3823
+ id: "phone",
3824
+ name: "\u770B\u624B\u673A",
3825
+ hidden: true,
3826
+ controlValues: {
3827
+ torsoBend: 5,
3828
+ headNod: 17,
3829
+ lArmRaise: 55,
3830
+ lArmStraddle: -10,
3831
+ lArmTurn: -4,
3832
+ rArmRaise: 24,
3833
+ rArmStraddle: -10,
3834
+ rArmTurn: -13,
3835
+ lElbowBend: 89,
3836
+ rElbowBend: 43,
3837
+ lLegStraddle: 5,
3838
+ rLegStraddle: 5
3839
+ }
3840
+ }
3841
+ ];
3842
+ var POSE_PRESET_BY_ID = Object.fromEntries(
3843
+ POSE_PRESETS.map((p) => [p.id, p])
3844
+ );
3845
+
3846
+ // src/evaluate/studioIntents.ts
3847
+ var CHARACTER_COLORS = ["#7fb2e0", "#e0a37f", "#9fe07f", "#d07fe0", "#e0d06a", "#6ad0c0"];
3848
+ var DEFAULT_MOTION_DURATION_SECONDS = 2;
3849
+ var StudioIntentError = class extends Error {
3850
+ constructor(code, message) {
3851
+ super(message ?? code);
3852
+ this.name = "StudioIntentError";
3853
+ this.code = code;
3854
+ }
3855
+ };
3856
+ function uniqueNodeName(doc, base) {
3857
+ const names = doc.content.nodes.map((n) => n.name);
3858
+ const taken = new Set(names);
3859
+ const prefix = `${base}_`;
3860
+ let max = taken.has(base) ? 1 : 0;
3861
+ for (const name of names) {
3862
+ if (!name.startsWith(prefix))
3863
+ continue;
3864
+ const n = Number(name.slice(prefix.length));
3865
+ if (Number.isInteger(n) && n > 0)
3866
+ max = Math.max(max, n);
3867
+ }
3868
+ let next = max + 1;
3869
+ let candidate = `${base}_${next}`;
3870
+ while (taken.has(candidate)) {
3871
+ next += 1;
3872
+ candidate = `${base}_${next}`;
3873
+ }
3874
+ return candidate;
3875
+ }
3876
+ function nextIndexedId(existing, prefix) {
3877
+ let n = 1;
3878
+ while (existing.includes(`${prefix}${n}`))
3879
+ n += 1;
3880
+ return `${prefix}${n}`;
3881
+ }
3882
+ function clampCameraFov(fov) {
3883
+ return Math.min(CAMERA_FOV_MAX, Math.max(CAMERA_FOV_MIN, fov));
3884
+ }
3885
+ function dollyTowardLookAt(pos, lookAt, distance) {
3886
+ const dx = pos.x - lookAt.x;
3887
+ const dy = pos.y - lookAt.y;
3888
+ const dz = pos.z - lookAt.z;
3889
+ const old = Math.hypot(dx, dy, dz);
3890
+ if (old < 1e-6)
3891
+ return { x: lookAt.x, y: lookAt.y, z: lookAt.z + distance };
3892
+ const scale = distance / old;
3893
+ return { x: lookAt.x + dx * scale, y: lookAt.y + dy * scale, z: lookAt.z + dz * scale };
3894
+ }
3895
+ function listStudioCameraPresets() {
3896
+ return CAMERA_PRESETS.filter((preset) => preset.id !== "current").map((preset) => ({ id: preset.id, name: preset.name, fov: preset.fov }));
3897
+ }
3898
+ function allIds(doc) {
3899
+ return [
3900
+ ...doc.content.nodes.map((n) => n.id),
3901
+ ...doc.content.timeline.animation.motionClips.map((c) => c.id),
3902
+ ...doc.content.timeline.animation.cameraMotionClips.map((c) => c.id),
3903
+ ...doc.content.asset.motionPath.map((e) => e.id)
3904
+ ];
3905
+ }
3906
+ function requireUnusedId(doc, id) {
3907
+ if (allIds(doc).includes(id))
3908
+ throw new StudioIntentError("node-exists", id);
3909
+ return id;
3910
+ }
3911
+ function buildCharacterNode(doc, intent, poseBank = defaultPoseLibraryBank) {
3912
+ const charCount = doc.content.nodes.filter((n) => n.type === "character").length;
3913
+ const id = intent.nodeId ? requireUnusedId(doc, intent.nodeId) : nextIndexedId(allIds(doc), "user_chr_");
3914
+ return {
3915
+ id,
3916
+ type: "character",
3917
+ name: uniqueNodeName(doc, intent.name),
3918
+ visible: true,
3919
+ locked: false,
3920
+ transform: {
3921
+ position: { x: charCount * 1, y: 0, z: 0 },
3922
+ rotation: { x: 0, y: 0, z: 0 },
3923
+ scale: { x: 1, y: 1, z: 1 }
3924
+ },
3925
+ metadata: {
3926
+ modelUrl: intent.modelUrl,
3927
+ assetId: intent.libraryId,
3928
+ ...intent.rig ? { rig: intent.rig } : {}
3929
+ },
3930
+ character: {
3931
+ placeholder: false,
3932
+ gender: "unknown",
3933
+ motionId: null,
3934
+ appearance: { color: CHARACTER_COLORS[charCount % CHARACTER_COLORS.length] },
3935
+ label: { showLabel: true, scale: 1, yOffset: 0 },
3936
+ animation: { mode: "pose", posePresetId: poseBank.defaultId(), controlValues: {} }
3937
+ }
3938
+ };
3939
+ }
3940
+ function buildPropNode(doc, intent) {
3941
+ const id = intent.nodeId ? requireUnusedId(doc, intent.nodeId) : nextIndexedId(allIds(doc), "user_prop_");
3942
+ return {
3943
+ id,
3944
+ type: "prop",
3945
+ name: intent.name,
3946
+ visible: true,
3947
+ locked: false,
3948
+ transform: {
3949
+ position: { x: 0, y: 0, z: 0 },
3950
+ rotation: { x: 0, y: 0, z: 0 },
3951
+ scale: { x: 1, y: 1, z: 1 }
3952
+ },
3953
+ metadata: { modelUrl: intent.modelUrl, assetId: intent.libraryId },
3954
+ prop: { category: "user" }
3955
+ };
3956
+ }
3957
+ function buildCameraNode(doc, intent) {
3958
+ if (intent.presetId === "current") {
3959
+ throw new StudioIntentError("current-viewport-unavailable");
3960
+ }
3961
+ const preset = CAMERA_PRESETS.find((p) => p.id === intent.presetId);
3962
+ if (!preset)
3963
+ throw new StudioIntentError("preset-not-found", intent.presetId);
3964
+ const id = intent.nodeId ? requireUnusedId(doc, intent.nodeId) : nextIndexedId(allIds(doc), "user_cam_");
3965
+ const node = {
3966
+ id,
3967
+ type: "camera",
3968
+ name: uniqueNodeName(doc, preset.name),
3969
+ visible: true,
3970
+ locked: false,
3971
+ transform: {
3972
+ position: { ...preset.position },
3973
+ rotation: { ...preset.rotation },
3974
+ scale: { x: 1, y: 1, z: 1 }
3975
+ },
3976
+ camera: {
3977
+ projection: "perspective",
3978
+ fov: preset.fov,
3979
+ fovAxis: "vertical",
3980
+ near: 0.1,
3981
+ far: 2e3,
3982
+ isPrimary: false,
3983
+ lookAt: { ...preset.lookAt }
3984
+ }
3985
+ };
3986
+ applyCameraPresetToNode(node, preset);
3987
+ if (intent.subjectNodeId) {
3988
+ const subject = doc.content.nodes.find(
3989
+ (n) => n.id === intent.subjectNodeId && n.type === "character"
3990
+ );
3991
+ if (!subject)
3992
+ throw new StudioIntentError("missing-character", intent.subjectNodeId);
3993
+ applyCameraPresetToSubject(node, preset, subject);
3994
+ }
3995
+ return node;
3996
+ }
3997
+ function resolveMotionDurationSeconds(doc, intent) {
3998
+ if (intent.durationSeconds !== void 0 && Number.isFinite(intent.durationSeconds)) {
3999
+ return Math.max(1 / Math.max(doc.content.timeline.fps, 1), intent.durationSeconds);
4000
+ }
4001
+ return DEFAULT_MOTION_DURATION_SECONDS;
4002
+ }
4003
+ function shiftOverlappingMotionClips(sameTarget, frameStart, frameEnd) {
4004
+ const shifted = /* @__PURE__ */ new Map();
4005
+ let cursorStart = frameStart;
4006
+ let cursorEnd = frameEnd;
4007
+ for (const existing of sameTarget) {
4008
+ if (existing.frameEnd <= cursorStart || existing.frameStart >= cursorEnd)
4009
+ continue;
4010
+ const next = {
4011
+ ...existing,
4012
+ frameStart: cursorEnd,
4013
+ frameEnd: cursorEnd + (existing.frameEnd - existing.frameStart)
4014
+ };
4015
+ shifted.set(existing.id, next);
4016
+ cursorStart = next.frameStart;
4017
+ cursorEnd = next.frameEnd;
4018
+ }
4019
+ return shifted;
4020
+ }
4021
+ function appendMotionClip(doc, intent) {
4022
+ const target = doc.content.nodes.find((n) => n.id === intent.targetNodeId && n.type === "character");
4023
+ if (!target)
4024
+ throw new StudioIntentError("missing-character", intent.targetNodeId);
4025
+ const fps = doc.content.timeline.fps || 30;
4026
+ const durationSeconds = resolveMotionDurationSeconds(doc, intent);
4027
+ const durationFrames = Math.max(1, Math.round(durationSeconds * fps));
4028
+ const frameStart = Math.max(0, Math.floor(intent.frameStart ?? 0));
4029
+ const frameEnd = frameStart + durationFrames;
4030
+ const clipId = intent.clipId ? requireUnusedId(doc, intent.clipId) : nextIndexedId(allIds(doc), "user_mc_");
4031
+ const assetId = intent.libraryId;
4032
+ const clip = {
4033
+ id: clipId,
4034
+ source: "user-library",
4035
+ sourceDuration: durationSeconds,
4036
+ frameStart,
4037
+ frameEnd,
4038
+ target: { type: "character", nodeId: target.id },
4039
+ playback: { version: 1, speed: 1, loop: true, loopMode: "repeat" },
4040
+ motion: {
4041
+ assetId,
4042
+ name: intent.name,
4043
+ source: "user-library",
4044
+ sourceRig: "mixamorig",
4045
+ url: intent.fbxKey,
4046
+ inPlace: true,
4047
+ loop: true,
4048
+ speed: 1,
4049
+ time: 0
4050
+ }
4051
+ };
4052
+ const tl = doc.content.timeline;
4053
+ const sameTarget = tl.animation.motionClips.filter((c) => c.target.nodeId === target.id).sort((a, b) => a.frameStart - b.frameStart);
4054
+ const shifted = shiftOverlappingMotionClips(sameTarget, frameStart, frameEnd);
4055
+ tl.animation.motionClips = tl.animation.motionClips.map((c) => shifted.get(c.id) ?? c).concat(clip);
4056
+ if (!doc.content.asset.motionPath.some((e) => e.id === assetId || e.path === intent.fbxKey)) {
4057
+ doc.content.asset.motionPath.push({ id: assetId, path: intent.fbxKey });
4058
+ }
4059
+ return clip;
4060
+ }
4061
+ function applyPoseInPlace(doc, intent, poseBank = defaultPoseLibraryBank) {
4062
+ const node = doc.content.nodes.find((n) => n.id === intent.nodeId);
4063
+ if (!node)
4064
+ throw new StudioIntentError("missing-node", intent.nodeId);
4065
+ if (!node.character)
4066
+ throw new StudioIntentError("missing-character", intent.nodeId);
4067
+ const libraryPose = poseBank.get(intent.presetId);
4068
+ const preset = libraryPose ? void 0 : POSE_PRESET_BY_ID[intent.presetId];
4069
+ if (!libraryPose && !preset)
4070
+ throw new StudioIntentError("preset-not-found", intent.presetId);
4071
+ const anim = node.character.animation;
4072
+ anim.mode = "pose";
4073
+ if (libraryPose) {
4074
+ anim.posePresetId = libraryPose.id;
4075
+ anim.controlValues = {};
4076
+ anim.rootPositionOffset = poseBank.rootOffset(libraryPose.id) ?? { x: 0, y: 0, z: 0 };
4077
+ return;
4078
+ }
4079
+ if (preset) {
4080
+ anim.posePresetId = preset.id;
4081
+ anim.controlValues = Object.fromEntries(
4082
+ POSE_CONTROLS.map((c) => [c.key, preset.controlValues[c.key] ?? 0])
4083
+ );
4084
+ anim.rootPositionOffset = { x: 0, y: preset.rootOffsetY ?? 0, z: 0 };
4085
+ }
4086
+ }
4087
+ function setCameraFovInPlace(doc, intent) {
4088
+ const cam = doc.content.nodes.find((n) => n.id === intent.cameraNodeId && n.type === "camera");
4089
+ if (!cam?.camera)
4090
+ throw new StudioIntentError("missing-camera", intent.cameraNodeId);
4091
+ cam.camera.fov = clampCameraFov(intent.fov);
4092
+ }
4093
+ function setCameraDistanceInPlace(doc, intent) {
4094
+ const cam = doc.content.nodes.find((n) => n.id === intent.cameraNodeId && n.type === "camera");
4095
+ if (!cam?.camera)
4096
+ throw new StudioIntentError("missing-camera", intent.cameraNodeId);
4097
+ const nextDist = Math.max(0.1, intent.distance);
4098
+ const lookAt = cam.camera.lookAt;
4099
+ const nextPos = dollyTowardLookAt(cam.transform.position, lookAt, nextDist);
4100
+ const binding = cam.camera.subject;
4101
+ if (binding) {
4102
+ const subject = doc.content.nodes.find((n) => n.id === binding.nodeId && n.type === "character");
4103
+ if (!subject)
4104
+ throw new StudioIntentError("subject-missing", binding.nodeId);
4105
+ const yawDeg = binding.followRotation ? subjectYawDeg(subject.transform.rotation) : 0;
4106
+ binding.offset = subjectWorldToLocal(nextPos, subject.transform.position, yawDeg);
4107
+ binding.lookAtOffset = subjectWorldToLocal(lookAt, subject.transform.position, yawDeg);
4108
+ binding.distance = nextDist;
4109
+ }
4110
+ cam.transform.position = nextPos;
4111
+ }
4112
+ function mergeVec3(base, patch) {
4113
+ if (!patch)
4114
+ return { ...base };
4115
+ return {
4116
+ x: patch.x === void 0 ? base.x : patch.x,
4117
+ y: patch.y === void 0 ? base.y : patch.y,
4118
+ z: patch.z === void 0 ? base.z : patch.z
4119
+ };
4120
+ }
4121
+ function patchNodeTransformInPlace(doc, intent) {
4122
+ const node = doc.content.nodes.find((n) => n.id === intent.nodeId);
4123
+ if (!node)
4124
+ throw new StudioIntentError("missing-node", intent.nodeId);
4125
+ node.transform.position = mergeVec3(node.transform.position, intent.position);
4126
+ node.transform.rotation = mergeVec3(node.transform.rotation, intent.rotation);
4127
+ node.transform.scale = mergeVec3(node.transform.scale, intent.scale);
4128
+ }
4129
+ function applyStudioIntentInPlace(document, intent, poseBank = defaultPoseLibraryBank) {
4130
+ switch (intent.type) {
4131
+ case "add-character": {
4132
+ const node = buildCharacterNode(document, intent, poseBank);
4133
+ document.content.nodes.push(node);
4134
+ return { createdIds: [node.id], nodeId: node.id };
4135
+ }
4136
+ case "add-prop": {
4137
+ const node = buildPropNode(document, intent);
4138
+ document.content.nodes.push(node);
4139
+ return { createdIds: [node.id], nodeId: node.id };
4140
+ }
4141
+ case "add-camera": {
4142
+ const node = buildCameraNode(document, intent);
4143
+ document.content.nodes.push(node);
4144
+ return { createdIds: [node.id], nodeId: node.id };
4145
+ }
4146
+ case "add-motion": {
4147
+ const clip = appendMotionClip(document, intent);
4148
+ return { createdIds: [clip.id], clipId: clip.id };
4149
+ }
4150
+ case "apply-pose":
4151
+ applyPoseInPlace(document, intent, poseBank);
4152
+ return { createdIds: [], nodeId: intent.nodeId };
4153
+ case "set-fov":
4154
+ setCameraFovInPlace(document, intent);
4155
+ return { createdIds: [], nodeId: intent.cameraNodeId };
4156
+ case "set-distance":
4157
+ setCameraDistanceInPlace(document, intent);
4158
+ return { createdIds: [], nodeId: intent.cameraNodeId };
4159
+ case "patch-transform":
4160
+ patchNodeTransformInPlace(document, intent);
4161
+ return { createdIds: [], nodeId: intent.nodeId };
4162
+ default: {
4163
+ const neverIntent = intent;
4164
+ throw new StudioIntentError("invalid-intent", JSON.stringify(neverIntent));
4165
+ }
4166
+ }
4167
+ }
4168
+ function applyStudioIntent(input) {
4169
+ const document = JSON.parse(JSON.stringify(input.document));
4170
+ const applied = applyStudioIntentInPlace(document, input.intent, input.poseBank);
4171
+ return {
4172
+ document,
4173
+ fcurves: input.fcurves,
4174
+ createdIds: applied.createdIds,
4175
+ nodeId: applied.nodeId,
4176
+ clipId: applied.clipId
4177
+ };
4178
+ }
4179
+
4180
+ // src/evaluate/initialDraft.ts
4181
+ var CHARACTER_PAGE_SIZE = 100;
4182
+ function pickDefaultCharacter(items, preset = DEFAULT_CHARACTER) {
4183
+ const characters = items.filter(
4184
+ (item) => "file" in item && typeof item.file === "string" && item.file.length > 0
4185
+ );
4186
+ const wanted = preset.name.trim().toLowerCase();
4187
+ return characters.find((item) => item.name.trim().toLowerCase() === wanted) ?? characters[0] ?? null;
4188
+ }
4189
+ function seedDefaultCharacterInPlace(doc, entry, preset = DEFAULT_CHARACTER) {
4190
+ const node = buildCharacterNode(doc, {
4191
+ libraryId: entry.id,
4192
+ name: entry.name,
4193
+ modelUrl: entry.file,
4194
+ rig: entry.rig
4195
+ });
4196
+ node.transform.position = { x: 0, y: 0, z: 0 };
4197
+ if (node.character) {
4198
+ node.character.gender = preset.gender;
4199
+ node.character.appearance.color = preset.color;
4200
+ }
4201
+ doc.content.nodes.push(node);
4202
+ applyPoseInPlace(doc, { nodeId: node.id, presetId: preset.posePresetId });
4203
+ doc.extra = {
4204
+ ...typeof doc.extra === "object" && doc.extra !== null ? doc.extra : {},
4205
+ initialSeed: INITIAL_DRAFT_SEED
4206
+ };
4207
+ return node.id;
4208
+ }
4209
+ async function createInitialDraft(adapter, input) {
4210
+ const preset = input.character ?? DEFAULT_CHARACTER;
4211
+ const doc = makeEmptyDraft(input.name, input.fps, input.totalFrames, input.camera);
4212
+ let items;
4213
+ try {
4214
+ const page = await adapter.searchAssets({
4215
+ kind: "character",
4216
+ pageNo: 1,
4217
+ pageSize: CHARACTER_PAGE_SIZE
4218
+ });
4219
+ items = page.items;
4220
+ } catch {
4221
+ return doc;
4222
+ }
4223
+ const entry = pickDefaultCharacter(items, preset);
4224
+ if (entry)
4225
+ seedDefaultCharacterInPlace(doc, entry, preset);
4226
+ return doc;
4227
+ }
4228
+
4229
+ export { CAMERA_MOTIONS, CAMERA_PRESETS, DEFAULT_CHARACTER, DEFAULT_SKY_COLOR, FCurveSet, INITIAL_DRAFT_SEED, StudioIntentError, activateSequence, allocateClipId, allocateSequenceId, appendMotionClip, applyCameraPresetToNode, applyPoseInPlace, applyStudioIntent, applyStudioIntentInPlace, bakeCameraMotionClip as bakeCameraMotion, bakeCameraMotionClip, bakeCameraMotionPoses, buildCameraNode, buildCharacterNode, buildPropNode, clampCameraFov, clearSequenceClips, clipSequenceSpan, createFrameSnapshot, createInitialDraft, createSequence, defaultInsertRange, defaultVideoExportEndFrame, deleteEditClip, deleteSequence, directorDocumentSchema, dollyTowardLookAt, duplicateEditClip, duplicateSequence, emptyEditorial2 as emptyEditorial, evaluateFrame, evaluatePathMotion, evaluatePathMotionInto, fcurvesCompactV1Schema, findEditClip, formatTimecode, framesToSeconds, getActiveEditSequence, getClipDurationFrames, getEditSequence, getEditSequenceDurationFrames, getEditorial, insertEditClip, listStudioCameraPresets, makeEmptyDraft, materializeEditorial, moveEditClip, parseDirectorDocument, parseTimecode, patchNodeTransformInPlace, pathArcLength, pathClipDurationFrames, pathControlCentroid, pickDefaultCharacter, playbackIssues, prepareFrameSnapshot, renameSequence, resolveEditSequenceFrame, samplePathPoints, sceneFromDocument, secondsToFrame, seedDefaultCharacterInPlace, setCameraDistanceInPlace, setCameraFovInPlace, transformPathLocalPoint, uniqueNodeName, updateEditClip, validateEditorial };