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.
- topview_3d_cli/__init__.py +3 -0
- topview_3d_cli/__main__.py +5 -0
- topview_3d_cli/_runtime/builtin-assets/LICENSE +396 -0
- topview_3d_cli/_runtime/builtin-assets/NOTICE +24 -0
- topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_30ec53c93eae4173946b38639691ebe0.glb +0 -0
- topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_461b4f4b76ae4b198f439fa4f6f743b6.glb +0 -0
- topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_7d220ac359844c80a8faaf0e25478477.glb +0 -0
- topview_3d_cli/_runtime/builtin-assets/characters/a3d_char_de37873a79244fbe8a0660ea08d1b4ab.glb +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-attack-jab.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-attack-lunge.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-attack-rear.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-crawling-forward-reaching-with-le.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-exaggerated-punch-with-right-hand.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-examine-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-holding-bow.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-jab-with-right-hand.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-on-hands-and-toes-right-leg-forwa.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-on-right-leg.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-push-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-pushing-backwards.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-pushing-with-both-hands.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-pushing-with-left-side-of-body.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_action-sliding.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-arms-spread-looking-upwards.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-examine-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-lean-fwd-right-hand-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-leaning-against-object-look-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-back.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-down-back.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-fwd-f.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-look-fwd-m.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-on-knees-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_crouch-sneaking.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-l-toe-top-of-right-foot.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-legs-spread-lean-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-legs-spread-lean-r.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-legs-x.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-r-foot-arms-extended-backwards.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-r-toe-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-r-toe-l-leg-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-toes-arch.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dance-toes-reach.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-bent-forwards.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-left-leg-bent-hands-spread.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-legs-bent-and-arms-spread.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-floating-legs-raised-arms-extended-u.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-l-toe-look-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dyn-leaping-forward-arms-extended-backwa.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dynamic-float-arch.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_dynamic-float-dive.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-back-arched-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-legs-to-side-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-legs-x-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-back-left-leg-bent-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-back-left-leg-bent.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-back-legs-x-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-left-side-left-arm-supporting-hea.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-left-side-left-arm-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-right-side-right-arm-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-right-side-right-hand-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-on-stomach-elbows-supporting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-right-arm-supporting-l-arm-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_lay-right-leg-forward-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-run-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-run-reach.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-sneaking-forward.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-sprinting.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-stepping-forward-look-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walk-fwd-look-r.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walk-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walking-arms-on-hips.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walking-right-arm-on-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_loco-walking.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-holding-left-knee-reclining.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-holding-right-knee.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-resting-on-knees.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-elbows-on-legs-lean-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-elbows-on-legs-lean-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-knees-up-hold.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-lean-back-arms-supp.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-lean-fwd-elbows-on-knees.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-lean-fwd-hands-in-front.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-arms-x.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-hold.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-reclining.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-legs-x-right-arm-over-knee.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reaching-hands-towards-toes.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reaching-left-hand-towards-right-toe.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-facepalm.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-left-leg-crossed-hands-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-left-leg-crossed.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-look-down.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-reclined-right-leg-crossed-hands-hea.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-rev-sit-hands-grasping.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_sit-rev-sit-hands-relaxed.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-arms-behind-back.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-arms-down.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-arms-slightly-spread.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-bending-forward-arms-x-over-knees.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-hands-on-hips-look-back.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-l-hip-f.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-l-hip-m.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-l-leg-r-hip-l-arm-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-lean-back-arms-behind.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-lean-back-l-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-left-elbow-resting-obj.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-left-leg-obj-left-hand-on-knee-r-h.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-left-leg-obj-left-hand-on-knee.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-arms-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-l-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-leaning-obj.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-legs-x-left-arm-obj-r-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-down-r.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-fwd.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-l.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-look-r.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-arm-up-l-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-foot-l-leg-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-foot-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-hip-l-arm-up.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-r-hip.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-right-hand-behind-head.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-right-leg-obj-arms-x.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_stand-right-leg-obj-l-hip-right-hand-on.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/covers/a3d_pose_t-pose.webp +0 -0
- topview_3d_cli/_runtime/builtin-assets/manifest.json +2372 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-attack-jab.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-attack-lunge.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-attack-rear.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-crawling-forward-reaching-with-le.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-exaggerated-punch-with-right-hand.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-examine-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-holding-bow.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-jab-with-right-hand.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-on-hands-and-toes-right-leg-forwa.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-on-right-leg.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-push-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-pushing-backwards.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-pushing-with-both-hands.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-pushing-with-left-side-of-body.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_action-sliding.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-arms-spread-looking-upwards.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-examine-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-lean-fwd-right-hand-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-leaning-against-object-look-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-back.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-down-back.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-fwd-f.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-look-fwd-m.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-on-knees-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_crouch-sneaking.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-l-toe-top-of-right-foot.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-legs-spread-lean-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-legs-spread-lean-r.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-legs-x.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-r-foot-arms-extended-backwards.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-r-toe-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-r-toe-l-leg-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-toes-arch.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dance-toes-reach.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-bent-forwards.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-left-leg-bent-hands-spread.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-legs-bent-and-arms-spread.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-floating-legs-raised-arms-extended-u.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-l-toe-look-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dyn-leaping-forward-arms-extended-backwa.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dynamic-float-arch.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_dynamic-float-dive.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-back-arched-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-legs-to-side-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-legs-x-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-back-left-leg-bent-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-back-left-leg-bent.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-back-legs-x-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-left-side-left-arm-supporting-hea.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-left-side-left-arm-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-right-side-right-arm-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-right-side-right-hand-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-on-stomach-elbows-supporting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-right-arm-supporting-l-arm-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_lay-right-leg-forward-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-run-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-run-reach.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-sneaking-forward.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-sprinting.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-stepping-forward-look-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walk-fwd-look-r.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walk-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walking-arms-on-hips.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walking-right-arm-on-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_loco-walking.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-holding-left-knee-reclining.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-holding-right-knee.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-resting-on-knees.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-elbows-on-legs-lean-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-elbows-on-legs-lean-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-knees-up-hold.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-lean-back-arms-supp.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-lean-fwd-elbows-on-knees.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-lean-fwd-hands-in-front.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-arms-x.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-hold.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-reclining.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-legs-x-right-arm-over-knee.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reaching-hands-towards-toes.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reaching-left-hand-towards-right-toe.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-facepalm.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-left-leg-crossed-hands-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-left-leg-crossed.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-look-down.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-reclined-right-leg-crossed-hands-hea.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-rev-sit-hands-grasping.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_sit-rev-sit-hands-relaxed.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-arms-behind-back.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-arms-down.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-arms-slightly-spread.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-bending-forward-arms-x-over-knees.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-hands-on-hips-look-back.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-l-hip-f.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-l-hip-m.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-l-leg-r-hip-l-arm-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-lean-back-arms-behind.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-lean-back-l-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-left-elbow-resting-obj.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-left-leg-obj-left-hand-on-knee-r-h.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-left-leg-obj-left-hand-on-knee.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-arms-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-l-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-leaning-obj.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-legs-x-left-arm-obj-r-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-down-r.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-fwd.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-l.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-look-r.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-arm-up-l-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-foot-l-leg-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-foot-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-hip-l-arm-up.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-r-hip.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-right-hand-behind-head.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-right-leg-obj-arms-x.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_stand-right-leg-obj-l-hip-right-hand-on.json +1 -0
- topview_3d_cli/_runtime/builtin-assets/poses/a3d_pose_t-pose.json +1 -0
- topview_3d_cli/_runtime/director-cli/applyIntent.mjs +35 -0
- topview_3d_cli/_runtime/director-cli/browser.mjs +47 -0
- topview_3d_cli/_runtime/director-cli/cli.mjs +56 -0
- topview_3d_cli/_runtime/director-cli/editSequence.mjs +125 -0
- topview_3d_cli/_runtime/director-cli/evaluate.mjs +254 -0
- topview_3d_cli/_runtime/director-cli/mediaUrl.mjs +47 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/draco/draco_decoder.js +34 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/draco/draco_decoder.wasm +0 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/draco/draco_wasm_wrapper.js +117 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/engine/index.mjs +22219 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/evaluate/index.mjs +4229 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/dist/headless/index.mjs +22832 -0
- topview_3d_cli/_runtime/director-cli/node_modules/@topview/3d-builder/package.json +75 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/LICENSE +21 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/build/three.core.js +59732 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/build/three.module.js +19552 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/controls/OrbitControls.js +1963 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/curves/NURBSCurve.js +155 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/curves/NURBSUtils.js +532 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/draco/draco_decoder.js +34 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/draco/draco_decoder.wasm +0 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/draco/draco_wasm_wrapper.js +117 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/libs/fflate.module.js +2672 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/lines/LineMaterial.js +703 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/lines/LineSegments2.js +426 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/lines/LineSegmentsGeometry.js +298 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/loaders/DRACOLoader.js +739 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/loaders/FBXLoader.js +4580 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/loaders/GLTFLoader.js +4860 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/utils/BufferGeometryUtils.js +1434 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/examples/jsm/utils/SkeletonUtils.js +496 -0
- topview_3d_cli/_runtime/director-cli/node_modules/three/package.json +123 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/LICENSE +21 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/index.js +4 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/package.json +118 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/ZodError.js +133 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/errors.js +9 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/external.js +6 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/errorUtil.js +6 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/parseUtil.js +109 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/typeAliases.js +1 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/helpers/util.js +133 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/locales/en.js +109 -0
- topview_3d_cli/_runtime/director-cli/node_modules/zod/v3/types.js +3693 -0
- topview_3d_cli/_runtime/director-cli/package.json +6 -0
- topview_3d_cli/_runtime/director-cli/playwright.mjs +26 -0
- topview_3d_cli/_runtime/director-cli/render.mjs +336 -0
- topview_3d_cli/_runtime/director-cli/static/headless.html +124 -0
- topview_3d_cli/_runtime/director-cli/static/measure.mjs +61 -0
- topview_3d_cli/_runtime/director-cli/static/pose.mjs +71 -0
- topview_3d_cli/_runtime/director-cli/staticIntent.mjs +135 -0
- topview_3d_cli/_runtime/runtime.json +6 -0
- topview_3d_cli/bom.py +326 -0
- topview_3d_cli/contracts/director-v1/schema/director-document.schema.json +1097 -0
- topview_3d_cli/contracts/director-v1/schema/fcurves-compact-v1.schema.json +105 -0
- topview_3d_cli/director_camera_view.py +54 -0
- topview_3d_cli/director_document.py +793 -0
- topview_3d_cli/director_geometry.py +66 -0
- topview_3d_cli/director_operations.py +679 -0
- topview_3d_cli/director_static.py +134 -0
- topview_3d_cli/local_assets.py +248 -0
- topview_3d_cli/local_bom.py +108 -0
- topview_3d_cli/local_catalog.py +156 -0
- topview_3d_cli/local_cli.py +493 -0
- topview_3d_cli/local_edit.py +350 -0
- topview_3d_cli/local_errors.py +104 -0
- topview_3d_cli/local_inspect.py +302 -0
- topview_3d_cli/local_project.py +280 -0
- topview_3d_cli/local_read.py +139 -0
- topview_3d_cli/local_render.py +97 -0
- topview_3d_cli/node_batch.py +299 -0
- topview_3d_cli/renderer.py +75 -0
- topview_3d_cli/runtime.py +107 -0
- topview_3d_cli-0.1.0.dist-info/METADATA +48 -0
- topview_3d_cli-0.1.0.dist-info/RECORD +332 -0
- topview_3d_cli-0.1.0.dist-info/WHEEL +5 -0
- topview_3d_cli-0.1.0.dist-info/entry_points.txt +2 -0
- topview_3d_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
- topview_3d_cli-0.1.0.dist-info/licenses/LICENSE-ASSETS +396 -0
- topview_3d_cli-0.1.0.dist-info/licenses/NOTICE +18 -0
- topview_3d_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { applyStudioIntent } from '@topview/3d-builder/evaluate'
|
|
2
|
+
import { evaluateDocumentFrames } from './evaluate.mjs'
|
|
3
|
+
|
|
4
|
+
const axes = ['x', 'y', 'z']
|
|
5
|
+
function finite(value, label) {
|
|
6
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) throw new Error(`INVALID_${label}`)
|
|
7
|
+
return value
|
|
8
|
+
}
|
|
9
|
+
function vector(value, label, positive = false) {
|
|
10
|
+
if (!value || typeof value !== 'object' || !Object.keys(value).length) throw new Error(`INVALID_${label}`)
|
|
11
|
+
for (const [key, number] of Object.entries(value)) {
|
|
12
|
+
if (!axes.includes(key) || (positive && number <= 0)) throw new Error(`INVALID_${label}`)
|
|
13
|
+
finite(number, label)
|
|
14
|
+
}
|
|
15
|
+
return value
|
|
16
|
+
}
|
|
17
|
+
function length(a, b) { return Math.hypot(...axes.map(axis => a[axis] - b[axis])) }
|
|
18
|
+
function worldToSubject(world, subject, binding) {
|
|
19
|
+
const r = subject.transform.rotation
|
|
20
|
+
const yaw = binding.followRotation ? Math.atan2(Math.sin(r.y * Math.PI / 180), Math.cos(r.x * Math.PI / 180) * Math.cos(r.y * Math.PI / 180)) : 0
|
|
21
|
+
const dx = world.x - subject.transform.position.x, dz = world.z - subject.transform.position.z
|
|
22
|
+
return { x: dx * Math.cos(yaw) - dz * Math.sin(yaw), y: world.y - subject.transform.position.y, z: dx * Math.sin(yaw) + dz * Math.cos(yaw) }
|
|
23
|
+
}
|
|
24
|
+
function subjectToWorld(local, subject, binding) {
|
|
25
|
+
const r = subject.transform.rotation
|
|
26
|
+
const yaw = binding.followRotation ? Math.atan2(Math.sin(r.y * Math.PI / 180), Math.cos(r.x * Math.PI / 180) * Math.cos(r.y * Math.PI / 180)) : 0
|
|
27
|
+
const p = subject.transform.position
|
|
28
|
+
return { x: p.x + local.x * Math.cos(yaw) + local.z * Math.sin(yaw), y: p.y + local.y, z: p.z - local.x * Math.sin(yaw) + local.z * Math.cos(yaw) }
|
|
29
|
+
}
|
|
30
|
+
function resolveBoundCamera(document, node) {
|
|
31
|
+
const binding = node.camera?.subject
|
|
32
|
+
if (binding?.follow) {
|
|
33
|
+
const subject = document.content.nodes.find(n => n.id === binding.nodeId && n.type === 'character')
|
|
34
|
+
if (!subject) throw new Error('CAMERA_SUBJECT_MISSING')
|
|
35
|
+
node.transform.position = subjectToWorld(binding.offset, subject, binding)
|
|
36
|
+
node.camera.lookAt = subjectToWorld(binding.lookAtOffset, subject, binding)
|
|
37
|
+
}
|
|
38
|
+
const target = node.camera?.lookAtTarget
|
|
39
|
+
if (target) {
|
|
40
|
+
const subject = document.content.nodes.find(n => n.id === target.nodeId)
|
|
41
|
+
if (!subject) throw new Error('CAMERA_TARGET_MISSING')
|
|
42
|
+
const offset = target.offset || { x: 0, y: 1.2, z: 0 }
|
|
43
|
+
node.camera.lookAt = Object.fromEntries(axes.map(axis => [axis, subject.transform.position[axis] + offset[axis]]))
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Same Euler-to-look-at convention used by the Editor camera inspector.
|
|
47
|
+
function aim(position, rotation, distance) {
|
|
48
|
+
const [x, y, z] = axes.map(axis => rotation[axis] * Math.PI / 180)
|
|
49
|
+
const ix = -Math.sin(y) * Math.cos(x), iy = Math.sin(x), iz = -Math.cos(y) * Math.cos(x)
|
|
50
|
+
return { x: position.x + (Math.cos(z) * ix - Math.sin(z) * iy) * distance,
|
|
51
|
+
y: position.y + (Math.sin(z) * ix + Math.cos(z) * iy) * distance,
|
|
52
|
+
z: position.z + iz * distance }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function applyStaticIntent(body) {
|
|
56
|
+
const intent = { ...body.intent }
|
|
57
|
+
const document = structuredClone(body.document)
|
|
58
|
+
const id = intent.nodeId || intent.cameraNodeId
|
|
59
|
+
const node = document.content.nodes.find(n => n.id === id)
|
|
60
|
+
const requireNode = (nodeId, type) => {
|
|
61
|
+
const target = document.content.nodes.find(n => n.id === nodeId)
|
|
62
|
+
if (!target) throw new Error(`NODE_NOT_FOUND:${nodeId}`)
|
|
63
|
+
if (type && target.type !== type) throw new Error(`NODE_TYPE_REQUIRED:${nodeId}:${type}`)
|
|
64
|
+
return target
|
|
65
|
+
}
|
|
66
|
+
// Validate references and role types before passing an intent to the builder.
|
|
67
|
+
if (intent.cameraNodeId) requireNode(intent.cameraNodeId, 'camera')
|
|
68
|
+
if (intent.subjectNodeId) requireNode(intent.subjectNodeId, 'character')
|
|
69
|
+
if (intent.type === 'add-camera' && node) throw new Error(`NODE_ALREADY_EXISTS:${id}`)
|
|
70
|
+
if (['patch-transform', 'move-node', 'set-distance', 'set-fov'].includes(intent.type)) {
|
|
71
|
+
if (!node) throw new Error(`NODE_NOT_FOUND:${id}`)
|
|
72
|
+
if (node.locked) throw new Error(`NODE_LOCKED:${id}`)
|
|
73
|
+
resolveBoundCamera(document, node)
|
|
74
|
+
}
|
|
75
|
+
if (intent.type === 'move-node') {
|
|
76
|
+
if (Boolean(intent.position) === Boolean(intent.offset)) throw new Error('POSITION_OR_OFFSET_REQUIRED')
|
|
77
|
+
if (intent.offset) {
|
|
78
|
+
vector(intent.offset, 'OFFSET')
|
|
79
|
+
intent.position = Object.fromEntries(axes.map(axis => [axis, node.transform.position[axis] + (intent.offset[axis] ?? 0)]))
|
|
80
|
+
}
|
|
81
|
+
intent.type = 'patch-transform'
|
|
82
|
+
}
|
|
83
|
+
if (intent.type === 'set-fov') {
|
|
84
|
+
finite(intent.fov, 'FOV')
|
|
85
|
+
if (intent.fov < 12 || intent.fov > 120) throw new Error('FOV_OUT_OF_RANGE')
|
|
86
|
+
}
|
|
87
|
+
if (intent.type === 'set-distance') {
|
|
88
|
+
finite(intent.distance, 'DISTANCE')
|
|
89
|
+
if (intent.distance < 0.1 || intent.distance > 200) throw new Error('DISTANCE_OUT_OF_RANGE')
|
|
90
|
+
}
|
|
91
|
+
if (intent.type === 'patch-transform') {
|
|
92
|
+
if (!['position', 'rotation', 'scale', 'name'].some(key => intent[key] !== undefined)) throw new Error('EMPTY_NODE_PATCH')
|
|
93
|
+
for (const key of ['position', 'rotation', 'scale']) if (intent[key] !== undefined) vector(intent[key], key.toUpperCase(), key === 'scale')
|
|
94
|
+
if (intent.name !== undefined && (typeof intent.name !== 'string' || !intent.name.trim() || intent.name.length > 128)) throw new Error('INVALID_NODE_NAME')
|
|
95
|
+
}
|
|
96
|
+
const before = node ? structuredClone(node) : null
|
|
97
|
+
const result = applyStudioIntent({ ...body, document, intent })
|
|
98
|
+
if (intent.type === 'patch-transform') {
|
|
99
|
+
const patched = result.document.content.nodes.find(n => n.id === intent.nodeId)
|
|
100
|
+
if (intent.name !== undefined) patched.name = intent.name.trim()
|
|
101
|
+
if (patched.camera && (intent.position || intent.rotation)) {
|
|
102
|
+
const distance = Math.max(0.1, length(before.transform.position, before.camera.lookAt))
|
|
103
|
+
const pinned = before.camera.lookAtTarget || before.camera.subject?.follow
|
|
104
|
+
patched.camera.lookAt = intent.rotation ? aim(patched.transform.position, patched.transform.rotation, distance)
|
|
105
|
+
: pinned ? { ...before.camera.lookAt }
|
|
106
|
+
: Object.fromEntries(axes.map(axis => [axis, before.camera.lookAt[axis] + patched.transform.position[axis] - before.transform.position[axis]]))
|
|
107
|
+
const target = patched.camera.lookAtTarget
|
|
108
|
+
if (target) {
|
|
109
|
+
const subject = result.document.content.nodes.find(n => n.id === target.nodeId)
|
|
110
|
+
target.offset = Object.fromEntries(axes.map(axis => [axis, patched.camera.lookAt[axis] - subject.transform.position[axis]]))
|
|
111
|
+
}
|
|
112
|
+
const binding = patched.camera.subject
|
|
113
|
+
if (binding) {
|
|
114
|
+
const subject = result.document.content.nodes.find(n => n.id === binding.nodeId)
|
|
115
|
+
binding.offset = worldToSubject(patched.transform.position, subject, binding)
|
|
116
|
+
binding.lookAtOffset = worldToSubject(patched.camera.lookAt, subject, binding)
|
|
117
|
+
binding.distance = length(patched.transform.position, patched.camera.lookAt)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return result
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function evaluateStaticPlan(body) {
|
|
125
|
+
let document = structuredClone(body.document)
|
|
126
|
+
if (!Array.isArray(body.changes) || !body.changes.length || body.changes.length > 64) throw new Error('INVALID_PLAN_CHANGES')
|
|
127
|
+
for (const change of body.changes) {
|
|
128
|
+
const patch = Object.fromEntries(['position', 'rotation', 'scale', 'name'].filter(k => change[k] !== undefined).map(k => [k, change[k]]))
|
|
129
|
+
if (!Object.keys(patch).length && change.fov === undefined && change.distance === undefined) throw new Error('EMPTY_PLAN_CHANGE')
|
|
130
|
+
if (Object.keys(patch).length) document = applyStaticIntent({ document, intent: { type: 'patch-transform', nodeId: change.nodeId, ...patch } }).document
|
|
131
|
+
if (change.fov !== undefined) document = applyStaticIntent({ document, intent: { type: 'set-fov', cameraNodeId: change.nodeId, fov: change.fov } }).document
|
|
132
|
+
if (change.distance !== undefined) document = applyStaticIntent({ document, intent: { type: 'set-distance', cameraNodeId: change.nodeId, distance: change.distance } }).document
|
|
133
|
+
}
|
|
134
|
+
return { ...evaluateDocumentFrames({ ...body, document }), dryRun: true }
|
|
135
|
+
}
|
topview_3d_cli/bom.py
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Plan / constraint record (BOM): intent, relationships, constraints, camera roles, model review.
|
|
2
|
+
|
|
3
|
+
Pure merge and evidence logic behind the ``topview-3d-cli bom`` and ``inspect views`` commands (stored as
|
|
4
|
+
``.topview3d/bom.json``). ``checkpoint`` fields are model-owned; the
|
|
5
|
+
runtime owns ``observed`` and ``verification``. Errors are ``ValueError("<CODE>: ...")``.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from jsonschema import Draft7Validator
|
|
13
|
+
|
|
14
|
+
BOM_VERSION = 2
|
|
15
|
+
BOM_MAX_BYTES = 64 * 1024
|
|
16
|
+
CHECKPOINT_FIELDS = ("intent", "relationships", "constraints", "cameras", "notes")
|
|
17
|
+
GUIDES_READ_LIMIT = 32
|
|
18
|
+
OBSERVED_NODE_LIMIT = 200
|
|
19
|
+
|
|
20
|
+
_short = {"type": "string", "maxLength": 2000}
|
|
21
|
+
_entry = {"type": "object", "maxProperties": 24,
|
|
22
|
+
"properties": {"id": {"type": "string", "minLength": 1, "maxLength": 128}},
|
|
23
|
+
"required": ["id"], "additionalProperties": True}
|
|
24
|
+
_review_status = {"enum": ["passed", "failed", "unknown"]}
|
|
25
|
+
CHECKPOINT_SCHEMA = {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": False,
|
|
28
|
+
"required": ["expectedSceneSequence", "expectedBomRevision"],
|
|
29
|
+
"anyOf": [{"required": [key]} for key in (*CHECKPOINT_FIELDS, "remove", "modelReview")],
|
|
30
|
+
"properties": {
|
|
31
|
+
"expectedSceneSequence": {"type": "integer", "minimum": 0},
|
|
32
|
+
"expectedBomRevision": {"type": "integer", "minimum": 0},
|
|
33
|
+
"intent": {"anyOf": [_short, {"type": "object", "maxProperties": 16}]},
|
|
34
|
+
"relationships": {"type": "array", "maxItems": 128, "items": _entry},
|
|
35
|
+
"constraints": {"type": "array", "maxItems": 128, "items": _entry},
|
|
36
|
+
"cameras": {"type": "array", "maxItems": 32, "items": _entry},
|
|
37
|
+
"notes": {"type": "array", "maxItems": 64, "items": _short},
|
|
38
|
+
"remove": {
|
|
39
|
+
"type": "object", "additionalProperties": False,
|
|
40
|
+
"properties": {key: {"type": "array", "maxItems": 128, "uniqueItems": True,
|
|
41
|
+
"items": {"type": "string", "minLength": 1, "maxLength": 128}}
|
|
42
|
+
for key in ("relationships", "constraints", "cameras")},
|
|
43
|
+
},
|
|
44
|
+
"modelReview": {
|
|
45
|
+
"type": "object", "additionalProperties": False,
|
|
46
|
+
"required": ["views", "constraints"],
|
|
47
|
+
"properties": {
|
|
48
|
+
"views": {"type": "array", "minItems": 1, "maxItems": 24, "items": {
|
|
49
|
+
"type": "object", "additionalProperties": False,
|
|
50
|
+
"required": ["cameraNodeId", "frames", "status", "reason"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"cameraNodeId": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
53
|
+
"frames": {"type": "array", "minItems": 1, "maxItems": 3,
|
|
54
|
+
"items": {"type": "integer", "minimum": 0}},
|
|
55
|
+
"sha256": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
56
|
+
"status": _review_status,
|
|
57
|
+
"reason": {"type": "string", "minLength": 1, "maxLength": 2000},
|
|
58
|
+
},
|
|
59
|
+
}},
|
|
60
|
+
"constraints": {"type": "array", "maxItems": 128, "items": {
|
|
61
|
+
"type": "object", "additionalProperties": False,
|
|
62
|
+
"required": ["id", "status", "reason"],
|
|
63
|
+
"properties": {"id": {"type": "string", "minLength": 1, "maxLength": 128},
|
|
64
|
+
"status": _review_status,
|
|
65
|
+
"reason": {"type": "string", "minLength": 1, "maxLength": 2000}},
|
|
66
|
+
}},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
_CHECKPOINT_VALIDATOR = Draft7Validator(CHECKPOINT_SCHEMA)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def empty_bom(project_id: str) -> dict[str, Any]:
|
|
75
|
+
return {
|
|
76
|
+
"version": BOM_VERSION,
|
|
77
|
+
"projectId": project_id,
|
|
78
|
+
"bomRevision": 0,
|
|
79
|
+
"intent": None,
|
|
80
|
+
"relationships": [],
|
|
81
|
+
"constraints": [],
|
|
82
|
+
"cameras": [],
|
|
83
|
+
"notes": [],
|
|
84
|
+
"guidesRead": [],
|
|
85
|
+
"observed": {"sceneSequence": None, "nodes": []},
|
|
86
|
+
"verification": {"sceneSequence": None, "stale": True},
|
|
87
|
+
"modelReview": None,
|
|
88
|
+
"updatedAt": None,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def normalize_bom(raw: Any, project_id: str) -> dict[str, Any]:
|
|
93
|
+
"""Coerce a stored payload into the current shape; unknown / corrupt input falls back to empty."""
|
|
94
|
+
base = empty_bom(project_id)
|
|
95
|
+
if not isinstance(raw, dict):
|
|
96
|
+
return base
|
|
97
|
+
for key in CHECKPOINT_FIELDS:
|
|
98
|
+
if key in raw:
|
|
99
|
+
base[key] = raw[key]
|
|
100
|
+
# Upgrade v1 whole-field records to stable ids without dropping their meaning.
|
|
101
|
+
for key in ("relationships", "constraints", "cameras"):
|
|
102
|
+
upgraded = []
|
|
103
|
+
for index, entry in enumerate(base.get(key) if isinstance(base.get(key), list) else []):
|
|
104
|
+
if isinstance(entry, dict):
|
|
105
|
+
row = dict(entry)
|
|
106
|
+
row.setdefault("id", row.get("nodeId") or f"legacy-{key}-{index + 1}")
|
|
107
|
+
else:
|
|
108
|
+
row = {"id": f"legacy-{key}-{index + 1}", "text": str(entry)}
|
|
109
|
+
upgraded.append(row)
|
|
110
|
+
base[key] = upgraded
|
|
111
|
+
revision = raw.get("bomRevision")
|
|
112
|
+
base["bomRevision"] = revision if isinstance(revision, int) and revision >= 0 else 0
|
|
113
|
+
if isinstance(raw.get("guidesRead"), list):
|
|
114
|
+
base["guidesRead"] = [str(item) for item in raw["guidesRead"] if isinstance(item, str)][:GUIDES_READ_LIMIT]
|
|
115
|
+
if isinstance(raw.get("observed"), dict):
|
|
116
|
+
base["observed"] = {"sceneSequence": raw["observed"].get("sceneSequence"),
|
|
117
|
+
"nodes": list(raw["observed"].get("nodes") or [])[:OBSERVED_NODE_LIMIT]}
|
|
118
|
+
if isinstance(raw.get("verification"), dict):
|
|
119
|
+
base["verification"] = dict(raw["verification"])
|
|
120
|
+
base["verification"]["stale"] = bool(base["verification"].get("stale", True))
|
|
121
|
+
if isinstance(raw.get("modelReview"), dict):
|
|
122
|
+
base["modelReview"] = raw["modelReview"]
|
|
123
|
+
base["updatedAt"] = raw.get("updatedAt")
|
|
124
|
+
return base
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def encode_bom(bom: dict[str, Any]) -> bytes:
|
|
128
|
+
data = json.dumps(bom, ensure_ascii=False, indent=2, allow_nan=False).encode("utf-8")
|
|
129
|
+
if len(data) > BOM_MAX_BYTES:
|
|
130
|
+
raise ValueError(f"BOM_TOO_LARGE: {len(data)} bytes > {BOM_MAX_BYTES}")
|
|
131
|
+
return data
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _merge_entries(current: Any, updates: list[dict], removed: set[str], field: str) -> list[dict]:
|
|
135
|
+
entries = list(current) if isinstance(current, list) else []
|
|
136
|
+
if any(not isinstance(entry, dict) or not entry.get("id") for entry in entries):
|
|
137
|
+
raise ValueError(f"BOM_INVALID:{field} entries require stable id")
|
|
138
|
+
update_ids = [entry["id"] for entry in updates]
|
|
139
|
+
if len(update_ids) != len(set(update_ids)) or removed.intersection(update_ids):
|
|
140
|
+
raise ValueError(f"BOM_DUPLICATE_OR_CONFLICTING_IDS:{field}")
|
|
141
|
+
merged = [dict(entry) for entry in entries if entry["id"] not in removed]
|
|
142
|
+
by_id = {entry["id"]: entry for entry in merged}
|
|
143
|
+
for update in updates:
|
|
144
|
+
if update["id"] in by_id:
|
|
145
|
+
by_id[update["id"]].update(update)
|
|
146
|
+
else:
|
|
147
|
+
row = dict(update)
|
|
148
|
+
merged.append(row)
|
|
149
|
+
by_id[row["id"]] = row
|
|
150
|
+
return merged
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def apply_checkpoint(bom: dict[str, Any], patch: dict[str, Any]) -> dict[str, Any]:
|
|
154
|
+
"""Pure semantic merge; runtime-owned evidence is not writable."""
|
|
155
|
+
error = next(_CHECKPOINT_VALIDATOR.iter_errors(patch), None)
|
|
156
|
+
if error:
|
|
157
|
+
raise ValueError(f"BOM_CHECKPOINT_INVALID:{'.'.join(map(str, error.path))}: {error.message}")
|
|
158
|
+
json.dumps(patch, allow_nan=False)
|
|
159
|
+
merged = dict(bom)
|
|
160
|
+
for key in ("intent", "notes"):
|
|
161
|
+
if key in patch:
|
|
162
|
+
merged[key] = patch[key]
|
|
163
|
+
removals = patch.get("remove") or {}
|
|
164
|
+
for key in ("relationships", "constraints", "cameras"):
|
|
165
|
+
if key in patch or key in removals:
|
|
166
|
+
merged[key] = _merge_entries(merged.get(key), patch.get(key, []),
|
|
167
|
+
set(removals.get(key, [])), key)
|
|
168
|
+
return merged
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def merge_checkpoint(current: dict[str, Any], patch: dict[str, Any], sequence: Any) -> dict[str, Any]:
|
|
172
|
+
"""Check both CAS versions against ``sequence`` and the stored revision; return the merged BOM."""
|
|
173
|
+
if sequence != patch.get("expectedSceneSequence"):
|
|
174
|
+
raise ValueError("SCENE_SEQUENCE_CONFLICT: read current scene before checkpointing")
|
|
175
|
+
revision = int(current.get("bomRevision") or 0)
|
|
176
|
+
if revision != patch.get("expectedBomRevision"):
|
|
177
|
+
raise ValueError(f"BOM_REVISION_CONFLICT: current bomRevision={revision}; merge against current BOM")
|
|
178
|
+
merged = apply_checkpoint(current, patch)
|
|
179
|
+
semantic_change = any(key in patch for key in (*CHECKPOINT_FIELDS, "remove"))
|
|
180
|
+
if semantic_change and isinstance(merged.get("modelReview"), dict):
|
|
181
|
+
merged["modelReview"] = {**merged["modelReview"], "stale": True}
|
|
182
|
+
if "modelReview" in patch:
|
|
183
|
+
evidence = merged.get("verification") or {}
|
|
184
|
+
if evidence.get("stale") or evidence.get("sceneSequence") != sequence or not evidence.get("views"):
|
|
185
|
+
raise ValueError("BOM_REVIEW_REQUIRES_CURRENT_RUNTIME_EVIDENCE")
|
|
186
|
+
seen = set()
|
|
187
|
+
filled_views = []
|
|
188
|
+
for review in patch["modelReview"]["views"]:
|
|
189
|
+
key = (review["cameraNodeId"], tuple(review["frames"]))
|
|
190
|
+
if key in seen:
|
|
191
|
+
raise ValueError("BOM_REVIEW_DUPLICATE_VIEW")
|
|
192
|
+
seen.add(key)
|
|
193
|
+
filled_views.append(_bind_review_sha(review, evidence.get("views") or []))
|
|
194
|
+
patch = {**patch, "modelReview": {**patch["modelReview"], "views": filled_views}}
|
|
195
|
+
constraint_ids = [row["id"] for row in patch["modelReview"]["constraints"]]
|
|
196
|
+
if len(constraint_ids) != len(set(constraint_ids)):
|
|
197
|
+
raise ValueError("BOM_REVIEW_DUPLICATE_CONSTRAINT")
|
|
198
|
+
required = {row["id"] for row in merged.get("relationships", [])
|
|
199
|
+
if row.get("priority", "hard") == "hard"}
|
|
200
|
+
reviewed_cameras = {row["cameraNodeId"] for row in patch["modelReview"]["views"]}
|
|
201
|
+
cameras = merged.get("cameras") or []
|
|
202
|
+
missing = {
|
|
203
|
+
"constraints": sorted(required - set(constraint_ids)),
|
|
204
|
+
"cameras": sorted({row.get("nodeId") for row in cameras if row.get("nodeId")} - reviewed_cameras),
|
|
205
|
+
"cameraRoles": sorted({"story", "layout_overview"} - {row.get("role") for row in cameras}),
|
|
206
|
+
}
|
|
207
|
+
merged["modelReview"] = {"sceneSequence": sequence, "stale": False,
|
|
208
|
+
**patch["modelReview"], "missingCoverage": missing,
|
|
209
|
+
"coverageComplete": not any(missing.values())}
|
|
210
|
+
merged["bomRevision"] = revision + 1
|
|
211
|
+
encode_bom(merged) # fail before writing when the merged record is oversized
|
|
212
|
+
return merged
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def observed_from_snapshot(snapshot: Any) -> dict[str, Any]:
|
|
216
|
+
"""Compact node inventory of the authoritative document (ids, types, names, assets)."""
|
|
217
|
+
document = snapshot.get("document") if isinstance(snapshot, dict) else None
|
|
218
|
+
content = document.get("content") if isinstance(document, dict) else None
|
|
219
|
+
nodes = content.get("nodes") if isinstance(content, dict) else None
|
|
220
|
+
rows = []
|
|
221
|
+
for node in (nodes or [])[:OBSERVED_NODE_LIMIT]:
|
|
222
|
+
if not isinstance(node, dict):
|
|
223
|
+
continue
|
|
224
|
+
row = {"id": node.get("id"), "type": node.get("type")}
|
|
225
|
+
if node.get("name"):
|
|
226
|
+
row["name"] = node["name"]
|
|
227
|
+
metadata = node.get("metadata") if isinstance(node.get("metadata"), dict) else {}
|
|
228
|
+
if metadata.get("assetId"):
|
|
229
|
+
row["assetId"] = metadata["assetId"]
|
|
230
|
+
rows.append(row)
|
|
231
|
+
sequence = snapshot.get("sceneSequence") if isinstance(snapshot, dict) else None
|
|
232
|
+
return {"sceneSequence": sequence, "nodes": rows}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def derive_verification(bom: dict[str, Any], observed: dict[str, Any]) -> dict[str, Any]:
|
|
236
|
+
verification = dict(bom.get("verification") or {})
|
|
237
|
+
checked = verification.get("sceneSequence")
|
|
238
|
+
verification["stale"] = checked is None or checked != observed.get("sceneSequence")
|
|
239
|
+
verification.setdefault("sceneSequence", None)
|
|
240
|
+
return verification
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def mark_verified(bom: dict[str, Any], scene_sequence: int) -> dict[str, Any]:
|
|
244
|
+
"""Record that checks passed against ``scene_sequence``; ``turn_end_sync`` re-derives ``stale``."""
|
|
245
|
+
updated = dict(bom)
|
|
246
|
+
updated["verification"] = {"sceneSequence": scene_sequence, "stale": False}
|
|
247
|
+
return updated
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _evidence_view(views: list, camera_node_id, frames) -> dict[str, Any] | None:
|
|
251
|
+
for row in views:
|
|
252
|
+
if row.get("cameraNodeId") == camera_node_id and row.get("frames") == frames:
|
|
253
|
+
return row
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _bind_review_sha(review: dict[str, Any], views: list) -> dict[str, Any]:
|
|
258
|
+
"""Accept contact-sheet or per-frame hashes; fill sha256 from current evidence when omitted."""
|
|
259
|
+
evidence = _evidence_view(views, review["cameraNodeId"], review["frames"])
|
|
260
|
+
sheet = evidence.get("sha256") if isinstance(evidence, dict) else None
|
|
261
|
+
frame_shas = list((evidence or {}).get("frameSha256s") or [])
|
|
262
|
+
given = review.get("sha256")
|
|
263
|
+
accepted = {value for value in [sheet, *frame_shas] if value}
|
|
264
|
+
if not given:
|
|
265
|
+
if not sheet:
|
|
266
|
+
raise ValueError(
|
|
267
|
+
f"BOM_REVIEW_IMAGE_MISMATCH: {review['cameraNodeId']} frames={review['frames']} "
|
|
268
|
+
"has no current contactSheet sha256"
|
|
269
|
+
)
|
|
270
|
+
return {**review, "sha256": sheet}
|
|
271
|
+
if given in accepted:
|
|
272
|
+
return review
|
|
273
|
+
detail = f"expected sha256={sheet}(contactSheet)" if sheet else "no contactSheet sha256"
|
|
274
|
+
if frame_shas:
|
|
275
|
+
detail += f", frame sha={','.join(frame_shas)}"
|
|
276
|
+
raise ValueError(
|
|
277
|
+
f"BOM_REVIEW_IMAGE_MISMATCH: {review['cameraNodeId']} frames={review['frames']} {detail}"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _view_evidence(row: dict[str, Any]) -> dict[str, Any]:
|
|
282
|
+
render = row.get("render") if isinstance(row.get("render"), dict) else {}
|
|
283
|
+
sheet = render.get("contactSheet") if isinstance(render.get("contactSheet"), dict) else {}
|
|
284
|
+
frames = render.get("frames") if isinstance(render.get("frames"), list) else []
|
|
285
|
+
frame_shas = list(render.get("frameSha256s") or [])
|
|
286
|
+
if not frame_shas:
|
|
287
|
+
frame_shas = [frame.get("sha256") for frame in frames if isinstance(frame, dict) and frame.get("sha256")]
|
|
288
|
+
return {
|
|
289
|
+
"cameraNodeId": row.get("cameraNodeId"),
|
|
290
|
+
"frames": row.get("frames") or [],
|
|
291
|
+
"numericalOk": row.get("numerical", {}).get("ok") is True,
|
|
292
|
+
"numericalIssues": row.get("numerical", {}).get("issues") or [],
|
|
293
|
+
"renderStatus": row.get("renderStatus"),
|
|
294
|
+
"path": sheet.get("path"),
|
|
295
|
+
"sha256": sheet.get("sha256"),
|
|
296
|
+
"frameSha256s": frame_shas,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def record_runtime_verification(bom: dict[str, Any], result: dict[str, Any]) -> dict[str, Any]:
|
|
301
|
+
"""Merge runtime evidence from one inspection; keep same-sequence cameras not re-checked."""
|
|
302
|
+
updated = dict(bom)
|
|
303
|
+
sequence = result.get("sceneSequence")
|
|
304
|
+
previous = dict(bom.get("verification") or {})
|
|
305
|
+
merged: dict[tuple[Any, tuple], dict[str, Any]] = {}
|
|
306
|
+
if previous.get("sceneSequence") == sequence:
|
|
307
|
+
for row in previous.get("views") or []:
|
|
308
|
+
if isinstance(row, dict):
|
|
309
|
+
merged[(row.get("cameraNodeId"), tuple(row.get("frames") or []))] = dict(row)
|
|
310
|
+
for row in result.get("views") or []:
|
|
311
|
+
if not isinstance(row, dict):
|
|
312
|
+
continue
|
|
313
|
+
evidence = _view_evidence(row)
|
|
314
|
+
merged[(evidence.get("cameraNodeId"), tuple(evidence.get("frames") or []))] = evidence
|
|
315
|
+
updated["verification"] = {
|
|
316
|
+
"source": "runtime-v2",
|
|
317
|
+
"sceneSequence": sequence,
|
|
318
|
+
"stale": bool(result.get("stale", True)),
|
|
319
|
+
"checksComplete": bool(result.get("checksComplete")),
|
|
320
|
+
"views": list(merged.values()),
|
|
321
|
+
"geometry": result.get("geometry") or {},
|
|
322
|
+
**({"primaryStoryPreview": result["primaryStoryPreview"]}
|
|
323
|
+
if result.get("primaryStoryPreview") else {}),
|
|
324
|
+
}
|
|
325
|
+
updated["modelReview"] = None
|
|
326
|
+
return updated
|