framefields 2.2.0-beta.4 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/audio/index.d.mts +4 -4
- package/dist/audio/index.mjs +10 -10
- package/dist/{audio-BvYmTu5T.d.mts → audio-BNnEclHU.d.mts} +3 -3
- package/dist/{audio-BvYmTu5T.d.mts.map → audio-BNnEclHU.d.mts.map} +1 -1
- package/dist/{audio-DMUr01vS.mjs → audio-ChRqnFwB.mjs} +3 -3
- package/dist/{audio-DMUr01vS.mjs.map → audio-ChRqnFwB.mjs.map} +1 -1
- package/dist/{dist-cjxDXQdx.mjs → dist-PwT_Vmax.mjs} +102 -6
- package/dist/{dist-cjxDXQdx.mjs.map → dist-PwT_Vmax.mjs.map} +1 -1
- package/dist/{dist-CT___nEU.mjs → dist-t-NLlnhM.mjs} +7 -5
- package/dist/dist-t-NLlnhM.mjs.map +1 -0
- package/dist/effects/index.d.mts +5 -5
- package/dist/effects/index.mjs +36 -36
- package/dist/{effects-C06-AOge.mjs → effects-Duoqc_Oa.mjs} +532 -4
- package/dist/effects-Duoqc_Oa.mjs.map +1 -0
- package/dist/fonts/index.d.mts +1 -1
- package/dist/fonts/index.mjs +2 -2
- package/dist/{fonts-C0UuLQvr.mjs → fonts-CgNmeFXh.mjs} +2 -2
- package/dist/{fonts-C0UuLQvr.mjs.map → fonts-CgNmeFXh.mjs.map} +1 -1
- package/dist/{gpu-Du7GpVFW.mjs → gpu-1ZpAuQOR.mjs} +2 -2
- package/dist/{gpu-Du7GpVFW.mjs.map → gpu-1ZpAuQOR.mjs.map} +1 -1
- package/dist/{index-B-oUlLoO.d.mts → index-AdrZIWT0.d.mts} +27 -2
- package/dist/index-AdrZIWT0.d.mts.map +1 -0
- package/dist/{index-BcA8rep0.d.mts → index-B-55dXDu.d.mts} +1 -1
- package/dist/{index-BcA8rep0.d.mts.map → index-B-55dXDu.d.mts.map} +1 -1
- package/dist/{index-D-Iek5sq.d.mts → index-B3BwLHa6.d.mts} +3 -3
- package/dist/{index-D-Iek5sq.d.mts.map → index-B3BwLHa6.d.mts.map} +1 -1
- package/dist/{index-CwbszPDG.d.mts → index-BU51pxZO.d.mts} +956 -9
- package/dist/index-BU51pxZO.d.mts.map +1 -0
- package/dist/{index-XdQPNJnq.d.mts → index-CeRv1KC0.d.mts} +3 -3
- package/dist/{index-XdQPNJnq.d.mts.map → index-CeRv1KC0.d.mts.map} +1 -1
- package/dist/{index-DXfp_T-3.d.mts → index-Cg2ptXcs.d.mts} +2 -2
- package/dist/{index-DXfp_T-3.d.mts.map → index-Cg2ptXcs.d.mts.map} +1 -1
- package/dist/{index-CdyE1tst.d.mts → index-QmkcUL3W.d.mts} +2 -2
- package/dist/{index-CdyE1tst.d.mts.map → index-QmkcUL3W.d.mts.map} +1 -1
- package/dist/index.d.mts +1438 -66
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3122 -83
- package/dist/index.mjs.map +1 -1
- package/dist/preview/index.d.mts +2 -2
- package/dist/preview/index.mjs +10 -10
- package/dist/{preview-M0TsUzC6.mjs → preview-CArsjmkd.mjs} +2 -2
- package/dist/{preview-M0TsUzC6.mjs.map → preview-CArsjmkd.mjs.map} +1 -1
- package/dist/preview-engine/audio.mjs +1 -1
- package/dist/preview-engine/chunk-2JKXYG6J.mjs +127 -0
- package/dist/preview-engine/chunk-7I7PKKDL.mjs +214 -0
- package/dist/preview-engine/chunk-BQY4BUMN.mjs +1 -0
- package/dist/preview-engine/{chunk-RSMO7NPN.mjs → chunk-BSOJR4JU.mjs} +1 -1
- package/dist/preview-engine/chunk-BVA5NGSH.mjs +1 -0
- package/dist/preview-engine/chunk-DLAHSLNX.mjs +1522 -0
- package/dist/preview-engine/{chunk-ZTHWCQ2A.mjs → chunk-FNE22WGA.mjs} +1 -1
- package/dist/preview-engine/chunk-KERW7QJ4.mjs +174 -0
- package/dist/preview-engine/{chunk-7B5S5D4N.mjs → chunk-TPAEN6VV.mjs} +1 -1
- package/dist/preview-engine/chunk-YQRKT47I.mjs +1 -0
- package/dist/preview-engine/crypto-W5UAWSNT.mjs +1 -0
- package/dist/preview-engine/effects.mjs +1 -1
- package/dist/preview-engine/framefields.mjs +26 -26
- package/dist/preview-engine/player.mjs +5 -5
- package/dist/preview-engine/rapier-AHBIGHEL.mjs +1 -0
- package/dist/preview-engine/rapier-JD7KNQRV.mjs +1 -0
- package/dist/preview-engine/renderers-527ONFIA.mjs +228 -0
- package/dist/preview-engine/renderers-6ODPWZ2R.mjs +96 -0
- package/dist/preview-engine/renderers-7NCG6BYS.mjs +94 -0
- package/dist/preview-engine/renderers-BGGRPAVV.mjs +363 -0
- package/dist/preview-engine/renderers-DN2GF3I7.mjs +82 -0
- package/dist/preview-engine/renderers-FGHOLJJK.mjs +44 -0
- package/dist/preview-engine/renderers-FK72LKQC.mjs +275 -0
- package/dist/preview-engine/renderers-FQWWAETX.mjs +203 -0
- package/dist/preview-engine/renderers-HMR2NAD7.mjs +81 -0
- package/dist/preview-engine/renderers-HUW2ZMS3.mjs +1 -0
- package/dist/preview-engine/renderers-JLFGBYN4.mjs +78 -0
- package/dist/preview-engine/renderers-LLUEOW5T.mjs +77 -0
- package/dist/preview-engine/renderers-NC64EQDE.mjs +1 -0
- package/dist/preview-engine/renderers-PVYP2ONH.mjs +76 -0
- package/dist/preview-engine/renderers-TPBPNXWH.mjs +26 -0
- package/dist/preview-engine/renderers-UJ4YVJZT.mjs +80 -0
- package/dist/preview-engine/renderers-WHBDHTRI.mjs +78 -0
- package/dist/preview-engine/renderers-WP67KK5K.mjs +89 -0
- package/dist/preview-engine/renderers-YNFKFM7D.mjs +67 -0
- package/dist/preview-engine/renderers-ZSENYGIY.mjs +84 -0
- package/dist/preview-engine/signals.mjs +1 -1
- package/dist/{program-DzknMiY0.mjs → program-Dd9tJFkY.mjs} +346 -13
- package/dist/program-Dd9tJFkY.mjs.map +1 -0
- package/dist/renderer/index.d.mts +2 -2
- package/dist/renderer/index.mjs +2 -2
- package/dist/{renderer-C8uKdelk.mjs → renderer-0L9MEb3A.mjs} +2 -2
- package/dist/{renderer-C8uKdelk.mjs.map → renderer-0L9MEb3A.mjs.map} +1 -1
- package/dist/renderer-0lp9ClNc.mjs +5 -0
- package/dist/{renderer-BbdZ0YHR.mjs → renderer-257LxOyx.mjs} +3 -3
- package/dist/{renderer-BbdZ0YHR.mjs.map → renderer-257LxOyx.mjs.map} +1 -1
- package/dist/renderer-2BnBo6CX.mjs +219 -0
- package/dist/renderer-2BnBo6CX.mjs.map +1 -0
- package/dist/renderer-4g_BfiKp.mjs +5 -0
- package/dist/renderer-72A4SvOE.mjs +220 -0
- package/dist/renderer-72A4SvOE.mjs.map +1 -0
- package/dist/renderer-7Aw6AHqW.mjs +5 -0
- package/dist/{renderer-PKQNH21k.mjs → renderer-7OvgLE8w.mjs} +2 -2
- package/dist/{renderer-BerD3T-X.mjs → renderer-94byj7Mk.mjs} +2 -2
- package/dist/{renderer-BerD3T-X.mjs.map → renderer-94byj7Mk.mjs.map} +1 -1
- package/dist/{renderer-D-vRKssW.mjs → renderer-B2aXfdW_.mjs} +2 -2
- package/dist/{renderer-D-vRKssW.mjs.map → renderer-B2aXfdW_.mjs.map} +1 -1
- package/dist/{renderer-DD8napek.mjs → renderer-B3c-j-e5.mjs} +2 -2
- package/dist/{renderer-DD8napek.mjs.map → renderer-B3c-j-e5.mjs.map} +1 -1
- package/dist/renderer-B3ypX3MH.mjs +308 -0
- package/dist/renderer-B3ypX3MH.mjs.map +1 -0
- package/dist/renderer-B82v8PyC.mjs +5 -0
- package/dist/renderer-B8BDMJNz.mjs +5 -0
- package/dist/renderer-BARst3ZD.mjs +3 -0
- package/dist/renderer-BCPRrhP8.mjs +5 -0
- package/dist/{renderer-CMT4QcpV.mjs → renderer-BEdc-7JW.mjs} +3 -3
- package/dist/{renderer-CMT4QcpV.mjs.map → renderer-BEdc-7JW.mjs.map} +1 -1
- package/dist/renderer-BEpANdcO.mjs +204 -0
- package/dist/renderer-BEpANdcO.mjs.map +1 -0
- package/dist/renderer-BFhzO6En.mjs +5 -0
- package/dist/renderer-BH0KF-y-.mjs +6 -0
- package/dist/renderer-BHrqEgIA.mjs +541 -0
- package/dist/renderer-BHrqEgIA.mjs.map +1 -0
- package/dist/{renderer-DpW_LxLO.mjs → renderer-BId5hBLb.mjs} +3 -3
- package/dist/{renderer-DpW_LxLO.mjs.map → renderer-BId5hBLb.mjs.map} +1 -1
- package/dist/renderer-BK32cV7E.mjs +203 -0
- package/dist/renderer-BK32cV7E.mjs.map +1 -0
- package/dist/renderer-BTI5ybyH.mjs +5 -0
- package/dist/{renderer-0gY4IQhM.mjs → renderer-BVR0mEIT.mjs} +2 -2
- package/dist/{renderer-0gY4IQhM.mjs.map → renderer-BVR0mEIT.mjs.map} +1 -1
- package/dist/{renderer-CebB0v1y.mjs → renderer-BY08hHFS.mjs} +2 -2
- package/dist/{renderer-CebB0v1y.mjs.map → renderer-BY08hHFS.mjs.map} +1 -1
- package/dist/renderer-Ba7LH8U2.mjs +5 -0
- package/dist/renderer-BcQBlfhC.mjs +5 -0
- package/dist/renderer-BdMJzCTz.mjs +5 -0
- package/dist/{renderer-D1Cm21eq.mjs → renderer-BmQbSQHf.mjs} +2 -2
- package/dist/{renderer-D1Cm21eq.mjs.map → renderer-BmQbSQHf.mjs.map} +1 -1
- package/dist/{renderer-DqdhsSwS.mjs → renderer-BmTDXIUI.mjs} +2 -2
- package/dist/{renderer-DqdhsSwS.mjs.map → renderer-BmTDXIUI.mjs.map} +1 -1
- package/dist/renderer-BpdzhZE7.mjs +224 -0
- package/dist/renderer-BpdzhZE7.mjs.map +1 -0
- package/dist/renderer-Bxi2j-tG.mjs +5 -0
- package/dist/{renderer-D8thCpS7.mjs → renderer-C-Qj0BnT.mjs} +3 -3
- package/dist/{renderer-D8thCpS7.mjs.map → renderer-C-Qj0BnT.mjs.map} +1 -1
- package/dist/{renderer-BiwIh_w4.mjs → renderer-C4tUATd_.mjs} +3 -3
- package/dist/{renderer-BiwIh_w4.mjs.map → renderer-C4tUATd_.mjs.map} +1 -1
- package/dist/{renderer-1vhaKMeg.mjs → renderer-CC72sZ8l.mjs} +141 -56
- package/dist/renderer-CC72sZ8l.mjs.map +1 -0
- package/dist/renderer-CCn5ISVM.mjs +5 -0
- package/dist/renderer-CDRsdhg9.mjs +5 -0
- package/dist/{renderer-BC8XOVl5.mjs → renderer-CI9Pq77W.mjs} +3 -3
- package/dist/{renderer-BC8XOVl5.mjs.map → renderer-CI9Pq77W.mjs.map} +1 -1
- package/dist/{renderer-CrABA3lB.mjs → renderer-CIMI33Yd.mjs} +2 -2
- package/dist/{renderer-CrABA3lB.mjs.map → renderer-CIMI33Yd.mjs.map} +1 -1
- package/dist/renderer-CKSdl0Xf.mjs +331 -0
- package/dist/renderer-CKSdl0Xf.mjs.map +1 -0
- package/dist/{renderer-Cce2fgpX.mjs → renderer-CKpwEo1w.mjs} +3 -3
- package/dist/{renderer-Cce2fgpX.mjs.map → renderer-CKpwEo1w.mjs.map} +1 -1
- package/dist/{renderer-CursFKUf.mjs → renderer-CLUyYFqS.mjs} +2 -2
- package/dist/{renderer-CursFKUf.mjs.map → renderer-CLUyYFqS.mjs.map} +1 -1
- package/dist/{renderer-Br6KyRO_.mjs → renderer-CS-goRD3.mjs} +3 -3
- package/dist/{renderer-Br6KyRO_.mjs.map → renderer-CS-goRD3.mjs.map} +1 -1
- package/dist/{renderer-DtgMsjyL.mjs → renderer-CSPpKLi3.mjs} +2 -2
- package/dist/{renderer-DtgMsjyL.mjs.map → renderer-CSPpKLi3.mjs.map} +1 -1
- package/dist/{renderer-CuVPo-wP.mjs → renderer-CWQ-Bct7.mjs} +2 -2
- package/dist/{renderer-CuVPo-wP.mjs.map → renderer-CWQ-Bct7.mjs.map} +1 -1
- package/dist/{renderer-DHGTzype.mjs → renderer-ChoYV7Ko.mjs} +2 -2
- package/dist/{renderer-DHGTzype.mjs.map → renderer-ChoYV7Ko.mjs.map} +1 -1
- package/dist/renderer-CiCK8ZG_.mjs +5 -0
- package/dist/{renderer-CJB9nU1x.mjs → renderer-Ck2pRec0.mjs} +3 -3
- package/dist/{renderer-CJB9nU1x.mjs.map → renderer-Ck2pRec0.mjs.map} +1 -1
- package/dist/{renderer-BzDjy1_x.mjs → renderer-CrUudSLP.mjs} +2 -2
- package/dist/{renderer-BzDjy1_x.mjs.map → renderer-CrUudSLP.mjs.map} +1 -1
- package/dist/{renderer-DupsdRIw.mjs → renderer-CraudZUC.mjs} +3 -3
- package/dist/{renderer-DupsdRIw.mjs.map → renderer-CraudZUC.mjs.map} +1 -1
- package/dist/renderer-CsEk9TFz.mjs +5 -0
- package/dist/renderer-CvOzuDX3.mjs +215 -0
- package/dist/renderer-CvOzuDX3.mjs.map +1 -0
- package/dist/{renderer-DJfFc-XW.mjs → renderer-Cvysm2Jg.mjs} +3 -3
- package/dist/{renderer-DJfFc-XW.mjs.map → renderer-Cvysm2Jg.mjs.map} +1 -1
- package/dist/{renderer-Bn2eQE-t.mjs → renderer-CwwmwM7J.mjs} +3 -3
- package/dist/{renderer-Bn2eQE-t.mjs.map → renderer-CwwmwM7J.mjs.map} +1 -1
- package/dist/{renderer-KnCcN_Nz.mjs → renderer-CxP2e6Ov.mjs} +2 -2
- package/dist/{renderer-KnCcN_Nz.mjs.map → renderer-CxP2e6Ov.mjs.map} +1 -1
- package/dist/{renderer-BteUC_7n.mjs → renderer-D-yNHLG7.mjs} +3 -3
- package/dist/{renderer-BteUC_7n.mjs.map → renderer-D-yNHLG7.mjs.map} +1 -1
- package/dist/renderer-D0xLIq1r.mjs +203 -0
- package/dist/renderer-D0xLIq1r.mjs.map +1 -0
- package/dist/{renderer-id8TbKBY.mjs → renderer-D1MOTlWS.mjs} +2 -2
- package/dist/{renderer-id8TbKBY.mjs.map → renderer-D1MOTlWS.mjs.map} +1 -1
- package/dist/renderer-D1T7A3NF.mjs +233 -0
- package/dist/renderer-D1T7A3NF.mjs.map +1 -0
- package/dist/renderer-D1gx_-hi.mjs +5 -0
- package/dist/renderer-D5MCGNAF.mjs +5 -0
- package/dist/{renderer-Db3JIF1K.mjs → renderer-D6YcQTLI.mjs} +3 -3
- package/dist/{renderer-Db3JIF1K.mjs.map → renderer-D6YcQTLI.mjs.map} +1 -1
- package/dist/{renderer-BNqwn0gZ.mjs → renderer-D8_UYdDb.mjs} +2 -2
- package/dist/{renderer-BNqwn0gZ.mjs.map → renderer-D8_UYdDb.mjs.map} +1 -1
- package/dist/renderer-DD_MzvM-.mjs +5 -0
- package/dist/{renderer-BHskWZNG.mjs → renderer-DIqwz7dV.mjs} +2 -2
- package/dist/{renderer-57ATc_mQ.mjs → renderer-DJQD7tai.mjs} +2 -2
- package/dist/{renderer-57ATc_mQ.mjs.map → renderer-DJQD7tai.mjs.map} +1 -1
- package/dist/{renderer-CWBHVCvA.mjs → renderer-DKXK5hDm.mjs} +2 -2
- package/dist/{renderer-CWBHVCvA.mjs.map → renderer-DKXK5hDm.mjs.map} +1 -1
- package/dist/{renderer-p_C_T9Kn.mjs → renderer-DNYSf2Fl.mjs} +2 -2
- package/dist/{renderer-p_C_T9Kn.mjs.map → renderer-DNYSf2Fl.mjs.map} +1 -1
- package/dist/{renderer-DXqIslFk.mjs → renderer-DNaHFctD.mjs} +2 -2
- package/dist/{renderer-DXqIslFk.mjs.map → renderer-DNaHFctD.mjs.map} +1 -1
- package/dist/renderer-DO5r7ZV-.mjs +658 -0
- package/dist/renderer-DO5r7ZV-.mjs.map +1 -0
- package/dist/renderer-DP7YoZ0K.mjs +5 -0
- package/dist/{renderer-CE7fwGgw.mjs → renderer-DQtk282o.mjs} +29 -8
- package/dist/renderer-DQtk282o.mjs.map +1 -0
- package/dist/{renderer-u8ynJtAC.mjs → renderer-DWcbrx2M.mjs} +2 -2
- package/dist/{renderer-u8ynJtAC.mjs.map → renderer-DWcbrx2M.mjs.map} +1 -1
- package/dist/renderer-DXiWvKij.mjs +5 -0
- package/dist/renderer-DZnUiiBp.mjs +222 -0
- package/dist/renderer-DZnUiiBp.mjs.map +1 -0
- package/dist/renderer-Dc6fW-o1.mjs +5 -0
- package/dist/renderer-DiurlsNT.mjs +5 -0
- package/dist/{renderer-BmGPTWwV.mjs → renderer-Dju3SEAJ.mjs} +3 -3
- package/dist/{renderer-BmGPTWwV.mjs.map → renderer-Dju3SEAJ.mjs.map} +1 -1
- package/dist/{renderer-BT-_EoXw.d.mts → renderer-DlESyfg7.d.mts} +2 -2
- package/dist/{renderer-BT-_EoXw.d.mts.map → renderer-DlESyfg7.d.mts.map} +1 -1
- package/dist/{renderer-aArb4RwJ.mjs → renderer-DpQaiH6C.mjs} +2 -2
- package/dist/{renderer-aArb4RwJ.mjs.map → renderer-DpQaiH6C.mjs.map} +1 -1
- package/dist/{renderer-CeyAgJnw.mjs → renderer-DuxzqfoE.mjs} +2 -2
- package/dist/renderer-DvCUdgVz.mjs +5 -0
- package/dist/{renderer-AF884a7T.mjs → renderer-Dvg5nbgU.mjs} +3 -3
- package/dist/{renderer-AF884a7T.mjs.map → renderer-Dvg5nbgU.mjs.map} +1 -1
- package/dist/{renderer-DvFaC_-7.mjs → renderer-Dvq11SEA.mjs} +2 -2
- package/dist/{renderer-DvFaC_-7.mjs.map → renderer-Dvq11SEA.mjs.map} +1 -1
- package/dist/renderer-DyPgVl1l.mjs +223 -0
- package/dist/renderer-DyPgVl1l.mjs.map +1 -0
- package/dist/{renderer-DEuIwr-t.mjs → renderer-DzOzVxNS.mjs} +2 -2
- package/dist/{renderer-DEuIwr-t.mjs.map → renderer-DzOzVxNS.mjs.map} +1 -1
- package/dist/renderer-EhCkD11e.mjs +208 -0
- package/dist/renderer-EhCkD11e.mjs.map +1 -0
- package/dist/renderer-IiwAh9-K.mjs +5 -0
- package/dist/{renderer-jqByPgkD.mjs → renderer-JiEpssIz.mjs} +2 -2
- package/dist/{renderer-jqByPgkD.mjs.map → renderer-JiEpssIz.mjs.map} +1 -1
- package/dist/renderer-JlGXqH0E.mjs +5 -0
- package/dist/{renderer-Beg7FLt2.mjs → renderer-OxafT7Xm.mjs} +75 -27
- package/dist/renderer-OxafT7Xm.mjs.map +1 -0
- package/dist/renderer-TZFKKgL1.mjs +5 -0
- package/dist/renderer-UT0CcmMZ.mjs +191 -0
- package/dist/renderer-UT0CcmMZ.mjs.map +1 -0
- package/dist/{renderer-YbaCzPcG.mjs → renderer-YyEBKPcE.mjs} +2 -2
- package/dist/{renderer-jYxN4mHn.mjs → renderer-_AuL8mAi.mjs} +3 -3
- package/dist/{renderer-jYxN4mHn.mjs.map → renderer-_AuL8mAi.mjs.map} +1 -1
- package/dist/{renderer-C6md3msE.mjs → renderer-aCDIEYb3.mjs} +2 -2
- package/dist/{renderer-C6md3msE.mjs.map → renderer-aCDIEYb3.mjs.map} +1 -1
- package/dist/renderer-aEecoNPX.mjs +5 -0
- package/dist/{renderer-BeIUgp1r.mjs → renderer-aN60v8jR.mjs} +2 -2
- package/dist/{renderer-BeIUgp1r.mjs.map → renderer-aN60v8jR.mjs.map} +1 -1
- package/dist/renderer-ewhliYYP.mjs +5 -0
- package/dist/renderer-hQc6GgTi.mjs +5 -0
- package/dist/renderer-iuX0RZIi.mjs +5 -0
- package/dist/renderer-j4vkTzHa.mjs +5 -0
- package/dist/{renderer-EemDpvv3.mjs → renderer-mpInX4Iy.mjs} +3 -3
- package/dist/{renderer-EemDpvv3.mjs.map → renderer-mpInX4Iy.mjs.map} +1 -1
- package/dist/renderer-msPjwx_9.mjs +5 -0
- package/dist/renderer-pI2ndu-b.mjs +5 -0
- package/dist/renderer-x1OGMhsC.mjs +5 -0
- package/dist/{renderer-DZXbgVMK.mjs → renderer-xezE7IZX.mjs} +3 -3
- package/dist/{renderer-DZXbgVMK.mjs.map → renderer-xezE7IZX.mjs.map} +1 -1
- package/dist/renderer-yGAk7J9P.mjs +222 -0
- package/dist/renderer-yGAk7J9P.mjs.map +1 -0
- package/dist/{renderer-Bvx56GR6.mjs → renderer-zQIB2VZZ.mjs} +2 -2
- package/dist/{renderer-Bvx56GR6.mjs.map → renderer-zQIB2VZZ.mjs.map} +1 -1
- package/dist/{renderers-CP8CFWDs-BA-4l6By.mjs → renderers-CP8CFWDs-i6u_rhCX.mjs} +2 -2
- package/dist/{renderers-CP8CFWDs-BA-4l6By.mjs.map → renderers-CP8CFWDs-i6u_rhCX.mjs.map} +1 -1
- package/dist/signals/index.d.mts +5 -5
- package/dist/signals/index.mjs +4 -4
- package/package.json +58 -55
- package/dist/dist-CT___nEU.mjs.map +0 -1
- package/dist/effects-C06-AOge.mjs.map +0 -1
- package/dist/index-B-oUlLoO.d.mts.map +0 -1
- package/dist/index-CwbszPDG.d.mts.map +0 -1
- package/dist/preview-engine/chunk-G4K6TLNA.mjs +0 -1
- package/dist/preview-engine/chunk-HVCAOHNI.mjs +0 -1
- package/dist/preview-engine/chunk-KBO6EX3M.mjs +0 -1448
- package/dist/preview-engine/chunk-N4LHIPHZ.mjs +0 -340
- package/dist/preview-engine/chunk-NECOKNHA.mjs +0 -174
- package/dist/preview-engine/renderers-T4JMJXWL.mjs +0 -1
- package/dist/preview-engine/renderers-VFV5QWO3.mjs +0 -1
- package/dist/preview-engine/renderers-VUL4HBX3.mjs +0 -228
- package/dist/program-DzknMiY0.mjs.map +0 -1
- package/dist/renderer--IDyGdqT.mjs +0 -5
- package/dist/renderer-17K4tjid.mjs +0 -5
- package/dist/renderer-1vhaKMeg.mjs.map +0 -1
- package/dist/renderer-Ab9Ttn_m.mjs +0 -5
- package/dist/renderer-B-roDBBo.mjs +0 -5
- package/dist/renderer-B03uwoUX.mjs +0 -5
- package/dist/renderer-B3BI_7YJ.mjs +0 -5
- package/dist/renderer-BGD4lQ74.mjs +0 -5
- package/dist/renderer-BHXhYfeC.mjs +0 -5
- package/dist/renderer-BRsTIMFa.mjs +0 -5
- package/dist/renderer-Beg7FLt2.mjs.map +0 -1
- package/dist/renderer-BjMTtYZw.mjs +0 -5
- package/dist/renderer-BoCh3Yts.mjs +0 -5
- package/dist/renderer-BqPbR8c4.mjs +0 -5
- package/dist/renderer-Bxa4SDm9.mjs +0 -3
- package/dist/renderer-C4Da2I1-.mjs +0 -5
- package/dist/renderer-C93X6dgl.mjs +0 -5
- package/dist/renderer-CE7fwGgw.mjs.map +0 -1
- package/dist/renderer-CMAwFfGx.mjs +0 -6
- package/dist/renderer-CRXeSuX2.mjs +0 -5
- package/dist/renderer-CSp6WYBN.mjs +0 -5
- package/dist/renderer-CUXX7HTL.mjs +0 -5
- package/dist/renderer-CUgThpka.mjs +0 -5
- package/dist/renderer-CeVtufiT.mjs +0 -5
- package/dist/renderer-CespkZUk.mjs +0 -5
- package/dist/renderer-D1l47xhL.mjs +0 -5
- package/dist/renderer-DDuG18J8.mjs +0 -5
- package/dist/renderer-DFhrFSMn.mjs +0 -5
- package/dist/renderer-DO48vQ7Y.mjs +0 -5
- package/dist/renderer-DTC39B_n.mjs +0 -5
- package/dist/renderer-DbyU3hNF.mjs +0 -5
- package/dist/renderer-DftVpmfu.mjs +0 -5
- package/dist/renderer-Dj5jm63K.mjs +0 -5
- package/dist/renderer-Dju57ZaF.mjs +0 -5
- package/dist/renderer-Dn8TQCw7.mjs +0 -5
- package/dist/renderer-ETF46K0D.mjs +0 -5
- package/dist/renderer-azYzWGC3.mjs +0 -5
- package/dist/renderer-dxNoRW59.mjs +0 -5
- package/dist/renderer-fdfelx_I.mjs +0 -5
- package/dist/renderer-snwEKwMw.mjs +0 -5
package/dist/index.mjs
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
1
|
import "./chunk-GBe2k3y8.mjs";
|
|
2
2
|
import { $ as TimelineTransformSchema, $t as serializeSignalData, A as MIME_TYPES, At as gradientStopsToOklab, B as OutputItemSchema, Bt as parseCssColor, C as FileDataSchema, Ct as generateId, D as HandleTypeVal, Dt as getLeafMediaType, E as Gradient, Et as getFingerprint, F as ModerationUnrefundableError, Ft as isVirtualMediaData, G as SignalGateDataSchema, Gt as reportRenderDiagnostic, H as Signal, Ht as programmaticSignal, I as MultiOutputGenericSchema, It as limitGradientStops, J as SingleOutputGenericSchema, Jt as resolveNumber, K as SignalMathDataSchema, Kt as resolveBoolean, L as NodeMetadataSchema, Lt as mixColors, M as MediaOperationSchema, Mt as isCssGradient, N as MediaSourceSchema, Nt as isEqual, O as JsonValueSchema, Ot as getMediaType, P as ModerationError, Pt as isSignal, Q as TimelineSegmentSchema, Qt as sampleColorStops, R as NodeResultSchema, Rt as normalizeColorStops, S as FileAssetSchema, St as frameSignal, T as GlobalCompositeOperation, Tt as getFileDataMetadata, U as SignalChannelMetaSchema, Ut as progressSignal, V as ProgrammaticSignal, Vt as parseCssGradient, W as SignalDataSchema, Wt as registerSignalDependency, X as TEXT_LAYER_DEFAULTS, Xt as resolveString, Y as SourceOperationSchema, Yt as resolveParam, Z as TextOperationSchema, Zt as rgbToOklab, _ as DataTypeVal, _t as deserializeSignalData, a as BaseVideoLayerPropsSchema, an as timeSignal, at as VideoStylePropsSchema, b as ExportResultSchema, bt as formatHex, c as COMPOSITOR_DEFAULTS, ct as VirtualMediaDataSchema, d as ConfigSchemaRegistry, dt as computeVideoCropRenderProps, en as signal, et as TransitionSchema, f as DATA_TYPE_EXTENSIONS, ft as computed, g as DataType, gt as dataTypeColors, h as DEFAULT_MEDIA_DIMENSION, ht as createVirtualMedia, i as BaseMediaOperationPropsSchema, in as takeRenderDiagnostics, it as VideoSpatialPropsSchema, j as MediaMetadataSchema, jt as hasOnlySingleSource, k as MAX_GRADIENT_STOPS, kt as getRenderFingerprint, l as Color, lt as appendOperation, m as DEFAULT_FPS, mt as createOutputItemSchema, n as AnyOutputUnionSchema, nn as sineSignal, nt as VideoAnimationSchema, o as CAPTION_LAYER_DEFAULTS, on as updateClockSignals, ot as VideoTextPropsSchema, p as DEFAULT_DURATION_MS, pt as cosineSignal, q as SignalStatsSchema, qt as resolveMediaMimeType, r as AudioExtractorSignalDataSchema, rn as stampResult, rt as VideoLayerTypeSchema, s as COMPOSITE_OPERATIONS, st as VideoTimingPropsSchema, t as AnimationTypeSchema, tn as signalBuilder, tt as TransitionTypeSchema, u as ConfigHandleSchema, ut as checkNodeResultCache, v as DataTypes, vt as extractSvgDimensions, w as GeneratorSignalDataSchema, wt as getActiveMediaMetadata, x as ExtendedLayerSchema, xt as frameArraySignal, y as Effect$1, yt as findSourceAsset, z as NodeVisualConfigSchema, zt as oklabToRgb } from "./dist-fjKpBYkc.mjs";
|
|
3
|
-
import { $t as
|
|
4
|
-
import "./renderers-CP8CFWDs-
|
|
3
|
+
import { $t as TextSelectorSchema, A as ensureDevice, An as sampleAudioSpectrumScale, At as GlyphAnchorGroupingSchema, B as loadModel3D, Cn as evaluateTypewriterState, Ct as AudioSpectrumTypographySchema, Dn as getScramblePool, Dt as EnhancedTextNodeSchema, En as generatePathSegments, Et as DynamicStrokePropertiesSchema, Ft as ParticleDissolveConfigSchema, G as parseColor, Gt as TextAnimatorBuilder, H as lutStore, Ht as Spatial3DPropertiesSchema, It as SelectorBasedOnSchema, J as parseOBJ, Jt as TextPathBuilder, K as parseFBX, Kt as TextCursorConfigSchema, L as inputStore, Lt as SelectorModeSchema, M as generateExtrudedTextGeometry, Mn as samplePathChainGeometry, Mt as KineticMarqueeConfigSchema, Nn as signalRegistry, Nt as LineSpacingDynamicsSchema, On as getShuffledOrder, Ot as ExpressionSelectorSchema, P as getPrimitiveGeometry, Pn as waveToCubicBeziers, Pt as LongShadowConfigSchema, Qt as TextScrambleConfigSchema, Rt as SelectorShapeSchema, Sn as evaluateSpringProgress, Tn as generateArcLengthLUT, Tt as CubicBezierSegmentSchema, Ut as SpringPhysicsSchema, V as loadOBJ, Vt as SkewPropertiesSchema, W as mediaDecoderCache, Wt as TextAnchorConfigSchema, Xt as TextPathSourceSchema, Y as parseSvgPathToSegments, Yt as TextPathOptionsSchema, Zt as TextRangeSelectorSchema, _ as buildShapeSvgPath, _n as evaluateCubicBezierDerivative, _t as AdvancedTextAnimatorBuilder, a as GlassPipeline, an as Vec2Schema, bn as evaluateSelectorCombination, bt as AnchorPresetSchema, c as Renderer2D, cn as buildPathLUT, ct as LIGHT_UNIFORM_SIZE_BYTES, dn as computeLineLeadingDisplacement, dt as Matrix4Math, en as TextSignalSelectorSchema, et as shaderStore, f as ShadowPipeline, fn as computeRangeSelectorWeight, ft as Path3D, gn as evaluateCubicBezier, gt as AETextAnimatorSchema, h as SlugPipeline, hn as ellipseToCubicBeziers, ht as resolveEnvironment, i as EffectPipeline, in as VariableFontAxesSchema, jn as sampleCurveGeometry, jt as GlyphAnchorPointSchema, kn as parseSvgPathToCubicBeziers, kt as ExtendedGlyphPropertiesSchema, l as Renderer3D, lt as Light3D, mn as distanceToCurveParameter, mt as parseColorToRgb, n as AudioLatentTrackCache, nn as TypewriterAnimatorSchema, o as MotionBlurPipeline, on as VolumetricFormationConfigSchema, ot as Camera3D, p as SlugFontCache, pn as deterministicWigglyNoise, pt as Vector3Math, q as parseMTL, qt as TextCursorStyleSchema, r as AudioMeshDeformPipeline, rn as TypingCadenceSchema, rt as textureCache, sn as VolumetricFormationModeSchema, st as ENVIRONMENT_PRESETS, t as AudioLatentEngine, tn as TextWigglySelectorSchema, u as SSAOPipeline, un as computeAnchorPivotOffset, ut as MAX_LIGHTS, vn as evaluateExpressionSelector, vt as AdvancedTextAnimatorSchema, wn as evaluateVolumetricFormation, wt as ChromaticGlitchConfigSchema, x as describeGpu, xn as evaluateSkewMatrix, xt as AnimatableGlyphPropsSchema, yn as evaluateMarqueeOffset, yt as AnchorGroupingSchema, z as loadFBX, zt as SelectorUnitsSchema } from "./dist-PwT_Vmax.mjs";
|
|
4
|
+
import "./renderers-CP8CFWDs-i6u_rhCX.mjs";
|
|
5
5
|
import "./signals-DGRs6ddH.mjs";
|
|
6
|
-
import "./renderer-
|
|
6
|
+
import "./renderer-Dju3SEAJ.mjs";
|
|
7
7
|
import "./renderer-BajIOuZH.mjs";
|
|
8
|
-
import { _ as spring, c as computeStrokeRect, d as measureSpring, f as measureText, g as requiresIsolation, h as preloadFont, i as mixAudioTracks, l as interpolate, m as preloadCompositionFonts, o as Easing, p as measureTextAsync, s as bezier, u as mapBlendModeToSkia, v as springCalculation } from "./dist-
|
|
9
|
-
import { t as AudioSignal } from "./audio-
|
|
10
|
-
import { t as FontManager } from "./fonts-
|
|
11
|
-
import "./renderer-
|
|
12
|
-
import "./renderer-
|
|
13
|
-
import "./renderer-
|
|
14
|
-
import "./renderer-
|
|
15
|
-
import "./renderer-
|
|
16
|
-
import "./renderer-
|
|
8
|
+
import { _ as spring, c as computeStrokeRect, d as measureSpring, f as measureText, g as requiresIsolation, h as preloadFont, i as mixAudioTracks, l as interpolate, m as preloadCompositionFonts, o as Easing, p as measureTextAsync, s as bezier, u as mapBlendModeToSkia, v as springCalculation } from "./dist-t-NLlnhM.mjs";
|
|
9
|
+
import { t as AudioSignal } from "./audio-ChRqnFwB.mjs";
|
|
10
|
+
import { t as FontManager } from "./fonts-CgNmeFXh.mjs";
|
|
11
|
+
import "./renderer-DKXK5hDm.mjs";
|
|
12
|
+
import "./renderer-BY08hHFS.mjs";
|
|
13
|
+
import "./renderer-CSPpKLi3.mjs";
|
|
14
|
+
import "./renderer-Dvq11SEA.mjs";
|
|
15
|
+
import "./renderer-zQIB2VZZ.mjs";
|
|
16
|
+
import "./renderer-DWcbrx2M.mjs";
|
|
17
17
|
import "./renderer-B-BcgA6d.mjs";
|
|
18
|
-
import "./renderer-
|
|
18
|
+
import "./renderer-BmQbSQHf.mjs";
|
|
19
19
|
import { i as softLimit, n as mixSfxInto, r as renderSfx, t as encodeStereoWav } from "./audio-DJeVoE8b.mjs";
|
|
20
20
|
import { i as OpticalFlowComputePipeline, n as ControlNetMultiplexer, o as TemporalDeflickerPipeline, r as DepthNormalsComputePipeline, s as TensorPipeline, t as CannyComputePipeline } from "./dist-0QGroRYZ.mjs";
|
|
21
|
-
import { a as
|
|
22
|
-
import { A as applyStaggerToChildren, B as
|
|
23
|
-
import "./renderer-
|
|
24
|
-
import "./renderer-
|
|
25
|
-
import "./renderer-
|
|
26
|
-
import "./renderer-
|
|
27
|
-
import "./renderer-
|
|
28
|
-
import "./renderer-
|
|
29
|
-
import "./renderer-
|
|
30
|
-
import "./renderer-
|
|
31
|
-
import "./renderer-
|
|
32
|
-
import "./renderer-
|
|
33
|
-
import "./renderer-
|
|
34
|
-
import "./renderer-
|
|
35
|
-
import "./renderer-
|
|
36
|
-
import "./renderer-
|
|
37
|
-
import "./renderer-
|
|
38
|
-
import "./renderer-
|
|
39
|
-
import "./renderer-
|
|
40
|
-
import "./renderer-
|
|
41
|
-
import "./renderer-
|
|
42
|
-
import "./renderer-
|
|
43
|
-
import "./renderer-
|
|
44
|
-
import "./renderer-
|
|
45
|
-
import "./renderer-
|
|
46
|
-
import "./renderer-
|
|
47
|
-
import "./renderer-
|
|
48
|
-
import "./renderer-
|
|
49
|
-
import "./renderer-
|
|
50
|
-
import "./renderer-
|
|
51
|
-
import "./renderer-
|
|
52
|
-
import "./renderer-
|
|
53
|
-
import "./renderer-
|
|
54
|
-
import { $ as
|
|
55
|
-
import { a as HeadlessMediaRenderer, c as renderSemaphore, i as resetRenderCaches, n as renderFrameGrid, o as HeadlessWebGPURenderer, r as resolveSamplePoints, s as formatQaReport, t as computeGridLayout } from "./renderer-
|
|
56
|
-
import { t as startPreview } from "./preview-
|
|
21
|
+
import { _ as COCO_CLASSES, a as COCO17_KEYPOINT_NAMES, c as SpatialLandmarkTransformer, d as VisionNode, f as VisionRunner, g as pinNodeToObject, h as pinNodeToLandmark, i as COCO17_KEYPOINTS, l as TemporalObjectTracker, m as mergeSubjectMask, n as CLIP_SUBJECT, o as PoseSkeletonRenderer, p as analyzeSequence, r as COCO17_BONES, s as SegmentationTexturePool, u as VisionBundle } from "./renderer-DQtk282o.mjs";
|
|
22
|
+
import { A as applyStaggerToChildren, B as evaluateTrackAtFrame, C as ShapeNodeSchema, D as TextSpanMarkSchema, E as TextNodeSchema, F as computeLayout, I as computeLayoutSync, L as computeStaggerDelay, M as collectNodeOps, N as compositorToProgram, O as TextSpanSchema, P as computeCarrierContinuity, R as evaluateAnimationAtFrame, S as Sequence, T as TextAnimatorSchema, U as lintComposition, V as hoistMattes, W as mergeAnimationTracks, _ as MediaNodeSchema, a as CarrierHandoffConfigSchema, b as SceneDefinitionSchema, c as EnvironmentFogSchema, d as LayerAnimation, f as LayoutNodeSchema, g as MatteModeSchema, h as MaterialTypeSchema, i as CameraNodeSchema, j as calculateSequenceTimeline, k as ToneMappingSchema, l as EnvironmentPresetSchema, m as LightTypeSchema, n as BoxNodeSchema, o as ColorStopSchema, p as LightNodeSchema, r as CameraAnimation, s as CompositorProgramSchema, t as BloomConfigSchema, u as FlexNodeSchema, v as Model3DNodeSchema, w as TextAnimator, x as SceneTransitionTypeSchema, y as Scene } from "./program-Dd9tJFkY.mjs";
|
|
23
|
+
import "./renderer-Ck2pRec0.mjs";
|
|
24
|
+
import "./renderer-aN60v8jR.mjs";
|
|
25
|
+
import "./renderer-BId5hBLb.mjs";
|
|
26
|
+
import "./renderer-D1MOTlWS.mjs";
|
|
27
|
+
import "./renderer-DzOzVxNS.mjs";
|
|
28
|
+
import "./renderer-BmTDXIUI.mjs";
|
|
29
|
+
import "./renderer-BVR0mEIT.mjs";
|
|
30
|
+
import "./renderer-mpInX4Iy.mjs";
|
|
31
|
+
import "./renderer-257LxOyx.mjs";
|
|
32
|
+
import "./renderer-CLUyYFqS.mjs";
|
|
33
|
+
import "./renderer-C-Qj0BnT.mjs";
|
|
34
|
+
import "./renderer-CS-goRD3.mjs";
|
|
35
|
+
import "./renderer-JiEpssIz.mjs";
|
|
36
|
+
import "./renderer-0L9MEb3A.mjs";
|
|
37
|
+
import "./renderer-DNYSf2Fl.mjs";
|
|
38
|
+
import "./renderer-C4tUATd_.mjs";
|
|
39
|
+
import "./renderer-CrUudSLP.mjs";
|
|
40
|
+
import "./renderer-CwwmwM7J.mjs";
|
|
41
|
+
import "./renderer-CIMI33Yd.mjs";
|
|
42
|
+
import "./renderer-B2aXfdW_.mjs";
|
|
43
|
+
import "./renderer-DpQaiH6C.mjs";
|
|
44
|
+
import "./renderer-B3c-j-e5.mjs";
|
|
45
|
+
import "./renderer-ChoYV7Ko.mjs";
|
|
46
|
+
import "./renderer-D6YcQTLI.mjs";
|
|
47
|
+
import "./renderer-CKpwEo1w.mjs";
|
|
48
|
+
import "./renderer-D-yNHLG7.mjs";
|
|
49
|
+
import "./renderer-aCDIEYb3.mjs";
|
|
50
|
+
import "./renderer-DJQD7tai.mjs";
|
|
51
|
+
import "./renderer-D8_UYdDb.mjs";
|
|
52
|
+
import "./renderer-94byj7Mk.mjs";
|
|
53
|
+
import "./renderer-DNaHFctD.mjs";
|
|
54
|
+
import { $ as NoiseGenerator, A as AudioParametricEq, B as Datamosh, C as effectFactories, D as AudioDelay, E as AudioCompressor, F as ChannelSplitter, G as Gooey, H as Dither, I as ColorBalance, J as KenBurns, K as Iridescence, L as ColorKey, M as CRT, N as CameraParallax3D, O as AudioFade, P as ChannelMerger, Q as MeshWarp, R as Contour, S as Vision, T as Ascii, U as ExtractLUT, V as DisplacementMap, W as Flip, X as Liquify, Y as LayerStyle, Z as MaskMath, _ as SelectiveColor, a as CustomEffect, at as Posterize, b as TileOffset, c as GradientMap, ct as RefineEdge, d as Levels, dt as Rotate, et as OpArt, f as Modulate, ft as StereoPanning, g as SSAO, gt as effectMeta, h as Relight3D, ht as Vignette, i as Curves, it as Pixelate, j as AudioReverb, k as AudioNoiseGate, l as HalftoneScreen, lt as RefractionCaustics3D, m as PBRGlass, mt as Trails, n as AudioSignalExtractor, nt as Painterly, o as DepthOfField, ot as ProceduralVFX, p as MotionBlur, pt as Toon, q as Kaleidoscope, r as Crop, rt as PatchHeal, s as FilmGrain, st as RGBSplit, t as ApplyLUT, tt as Paint, u as HighPass, ut as ResizerScaler, v as ShadowsHighlights, w as Blur, x as UnsharpMask, y as TemporalDeflicker, z as CornerPin } from "./effects-Duoqc_Oa.mjs";
|
|
55
|
+
import { a as HeadlessMediaRenderer, c as renderSemaphore, i as resetRenderCaches, n as renderFrameGrid, o as HeadlessWebGPURenderer, r as resolveSamplePoints, s as formatQaReport, t as computeGridLayout } from "./renderer-CC72sZ8l.mjs";
|
|
56
|
+
import { t as startPreview } from "./preview-CArsjmkd.mjs";
|
|
57
57
|
import path from "node:path";
|
|
58
58
|
import fs from "node:fs/promises";
|
|
59
59
|
import { format } from "d3-format";
|
|
@@ -261,8 +261,8 @@ function parseMarkup(markup) {
|
|
|
261
261
|
} else if (tagName === "accent") style.fill = "#6366f1";
|
|
262
262
|
else if (tagName === "color") style.fill = attrs.value ?? attrs.hex ?? attrs.fill;
|
|
263
263
|
else if (tagName === "size") {
|
|
264
|
-
const num = parseFloat(attrs.value ?? attrs.px ?? attrs.pt ?? "0");
|
|
265
|
-
if (num > 0) style.fontSize = num;
|
|
264
|
+
const num$2 = parseFloat(attrs.value ?? attrs.px ?? attrs.pt ?? "0");
|
|
265
|
+
if (num$2 > 0) style.fontSize = num$2;
|
|
266
266
|
} else if (tagName === "mark") {
|
|
267
267
|
style.mark = {
|
|
268
268
|
background: attrs.bg ?? attrs.background ?? "#6366f133",
|
|
@@ -536,9 +536,9 @@ function buildChart(options, props, defaultFontFamily) {
|
|
|
536
536
|
}
|
|
537
537
|
const yValues = [0];
|
|
538
538
|
series.forEach((s, i) => {
|
|
539
|
-
const range = stackRanges.get(i);
|
|
539
|
+
const range$1 = stackRanges.get(i);
|
|
540
540
|
if (typeOf(s) === "candlestick") for (const c of s.data) yValues.push(c.high, c.low);
|
|
541
|
-
else if (range) for (const [a, b] of range) yValues.push(a, b);
|
|
541
|
+
else if (range$1) for (const [a, b] of range$1) yValues.push(a, b);
|
|
542
542
|
else for (const p of points[i]) yValues.push(p.y);
|
|
543
543
|
});
|
|
544
544
|
if (series.every((s) => typeOf(s) === "candlestick" || typeOf(s) === "scatter" || typeOf(s) === "line")) yValues.shift();
|
|
@@ -685,14 +685,14 @@ function buildChart(options, props, defaultFontFamily) {
|
|
|
685
685
|
const color$1 = colorOf(s, si);
|
|
686
686
|
const kind = typeOf(s);
|
|
687
687
|
const pts = points[si];
|
|
688
|
-
const range = stackRanges.get(si);
|
|
688
|
+
const range$1 = stackRanges.get(si);
|
|
689
689
|
if (kind === "bar" && band) {
|
|
690
690
|
const groupSize = stacked ? 1 : barSeries.length;
|
|
691
691
|
const groupIdx = stacked ? 0 : barSeries.indexOf(si);
|
|
692
692
|
const bw = band.bandwidth() / groupSize;
|
|
693
693
|
const gap = groupSize > 1 ? Math.min(4, bw * .12) : 0;
|
|
694
694
|
pts.forEach((p, i) => {
|
|
695
|
-
const [lo$1, hi$1] = range ? range[i] : [0, p.y];
|
|
695
|
+
const [lo$1, hi$1] = range$1 ? range$1[i] : [0, p.y];
|
|
696
696
|
const top = plot.y + y(Math.max(lo$1, hi$1));
|
|
697
697
|
const bottom = plot.y + y(Math.min(lo$1, hi$1));
|
|
698
698
|
const h = Math.max(0, bottom - top);
|
|
@@ -795,8 +795,8 @@ function buildChart(options, props, defaultFontFamily) {
|
|
|
795
795
|
const m = Math.ceil(sw * 2 + (s.pointRadius ?? 0) + 2);
|
|
796
796
|
const xy = pts.map((p, i) => ({
|
|
797
797
|
x: xPos(p.x) + m,
|
|
798
|
-
top: (range ? y(range[i][1]) : y(p.y)) + m,
|
|
799
|
-
base: (range ? y(range[i][0]) : y(Math.max(y.domain()[0], Math.min(0, y.domain()[1])))) + m
|
|
798
|
+
top: (range$1 ? y(range$1[i][1]) : y(p.y)) + m,
|
|
799
|
+
base: (range$1 ? y(range$1[i][0]) : y(Math.max(y.domain()[0], Math.min(0, y.domain()[1])))) + m
|
|
800
800
|
}));
|
|
801
801
|
const curve = curveOf(s.curve);
|
|
802
802
|
const frame = {
|
|
@@ -843,16 +843,16 @@ function buildChart(options, props, defaultFontFamily) {
|
|
|
843
843
|
}));
|
|
844
844
|
children.push(node);
|
|
845
845
|
}
|
|
846
|
-
if ((s.pointRadius ?? 0) > 0) dots(si, pts, color$1, s.pointRadius ?? 0, delay + timing.duration * .5, range);
|
|
846
|
+
if ((s.pointRadius ?? 0) > 0) dots(si, pts, color$1, s.pointRadius ?? 0, delay + timing.duration * .5, range$1);
|
|
847
847
|
return;
|
|
848
848
|
}
|
|
849
|
-
if (kind === "scatter") dots(si, pts, color$1, s.pointRadius ?? 6, rank * (timing.duration / 3), range);
|
|
849
|
+
if (kind === "scatter") dots(si, pts, color$1, s.pointRadius ?? 6, rank * (timing.duration / 3), range$1);
|
|
850
850
|
});
|
|
851
851
|
if (showLegend) legendRow(legendItems);
|
|
852
|
-
function dots(si, pts, color$1, r, baseDelay, range) {
|
|
852
|
+
function dots(si, pts, color$1, r, baseDelay, range$1) {
|
|
853
853
|
const step = staggerStep(pts.length);
|
|
854
854
|
pts.forEach((p, i) => {
|
|
855
|
-
const py = range ? y(range[i][1]) : y(p.y);
|
|
855
|
+
const py = range$1 ? y(range$1[i][1]) : y(p.y);
|
|
856
856
|
const node = Layer.shape("circle", {
|
|
857
857
|
id: `${id}-point-${si}-${i}`,
|
|
858
858
|
position: "absolute",
|
|
@@ -983,6 +983,1168 @@ function translatePath(d, dx, dy) {
|
|
|
983
983
|
return out.join("");
|
|
984
984
|
}
|
|
985
985
|
|
|
986
|
+
//#endregion
|
|
987
|
+
//#region src/clip-vision.ts
|
|
988
|
+
/** Frames looked back over to bridge a gap where the person is lost. */
|
|
989
|
+
const HOLD = 24;
|
|
990
|
+
const CACHE_VERSION = 1;
|
|
991
|
+
/**
|
|
992
|
+
* Vision results for one clip, computed per frame on demand and shared by
|
|
993
|
+
* everything that reads the clip: physics, layout, signals. Unlike the
|
|
994
|
+
* render-time `withVision`, which analyses a layer's previous rendered frame,
|
|
995
|
+
* this analyses the clip's own frames, so a result exists before its frame
|
|
996
|
+
* renders. The composition analyses what each frame needs just before
|
|
997
|
+
* drawing it (and a live physics world before stepping), so preview and
|
|
998
|
+
* export fill the cache as they play; nothing has to be analysed up front.
|
|
999
|
+
*
|
|
1000
|
+
* Results depend only on the clip and settings, so they persist on disk in
|
|
1001
|
+
* Node: a second run, or a render after a preview, reuses them. A matte-mode
|
|
1002
|
+
* `withVision` layer of the same clip reuses them too (`keepSubjects`).
|
|
1003
|
+
*/
|
|
1004
|
+
var ClipVision = class {
|
|
1005
|
+
source;
|
|
1006
|
+
fps;
|
|
1007
|
+
startFrame;
|
|
1008
|
+
trimStartSec;
|
|
1009
|
+
smoothing;
|
|
1010
|
+
/** @internal */
|
|
1011
|
+
options;
|
|
1012
|
+
results = /* @__PURE__ */ new Map();
|
|
1013
|
+
sourceWidth = 0;
|
|
1014
|
+
sourceHeight = 0;
|
|
1015
|
+
cellW = 0;
|
|
1016
|
+
cellH = 0;
|
|
1017
|
+
cell;
|
|
1018
|
+
queue = Promise.resolve();
|
|
1019
|
+
runner;
|
|
1020
|
+
cacheLoaded;
|
|
1021
|
+
unsaved = 0;
|
|
1022
|
+
unsavedSubjects = 0;
|
|
1023
|
+
blurred = /* @__PURE__ */ new Map();
|
|
1024
|
+
smoothed = /* @__PURE__ */ new Map();
|
|
1025
|
+
personView;
|
|
1026
|
+
/** Full-resolution subject masks for render-time mattes (see `keepSubjects`). */
|
|
1027
|
+
subjects = /* @__PURE__ */ new Map();
|
|
1028
|
+
wantSubjects = false;
|
|
1029
|
+
subjectsLoaded;
|
|
1030
|
+
/** @internal Use `comp.vision(source, options)`. */
|
|
1031
|
+
constructor(source, fps, options = {}) {
|
|
1032
|
+
this.source = source;
|
|
1033
|
+
this.options = options;
|
|
1034
|
+
this.fps = options.fps ?? fps;
|
|
1035
|
+
this.startFrame = Math.round(options.startFrame ?? 0);
|
|
1036
|
+
this.trimStartSec = options.trimStartSec ?? 0;
|
|
1037
|
+
this.smoothing = Math.max(0, Math.round(options.smoothing ?? 2));
|
|
1038
|
+
this.cell = Math.max(1, Math.round(options.maskCell ?? 8));
|
|
1039
|
+
}
|
|
1040
|
+
/** The tracked person in source pixels; `.place()` maps it into the composition. */
|
|
1041
|
+
get person() {
|
|
1042
|
+
this.personView ??= new PersonTrack(this);
|
|
1043
|
+
return this.personView;
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Analyses what reading composition frame `frame` needs (it and a few
|
|
1047
|
+
* neighbours for smoothing). Pass a second frame to analyse a whole range.
|
|
1048
|
+
* Calls queue, so the clip is decoded once, in order.
|
|
1049
|
+
*/
|
|
1050
|
+
ready(frame, toFrame = frame) {
|
|
1051
|
+
const [from, to] = this.window(frame, toFrame);
|
|
1052
|
+
this.queue = this.queue.then(() => this.compute(from, to));
|
|
1053
|
+
return this.queue;
|
|
1054
|
+
}
|
|
1055
|
+
/** True when composition frame `frame` can be read synchronously. */
|
|
1056
|
+
isReady(frame) {
|
|
1057
|
+
const [from, to] = this.window(frame, frame);
|
|
1058
|
+
for (let c = from; c <= to; c++) if (!this.results.has(c)) return false;
|
|
1059
|
+
return true;
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* @internal Also keep each frame's full-resolution subject mask, merged
|
|
1063
|
+
* like the Vision effect's matte, so a matte layer of this clip reuses it
|
|
1064
|
+
* instead of segmenting the frame again (framefields links them).
|
|
1065
|
+
*/
|
|
1066
|
+
keepSubjects() {
|
|
1067
|
+
this.wantSubjects = true;
|
|
1068
|
+
}
|
|
1069
|
+
/**
|
|
1070
|
+
* @internal The subject at a source frame of the clip (`frame` at `fps`),
|
|
1071
|
+
* for a render-time matte: undefined until that frame is analysed with
|
|
1072
|
+
* subjects kept, null when it holds no subject.
|
|
1073
|
+
*/
|
|
1074
|
+
subject(frame, fps) {
|
|
1075
|
+
const c = frame * this.fps / fps;
|
|
1076
|
+
if (Math.abs(c - Math.round(c)) > 1e-6) return void 0;
|
|
1077
|
+
const s = this.subjects.get(Math.round(c));
|
|
1078
|
+
if (s === void 0) return s;
|
|
1079
|
+
if (s === null || !this.sourceWidth) return null;
|
|
1080
|
+
const width = this.sourceWidth;
|
|
1081
|
+
const height = this.sourceHeight;
|
|
1082
|
+
return {
|
|
1083
|
+
category: s.category,
|
|
1084
|
+
mask: decodeRuns(s, width * height),
|
|
1085
|
+
width,
|
|
1086
|
+
height,
|
|
1087
|
+
area: s.area,
|
|
1088
|
+
coverage: s.area / (width * height),
|
|
1089
|
+
detectionIndex: -1
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
/** Composition frame → clip frame (fractional). */
|
|
1093
|
+
clipFrame(frame) {
|
|
1094
|
+
return frame - this.startFrame + this.trimStartSec * this.fps;
|
|
1095
|
+
}
|
|
1096
|
+
window(frame, toFrame) {
|
|
1097
|
+
const a = Math.floor(this.clipFrame(Math.min(frame, toFrame)));
|
|
1098
|
+
const b = Math.ceil(this.clipFrame(Math.max(frame, toFrame)));
|
|
1099
|
+
return [Math.max(0, a - HOLD - this.smoothing), Math.max(0, b + this.smoothing)];
|
|
1100
|
+
}
|
|
1101
|
+
analysed(c) {
|
|
1102
|
+
return this.results.has(c) && (!this.wantSubjects || this.subjects.has(c));
|
|
1103
|
+
}
|
|
1104
|
+
async compute(from, to) {
|
|
1105
|
+
this.cacheLoaded ??= this.loadCache();
|
|
1106
|
+
await this.cacheLoaded;
|
|
1107
|
+
if (this.wantSubjects) {
|
|
1108
|
+
this.subjectsLoaded ??= this.loadSubjects();
|
|
1109
|
+
await this.subjectsLoaded;
|
|
1110
|
+
}
|
|
1111
|
+
let missing = false;
|
|
1112
|
+
for (let c = from; c <= to; c++) if (!this.analysed(c)) missing = true;
|
|
1113
|
+
if (!missing) return;
|
|
1114
|
+
const wantPose = this.options.pose ?? true;
|
|
1115
|
+
const wantMask = this.options.mask ?? true;
|
|
1116
|
+
this.runner ??= VisionRunner.create({
|
|
1117
|
+
modelsDir: this.options.modelsDir,
|
|
1118
|
+
baseUrl: this.options.baseUrl,
|
|
1119
|
+
...this.options.confidence !== void 0 && { confidence: this.options.confidence },
|
|
1120
|
+
...this.options.variant !== void 0 && { variant: this.options.variant }
|
|
1121
|
+
});
|
|
1122
|
+
const decoder = mediaDecoderCache.getVideo(this.source, true, "clip-vision");
|
|
1123
|
+
for (let c = from; c <= to; c++) {
|
|
1124
|
+
if (this.analysed(c)) continue;
|
|
1125
|
+
const image = await decodedPixels(await decoder.getFrame(c / this.fps));
|
|
1126
|
+
if (!image) {
|
|
1127
|
+
this.results.set(c, {
|
|
1128
|
+
kp: null,
|
|
1129
|
+
mask: null
|
|
1130
|
+
});
|
|
1131
|
+
if (this.wantSubjects) this.subjects.set(c, null);
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
if (!this.sourceWidth) this.setSize(image.width, image.height);
|
|
1135
|
+
const known = this.results.get(c);
|
|
1136
|
+
let kp = known?.kp ?? null;
|
|
1137
|
+
let mask = known?.mask ?? null;
|
|
1138
|
+
if (wantPose && !known) {
|
|
1139
|
+
const person = (await this.runner.pose(image)).people[0];
|
|
1140
|
+
if (person) {
|
|
1141
|
+
kp = new Float32Array(51);
|
|
1142
|
+
for (const [k, p] of person.keypoints.entries()) kp.set([
|
|
1143
|
+
p.x,
|
|
1144
|
+
p.y,
|
|
1145
|
+
p.visibility
|
|
1146
|
+
], k * 3);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
const seg = wantMask && !known || this.wantSubjects ? await this.runner.segment(image) : void 0;
|
|
1150
|
+
if (seg && this.wantSubjects) {
|
|
1151
|
+
const subject = mergeSubjectMask(seg.masks);
|
|
1152
|
+
this.subjects.set(c, subject ? {
|
|
1153
|
+
category: subject.category,
|
|
1154
|
+
area: subject.area,
|
|
1155
|
+
...encodeRuns(subject.mask)
|
|
1156
|
+
} : null);
|
|
1157
|
+
this.unsavedSubjects++;
|
|
1158
|
+
}
|
|
1159
|
+
if (seg && wantMask && !known) {
|
|
1160
|
+
const silhouette = personSilhouette(seg);
|
|
1161
|
+
if (silhouette) {
|
|
1162
|
+
mask = new Uint8Array(this.cellW * this.cellH);
|
|
1163
|
+
downsampleMean(silhouette, image.width, image.height, this.cell, mask, this.cellW, this.cellH);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
this.results.set(c, {
|
|
1167
|
+
kp,
|
|
1168
|
+
mask
|
|
1169
|
+
});
|
|
1170
|
+
if (!known) this.unsaved++;
|
|
1171
|
+
if (this.unsaved >= 24) await this.saveCache();
|
|
1172
|
+
if (this.unsavedSubjects >= 24) await this.saveSubjects();
|
|
1173
|
+
}
|
|
1174
|
+
if (this.unsaved > 0) await this.saveCache();
|
|
1175
|
+
if (this.unsavedSubjects > 0) await this.saveSubjects();
|
|
1176
|
+
}
|
|
1177
|
+
setSize(width, height) {
|
|
1178
|
+
this.sourceWidth = width;
|
|
1179
|
+
this.sourceHeight = height;
|
|
1180
|
+
this.cellW = Math.ceil(width / this.cell);
|
|
1181
|
+
this.cellH = Math.ceil(height / this.cell);
|
|
1182
|
+
}
|
|
1183
|
+
/** @internal */
|
|
1184
|
+
size() {
|
|
1185
|
+
return {
|
|
1186
|
+
width: this.sourceWidth,
|
|
1187
|
+
height: this.sourceHeight
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
/** @internal Raw result for a clip frame, or throws when it was never analysed. */
|
|
1191
|
+
result(c) {
|
|
1192
|
+
return this.results.get(Math.max(0, c));
|
|
1193
|
+
}
|
|
1194
|
+
/** @internal Confidence-weighted keypoint over the smoothing window, in source px. */
|
|
1195
|
+
keypoint(k, c) {
|
|
1196
|
+
c = Math.max(0, c);
|
|
1197
|
+
const key = `${k}:${c}`;
|
|
1198
|
+
const hit = this.smoothed.get(key);
|
|
1199
|
+
if (hit) return hit;
|
|
1200
|
+
const avg = (center) => {
|
|
1201
|
+
let sx = 0;
|
|
1202
|
+
let sy = 0;
|
|
1203
|
+
let sv = 0;
|
|
1204
|
+
let sw = 0;
|
|
1205
|
+
for (let g = center - this.smoothing; g <= center + this.smoothing; g++) {
|
|
1206
|
+
const r = this.mustHave(g < 0 ? 0 : g);
|
|
1207
|
+
if (!r.kp) continue;
|
|
1208
|
+
const v = r.kp[k * 3 + 2];
|
|
1209
|
+
const w = (this.smoothing + 1 - Math.abs(g - center)) * v * v;
|
|
1210
|
+
sx += r.kp[k * 3] * w;
|
|
1211
|
+
sy += r.kp[k * 3 + 1] * w;
|
|
1212
|
+
sv += v * w;
|
|
1213
|
+
sw += w;
|
|
1214
|
+
}
|
|
1215
|
+
return sw > 1e-6 ? [
|
|
1216
|
+
sx / sw,
|
|
1217
|
+
sy / sw,
|
|
1218
|
+
sv / sw
|
|
1219
|
+
] : null;
|
|
1220
|
+
};
|
|
1221
|
+
let out = null;
|
|
1222
|
+
for (let d = 0; d <= HOLD && !out; d++) if (c - d >= 0) out = avg(c - d);
|
|
1223
|
+
out ??= [
|
|
1224
|
+
this.sourceWidth / 2,
|
|
1225
|
+
this.sourceHeight / 2,
|
|
1226
|
+
0
|
|
1227
|
+
];
|
|
1228
|
+
this.smoothed.set(key, out);
|
|
1229
|
+
return out;
|
|
1230
|
+
}
|
|
1231
|
+
/** @internal Softened silhouette grid for a clip frame (null: no person). */
|
|
1232
|
+
blurredMask(c) {
|
|
1233
|
+
c = Math.max(0, c);
|
|
1234
|
+
if (this.blurred.has(c)) return this.blurred.get(c) ?? null;
|
|
1235
|
+
const r = this.mustHave(c);
|
|
1236
|
+
const grid = r.mask ? boxBlur(r.mask, this.cellW, this.cellH, 2) : null;
|
|
1237
|
+
this.blurred.set(c, grid);
|
|
1238
|
+
return grid;
|
|
1239
|
+
}
|
|
1240
|
+
/** @internal */
|
|
1241
|
+
grid() {
|
|
1242
|
+
return {
|
|
1243
|
+
cell: this.cell,
|
|
1244
|
+
width: this.cellW,
|
|
1245
|
+
height: this.cellH
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
mustHave(c) {
|
|
1249
|
+
const r = this.results.get(c);
|
|
1250
|
+
if (r) return r;
|
|
1251
|
+
throw new Error(`ClipVision "${this.source}": frame ${c} is not analysed yet. Read it while rendering (the composition prepares each frame), in a live physics world, or after \`await clip.ready(frame)\`.`);
|
|
1252
|
+
}
|
|
1253
|
+
cacheFile;
|
|
1254
|
+
async cachePath() {
|
|
1255
|
+
if (this.cacheFile !== void 0) return this.cacheFile;
|
|
1256
|
+
this.cacheFile = null;
|
|
1257
|
+
if (this.options.cacheDir === false || typeof process === "undefined" || !process.versions?.node) return null;
|
|
1258
|
+
try {
|
|
1259
|
+
const [fs$1, path$1, os, crypto] = await Promise.all([
|
|
1260
|
+
import("node:fs"),
|
|
1261
|
+
import("node:path"),
|
|
1262
|
+
import("node:os"),
|
|
1263
|
+
import("node:crypto")
|
|
1264
|
+
]);
|
|
1265
|
+
const stat = fs$1.statSync(this.source);
|
|
1266
|
+
const dir = this.options.cacheDir ?? process.env.FRAMEFIELDS_VISION_CACHE_DIR ?? path$1.join(os.homedir(), ".cache", "framefields", "vision");
|
|
1267
|
+
const o = this.options;
|
|
1268
|
+
const key = JSON.stringify([
|
|
1269
|
+
CACHE_VERSION,
|
|
1270
|
+
path$1.resolve(this.source),
|
|
1271
|
+
stat.size,
|
|
1272
|
+
stat.mtimeMs,
|
|
1273
|
+
this.fps,
|
|
1274
|
+
o.pose ?? true,
|
|
1275
|
+
o.mask ?? true,
|
|
1276
|
+
this.cell,
|
|
1277
|
+
o.variant ?? "s",
|
|
1278
|
+
o.confidence ?? null
|
|
1279
|
+
]);
|
|
1280
|
+
const hash = crypto.createHash("sha256").update(key).digest("hex").slice(0, 24);
|
|
1281
|
+
fs$1.mkdirSync(dir, { recursive: true });
|
|
1282
|
+
this.cacheFile = path$1.join(dir, `${path$1.basename(this.source)}.${hash}.json`);
|
|
1283
|
+
} catch {
|
|
1284
|
+
this.cacheFile = null;
|
|
1285
|
+
}
|
|
1286
|
+
return this.cacheFile;
|
|
1287
|
+
}
|
|
1288
|
+
async loadCache() {
|
|
1289
|
+
const file = await this.cachePath();
|
|
1290
|
+
if (!file) return;
|
|
1291
|
+
try {
|
|
1292
|
+
const fs$1 = await import("node:fs");
|
|
1293
|
+
if (!fs$1.existsSync(file)) return;
|
|
1294
|
+
const json = JSON.parse(fs$1.readFileSync(file, "utf8"));
|
|
1295
|
+
if (json.cell !== this.cell) return;
|
|
1296
|
+
this.setSize(json.width, json.height);
|
|
1297
|
+
for (const [c, r] of Object.entries(json.frames)) this.results.set(Number(c), {
|
|
1298
|
+
kp: r.kp ? Float32Array.from(r.kp) : null,
|
|
1299
|
+
mask: r.mask ? new Uint8Array(Buffer.from(r.mask, "base64")) : null
|
|
1300
|
+
});
|
|
1301
|
+
} catch {}
|
|
1302
|
+
}
|
|
1303
|
+
async saveCache() {
|
|
1304
|
+
this.unsaved = 0;
|
|
1305
|
+
const file = await this.cachePath();
|
|
1306
|
+
if (!file || !this.sourceWidth) return;
|
|
1307
|
+
const frames = {};
|
|
1308
|
+
for (const [c, r] of this.results) frames[c] = {
|
|
1309
|
+
kp: r.kp ? Array.from(r.kp, (v) => Math.round(v * 100) / 100) : null,
|
|
1310
|
+
mask: r.mask ? Buffer.from(r.mask).toString("base64") : null
|
|
1311
|
+
};
|
|
1312
|
+
const fs$1 = await import("node:fs");
|
|
1313
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
1314
|
+
fs$1.writeFileSync(tmp, JSON.stringify({
|
|
1315
|
+
width: this.sourceWidth,
|
|
1316
|
+
height: this.sourceHeight,
|
|
1317
|
+
cell: this.cell,
|
|
1318
|
+
frames
|
|
1319
|
+
}));
|
|
1320
|
+
fs$1.renameSync(tmp, file);
|
|
1321
|
+
}
|
|
1322
|
+
/** Subjects live beside the results: large, and only matte layers read them. */
|
|
1323
|
+
async subjectsPath() {
|
|
1324
|
+
const file = await this.cachePath();
|
|
1325
|
+
return file ? file.replace(/\.json$/, ".subjects.json") : null;
|
|
1326
|
+
}
|
|
1327
|
+
async loadSubjects() {
|
|
1328
|
+
const file = await this.subjectsPath();
|
|
1329
|
+
if (!file) return;
|
|
1330
|
+
try {
|
|
1331
|
+
const fs$1 = await import("node:fs");
|
|
1332
|
+
if (!fs$1.existsSync(file)) return;
|
|
1333
|
+
const json = JSON.parse(fs$1.readFileSync(file, "utf8"));
|
|
1334
|
+
if (this.sourceWidth && json.width !== this.sourceWidth) return;
|
|
1335
|
+
if (!this.sourceWidth) this.setSize(json.width, json.height);
|
|
1336
|
+
for (const [c, s] of Object.entries(json.frames)) {
|
|
1337
|
+
const lengths = Buffer.from(s?.lengths ?? "", "base64");
|
|
1338
|
+
this.subjects.set(Number(c), s && {
|
|
1339
|
+
category: s.category,
|
|
1340
|
+
area: s.area,
|
|
1341
|
+
values: new Uint8Array(Buffer.from(s.values, "base64")),
|
|
1342
|
+
lengths: new Uint32Array(lengths.buffer.slice(lengths.byteOffset, lengths.byteOffset + lengths.byteLength))
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
} catch {}
|
|
1346
|
+
}
|
|
1347
|
+
async saveSubjects() {
|
|
1348
|
+
this.unsavedSubjects = 0;
|
|
1349
|
+
const file = await this.subjectsPath();
|
|
1350
|
+
if (!file || !this.sourceWidth) return;
|
|
1351
|
+
const frames = {};
|
|
1352
|
+
for (const [c, s] of this.subjects) frames[c] = s && {
|
|
1353
|
+
category: s.category,
|
|
1354
|
+
area: s.area,
|
|
1355
|
+
values: Buffer.from(s.values).toString("base64"),
|
|
1356
|
+
lengths: Buffer.from(s.lengths.buffer, s.lengths.byteOffset, s.lengths.byteLength).toString("base64")
|
|
1357
|
+
};
|
|
1358
|
+
const fs$1 = await import("node:fs");
|
|
1359
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
1360
|
+
fs$1.writeFileSync(tmp, JSON.stringify({
|
|
1361
|
+
width: this.sourceWidth,
|
|
1362
|
+
height: this.sourceHeight,
|
|
1363
|
+
frames
|
|
1364
|
+
}));
|
|
1365
|
+
fs$1.renameSync(tmp, file);
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
/**
|
|
1369
|
+
* The person in a {@link ClipVision}: pose keypoints and silhouette at any
|
|
1370
|
+
* composition frame, in source pixels or, after `place()`, composition
|
|
1371
|
+
* pixels. Reads are synchronous; the frame must be analysed (it is while
|
|
1372
|
+
* rendering, inside a live physics world, or after `await clip.ready(frame)`).
|
|
1373
|
+
*/
|
|
1374
|
+
var PersonTrack = class PersonTrack {
|
|
1375
|
+
/** @internal */
|
|
1376
|
+
constructor(clip, t, placement) {
|
|
1377
|
+
this.clip = clip;
|
|
1378
|
+
this.t = t;
|
|
1379
|
+
this.placement = placement;
|
|
1380
|
+
}
|
|
1381
|
+
/** The same person in composition pixels for a clip laid out at `placement`. */
|
|
1382
|
+
place(placement) {
|
|
1383
|
+
return new PersonTrack(this.clip, void 0, placement);
|
|
1384
|
+
}
|
|
1385
|
+
transform() {
|
|
1386
|
+
if (this.t) return this.t;
|
|
1387
|
+
const p = this.placement;
|
|
1388
|
+
const { width: sw, height: sh } = this.clip.size();
|
|
1389
|
+
if (!p || !sw) return {
|
|
1390
|
+
sx: 1,
|
|
1391
|
+
sy: 1,
|
|
1392
|
+
ox: 0,
|
|
1393
|
+
oy: 0
|
|
1394
|
+
};
|
|
1395
|
+
let sx = p.width / sw;
|
|
1396
|
+
let sy = p.height / sh;
|
|
1397
|
+
if (p.fit !== "fill") {
|
|
1398
|
+
const s = p.fit === "contain" ? Math.min(sx, sy) : Math.max(sx, sy);
|
|
1399
|
+
sx = s;
|
|
1400
|
+
sy = s;
|
|
1401
|
+
}
|
|
1402
|
+
return {
|
|
1403
|
+
sx,
|
|
1404
|
+
sy,
|
|
1405
|
+
ox: (p.x ?? 0) + (p.width - sw * sx) / 2,
|
|
1406
|
+
oy: (p.y ?? 0) + (p.height - sh * sy) / 2
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
/** A keypoint at a composition frame (fractional frames interpolate). */
|
|
1410
|
+
point(name, frame) {
|
|
1411
|
+
const k = keypointIndex(name);
|
|
1412
|
+
const c = this.clip.clipFrame(frame);
|
|
1413
|
+
const c0 = Math.floor(c);
|
|
1414
|
+
const u = c - c0;
|
|
1415
|
+
const a = this.clip.keypoint(k, c0);
|
|
1416
|
+
const b = u > 0 ? this.clip.keypoint(k, c0 + 1) : a;
|
|
1417
|
+
const t = this.transform();
|
|
1418
|
+
return {
|
|
1419
|
+
x: (a[0] + (b[0] - a[0]) * u) * t.sx + t.ox,
|
|
1420
|
+
y: (a[1] + (b[1] - a[1]) * u) * t.sy + t.oy,
|
|
1421
|
+
visibility: a[2] + (b[2] - a[2]) * u
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
/** Signals for a keypoint, for `.signal("x", …)`; read while rendering. */
|
|
1425
|
+
signals(name) {
|
|
1426
|
+
const read = (axis) => Signal.builder({
|
|
1427
|
+
type: "custom",
|
|
1428
|
+
fn: (ctx) => this.point(name, ctx.frame)[axis]
|
|
1429
|
+
});
|
|
1430
|
+
return {
|
|
1431
|
+
x: read("x"),
|
|
1432
|
+
y: read("y"),
|
|
1433
|
+
visibility: read("visibility")
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* @internal The softened silhouette grid at a composition frame and how
|
|
1438
|
+
* placed px map onto it (`cell = (px − origin) / scale − 0.5`), for GPU
|
|
1439
|
+
* consumers such as particles. Null when no person was found.
|
|
1440
|
+
*/
|
|
1441
|
+
silhouetteGrid(frame) {
|
|
1442
|
+
const grid = this.clip.blurredMask(Math.round(this.clip.clipFrame(frame)));
|
|
1443
|
+
if (!grid) return null;
|
|
1444
|
+
const { cell, width, height } = this.clip.grid();
|
|
1445
|
+
const t = this.transform();
|
|
1446
|
+
return {
|
|
1447
|
+
data: grid,
|
|
1448
|
+
width,
|
|
1449
|
+
height,
|
|
1450
|
+
originX: t.ox,
|
|
1451
|
+
originY: t.oy,
|
|
1452
|
+
scaleX: cell * t.sx,
|
|
1453
|
+
scaleY: cell * t.sy
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
/**
|
|
1457
|
+
* The silhouette near a point at a composition frame: `alpha` 0–1 (softened
|
|
1458
|
+
* over a few cells; inside > 0.5) and its gradient `gx`, `gy` per px,
|
|
1459
|
+
* pointing into the body. Push things out with `-gradient`.
|
|
1460
|
+
*/
|
|
1461
|
+
sample(frame, x, y) {
|
|
1462
|
+
const grid = this.clip.blurredMask(Math.round(this.clip.clipFrame(frame)));
|
|
1463
|
+
if (!grid) return {
|
|
1464
|
+
alpha: 0,
|
|
1465
|
+
gx: 0,
|
|
1466
|
+
gy: 0
|
|
1467
|
+
};
|
|
1468
|
+
const { cell, width: w, height: h } = this.clip.grid();
|
|
1469
|
+
const t = this.transform();
|
|
1470
|
+
const cx = (x - t.ox) / t.sx / cell - .5;
|
|
1471
|
+
const cy = (y - t.oy) / t.sy / cell - .5;
|
|
1472
|
+
const read = (gx, gy) => gx < 0 || gy < 0 || gx >= w || gy >= h ? 0 : grid[gy * w + gx];
|
|
1473
|
+
const bil = (px, py) => {
|
|
1474
|
+
const x0 = Math.floor(px);
|
|
1475
|
+
const y0 = Math.floor(py);
|
|
1476
|
+
const ux = px - x0;
|
|
1477
|
+
const uy = py - y0;
|
|
1478
|
+
const a = read(x0, y0) + (read(x0 + 1, y0) - read(x0, y0)) * ux;
|
|
1479
|
+
return a + (read(x0, y0 + 1) + (read(x0 + 1, y0 + 1) - read(x0, y0 + 1)) * ux - a) * uy;
|
|
1480
|
+
};
|
|
1481
|
+
return {
|
|
1482
|
+
alpha: bil(cx, cy),
|
|
1483
|
+
gx: (bil(cx + 1, cy) - bil(cx - 1, cy)) / (2 * cell * t.sx),
|
|
1484
|
+
gy: (bil(cx, cy + 1) - bil(cx, cy - 1)) / (2 * cell * t.sy)
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
function keypointIndex(name) {
|
|
1489
|
+
const i = COCO17_KEYPOINT_NAMES.indexOf(name);
|
|
1490
|
+
if (i < 0) throw new Error(`Unknown keypoint "${name}". Use one of ${COCO17_KEYPOINT_NAMES.join(", ")}.`);
|
|
1491
|
+
return i;
|
|
1492
|
+
}
|
|
1493
|
+
/** RGBA pixels of a decoded frame (headless buffer, or a canvas in the browser). */
|
|
1494
|
+
async function decodedPixels(frame) {
|
|
1495
|
+
if (!frame || frame.width <= 0) return null;
|
|
1496
|
+
if (frame.buffer) return {
|
|
1497
|
+
data: frame.buffer,
|
|
1498
|
+
width: frame.width,
|
|
1499
|
+
height: frame.height
|
|
1500
|
+
};
|
|
1501
|
+
const ctx = frame.canvas?.getContext("2d");
|
|
1502
|
+
if (!ctx) return null;
|
|
1503
|
+
return {
|
|
1504
|
+
data: ctx.getImageData(0, 0, frame.width, frame.height).data,
|
|
1505
|
+
width: frame.width,
|
|
1506
|
+
height: frame.height
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* The largest person's mask, plus instances that overlap it and are no more
|
|
1511
|
+
* than twice its size (a flowing dress often comes back as "kite").
|
|
1512
|
+
*/
|
|
1513
|
+
function personSilhouette(seg) {
|
|
1514
|
+
const people = seg.masks.filter((m) => m.category === "person");
|
|
1515
|
+
if (people.length === 0) return void 0;
|
|
1516
|
+
const person = people.reduce((a, b) => b.area > a.area ? b : a);
|
|
1517
|
+
const box = seg.detections[person.detectionIndex]?.boundingBox;
|
|
1518
|
+
const out = new Uint8Array(person.mask);
|
|
1519
|
+
if (!box) return out;
|
|
1520
|
+
for (const m of seg.masks) {
|
|
1521
|
+
if (m === person || m.area > person.area * 2) continue;
|
|
1522
|
+
const b = seg.detections[m.detectionIndex]?.boundingBox;
|
|
1523
|
+
if (!b) continue;
|
|
1524
|
+
if (Math.max(0, Math.min(box.originX + box.width, b.originX + b.width) - Math.max(box.originX, b.originX)) * Math.max(0, Math.min(box.originY + box.height, b.originY + b.height) - Math.max(box.originY, b.originY)) < .3 * b.width * b.height) continue;
|
|
1525
|
+
for (let i = 0; i < out.length; i++) if (m.mask[i] > out[i]) out[i] = m.mask[i];
|
|
1526
|
+
}
|
|
1527
|
+
return out;
|
|
1528
|
+
}
|
|
1529
|
+
function downsampleMean(src, w, h, cell, dst, mw, mh) {
|
|
1530
|
+
for (let my = 0; my < mh; my++) for (let mx = 0; mx < mw; mx++) {
|
|
1531
|
+
let sum = 0;
|
|
1532
|
+
let n = 0;
|
|
1533
|
+
for (let y = my * cell; y < Math.min(h, (my + 1) * cell); y++) for (let x = mx * cell; x < Math.min(w, (mx + 1) * cell); x++) {
|
|
1534
|
+
sum += src[y * w + x];
|
|
1535
|
+
n++;
|
|
1536
|
+
}
|
|
1537
|
+
dst[my * mw + mx] = n ? Math.round(sum / n) : 0;
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
/** A mask as (value, run length) pairs: long flat runs with soft edges. */
|
|
1541
|
+
function encodeRuns(mask) {
|
|
1542
|
+
let runs = 0;
|
|
1543
|
+
for (let i = 1; i <= mask.length; i++) if (i === mask.length || mask[i] !== mask[i - 1]) runs++;
|
|
1544
|
+
const values = new Uint8Array(runs);
|
|
1545
|
+
const lengths = new Uint32Array(runs);
|
|
1546
|
+
let r = 0;
|
|
1547
|
+
let start = 0;
|
|
1548
|
+
for (let i = 1; i <= mask.length; i++) if (i === mask.length || mask[i] !== mask[start]) {
|
|
1549
|
+
values[r] = mask[start];
|
|
1550
|
+
lengths[r++] = i - start;
|
|
1551
|
+
start = i;
|
|
1552
|
+
}
|
|
1553
|
+
return {
|
|
1554
|
+
values,
|
|
1555
|
+
lengths
|
|
1556
|
+
};
|
|
1557
|
+
}
|
|
1558
|
+
function decodeRuns(runs, size$1) {
|
|
1559
|
+
const out = new Uint8Array(size$1);
|
|
1560
|
+
let at = 0;
|
|
1561
|
+
for (let r = 0; r < runs.values.length; r++) {
|
|
1562
|
+
const end = Math.min(size$1, at + runs.lengths[r]);
|
|
1563
|
+
if (runs.values[r] !== 0) out.fill(runs.values[r], at, end);
|
|
1564
|
+
at = end;
|
|
1565
|
+
}
|
|
1566
|
+
return out;
|
|
1567
|
+
}
|
|
1568
|
+
/** Separable box blur to 0–1, so gradients reach a little past the edge. */
|
|
1569
|
+
function boxBlur(src, w, h, r) {
|
|
1570
|
+
const tmp = new Float32Array(w * h);
|
|
1571
|
+
const dst = new Float32Array(w * h);
|
|
1572
|
+
for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
|
|
1573
|
+
let s = 0;
|
|
1574
|
+
let n = 0;
|
|
1575
|
+
for (let k = -r; k <= r; k++) {
|
|
1576
|
+
const xx = x + k;
|
|
1577
|
+
if (xx >= 0 && xx < w) {
|
|
1578
|
+
s += src[y * w + xx];
|
|
1579
|
+
n++;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
tmp[y * w + x] = s / n / 255;
|
|
1583
|
+
}
|
|
1584
|
+
for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) {
|
|
1585
|
+
let s = 0;
|
|
1586
|
+
let n = 0;
|
|
1587
|
+
for (let k = -r; k <= r; k++) {
|
|
1588
|
+
const yy = y + k;
|
|
1589
|
+
if (yy >= 0 && yy < h) {
|
|
1590
|
+
s += tmp[yy * w + x];
|
|
1591
|
+
n++;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
dst[y * w + x] = s / n;
|
|
1595
|
+
}
|
|
1596
|
+
return dst;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
//#endregion
|
|
1600
|
+
//#region src/physics/core.ts
|
|
1601
|
+
const CHANNEL_TOLERANCE_SCALE = {
|
|
1602
|
+
rotation: .2,
|
|
1603
|
+
rotateX: .2,
|
|
1604
|
+
rotateY: .2,
|
|
1605
|
+
rotateZ: .2
|
|
1606
|
+
};
|
|
1607
|
+
function sampleDriver(driver, frame, fps) {
|
|
1608
|
+
if (typeof driver === "number") return driver;
|
|
1609
|
+
if (typeof driver === "function") return driver(frame);
|
|
1610
|
+
const time = frame / fps;
|
|
1611
|
+
return driver.get({
|
|
1612
|
+
frame,
|
|
1613
|
+
fps,
|
|
1614
|
+
time,
|
|
1615
|
+
duration: 0,
|
|
1616
|
+
durationMs: 0,
|
|
1617
|
+
progress: 0,
|
|
1618
|
+
deltaTime: 1 / fps
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
/** Shortest-path angle continuation so rotation never jumps by 360. */
|
|
1622
|
+
function unwrapDegrees(previous, next) {
|
|
1623
|
+
let d = next - previous;
|
|
1624
|
+
d -= Math.round(d / 360) * 360;
|
|
1625
|
+
return previous + d;
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Ramer–Douglas–Peucker over one channel: the fewest linear keyframes that
|
|
1629
|
+
* stay within `tolerance` of every simulated frame.
|
|
1630
|
+
*/
|
|
1631
|
+
function simplifyKeys(values, tolerance) {
|
|
1632
|
+
const n = values.length;
|
|
1633
|
+
if (n === 0) return [];
|
|
1634
|
+
const keep = new Uint8Array(n);
|
|
1635
|
+
keep[0] = 1;
|
|
1636
|
+
keep[n - 1] = 1;
|
|
1637
|
+
const stack$1 = [[0, n - 1]];
|
|
1638
|
+
while (stack$1.length > 0) {
|
|
1639
|
+
const [a, b] = stack$1.pop();
|
|
1640
|
+
let worst = -1;
|
|
1641
|
+
let worstErr = tolerance;
|
|
1642
|
+
for (let i = a + 1; i < b; i++) {
|
|
1643
|
+
const t = (i - a) / (b - a);
|
|
1644
|
+
const lerp = values[a] + (values[b] - values[a]) * t;
|
|
1645
|
+
const err = Math.abs(values[i] - lerp);
|
|
1646
|
+
if (err > worstErr) {
|
|
1647
|
+
worstErr = err;
|
|
1648
|
+
worst = i;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
if (worst >= 0) {
|
|
1652
|
+
keep[worst] = 1;
|
|
1653
|
+
stack$1.push([a, worst], [worst, b]);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
const keys = [];
|
|
1657
|
+
for (let i = 0; i < n; i++) if (keep[i]) keys.push([
|
|
1658
|
+
i,
|
|
1659
|
+
round4(values[i]),
|
|
1660
|
+
"none"
|
|
1661
|
+
]);
|
|
1662
|
+
if (keys.length === 2 && keys[0][1] === keys[1][1]) keys.pop();
|
|
1663
|
+
return keys;
|
|
1664
|
+
}
|
|
1665
|
+
function round4(v) {
|
|
1666
|
+
return Math.round(v * 1e4) / 1e4;
|
|
1667
|
+
}
|
|
1668
|
+
/**
|
|
1669
|
+
* Per-frame recordings of one body, indexed by composition frame. Arrays are
|
|
1670
|
+
* filled when the world simulates; reading a signal before that simulates.
|
|
1671
|
+
*/
|
|
1672
|
+
var Recording = class {
|
|
1673
|
+
channels = /* @__PURE__ */ new Map();
|
|
1674
|
+
signals = /* @__PURE__ */ new Map();
|
|
1675
|
+
constructor(length, fps, ensure) {
|
|
1676
|
+
this.length = length;
|
|
1677
|
+
this.fps = fps;
|
|
1678
|
+
this.ensure = ensure;
|
|
1679
|
+
}
|
|
1680
|
+
channel(name) {
|
|
1681
|
+
let arr = this.channels.get(name);
|
|
1682
|
+
if (!arr) {
|
|
1683
|
+
arr = new Float32Array(this.length);
|
|
1684
|
+
this.channels.set(name, arr);
|
|
1685
|
+
}
|
|
1686
|
+
return arr;
|
|
1687
|
+
}
|
|
1688
|
+
/**
|
|
1689
|
+
* A per-frame signal over a channel. Creating it does nothing; the first
|
|
1690
|
+
* value read runs the simulation (offline worlds), so signals can be bound
|
|
1691
|
+
* while bodies are still being added.
|
|
1692
|
+
*/
|
|
1693
|
+
signal(name) {
|
|
1694
|
+
let sig = this.signals.get(name);
|
|
1695
|
+
if (!sig) {
|
|
1696
|
+
const arr = this.channel(name);
|
|
1697
|
+
const fps = this.fps;
|
|
1698
|
+
sig = Signal.builder({
|
|
1699
|
+
type: "custom",
|
|
1700
|
+
fn: (ctx) => {
|
|
1701
|
+
this.ensure();
|
|
1702
|
+
const f = ctx.fps && ctx.fps !== fps ? ctx.frame / ctx.fps * fps : ctx.frame;
|
|
1703
|
+
return arr[Math.max(0, Math.min(arr.length - 1, Math.round(f)))];
|
|
1704
|
+
}
|
|
1705
|
+
});
|
|
1706
|
+
this.signals.set(name, sig);
|
|
1707
|
+
}
|
|
1708
|
+
return sig;
|
|
1709
|
+
}
|
|
1710
|
+
};
|
|
1711
|
+
/**
|
|
1712
|
+
* Stepping, recording, impacts and output shared by `PhysicsWorld2D` and
|
|
1713
|
+
* `PhysicsWorld3D`. V is a vector in backend units (meters).
|
|
1714
|
+
*/
|
|
1715
|
+
var WorldCore = class {
|
|
1716
|
+
fps;
|
|
1717
|
+
frames;
|
|
1718
|
+
startFrame;
|
|
1719
|
+
pixelsPerMeter;
|
|
1720
|
+
substeps;
|
|
1721
|
+
impactDecay;
|
|
1722
|
+
impactReference;
|
|
1723
|
+
bodies = [];
|
|
1724
|
+
forces = [];
|
|
1725
|
+
impulses = [];
|
|
1726
|
+
fields = [];
|
|
1727
|
+
worldRecording;
|
|
1728
|
+
allCollisions = [];
|
|
1729
|
+
/** No more bodies, joints or forces: the world has started (or finished). */
|
|
1730
|
+
locked = false;
|
|
1731
|
+
liveMode = false;
|
|
1732
|
+
stepper;
|
|
1733
|
+
chain = Promise.resolve();
|
|
1734
|
+
constructor(sim, gravity, options) {
|
|
1735
|
+
this.sim = sim;
|
|
1736
|
+
this.gravity = gravity;
|
|
1737
|
+
if (!(options.fps > 0)) throw new Error("Physics: `fps` must be > 0.");
|
|
1738
|
+
if (!(options.frames >= 1) || !Number.isInteger(options.frames)) throw new Error("Physics: `frames` must be a positive integer.");
|
|
1739
|
+
this.fps = options.fps;
|
|
1740
|
+
this.frames = options.frames;
|
|
1741
|
+
this.startFrame = Math.max(0, Math.round(options.startFrame ?? 0));
|
|
1742
|
+
this.pixelsPerMeter = options.pixelsPerMeter ?? 100;
|
|
1743
|
+
this.substeps = Math.max(1, Math.round(options.substeps ?? 4));
|
|
1744
|
+
this.impactDecay = Math.max(.5, options.impactDecay ?? 8);
|
|
1745
|
+
this.impactReference = options.impactReference ?? 1500;
|
|
1746
|
+
this.worldRecording = this.newRecording();
|
|
1747
|
+
}
|
|
1748
|
+
/** First contacts so far (all of them once simulated), in frame order. */
|
|
1749
|
+
get collisions() {
|
|
1750
|
+
this.ensureSimulated();
|
|
1751
|
+
return this.allCollisions;
|
|
1752
|
+
}
|
|
1753
|
+
/** Loudest impact envelope of any body each frame: camera shake, flashes, hit sounds. */
|
|
1754
|
+
get impact() {
|
|
1755
|
+
return this.worldRecording.signal("impact");
|
|
1756
|
+
}
|
|
1757
|
+
/** Runs the whole simulation now (once) and binds every body's layer per its `output`. */
|
|
1758
|
+
simulate() {
|
|
1759
|
+
if (this.liveMode) throw new Error("Physics: this world is live; it steps as frames render.");
|
|
1760
|
+
this.ensureSimulated();
|
|
1761
|
+
return this;
|
|
1762
|
+
}
|
|
1763
|
+
/** Like {@link simulate}, but writes keyframes for every body whose `output` was left at its default. */
|
|
1764
|
+
bakeKeyframes(options = {}) {
|
|
1765
|
+
if (this.locked) throw new Error("Physics: bakeKeyframes() must come before simulate(), live() or reading any body signal.");
|
|
1766
|
+
this.bakeDefault = options;
|
|
1767
|
+
this.ensureSimulated();
|
|
1768
|
+
return this;
|
|
1769
|
+
}
|
|
1770
|
+
/**
|
|
1771
|
+
* Steps the world as the composition renders instead of all at once: before
|
|
1772
|
+
* each frame draws, the world advances to it, after the composition's
|
|
1773
|
+
* vision clips (`comp.vision`) and any `inputs` have that frame ready. Use
|
|
1774
|
+
* it when bodies follow a performer, so nothing is analysed up front and a
|
|
1775
|
+
* preview starts at once. Frames already simulated are kept, so scrubbing
|
|
1776
|
+
* back costs nothing; jumping ahead simulates the frames in between.
|
|
1777
|
+
* Signals only; `bakeKeyframes()` needs the whole run up front.
|
|
1778
|
+
*/
|
|
1779
|
+
live(comp, options = {}) {
|
|
1780
|
+
if (this.locked) throw new Error("Physics: live() must come before simulate() or reading any body signal.");
|
|
1781
|
+
this.locked = true;
|
|
1782
|
+
this.liveMode = true;
|
|
1783
|
+
const prepare = async (frame) => {
|
|
1784
|
+
await comp.prepareVision?.(frame);
|
|
1785
|
+
for (const input of options.inputs ?? []) await input.ready(frame);
|
|
1786
|
+
};
|
|
1787
|
+
this.prepareInputs = prepare;
|
|
1788
|
+
for (const body of this.bodies) this.bind(body);
|
|
1789
|
+
comp.onPrepareFrame((ctx) => this.advanceTo(ctx.frame));
|
|
1790
|
+
return this;
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Live worlds: simulates up to composition frame `frame` (no-op when it
|
|
1794
|
+
* already has). Calls queue, so frames are stepped once, in order.
|
|
1795
|
+
*/
|
|
1796
|
+
advanceTo(frame) {
|
|
1797
|
+
this.chain = this.chain.then(async () => {
|
|
1798
|
+
const target = Math.min(this.frames - 1, Math.round(frame) - this.startFrame);
|
|
1799
|
+
if (!this.stepper) {
|
|
1800
|
+
await this.prepareInputs?.(this.startFrame);
|
|
1801
|
+
this.stepper = this.begin();
|
|
1802
|
+
}
|
|
1803
|
+
const st = this.stepper;
|
|
1804
|
+
while (st.current < target) {
|
|
1805
|
+
const next = st.current + 1;
|
|
1806
|
+
await this.prepareInputs?.(this.startFrame + next);
|
|
1807
|
+
this.stepFrame(st, next);
|
|
1808
|
+
}
|
|
1809
|
+
if (st.current === this.frames - 1 && !st.freed) {
|
|
1810
|
+
st.freed = true;
|
|
1811
|
+
this.sim.free();
|
|
1812
|
+
}
|
|
1813
|
+
});
|
|
1814
|
+
return this.chain;
|
|
1815
|
+
}
|
|
1816
|
+
prepareInputs;
|
|
1817
|
+
bakeDefault;
|
|
1818
|
+
newRecording() {
|
|
1819
|
+
return new Recording(this.startFrame + this.frames, this.fps, () => this.ensureSimulated());
|
|
1820
|
+
}
|
|
1821
|
+
assertOpen() {
|
|
1822
|
+
if (this.locked) throw new Error("Physics: the world has already started; add every body, joint and force before simulate(), live() or reading signals.");
|
|
1823
|
+
}
|
|
1824
|
+
addBody(body) {
|
|
1825
|
+
this.assertOpen();
|
|
1826
|
+
this.bodies.push(body);
|
|
1827
|
+
return body;
|
|
1828
|
+
}
|
|
1829
|
+
addForce(body, accel) {
|
|
1830
|
+
this.assertOpen();
|
|
1831
|
+
this.forces.push({
|
|
1832
|
+
body,
|
|
1833
|
+
accel
|
|
1834
|
+
});
|
|
1835
|
+
}
|
|
1836
|
+
addField(accel, targets) {
|
|
1837
|
+
this.assertOpen();
|
|
1838
|
+
this.fields.push({
|
|
1839
|
+
accel,
|
|
1840
|
+
...targets && { targets: new Set(targets) }
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
addImpulse(body, frame, dv) {
|
|
1844
|
+
this.assertOpen();
|
|
1845
|
+
this.impulses.push({
|
|
1846
|
+
body,
|
|
1847
|
+
frame: Math.round(frame),
|
|
1848
|
+
dv
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1851
|
+
ensureSimulated() {
|
|
1852
|
+
if (this.locked) return;
|
|
1853
|
+
this.locked = true;
|
|
1854
|
+
const st = this.begin();
|
|
1855
|
+
for (let f = 1; f < this.frames; f++) this.stepFrame(st, f);
|
|
1856
|
+
st.freed = true;
|
|
1857
|
+
this.sim.free();
|
|
1858
|
+
for (const body of this.bodies) this.bind(body);
|
|
1859
|
+
}
|
|
1860
|
+
/** Records frame 0 and sets every channel to it, so frames not simulated yet hold the start pose. */
|
|
1861
|
+
begin() {
|
|
1862
|
+
const { startFrame } = this;
|
|
1863
|
+
const bodies = this.bodies;
|
|
1864
|
+
const bySim = /* @__PURE__ */ new Map();
|
|
1865
|
+
for (const b of bodies) bySim.set(b.sim, b);
|
|
1866
|
+
for (const b of bodies) if (b.startFrame > 0) b.sim.setEnabled(false);
|
|
1867
|
+
const kinematicFrom = /* @__PURE__ */ new Map();
|
|
1868
|
+
for (const b of bodies) {
|
|
1869
|
+
b.record(startFrame, -1);
|
|
1870
|
+
b.recording.channel("impact");
|
|
1871
|
+
const t = b.kinematicTarget?.(startFrame);
|
|
1872
|
+
if (t) kinematicFrom.set(b, t);
|
|
1873
|
+
}
|
|
1874
|
+
this.worldRecording.channel("impact");
|
|
1875
|
+
for (const rec of [...bodies.map((b) => b.recording), this.worldRecording]) for (const arr of rec.channels.values()) arr.fill(arr[startFrame]);
|
|
1876
|
+
return {
|
|
1877
|
+
current: 0,
|
|
1878
|
+
freed: false,
|
|
1879
|
+
bySim,
|
|
1880
|
+
kinematicFrom,
|
|
1881
|
+
level: new Map(bodies.map((b) => [b, 0]))
|
|
1882
|
+
};
|
|
1883
|
+
}
|
|
1884
|
+
stepFrame(st, f) {
|
|
1885
|
+
const { fps, startFrame, substeps } = this;
|
|
1886
|
+
const dt = 1 / (fps * substeps);
|
|
1887
|
+
const bodies = this.bodies;
|
|
1888
|
+
const frame = startFrame + f;
|
|
1889
|
+
const dims = this.gravity.length;
|
|
1890
|
+
const prevVel = /* @__PURE__ */ new Map();
|
|
1891
|
+
for (const b of bodies) {
|
|
1892
|
+
if (b.startFrame > 0 && f === b.startFrame + 1) b.sim.setEnabled(true);
|
|
1893
|
+
prevVel.set(b, b.sim.velocity());
|
|
1894
|
+
}
|
|
1895
|
+
const isActive = (b) => b.type === "dynamic" && f > b.startFrame;
|
|
1896
|
+
const expected = /* @__PURE__ */ new Map();
|
|
1897
|
+
for (const b of bodies) expected.set(b, this.gravity.map((g) => isActive(b) ? g * b.gravityScale / fps : 0));
|
|
1898
|
+
for (const imp of this.impulses) {
|
|
1899
|
+
if (imp.frame !== f) continue;
|
|
1900
|
+
const body = imp.body;
|
|
1901
|
+
if (!isActive(body)) continue;
|
|
1902
|
+
const m = body.sim.mass();
|
|
1903
|
+
body.sim.applyImpulse(imp.dv.map((v) => v * m));
|
|
1904
|
+
const e = expected.get(body);
|
|
1905
|
+
for (let i = 0; i < dims; i++) e[i] += imp.dv[i];
|
|
1906
|
+
}
|
|
1907
|
+
const frameForces = this.forces.filter((fe) => isActive(fe.body)).map((fe) => {
|
|
1908
|
+
const body = fe.body;
|
|
1909
|
+
const a = fe.accel(frame);
|
|
1910
|
+
const e = expected.get(body);
|
|
1911
|
+
for (let i = 0; i < dims; i++) e[i] += a[i] / fps;
|
|
1912
|
+
return {
|
|
1913
|
+
body,
|
|
1914
|
+
a
|
|
1915
|
+
};
|
|
1916
|
+
});
|
|
1917
|
+
const kinematicTo = /* @__PURE__ */ new Map();
|
|
1918
|
+
for (const b of bodies) {
|
|
1919
|
+
const t = b.kinematicTarget?.(frame);
|
|
1920
|
+
if (t) kinematicTo.set(b, t);
|
|
1921
|
+
}
|
|
1922
|
+
const started = /* @__PURE__ */ new Set();
|
|
1923
|
+
const pairs = [];
|
|
1924
|
+
for (let s = 1; s <= substeps; s++) {
|
|
1925
|
+
for (const { body, a } of frameForces) {
|
|
1926
|
+
const m = body.sim.mass();
|
|
1927
|
+
body.sim.applyImpulse(a.map((v) => v * m * dt));
|
|
1928
|
+
}
|
|
1929
|
+
for (const field of this.fields) for (const b of bodies) {
|
|
1930
|
+
if (!isActive(b) || field.targets && !field.targets.has(b)) continue;
|
|
1931
|
+
const a = field.accel(b, frame);
|
|
1932
|
+
if (!a) continue;
|
|
1933
|
+
const m = b.sim.mass();
|
|
1934
|
+
b.sim.applyImpulse(a.map((v) => v * m * dt));
|
|
1935
|
+
const e = expected.get(b);
|
|
1936
|
+
for (let i = 0; i < dims; i++) e[i] += a[i] * dt;
|
|
1937
|
+
}
|
|
1938
|
+
for (const [b, to] of kinematicTo) {
|
|
1939
|
+
const from = st.kinematicFrom.get(b) ?? to;
|
|
1940
|
+
const k = s / substeps;
|
|
1941
|
+
b.sim.setKinematicTarget(lerpVec(from.position, to.position, k), this.lerpRotation(from.rotation, to.rotation, k));
|
|
1942
|
+
}
|
|
1943
|
+
this.sim.step(dt);
|
|
1944
|
+
for (const [sa, sb] of this.sim.drainCollisionStarts()) {
|
|
1945
|
+
const a = st.bySim.get(sa);
|
|
1946
|
+
const b = st.bySim.get(sb);
|
|
1947
|
+
if (!a || !b) continue;
|
|
1948
|
+
const key = `${bodies.indexOf(a)}:${bodies.indexOf(b)}`;
|
|
1949
|
+
if (started.has(key)) continue;
|
|
1950
|
+
started.add(key);
|
|
1951
|
+
pairs.push([a, b]);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
st.kinematicFrom = kinematicTo;
|
|
1955
|
+
for (const b of bodies) b.record(frame, frame - 1);
|
|
1956
|
+
const raw = /* @__PURE__ */ new Map();
|
|
1957
|
+
if (pairs.length > 0) {
|
|
1958
|
+
const ppm = this.pixelsPerMeter;
|
|
1959
|
+
const hit = (b) => {
|
|
1960
|
+
if (b.type !== "dynamic") return 0;
|
|
1961
|
+
const v = b.sim.velocity();
|
|
1962
|
+
const v0 = prevVel.get(b);
|
|
1963
|
+
const e = expected.get(b);
|
|
1964
|
+
let sq = 0;
|
|
1965
|
+
for (let i = 0; i < dims; i++) {
|
|
1966
|
+
const d = v[i] - (v0[i] + e[i]);
|
|
1967
|
+
sq += d * d;
|
|
1968
|
+
}
|
|
1969
|
+
return Math.sqrt(sq) * ppm;
|
|
1970
|
+
};
|
|
1971
|
+
for (const [a, b] of pairs) {
|
|
1972
|
+
const speed = Math.max(hit(a), hit(b));
|
|
1973
|
+
for (const [self, other] of [[a, b], [b, a]]) {
|
|
1974
|
+
raw.set(self, Math.max(raw.get(self) ?? 0, speed));
|
|
1975
|
+
const c = {
|
|
1976
|
+
frame,
|
|
1977
|
+
with: other,
|
|
1978
|
+
speed
|
|
1979
|
+
};
|
|
1980
|
+
self.collisions.push(c);
|
|
1981
|
+
if (self === a) this.allCollisions.push({
|
|
1982
|
+
...c,
|
|
1983
|
+
body: a
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
const decay = Math.exp(-1 / this.impactDecay);
|
|
1989
|
+
let loudest = 0;
|
|
1990
|
+
for (const b of bodies) {
|
|
1991
|
+
const level = Math.max((raw.get(b) ?? 0) / this.impactReference, (st.level.get(b) ?? 0) * decay);
|
|
1992
|
+
st.level.set(b, level);
|
|
1993
|
+
b.recording.channel("impact")[frame] = level;
|
|
1994
|
+
if (level > loudest) loudest = level;
|
|
1995
|
+
}
|
|
1996
|
+
this.worldRecording.channel("impact")[frame] = loudest;
|
|
1997
|
+
st.current = f;
|
|
1998
|
+
}
|
|
1999
|
+
bind(body) {
|
|
2000
|
+
const layer = body.layer;
|
|
2001
|
+
const output = body.output ?? (this.bakeDefault ? "keyframes" : "signals");
|
|
2002
|
+
if (typeof layer?.animate !== "function" || output === "none") return;
|
|
2003
|
+
if (body.type === "fixed") return;
|
|
2004
|
+
const channels = body.boundChannels();
|
|
2005
|
+
if (output === "keyframes" && this.liveMode) throw new Error("Physics: live worlds bind signals; drop `output: \"keyframes\"` or use simulate().");
|
|
2006
|
+
if (output === "signals") {
|
|
2007
|
+
const anim$1 = LayerAnimation.create();
|
|
2008
|
+
for (const [prop, channel] of channels) anim$1.signal(prop, body.recording.signal(channel));
|
|
2009
|
+
layer.animate(anim$1);
|
|
2010
|
+
return;
|
|
2011
|
+
}
|
|
2012
|
+
const tolerance = this.bakeDefault?.tolerance ?? .25;
|
|
2013
|
+
const anim = LayerAnimation.create();
|
|
2014
|
+
for (const [prop, channel] of channels) {
|
|
2015
|
+
const local = body.recording.channel(channel).subarray(this.startFrame);
|
|
2016
|
+
const scale = CHANNEL_TOLERANCE_SCALE[prop] ?? 1;
|
|
2017
|
+
anim.keys(prop, simplifyKeys(local, tolerance * scale));
|
|
2018
|
+
}
|
|
2019
|
+
layer.animate(anim);
|
|
2020
|
+
}
|
|
2021
|
+
};
|
|
2022
|
+
function lerpVec(a, b, t) {
|
|
2023
|
+
return a.map((v, i) => v + (b[i] - v) * t);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
//#endregion
|
|
2027
|
+
//#region src/particles.ts
|
|
2028
|
+
const range = (r, fallback) => r === void 0 ? fallback : typeof r === "number" ? [r, r] : r;
|
|
2029
|
+
/**
|
|
2030
|
+
* A GPU particle system: tens of thousands of particles simulated in a
|
|
2031
|
+
* compute shader and drawn in one call. Used through `Layer.particles()`.
|
|
2032
|
+
* It steps as the composition renders (snapshots make scrubbing back
|
|
2033
|
+
* cheap), and its drivers are read every frame.
|
|
2034
|
+
*/
|
|
2035
|
+
var Particles = class extends Effect$1 {
|
|
2036
|
+
op = "Particles";
|
|
2037
|
+
particleStatic;
|
|
2038
|
+
particleState;
|
|
2039
|
+
options;
|
|
2040
|
+
constructor(options) {
|
|
2041
|
+
super({});
|
|
2042
|
+
this.options = options;
|
|
2043
|
+
const e = options.emitter ?? {};
|
|
2044
|
+
const staticPos = (d) => typeof d === "number" ? d : void 0;
|
|
2045
|
+
const width = options.width;
|
|
2046
|
+
const height = options.height;
|
|
2047
|
+
const colors = (Array.isArray(options.color) ? options.color : [options.color ?? "#ffffff"]).map((c) => {
|
|
2048
|
+
const p = parseColor(c);
|
|
2049
|
+
return [
|
|
2050
|
+
p.r,
|
|
2051
|
+
p.g,
|
|
2052
|
+
p.b,
|
|
2053
|
+
p.a
|
|
2054
|
+
];
|
|
2055
|
+
});
|
|
2056
|
+
this.particleStatic = {
|
|
2057
|
+
max: Math.max(1, Math.round(options.max ?? 2e4)),
|
|
2058
|
+
seed: Math.round(options.seed ?? 1) >>> 0,
|
|
2059
|
+
layer: {
|
|
2060
|
+
x: options.x ?? 0,
|
|
2061
|
+
y: options.y ?? 0,
|
|
2062
|
+
width,
|
|
2063
|
+
height
|
|
2064
|
+
},
|
|
2065
|
+
emitter: {
|
|
2066
|
+
shape: e.shape ?? "rect",
|
|
2067
|
+
x: staticPos(e.x) ?? (e.shape === "circle" || e.shape === "ring" || e.shape === "point" ? width / 2 : 0),
|
|
2068
|
+
y: staticPos(e.y) ?? (e.shape === "circle" || e.shape === "ring" || e.shape === "point" ? height / 2 : 0),
|
|
2069
|
+
width: e.width ?? width,
|
|
2070
|
+
height: e.height ?? (e.shape === "line" ? 0 : height),
|
|
2071
|
+
radius: e.radius ?? Math.min(width, height) / 4,
|
|
2072
|
+
radial: e.radial ?? false
|
|
2073
|
+
},
|
|
2074
|
+
lifetime: range(options.lifetime, [2, 4]),
|
|
2075
|
+
speed: range(options.speed, [40, 160]),
|
|
2076
|
+
direction: options.direction ?? -90,
|
|
2077
|
+
spread: options.spread ?? 360,
|
|
2078
|
+
bounds: options.bounds ?? "none",
|
|
2079
|
+
floor: options.floor ?? null,
|
|
2080
|
+
bounce: options.bounce ?? .3,
|
|
2081
|
+
size: range(options.size, [6, 0]),
|
|
2082
|
+
sizeJitter: options.sizeJitter ?? .5,
|
|
2083
|
+
fade: options.fade ?? [.1, .3],
|
|
2084
|
+
shape: options.shape ?? "circle",
|
|
2085
|
+
stretch: options.stretch ?? .04,
|
|
2086
|
+
blend: options.blend ?? "normal",
|
|
2087
|
+
colors: colors.slice(0, 8),
|
|
2088
|
+
colorBySpeed: options.colorBySpeed ?? null,
|
|
2089
|
+
opacity: options.opacity ?? 1,
|
|
2090
|
+
substeps: options.substeps ?? 2,
|
|
2091
|
+
prewarm: options.prewarm ?? 0
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
particleFrame(frame, fps) {
|
|
2095
|
+
const o = this.options;
|
|
2096
|
+
const read = (d, fallback) => d === void 0 ? fallback : sampleDriver(d, frame, fps);
|
|
2097
|
+
const layerFrame = frame - (o.startFrame ?? 0);
|
|
2098
|
+
const burst = (o.emitter?.bursts ?? []).filter((b) => Math.round(b.frame) === layerFrame).reduce((n, b) => n + b.count, 0);
|
|
2099
|
+
const g = o.gravity;
|
|
2100
|
+
const gravity = Array.isArray(g) ? [read(g[0], 0), read(g[1], 0)] : [0, read(g, 0)];
|
|
2101
|
+
let push = null;
|
|
2102
|
+
if (o.push) push = {
|
|
2103
|
+
strength: read(o.push.strength, 6e3),
|
|
2104
|
+
sweep: read(o.push.sweep, 6e4),
|
|
2105
|
+
mask: o.push.person.silhouetteGrid(frame),
|
|
2106
|
+
previous: o.push.person.silhouetteGrid(Math.max(0, frame - 1))
|
|
2107
|
+
};
|
|
2108
|
+
const ex = o.emitter?.x;
|
|
2109
|
+
const ey = o.emitter?.y;
|
|
2110
|
+
const moving = ex !== void 0 && typeof ex !== "number" || ey !== void 0 && typeof ey !== "number";
|
|
2111
|
+
const at = (f) => ({
|
|
2112
|
+
x: ex === void 0 ? this.particleStatic.emitter.x : sampleDriver(ex, f, fps),
|
|
2113
|
+
y: ey === void 0 ? this.particleStatic.emitter.y : sampleDriver(ey, f, fps)
|
|
2114
|
+
});
|
|
2115
|
+
let emitter;
|
|
2116
|
+
if (moving) {
|
|
2117
|
+
const now = at(frame);
|
|
2118
|
+
const before = at(Math.max(0, frame - 1));
|
|
2119
|
+
emitter = {
|
|
2120
|
+
x: now.x,
|
|
2121
|
+
y: now.y,
|
|
2122
|
+
previousX: before.x,
|
|
2123
|
+
previousY: before.y
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
return {
|
|
2127
|
+
...emitter && { emitter },
|
|
2128
|
+
rate: read(o.emitter?.rate, 1e3),
|
|
2129
|
+
burst,
|
|
2130
|
+
gravity,
|
|
2131
|
+
drag: read(o.drag, .4),
|
|
2132
|
+
noise: {
|
|
2133
|
+
strength: read(o.noise?.strength, 0),
|
|
2134
|
+
scale: o.noise?.scale ?? 300,
|
|
2135
|
+
speed: o.noise?.speed ?? .3
|
|
2136
|
+
},
|
|
2137
|
+
attractors: (o.attractors ?? []).map((a) => ({
|
|
2138
|
+
x: read(a.x, 0),
|
|
2139
|
+
y: read(a.y, 0),
|
|
2140
|
+
strength: read(a.strength, 0),
|
|
2141
|
+
radius: a.radius ?? 200
|
|
2142
|
+
})),
|
|
2143
|
+
push
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
};
|
|
2147
|
+
|
|
986
2148
|
//#endregion
|
|
987
2149
|
//#region src/shapes3d.ts
|
|
988
2150
|
const MATERIAL_KEYS = [
|
|
@@ -1954,25 +3116,1755 @@ const Lighting = {
|
|
|
1954
3116
|
};
|
|
1955
3117
|
|
|
1956
3118
|
//#endregion
|
|
1957
|
-
//#region src/
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
3119
|
+
//#region src/physics/backend.ts
|
|
3120
|
+
const RAPIER_2D = "@dimforge/rapier2d-deterministic-compat";
|
|
3121
|
+
const RAPIER_3D = "@dimforge/rapier3d-deterministic-compat";
|
|
3122
|
+
async function loadRapier(specifier, load) {
|
|
3123
|
+
let mod;
|
|
3124
|
+
try {
|
|
3125
|
+
mod = await load();
|
|
3126
|
+
} catch (error) {
|
|
3127
|
+
throw new Error(`Physics needs the optional dependency "${specifier}". Install it with \`npm i ${specifier}\`.`, { cause: error });
|
|
3128
|
+
}
|
|
3129
|
+
const rapier = mod.default ?? mod;
|
|
3130
|
+
await rapier.init();
|
|
3131
|
+
return rapier;
|
|
3132
|
+
}
|
|
3133
|
+
let rapier2d;
|
|
3134
|
+
let rapier3d;
|
|
3135
|
+
/** Loads and initializes Rapier once per process; 2D and 3D are separate modules. */
|
|
3136
|
+
async function rapierBackend(dims) {
|
|
3137
|
+
if (dims === 2) {
|
|
3138
|
+
rapier2d ??= loadRapier(RAPIER_2D, () => import("@dimforge/rapier2d-deterministic-compat"));
|
|
3139
|
+
const R$1 = await rapier2d;
|
|
3140
|
+
return {
|
|
3141
|
+
createWorld2D: (gravity) => new RapierWorld2D(R$1, gravity),
|
|
3142
|
+
createWorld3D: () => {
|
|
3143
|
+
throw new Error("This backend was loaded for 2D.");
|
|
1971
3144
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
rapier3d ??= loadRapier(RAPIER_3D, () => import("@dimforge/rapier3d-deterministic-compat"));
|
|
3148
|
+
const R = await rapier3d;
|
|
3149
|
+
return {
|
|
3150
|
+
createWorld2D: () => {
|
|
3151
|
+
throw new Error("This backend was loaded for 3D.");
|
|
1974
3152
|
},
|
|
1975
|
-
|
|
3153
|
+
createWorld3D: (gravity) => new RapierWorld3D(R, gravity)
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
var RapierWorld2D = class {
|
|
3157
|
+
world;
|
|
3158
|
+
events;
|
|
3159
|
+
byCollider = /* @__PURE__ */ new Map();
|
|
3160
|
+
started = [];
|
|
3161
|
+
constructor(R, gravity) {
|
|
3162
|
+
this.R = R;
|
|
3163
|
+
this.world = new R.World({
|
|
3164
|
+
x: gravity[0],
|
|
3165
|
+
y: gravity[1]
|
|
3166
|
+
});
|
|
3167
|
+
this.events = new R.EventQueue(true);
|
|
3168
|
+
}
|
|
3169
|
+
addBody(spec$1) {
|
|
3170
|
+
const R = this.R;
|
|
3171
|
+
const desc = bodyDesc(R, spec$1.type).setTranslation(spec$1.position[0], spec$1.position[1]).setRotation(spec$1.angle).setLinvel(spec$1.velocity[0], spec$1.velocity[1]).setAngvel(spec$1.angularVelocity).setLinearDamping(spec$1.linearDamping).setAngularDamping(spec$1.angularDamping).setGravityScale(spec$1.gravityScale).setCcdEnabled(spec$1.ccd).setEnabled(spec$1.enabled);
|
|
3172
|
+
if (spec$1.lockRotation) desc.lockRotations();
|
|
3173
|
+
const body = this.world.createRigidBody(desc);
|
|
3174
|
+
const collider = this.world.createCollider(shapeDesc2D(R, spec$1.shape).setDensity(spec$1.density).setFriction(spec$1.friction).setRestitution(spec$1.restitution).setActiveEvents(R.ActiveEvents.COLLISION_EVENTS).setSensor(spec$1.sensor ?? false), body);
|
|
3175
|
+
const sim = {
|
|
3176
|
+
raw: body,
|
|
3177
|
+
position: () => {
|
|
3178
|
+
const t = body.translation();
|
|
3179
|
+
return [t.x, t.y];
|
|
3180
|
+
},
|
|
3181
|
+
rotation: () => body.rotation(),
|
|
3182
|
+
velocity: () => {
|
|
3183
|
+
const v = body.linvel();
|
|
3184
|
+
return [v.x, v.y];
|
|
3185
|
+
},
|
|
3186
|
+
angularVelocity: () => body.angvel(),
|
|
3187
|
+
sleeping: () => body.isSleeping(),
|
|
3188
|
+
mass: () => body.mass(),
|
|
3189
|
+
setEnabled: (on) => body.setEnabled(on),
|
|
3190
|
+
applyImpulse: ([x, y]) => body.applyImpulse({
|
|
3191
|
+
x,
|
|
3192
|
+
y
|
|
3193
|
+
}, true),
|
|
3194
|
+
setKinematicTarget: ([x, y], angle) => {
|
|
3195
|
+
body.setNextKinematicTranslation({
|
|
3196
|
+
x,
|
|
3197
|
+
y
|
|
3198
|
+
});
|
|
3199
|
+
body.setNextKinematicRotation(angle);
|
|
3200
|
+
}
|
|
3201
|
+
};
|
|
3202
|
+
this.byCollider.set(collider.handle, sim);
|
|
3203
|
+
return sim;
|
|
3204
|
+
}
|
|
3205
|
+
addJoint(a, b, spec$1) {
|
|
3206
|
+
const R = this.R;
|
|
3207
|
+
const a1 = {
|
|
3208
|
+
x: spec$1.anchorA[0],
|
|
3209
|
+
y: spec$1.anchorA[1]
|
|
3210
|
+
};
|
|
3211
|
+
const a2 = {
|
|
3212
|
+
x: spec$1.anchorB[0],
|
|
3213
|
+
y: spec$1.anchorB[1]
|
|
3214
|
+
};
|
|
3215
|
+
let data;
|
|
3216
|
+
switch (spec$1.type) {
|
|
3217
|
+
case "revolute":
|
|
3218
|
+
case "spherical":
|
|
3219
|
+
data = R.JointData.revolute(a1, a2);
|
|
3220
|
+
break;
|
|
3221
|
+
case "fixed":
|
|
3222
|
+
data = R.JointData.fixed(a1, 0, a2, 0);
|
|
3223
|
+
break;
|
|
3224
|
+
case "rope":
|
|
3225
|
+
data = R.JointData.rope(spec$1.length, a1, a2);
|
|
3226
|
+
break;
|
|
3227
|
+
case "spring":
|
|
3228
|
+
data = R.JointData.spring(spec$1.restLength, spec$1.stiffness, spec$1.damping, a1, a2);
|
|
3229
|
+
break;
|
|
3230
|
+
}
|
|
3231
|
+
this.world.createImpulseJoint(data, rawBody(a), rawBody(b), true).setContactsEnabled(false);
|
|
3232
|
+
}
|
|
3233
|
+
step(dt) {
|
|
3234
|
+
this.world.timestep = dt;
|
|
3235
|
+
this.world.step(this.events);
|
|
3236
|
+
this.events.drainCollisionEvents((h1, h2, started) => {
|
|
3237
|
+
if (!started) return;
|
|
3238
|
+
const a = this.byCollider.get(h1);
|
|
3239
|
+
const b = this.byCollider.get(h2);
|
|
3240
|
+
if (a && b) this.started.push([a, b]);
|
|
3241
|
+
});
|
|
3242
|
+
}
|
|
3243
|
+
drainCollisionStarts() {
|
|
3244
|
+
return this.started.splice(0);
|
|
3245
|
+
}
|
|
3246
|
+
free() {
|
|
3247
|
+
this.events.free();
|
|
3248
|
+
this.world.free();
|
|
3249
|
+
}
|
|
3250
|
+
};
|
|
3251
|
+
var RapierWorld3D = class {
|
|
3252
|
+
world;
|
|
3253
|
+
events;
|
|
3254
|
+
byCollider = /* @__PURE__ */ new Map();
|
|
3255
|
+
started = [];
|
|
3256
|
+
constructor(R, gravity) {
|
|
3257
|
+
this.R = R;
|
|
3258
|
+
this.world = new R.World({
|
|
3259
|
+
x: gravity[0],
|
|
3260
|
+
y: gravity[1],
|
|
3261
|
+
z: gravity[2]
|
|
3262
|
+
});
|
|
3263
|
+
this.events = new R.EventQueue(true);
|
|
3264
|
+
}
|
|
3265
|
+
addBody(spec$1) {
|
|
3266
|
+
const R = this.R;
|
|
3267
|
+
const [qx, qy, qz, qw] = spec$1.rotation;
|
|
3268
|
+
const desc = bodyDesc(R, spec$1.type).setTranslation(spec$1.position[0], spec$1.position[1], spec$1.position[2]).setRotation({
|
|
3269
|
+
x: qx,
|
|
3270
|
+
y: qy,
|
|
3271
|
+
z: qz,
|
|
3272
|
+
w: qw
|
|
3273
|
+
}).setLinvel(spec$1.velocity[0], spec$1.velocity[1], spec$1.velocity[2]).setAngvel(vec3(spec$1.lockRotation ? [
|
|
3274
|
+
0,
|
|
3275
|
+
0,
|
|
3276
|
+
0
|
|
3277
|
+
] : spec$1.angularVelocity.map((w, i) => spec$1.enabledRotations && !spec$1.enabledRotations[i] ? 0 : w))).setLinearDamping(spec$1.linearDamping).setAngularDamping(spec$1.angularDamping).setGravityScale(spec$1.gravityScale).setCcdEnabled(spec$1.ccd).setEnabled(spec$1.enabled);
|
|
3278
|
+
if (spec$1.lockRotation) desc.lockRotations();
|
|
3279
|
+
else if (spec$1.enabledRotations) desc.enabledRotations(...spec$1.enabledRotations);
|
|
3280
|
+
const body = this.world.createRigidBody(desc);
|
|
3281
|
+
const shape = shapeDesc3D(R, spec$1.shape);
|
|
3282
|
+
const collider = this.world.createCollider(shape.setDensity(spec$1.density).setFriction(spec$1.friction).setRestitution(spec$1.restitution).setActiveEvents(R.ActiveEvents.COLLISION_EVENTS).setSensor(spec$1.sensor ?? false), body);
|
|
3283
|
+
const sim = {
|
|
3284
|
+
raw: body,
|
|
3285
|
+
position: () => {
|
|
3286
|
+
const t = body.translation();
|
|
3287
|
+
return [
|
|
3288
|
+
t.x,
|
|
3289
|
+
t.y,
|
|
3290
|
+
t.z
|
|
3291
|
+
];
|
|
3292
|
+
},
|
|
3293
|
+
rotation: () => {
|
|
3294
|
+
const q = body.rotation();
|
|
3295
|
+
return [
|
|
3296
|
+
q.x,
|
|
3297
|
+
q.y,
|
|
3298
|
+
q.z,
|
|
3299
|
+
q.w
|
|
3300
|
+
];
|
|
3301
|
+
},
|
|
3302
|
+
velocity: () => {
|
|
3303
|
+
const v = body.linvel();
|
|
3304
|
+
return [
|
|
3305
|
+
v.x,
|
|
3306
|
+
v.y,
|
|
3307
|
+
v.z
|
|
3308
|
+
];
|
|
3309
|
+
},
|
|
3310
|
+
angularVelocity: () => {
|
|
3311
|
+
const w = body.angvel();
|
|
3312
|
+
return [
|
|
3313
|
+
w.x,
|
|
3314
|
+
w.y,
|
|
3315
|
+
w.z
|
|
3316
|
+
];
|
|
3317
|
+
},
|
|
3318
|
+
sleeping: () => body.isSleeping(),
|
|
3319
|
+
mass: () => body.mass(),
|
|
3320
|
+
setEnabled: (on) => body.setEnabled(on),
|
|
3321
|
+
applyImpulse: (v) => body.applyImpulse(vec3(v), true),
|
|
3322
|
+
setKinematicTarget: (p, [x, y, z, w]) => {
|
|
3323
|
+
body.setNextKinematicTranslation(vec3(p));
|
|
3324
|
+
body.setNextKinematicRotation({
|
|
3325
|
+
x,
|
|
3326
|
+
y,
|
|
3327
|
+
z,
|
|
3328
|
+
w
|
|
3329
|
+
});
|
|
3330
|
+
}
|
|
3331
|
+
};
|
|
3332
|
+
this.byCollider.set(collider.handle, sim);
|
|
3333
|
+
return sim;
|
|
3334
|
+
}
|
|
3335
|
+
addJoint(a, b, spec$1) {
|
|
3336
|
+
const R = this.R;
|
|
3337
|
+
const a1 = vec3(spec$1.anchorA);
|
|
3338
|
+
const a2 = vec3(spec$1.anchorB);
|
|
3339
|
+
let data;
|
|
3340
|
+
switch (spec$1.type) {
|
|
3341
|
+
case "revolute":
|
|
3342
|
+
data = R.JointData.revolute(a1, a2, vec3(spec$1.axis ?? [
|
|
3343
|
+
0,
|
|
3344
|
+
0,
|
|
3345
|
+
1
|
|
3346
|
+
]));
|
|
3347
|
+
break;
|
|
3348
|
+
case "spherical":
|
|
3349
|
+
data = R.JointData.spherical(a1, a2);
|
|
3350
|
+
break;
|
|
3351
|
+
case "fixed": {
|
|
3352
|
+
const identity = {
|
|
3353
|
+
x: 0,
|
|
3354
|
+
y: 0,
|
|
3355
|
+
z: 0,
|
|
3356
|
+
w: 1
|
|
3357
|
+
};
|
|
3358
|
+
data = R.JointData.fixed(a1, identity, a2, identity);
|
|
3359
|
+
break;
|
|
3360
|
+
}
|
|
3361
|
+
case "rope":
|
|
3362
|
+
data = R.JointData.rope(spec$1.length, a1, a2);
|
|
3363
|
+
break;
|
|
3364
|
+
case "spring":
|
|
3365
|
+
data = R.JointData.spring(spec$1.restLength, spec$1.stiffness, spec$1.damping, a1, a2);
|
|
3366
|
+
break;
|
|
3367
|
+
}
|
|
3368
|
+
this.world.createImpulseJoint(data, rawBody(a), rawBody(b), true).setContactsEnabled(false);
|
|
3369
|
+
}
|
|
3370
|
+
step(dt) {
|
|
3371
|
+
this.world.timestep = dt;
|
|
3372
|
+
this.world.step(this.events);
|
|
3373
|
+
this.events.drainCollisionEvents((h1, h2, started) => {
|
|
3374
|
+
if (!started) return;
|
|
3375
|
+
const a = this.byCollider.get(h1);
|
|
3376
|
+
const b = this.byCollider.get(h2);
|
|
3377
|
+
if (a && b) this.started.push([a, b]);
|
|
3378
|
+
});
|
|
3379
|
+
}
|
|
3380
|
+
drainCollisionStarts() {
|
|
3381
|
+
return this.started.splice(0);
|
|
3382
|
+
}
|
|
3383
|
+
free() {
|
|
3384
|
+
this.events.free();
|
|
3385
|
+
this.world.free();
|
|
3386
|
+
}
|
|
3387
|
+
};
|
|
3388
|
+
function vec3([x, y, z]) {
|
|
3389
|
+
return {
|
|
3390
|
+
x,
|
|
3391
|
+
y,
|
|
3392
|
+
z
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
function rawBody(body) {
|
|
3396
|
+
return body.raw;
|
|
3397
|
+
}
|
|
3398
|
+
function bodyDesc(R, type) {
|
|
3399
|
+
const D = R.RigidBodyDesc;
|
|
3400
|
+
return type === "fixed" ? D.fixed() : type === "kinematic" ? D.kinematicPositionBased() : D.dynamic();
|
|
3401
|
+
}
|
|
3402
|
+
function shapeDesc2D(R, shape) {
|
|
3403
|
+
switch (shape.kind) {
|
|
3404
|
+
case "ball": return R.ColliderDesc.ball(shape.radius);
|
|
3405
|
+
case "box": {
|
|
3406
|
+
const r = Math.min(shape.radius ?? 0, shape.hx / 2, shape.hy / 2);
|
|
3407
|
+
return r > 0 ? R.ColliderDesc.roundCuboid(shape.hx - r, shape.hy - r, r) : R.ColliderDesc.cuboid(shape.hx, shape.hy);
|
|
3408
|
+
}
|
|
3409
|
+
case "hull": {
|
|
3410
|
+
const desc = R.ColliderDesc.convexHull(new Float32Array(shape.points));
|
|
3411
|
+
if (!desc) throw new Error("Physics: could not build a convex hull for this shape.");
|
|
3412
|
+
return desc;
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
function shapeDesc3D(R, shape) {
|
|
3417
|
+
switch (shape.kind) {
|
|
3418
|
+
case "ball": return R.ColliderDesc.ball(shape.radius);
|
|
3419
|
+
case "box": {
|
|
3420
|
+
const r = Math.min(shape.radius ?? 0, shape.hx / 2, shape.hy / 2, shape.hz / 2);
|
|
3421
|
+
return r > 0 ? R.ColliderDesc.roundCuboid(shape.hx - r, shape.hy - r, shape.hz - r, r) : R.ColliderDesc.cuboid(shape.hx, shape.hy, shape.hz);
|
|
3422
|
+
}
|
|
3423
|
+
case "cylinder": return R.ColliderDesc.cylinder(shape.halfHeight, shape.radius);
|
|
3424
|
+
case "hull": {
|
|
3425
|
+
const desc = R.ColliderDesc.convexHull(new Float32Array(shape.points));
|
|
3426
|
+
if (!desc) throw new Error("Physics: could not build a convex hull for this shape.");
|
|
3427
|
+
return desc;
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
|
|
3432
|
+
//#endregion
|
|
3433
|
+
//#region src/physics/world2d.ts
|
|
3434
|
+
/**
|
|
3435
|
+
* A simulated body. Its signals hold one value per composition frame; bind
|
|
3436
|
+
* them to any prop or effect uniform (`.signal("opacity", body.impact)`).
|
|
3437
|
+
* Reading a signal runs the simulation if it has not run yet.
|
|
3438
|
+
*/
|
|
3439
|
+
var PhysicsBody2D = class {
|
|
3440
|
+
collisions = [];
|
|
3441
|
+
/** @internal */ recording;
|
|
3442
|
+
/** @internal */ sim;
|
|
3443
|
+
/** @internal */
|
|
3444
|
+
constructor(world, sim, type, layer, frame, options, recording) {
|
|
3445
|
+
this.world = world;
|
|
3446
|
+
this.type = type;
|
|
3447
|
+
this.layer = layer;
|
|
3448
|
+
this.frame = frame;
|
|
3449
|
+
this.options = options;
|
|
3450
|
+
this.sim = sim;
|
|
3451
|
+
this.recording = recording;
|
|
3452
|
+
}
|
|
3453
|
+
get startFrame() {
|
|
3454
|
+
return Math.max(0, Math.round(this.options.startFrame ?? 0));
|
|
3455
|
+
}
|
|
3456
|
+
get gravityScale() {
|
|
3457
|
+
return this.options.gravityScale ?? 1;
|
|
3458
|
+
}
|
|
3459
|
+
get output() {
|
|
3460
|
+
return this.options.output;
|
|
3461
|
+
}
|
|
3462
|
+
/** The layer's `x` (top-left) every frame. */
|
|
3463
|
+
get x() {
|
|
3464
|
+
return this.recording.signal("x");
|
|
3465
|
+
}
|
|
3466
|
+
/** The layer's `y` (top-left) every frame. */
|
|
3467
|
+
get y() {
|
|
3468
|
+
return this.recording.signal("y");
|
|
3469
|
+
}
|
|
3470
|
+
/** Degrees, clockwise, continuous (no jump at 360). */
|
|
3471
|
+
get rotation() {
|
|
3472
|
+
return this.recording.signal("rotation");
|
|
3473
|
+
}
|
|
3474
|
+
/** Body center, px. */
|
|
3475
|
+
get centerX() {
|
|
3476
|
+
return this.recording.signal("centerX");
|
|
3477
|
+
}
|
|
3478
|
+
get centerY() {
|
|
3479
|
+
return this.recording.signal("centerY");
|
|
3480
|
+
}
|
|
3481
|
+
/** px/s. */
|
|
3482
|
+
get vx() {
|
|
3483
|
+
return this.recording.signal("vx");
|
|
3484
|
+
}
|
|
3485
|
+
get vy() {
|
|
3486
|
+
return this.recording.signal("vy");
|
|
3487
|
+
}
|
|
3488
|
+
/** px/s. */
|
|
3489
|
+
get speed() {
|
|
3490
|
+
return this.recording.signal("speed");
|
|
3491
|
+
}
|
|
3492
|
+
/** Degrees per second. */
|
|
3493
|
+
get angularVelocity() {
|
|
3494
|
+
return this.recording.signal("angularVelocity");
|
|
3495
|
+
}
|
|
3496
|
+
/**
|
|
3497
|
+
* Hit envelope: jumps on each new contact (1 ≈ a 1500 px/s velocity
|
|
3498
|
+
* change, see `impactReference`) and decays over `impactDecay` frames.
|
|
3499
|
+
*/
|
|
3500
|
+
get impact() {
|
|
3501
|
+
return this.recording.signal("impact");
|
|
3502
|
+
}
|
|
3503
|
+
/** 1 while the body is at rest, else 0. */
|
|
3504
|
+
get sleeping() {
|
|
3505
|
+
return this.recording.signal("sleeping");
|
|
3506
|
+
}
|
|
3507
|
+
/** @internal */
|
|
3508
|
+
kinematicTarget(frame) {
|
|
3509
|
+
const follow = this.options.follow;
|
|
3510
|
+
if (this.type !== "kinematic" || !follow) return void 0;
|
|
3511
|
+
const f = this.frame;
|
|
3512
|
+
const fps = this.world.fps;
|
|
3513
|
+
const x = follow.x === void 0 ? void 0 : sampleDriver(follow.x, frame, fps);
|
|
3514
|
+
const y = follow.y === void 0 ? void 0 : sampleDriver(follow.y, frame, fps);
|
|
3515
|
+
const rotation = follow.rotation === void 0 ? f.rotation : sampleDriver(follow.rotation, frame, fps);
|
|
3516
|
+
const start = this.initialLayerXY();
|
|
3517
|
+
const center = layerToCenter(x ?? start[0], y ?? start[1], rotation, f);
|
|
3518
|
+
const ppm = this.world.pixelsPerMeter;
|
|
3519
|
+
return {
|
|
3520
|
+
position: [center[0] / ppm, center[1] / ppm],
|
|
3521
|
+
rotation: rotation * Math.PI / 180
|
|
3522
|
+
};
|
|
3523
|
+
}
|
|
3524
|
+
initialLayerXY() {
|
|
3525
|
+
const l = this.layer;
|
|
3526
|
+
return [Number(l?.x ?? 0), Number(l?.y ?? 0)];
|
|
3527
|
+
}
|
|
3528
|
+
/** @internal */
|
|
3529
|
+
record(index, previous) {
|
|
3530
|
+
const ppm = this.world.pixelsPerMeter;
|
|
3531
|
+
const [mx, my] = this.sim.position();
|
|
3532
|
+
const cx = mx * ppm;
|
|
3533
|
+
const cy = my * ppm;
|
|
3534
|
+
const rec = this.recording;
|
|
3535
|
+
const rotation = rec.channel("rotation");
|
|
3536
|
+
const deg = this.sim.rotation() * 180 / Math.PI;
|
|
3537
|
+
rotation[index] = previous < 0 ? unwrapDegrees(this.frame.rotation, deg) : unwrapDegrees(rotation[previous], deg);
|
|
3538
|
+
const [x, y] = centerToLayer(cx, cy, rotation[index], this.frame);
|
|
3539
|
+
rec.channel("x")[index] = x;
|
|
3540
|
+
rec.channel("y")[index] = y;
|
|
3541
|
+
rec.channel("centerX")[index] = cx;
|
|
3542
|
+
rec.channel("centerY")[index] = cy;
|
|
3543
|
+
const [vx, vy] = this.sim.velocity();
|
|
3544
|
+
rec.channel("vx")[index] = vx * ppm;
|
|
3545
|
+
rec.channel("vy")[index] = vy * ppm;
|
|
3546
|
+
rec.channel("speed")[index] = Math.hypot(vx, vy) * ppm;
|
|
3547
|
+
rec.channel("angularVelocity")[index] = this.sim.angularVelocity() * 180 / Math.PI;
|
|
3548
|
+
rec.channel("sleeping")[index] = this.sim.sleeping() ? 1 : 0;
|
|
3549
|
+
}
|
|
3550
|
+
/** @internal */
|
|
3551
|
+
boundChannels() {
|
|
3552
|
+
const out = [["x", "x"], ["y", "y"]];
|
|
3553
|
+
if (!this.options.lockRotation) out.push(["rotation", "rotation"]);
|
|
3554
|
+
return out;
|
|
3555
|
+
}
|
|
3556
|
+
/** @internal World-space point (px) → this body's local frame (meters), at the start pose. */
|
|
3557
|
+
localAnchor(point) {
|
|
3558
|
+
if (!point) return [0, 0];
|
|
3559
|
+
const ppm = this.world.pixelsPerMeter;
|
|
3560
|
+
const [px, py] = this.sim.position();
|
|
3561
|
+
const a = -this.sim.rotation();
|
|
3562
|
+
const dx = point[0] / ppm - px;
|
|
3563
|
+
const dy = point[1] / ppm - py;
|
|
3564
|
+
return [dx * Math.cos(a) - dy * Math.sin(a), dx * Math.sin(a) + dy * Math.cos(a)];
|
|
3565
|
+
}
|
|
3566
|
+
/** @internal Start-pose center in px. */
|
|
3567
|
+
center() {
|
|
3568
|
+
const ppm = this.world.pixelsPerMeter;
|
|
3569
|
+
const [x, y] = this.sim.position();
|
|
3570
|
+
return [x * ppm, y * ppm];
|
|
3571
|
+
}
|
|
3572
|
+
};
|
|
3573
|
+
/**
|
|
3574
|
+
* A deterministic 2D rigid-body world (Rapier) in layer pixels. Build it,
|
|
3575
|
+
* add bodies from layers, then `simulate()`: each body's layer is bound to
|
|
3576
|
+
* its simulated `x` / `y` / `rotation`, and every body exposes signals
|
|
3577
|
+
* (`impact`, `speed`, …) for driving anything else. The simulation runs
|
|
3578
|
+
* once while the composition is built, so frames render in any order and
|
|
3579
|
+
* identically on every machine.
|
|
3580
|
+
*/
|
|
3581
|
+
var PhysicsWorld2D = class extends WorldCore {
|
|
3582
|
+
/** @internal */
|
|
3583
|
+
constructor(backend, options) {
|
|
3584
|
+
const ppm = options.pixelsPerMeter ?? 100;
|
|
3585
|
+
const g = options.gravity ?? 2400;
|
|
3586
|
+
const gravity = typeof g === "number" ? [0, g / ppm] : [g[0] / ppm, g[1] / ppm];
|
|
3587
|
+
const sim = backend.createWorld2D(gravity);
|
|
3588
|
+
super(sim, gravity, options);
|
|
3589
|
+
}
|
|
3590
|
+
/**
|
|
3591
|
+
* Adds a body for a layer (absolutely positioned: `x`, `y`, and a size)
|
|
3592
|
+
* or for a plain rectangle. The layer's current pose is the start pose.
|
|
3593
|
+
*/
|
|
3594
|
+
body(target, options = {}) {
|
|
3595
|
+
this.assertOpen();
|
|
3596
|
+
const layer = typeof target.kind === "string" ? target : void 0;
|
|
3597
|
+
const geom = layerGeometry(target, options);
|
|
3598
|
+
const ppm = this.pixelsPerMeter;
|
|
3599
|
+
const type = options.type ?? "dynamic";
|
|
3600
|
+
const [cx, cy] = layerToCenter(geom.x, geom.y, geom.rotation, geom);
|
|
3601
|
+
const v = options.velocity ?? [0, 0];
|
|
3602
|
+
const spec$1 = {
|
|
3603
|
+
type,
|
|
3604
|
+
position: [cx / ppm, cy / ppm],
|
|
3605
|
+
angle: geom.rotation * Math.PI / 180,
|
|
3606
|
+
velocity: [v[0] / ppm, v[1] / ppm],
|
|
3607
|
+
angularVelocity: (options.angularVelocity ?? 0) * Math.PI / 180,
|
|
3608
|
+
linearDamping: options.linearDamping ?? 0,
|
|
3609
|
+
angularDamping: options.angularDamping ?? 0,
|
|
3610
|
+
gravityScale: options.gravityScale ?? 1,
|
|
3611
|
+
lockRotation: options.lockRotation ?? false,
|
|
3612
|
+
ccd: options.ccd ?? false,
|
|
3613
|
+
enabled: true,
|
|
3614
|
+
sensor: options.sensor ?? false,
|
|
3615
|
+
density: options.density ?? 1,
|
|
3616
|
+
friction: options.friction ?? .6,
|
|
3617
|
+
restitution: options.restitution ?? .3,
|
|
3618
|
+
shape: scaleShape$1(geom.shape, 1 / ppm)
|
|
3619
|
+
};
|
|
3620
|
+
const simBody = this.sim.addBody(spec$1);
|
|
3621
|
+
const body = new PhysicsBody2D(this, simBody, type, layer, geom, options, this.newRecording());
|
|
3622
|
+
return this.addBody(body);
|
|
3623
|
+
}
|
|
3624
|
+
/** A fixed, very wide floor whose top edge sits at `y`. */
|
|
3625
|
+
ground(options) {
|
|
3626
|
+
const t = 2e3;
|
|
3627
|
+
const w = 1e6;
|
|
3628
|
+
return this.body({
|
|
3629
|
+
x: -w / 2,
|
|
3630
|
+
y: options.y,
|
|
3631
|
+
width: w,
|
|
3632
|
+
height: t
|
|
3633
|
+
}, {
|
|
3634
|
+
type: "fixed",
|
|
3635
|
+
friction: options.friction,
|
|
3636
|
+
restitution: options.restitution ?? .2
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3639
|
+
/** Fixed floor, left and right walls (and optionally a ceiling) around a box, e.g. the canvas. */
|
|
3640
|
+
walls(options) {
|
|
3641
|
+
const { width: W, height: H } = options;
|
|
3642
|
+
const x = options.x ?? 0;
|
|
3643
|
+
const y = options.y ?? 0;
|
|
3644
|
+
const t = options.thickness ?? 400;
|
|
3645
|
+
const mat = {
|
|
3646
|
+
type: "fixed",
|
|
3647
|
+
friction: options.friction,
|
|
3648
|
+
restitution: options.restitution ?? .2
|
|
3649
|
+
};
|
|
3650
|
+
const tall = H * 4 + t;
|
|
3651
|
+
const result = {
|
|
3652
|
+
floor: this.body({
|
|
3653
|
+
x: x - t,
|
|
3654
|
+
y: y + H,
|
|
3655
|
+
width: W + 2 * t,
|
|
3656
|
+
height: t
|
|
3657
|
+
}, mat),
|
|
3658
|
+
left: this.body({
|
|
3659
|
+
x: x - t,
|
|
3660
|
+
y: y + H - tall,
|
|
3661
|
+
width: t,
|
|
3662
|
+
height: tall
|
|
3663
|
+
}, mat),
|
|
3664
|
+
right: this.body({
|
|
3665
|
+
x: x + W,
|
|
3666
|
+
y: y + H - tall,
|
|
3667
|
+
width: t,
|
|
3668
|
+
height: tall
|
|
3669
|
+
}, mat),
|
|
3670
|
+
ceiling: void 0
|
|
3671
|
+
};
|
|
3672
|
+
if (options.ceiling) result.ceiling = this.body({
|
|
3673
|
+
x: x - t,
|
|
3674
|
+
y: y - t,
|
|
3675
|
+
width: W + 2 * t,
|
|
3676
|
+
height: t
|
|
3677
|
+
}, mat);
|
|
3678
|
+
return result;
|
|
3679
|
+
}
|
|
3680
|
+
/** Pins a body to the world at a point (default its center); it can still swing around it. */
|
|
3681
|
+
pin(body, at) {
|
|
3682
|
+
const point = at ?? body.center();
|
|
3683
|
+
const anchor = this.anchorBody(point);
|
|
3684
|
+
this.joint(body, anchor, {
|
|
3685
|
+
type: "revolute",
|
|
3686
|
+
anchorA: body.localAnchor(point),
|
|
3687
|
+
anchorB: [0, 0]
|
|
3688
|
+
});
|
|
3689
|
+
return this;
|
|
3690
|
+
}
|
|
3691
|
+
/** Joins two bodies at a world point so they swing around it (a hinge). */
|
|
3692
|
+
hinge(a, b, at) {
|
|
3693
|
+
this.joint(a, b, {
|
|
3694
|
+
type: "revolute",
|
|
3695
|
+
anchorA: a.localAnchor(at),
|
|
3696
|
+
anchorB: b.localAnchor(at)
|
|
3697
|
+
});
|
|
3698
|
+
return this;
|
|
3699
|
+
}
|
|
3700
|
+
/** Glues two bodies together rigidly. */
|
|
3701
|
+
weld(a, b) {
|
|
3702
|
+
const at = a.center();
|
|
3703
|
+
this.joint(a, b, {
|
|
3704
|
+
type: "fixed",
|
|
3705
|
+
anchorA: [0, 0],
|
|
3706
|
+
anchorB: b.localAnchor(at)
|
|
3707
|
+
});
|
|
3708
|
+
return this;
|
|
3709
|
+
}
|
|
3710
|
+
/** A damped spring between two bodies, or from a body to a world point. */
|
|
3711
|
+
spring(a, b, options = {}) {
|
|
3712
|
+
const { other, pa, pb } = this.endpoints(a, b, options.anchorA, options.anchorB);
|
|
3713
|
+
const ppm = this.pixelsPerMeter;
|
|
3714
|
+
const length = (options.length ?? Math.hypot(pb[0] - pa[0], pb[1] - pa[1])) / ppm;
|
|
3715
|
+
const mass = effectiveMass(a, other);
|
|
3716
|
+
const w = 2 * Math.PI * (options.frequency ?? 2);
|
|
3717
|
+
this.joint(a, other, {
|
|
3718
|
+
type: "spring",
|
|
3719
|
+
anchorA: a.localAnchor(pa),
|
|
3720
|
+
anchorB: other.localAnchor(pb),
|
|
3721
|
+
restLength: length,
|
|
3722
|
+
stiffness: mass * w * w,
|
|
3723
|
+
damping: 2 * (options.dampingRatio ?? .2) * mass * w
|
|
3724
|
+
});
|
|
3725
|
+
return this;
|
|
3726
|
+
}
|
|
3727
|
+
/** A rope: never longer than `length`, slack when closer. */
|
|
3728
|
+
rope(a, b, options = {}) {
|
|
3729
|
+
const { other, pa, pb } = this.endpoints(a, b, options.anchorA, options.anchorB);
|
|
3730
|
+
const length = options.length ?? Math.hypot(pb[0] - pa[0], pb[1] - pa[1]);
|
|
3731
|
+
this.joint(a, other, {
|
|
3732
|
+
type: "rope",
|
|
3733
|
+
anchorA: a.localAnchor(pa),
|
|
3734
|
+
anchorB: other.localAnchor(pb),
|
|
3735
|
+
length: length / this.pixelsPerMeter
|
|
3736
|
+
});
|
|
3737
|
+
return this;
|
|
3738
|
+
}
|
|
3739
|
+
/** A steady push, px/s² (mass-independent): wind, magnets, audio. Drivers are read every frame. */
|
|
3740
|
+
force(body, accel) {
|
|
3741
|
+
const ppm = this.pixelsPerMeter;
|
|
3742
|
+
this.addForce(body, (frame) => [accel.x === void 0 ? 0 : sampleDriver(accel.x, frame, this.fps) / ppm, accel.y === void 0 ? 0 : sampleDriver(accel.y, frame, this.fps) / ppm]);
|
|
3743
|
+
return this;
|
|
3744
|
+
}
|
|
3745
|
+
/**
|
|
3746
|
+
* A force field: called for every awake dynamic body (or just `bodies`)
|
|
3747
|
+
* each solver step with its center and velocity in px, px/s; returns an
|
|
3748
|
+
* acceleration `[ax, ay]` px/s² (or nothing). Wind, vortices, magnets,
|
|
3749
|
+
* flow fields, a dancer's silhouette pushing particles.
|
|
3750
|
+
*/
|
|
3751
|
+
field(accel, options = {}) {
|
|
3752
|
+
const ppm = this.pixelsPerMeter;
|
|
3753
|
+
this.addField((body, frame) => {
|
|
3754
|
+
const [x, y] = body.sim.position();
|
|
3755
|
+
const [vx, vy] = body.sim.velocity();
|
|
3756
|
+
const a = accel({
|
|
3757
|
+
x: x * ppm,
|
|
3758
|
+
y: y * ppm,
|
|
3759
|
+
vx: vx * ppm,
|
|
3760
|
+
vy: vy * ppm,
|
|
3761
|
+
frame,
|
|
3762
|
+
body
|
|
3763
|
+
});
|
|
3764
|
+
return a ? [a[0] / ppm, a[1] / ppm] : null;
|
|
3765
|
+
}, options.bodies);
|
|
3766
|
+
return this;
|
|
3767
|
+
}
|
|
3768
|
+
/** An instant kick at a simulation frame: adds `[vx, vy]` px/s to the body's velocity. */
|
|
3769
|
+
impulse(body, frame, velocity) {
|
|
3770
|
+
const ppm = this.pixelsPerMeter;
|
|
3771
|
+
this.addImpulse(body, frame, [velocity[0] / ppm, velocity[1] / ppm]);
|
|
3772
|
+
return this;
|
|
3773
|
+
}
|
|
3774
|
+
lerpRotation(a, b, t) {
|
|
3775
|
+
return a + (b - a) * t;
|
|
3776
|
+
}
|
|
3777
|
+
joint(a, b, spec$1) {
|
|
3778
|
+
this.assertOpen();
|
|
3779
|
+
this.sim.addJoint(a.sim, b.sim, spec$1);
|
|
3780
|
+
}
|
|
3781
|
+
anchorBody(point) {
|
|
3782
|
+
return this.body({
|
|
3783
|
+
x: point[0] - .5,
|
|
3784
|
+
y: point[1] - .5,
|
|
3785
|
+
width: 1,
|
|
3786
|
+
height: 1
|
|
3787
|
+
}, {
|
|
3788
|
+
type: "fixed",
|
|
3789
|
+
output: "none",
|
|
3790
|
+
sensor: true
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
endpoints(a, b, anchorA, anchorB) {
|
|
3794
|
+
const pa = anchorA ?? a.center();
|
|
3795
|
+
if (Array.isArray(b)) {
|
|
3796
|
+
const pb = anchorB ?? b;
|
|
3797
|
+
return {
|
|
3798
|
+
other: this.anchorBody(pb),
|
|
3799
|
+
pa,
|
|
3800
|
+
pb
|
|
3801
|
+
};
|
|
3802
|
+
}
|
|
3803
|
+
return {
|
|
3804
|
+
other: b,
|
|
3805
|
+
pa,
|
|
3806
|
+
pb: anchorB ?? b.center()
|
|
3807
|
+
};
|
|
3808
|
+
}
|
|
3809
|
+
};
|
|
3810
|
+
function effectiveMass(a, b) {
|
|
3811
|
+
const ma = a.type === "dynamic" ? a.sim.mass() : 0;
|
|
3812
|
+
const mb = b.type === "dynamic" ? b.sim.mass() : 0;
|
|
3813
|
+
if (ma > 0 && mb > 0) return ma * mb / (ma + mb);
|
|
3814
|
+
return Math.max(ma, mb, .001);
|
|
3815
|
+
}
|
|
3816
|
+
function num$1(v, fallback) {
|
|
3817
|
+
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
3818
|
+
}
|
|
3819
|
+
function layerGeometry(layer, options) {
|
|
3820
|
+
const id = typeof layer.id === "string" ? ` "${layer.id}"` : "";
|
|
3821
|
+
if (typeof layer.x !== "number" || typeof layer.y !== "number") throw new Error(`Physics: layer${id} needs numeric \`x\` and \`y\` (absolute position) to become a body.`);
|
|
3822
|
+
let width = options.size?.[0] ?? (typeof layer.width === "number" ? layer.width : void 0);
|
|
3823
|
+
let height = options.size?.[1] ?? (typeof layer.height === "number" ? layer.height : void 0);
|
|
3824
|
+
if ((width === void 0 || height === void 0) && layer.kind === "text") {
|
|
3825
|
+
const m = FontManager.measure(String(layer.text ?? ""), {
|
|
3826
|
+
fontFamily: String(layer.fontFamily ?? "Inter"),
|
|
3827
|
+
fontSize: num$1(layer.fontSize, 48),
|
|
3828
|
+
fontWeight: layer.fontWeight,
|
|
3829
|
+
letterSpacing: num$1(layer.letterSpacing, 0),
|
|
3830
|
+
lineHeight: typeof layer.lineHeight === "number" ? layer.lineHeight : void 0
|
|
3831
|
+
});
|
|
3832
|
+
width ??= m.width;
|
|
3833
|
+
height ??= m.height;
|
|
3834
|
+
}
|
|
3835
|
+
if (width === void 0 || height === void 0 || width <= 0 || height <= 0) throw new Error(`Physics: layer${id} needs a numeric \`width\` and \`height\` (or the \`size\` option) to become a body.`);
|
|
3836
|
+
const scale = num$1(layer.scale, 1);
|
|
3837
|
+
return {
|
|
3838
|
+
x: layer.x,
|
|
3839
|
+
y: layer.y,
|
|
3840
|
+
width,
|
|
3841
|
+
height,
|
|
3842
|
+
scale,
|
|
3843
|
+
anchorX: num$1(layer.anchorX, .5),
|
|
3844
|
+
anchorY: num$1(layer.anchorY, .5),
|
|
3845
|
+
rotation: num$1(layer.rotation, 0),
|
|
3846
|
+
shape: scaleShape$1(colliderShape(layer, width, height, options.shape ?? "auto"), scale)
|
|
3847
|
+
};
|
|
3848
|
+
}
|
|
3849
|
+
function colliderShape(layer, w, h, mode) {
|
|
3850
|
+
const shapeType = layer.kind === "shape" ? String(layer.shapeType ?? "rect") : "rect";
|
|
3851
|
+
const radius = num$1(layer.borderRadius, num$1(layer.cornerRadius, 0));
|
|
3852
|
+
const box = {
|
|
3853
|
+
kind: "box",
|
|
3854
|
+
hx: w / 2,
|
|
3855
|
+
hy: h / 2,
|
|
3856
|
+
radius
|
|
3857
|
+
};
|
|
3858
|
+
if (mode === "box") return box;
|
|
3859
|
+
if (mode === "circle" || mode === "auto" && shapeType === "circle") return {
|
|
3860
|
+
kind: "ball",
|
|
3861
|
+
radius: Math.min(w, h) / 2
|
|
3862
|
+
};
|
|
3863
|
+
if (mode === "auto" && shapeType === "rect") return box;
|
|
3864
|
+
if (mode === "hull" || mode === "auto") {
|
|
3865
|
+
const points = outlinePoints(layer, shapeType, w, h);
|
|
3866
|
+
return points.length >= 6 ? {
|
|
3867
|
+
kind: "hull",
|
|
3868
|
+
points
|
|
3869
|
+
} : box;
|
|
3870
|
+
}
|
|
3871
|
+
return box;
|
|
3872
|
+
}
|
|
3873
|
+
/** The shape's outline as flat points around the box center, px. */
|
|
3874
|
+
function outlinePoints(layer, shapeType, w, h) {
|
|
3875
|
+
const d = shapeType === "path" ? String(layer.d ?? "") : buildShapeSvgPath({
|
|
3876
|
+
shapeType,
|
|
3877
|
+
width: w,
|
|
3878
|
+
height: h,
|
|
3879
|
+
cornerRadius: num$1(layer.cornerRadius, num$1(layer.borderRadius, 0)),
|
|
3880
|
+
polygonSides: layer.polygonSides,
|
|
3881
|
+
starPoints: layer.starPoints,
|
|
3882
|
+
starInnerRadiusRatio: layer.starInnerRadiusRatio,
|
|
3883
|
+
arrowHeadWidth: layer.arrowHeadWidth,
|
|
3884
|
+
arrowHeadLength: layer.arrowHeadLength,
|
|
3885
|
+
arrowShaftWidth: layer.arrowShaftWidth
|
|
3886
|
+
});
|
|
3887
|
+
const out = [];
|
|
3888
|
+
for (const seg of parseSvgPathToSegments(d)) out.push(seg.p0.x - w / 2, seg.p0.y - h / 2, seg.p1.x - w / 2, seg.p1.y - h / 2);
|
|
3889
|
+
return out;
|
|
3890
|
+
}
|
|
3891
|
+
function scaleShape$1(shape, s) {
|
|
3892
|
+
switch (shape.kind) {
|
|
3893
|
+
case "ball": return {
|
|
3894
|
+
kind: "ball",
|
|
3895
|
+
radius: shape.radius * s
|
|
3896
|
+
};
|
|
3897
|
+
case "box": return {
|
|
3898
|
+
kind: "box",
|
|
3899
|
+
hx: shape.hx * s,
|
|
3900
|
+
hy: shape.hy * s,
|
|
3901
|
+
radius: (shape.radius ?? 0) * s
|
|
3902
|
+
};
|
|
3903
|
+
case "hull": return {
|
|
3904
|
+
kind: "hull",
|
|
3905
|
+
points: shape.points.map((v) => v * s)
|
|
3906
|
+
};
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
/**
|
|
3910
|
+
* Layers rotate and scale around their pivot (`anchorX/Y` of the unscaled
|
|
3911
|
+
* box); bodies rotate around their center. These convert between the two.
|
|
3912
|
+
*/
|
|
3913
|
+
function pivotOffset(rotationDeg, f) {
|
|
3914
|
+
const a = rotationDeg * Math.PI / 180;
|
|
3915
|
+
const dx = (.5 - f.anchorX) * f.width * f.scale;
|
|
3916
|
+
const dy = (.5 - f.anchorY) * f.height * f.scale;
|
|
3917
|
+
return [dx * Math.cos(a) - dy * Math.sin(a), dx * Math.sin(a) + dy * Math.cos(a)];
|
|
3918
|
+
}
|
|
3919
|
+
function layerToCenter(x, y, rotationDeg, f) {
|
|
3920
|
+
const [ox, oy] = pivotOffset(rotationDeg, f);
|
|
3921
|
+
return [x + f.anchorX * f.width + ox, y + f.anchorY * f.height + oy];
|
|
3922
|
+
}
|
|
3923
|
+
function centerToLayer(cx, cy, rotationDeg, f) {
|
|
3924
|
+
const [ox, oy] = pivotOffset(rotationDeg, f);
|
|
3925
|
+
return [cx - ox - f.anchorX * f.width, cy - oy - f.anchorY * f.height];
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3928
|
+
//#endregion
|
|
3929
|
+
//#region src/physics/world3d.ts
|
|
3930
|
+
const DEG = Math.PI / 180;
|
|
3931
|
+
/** Quaternion of the renderer's Euler order: R = Rz · Ry · Rx (degrees). */
|
|
3932
|
+
function quatFromEuler(rx, ry, rz) {
|
|
3933
|
+
const half = (d) => d * DEG / 2;
|
|
3934
|
+
const qx = [
|
|
3935
|
+
Math.sin(half(rx)),
|
|
3936
|
+
0,
|
|
3937
|
+
0,
|
|
3938
|
+
Math.cos(half(rx))
|
|
3939
|
+
];
|
|
3940
|
+
const qy = [
|
|
3941
|
+
0,
|
|
3942
|
+
Math.sin(half(ry)),
|
|
3943
|
+
0,
|
|
3944
|
+
Math.cos(half(ry))
|
|
3945
|
+
];
|
|
3946
|
+
return quatMul(quatMul([
|
|
3947
|
+
0,
|
|
3948
|
+
0,
|
|
3949
|
+
Math.sin(half(rz)),
|
|
3950
|
+
Math.cos(half(rz))
|
|
3951
|
+
], qy), qx);
|
|
3952
|
+
}
|
|
3953
|
+
function quatMul(a, b) {
|
|
3954
|
+
const [ax, ay, az, aw] = a;
|
|
3955
|
+
const [bx, by, bz, bw] = b;
|
|
3956
|
+
return [
|
|
3957
|
+
aw * bx + ax * bw + ay * bz - az * by,
|
|
3958
|
+
aw * by - ax * bz + ay * bw + az * bx,
|
|
3959
|
+
aw * bz + ax * by - ay * bx + az * bw,
|
|
3960
|
+
aw * bw - ax * bx - ay * by - az * bz
|
|
3961
|
+
];
|
|
3962
|
+
}
|
|
3963
|
+
/**
|
|
3964
|
+
* Euler angles (degrees, R = Rz · Ry · Rx) for a quaternion, choosing of the
|
|
3965
|
+
* two equivalent solutions the one closest to `previous` and unwrapping each
|
|
3966
|
+
* angle, so rotations stay continuous frame to frame.
|
|
3967
|
+
*/
|
|
3968
|
+
function eulerFromQuat(q, previous) {
|
|
3969
|
+
const [x, y, z, w] = q;
|
|
3970
|
+
const r00 = 1 - 2 * (y * y + z * z);
|
|
3971
|
+
const r10 = 2 * (x * y + w * z);
|
|
3972
|
+
const r20 = 2 * (x * z - w * y);
|
|
3973
|
+
const r21 = 2 * (y * z + w * x);
|
|
3974
|
+
const r22 = 1 - 2 * (x * x + y * y);
|
|
3975
|
+
const r01 = 2 * (x * y - w * z);
|
|
3976
|
+
const r11 = 1 - 2 * (x * x + z * z);
|
|
3977
|
+
const sy = Math.max(-1, Math.min(1, -r20));
|
|
3978
|
+
let a;
|
|
3979
|
+
if (Math.abs(sy) > .999999) {
|
|
3980
|
+
const ay = Math.asin(sy);
|
|
3981
|
+
const keepX = previous ? previous[0] * DEG : 0;
|
|
3982
|
+
a = [
|
|
3983
|
+
keepX,
|
|
3984
|
+
ay,
|
|
3985
|
+
Math.atan2(-r01, r11) + (sy > 0 ? keepX : -keepX)
|
|
3986
|
+
];
|
|
3987
|
+
} else a = [
|
|
3988
|
+
Math.atan2(r21, r22),
|
|
3989
|
+
Math.asin(sy),
|
|
3990
|
+
Math.atan2(r10, r00)
|
|
3991
|
+
];
|
|
3992
|
+
const first = [
|
|
3993
|
+
a[0] / DEG,
|
|
3994
|
+
a[1] / DEG,
|
|
3995
|
+
a[2] / DEG
|
|
3996
|
+
];
|
|
3997
|
+
if (!previous) return first;
|
|
3998
|
+
const second = [
|
|
3999
|
+
first[0] + 180,
|
|
4000
|
+
180 - first[1],
|
|
4001
|
+
first[2] + 180
|
|
4002
|
+
];
|
|
4003
|
+
const unwrap = (c) => [
|
|
4004
|
+
unwrapDegrees(previous[0], c[0]),
|
|
4005
|
+
unwrapDegrees(previous[1], c[1]),
|
|
4006
|
+
unwrapDegrees(previous[2], c[2])
|
|
4007
|
+
];
|
|
4008
|
+
const p = unwrap(first);
|
|
4009
|
+
const s = unwrap(second);
|
|
4010
|
+
const dist$1 = (c) => Math.abs(c[0] - previous[0]) + Math.abs(c[1] - previous[1]) + Math.abs(c[2] - previous[2]);
|
|
4011
|
+
return dist$1(p) <= dist$1(s) ? p : s;
|
|
4012
|
+
}
|
|
4013
|
+
/**
|
|
4014
|
+
* A simulated 3D body. Like {@link PhysicsBody2D}, its signals hold one
|
|
4015
|
+
* value per composition frame and reading one runs the simulation.
|
|
4016
|
+
*/
|
|
4017
|
+
var PhysicsBody3D = class {
|
|
4018
|
+
collisions = [];
|
|
4019
|
+
/** @internal */ recording;
|
|
4020
|
+
/** @internal */ sim;
|
|
4021
|
+
/** @internal */
|
|
4022
|
+
constructor(world, sim, type, layer, start, options, recording) {
|
|
4023
|
+
this.world = world;
|
|
4024
|
+
this.type = type;
|
|
4025
|
+
this.layer = layer;
|
|
4026
|
+
this.start = start;
|
|
4027
|
+
this.options = options;
|
|
4028
|
+
this.sim = sim;
|
|
4029
|
+
this.recording = recording;
|
|
4030
|
+
}
|
|
4031
|
+
get startFrame() {
|
|
4032
|
+
return Math.max(0, Math.round(this.options.startFrame ?? 0));
|
|
4033
|
+
}
|
|
4034
|
+
get gravityScale() {
|
|
4035
|
+
return this.options.gravityScale ?? 1;
|
|
4036
|
+
}
|
|
4037
|
+
get output() {
|
|
4038
|
+
return this.options.output;
|
|
4039
|
+
}
|
|
4040
|
+
/** Center, px (what Layer3D's `x` / `y` / `z` mean). */
|
|
4041
|
+
get x() {
|
|
4042
|
+
return this.recording.signal("x");
|
|
4043
|
+
}
|
|
4044
|
+
get y() {
|
|
4045
|
+
return this.recording.signal("y");
|
|
4046
|
+
}
|
|
4047
|
+
get z() {
|
|
4048
|
+
return this.recording.signal("z");
|
|
4049
|
+
}
|
|
4050
|
+
/** Degrees, continuous. */
|
|
4051
|
+
get rotateX() {
|
|
4052
|
+
return this.recording.signal("rotateX");
|
|
4053
|
+
}
|
|
4054
|
+
get rotateY() {
|
|
4055
|
+
return this.recording.signal("rotateY");
|
|
4056
|
+
}
|
|
4057
|
+
get rotateZ() {
|
|
4058
|
+
return this.recording.signal("rotateZ");
|
|
4059
|
+
}
|
|
4060
|
+
/** px/s. */
|
|
4061
|
+
get vx() {
|
|
4062
|
+
return this.recording.signal("vx");
|
|
4063
|
+
}
|
|
4064
|
+
get vy() {
|
|
4065
|
+
return this.recording.signal("vy");
|
|
4066
|
+
}
|
|
4067
|
+
get vz() {
|
|
4068
|
+
return this.recording.signal("vz");
|
|
4069
|
+
}
|
|
4070
|
+
get speed() {
|
|
4071
|
+
return this.recording.signal("speed");
|
|
4072
|
+
}
|
|
4073
|
+
/** Hit envelope, as on 2D bodies. */
|
|
4074
|
+
get impact() {
|
|
4075
|
+
return this.recording.signal("impact");
|
|
4076
|
+
}
|
|
4077
|
+
/** 1 while the body is at rest, else 0. */
|
|
4078
|
+
get sleeping() {
|
|
4079
|
+
return this.recording.signal("sleeping");
|
|
4080
|
+
}
|
|
4081
|
+
/** @internal */
|
|
4082
|
+
kinematicTarget(frame) {
|
|
4083
|
+
const follow = this.options.follow;
|
|
4084
|
+
if (this.type !== "kinematic" || !follow) return void 0;
|
|
4085
|
+
const fps = this.world.fps;
|
|
4086
|
+
const read = (d, fallback) => d === void 0 ? fallback : sampleDriver(d, frame, fps);
|
|
4087
|
+
const p = this.start.position;
|
|
4088
|
+
const r = this.start.rotation;
|
|
4089
|
+
const ppm = this.world.pixelsPerMeter;
|
|
4090
|
+
return {
|
|
4091
|
+
position: [
|
|
4092
|
+
read(follow.x, p[0]) / ppm,
|
|
4093
|
+
read(follow.y, p[1]) / ppm,
|
|
4094
|
+
read(follow.z, p[2]) / ppm
|
|
4095
|
+
],
|
|
4096
|
+
rotation: quatFromEuler(read(follow.rotateX, r[0]), read(follow.rotateY, r[1]), read(follow.rotateZ, r[2]))
|
|
4097
|
+
};
|
|
4098
|
+
}
|
|
4099
|
+
/** @internal */
|
|
4100
|
+
record(index, previous) {
|
|
4101
|
+
const ppm = this.world.pixelsPerMeter;
|
|
4102
|
+
const rec = this.recording;
|
|
4103
|
+
const [x, y, z] = this.sim.position();
|
|
4104
|
+
rec.channel("x")[index] = x * ppm;
|
|
4105
|
+
rec.channel("y")[index] = y * ppm;
|
|
4106
|
+
rec.channel("z")[index] = z * ppm;
|
|
4107
|
+
const rx = rec.channel("rotateX");
|
|
4108
|
+
const ry = rec.channel("rotateY");
|
|
4109
|
+
const rz = rec.channel("rotateZ");
|
|
4110
|
+
const prev = previous < 0 ? this.start.rotation : [
|
|
4111
|
+
rx[previous],
|
|
4112
|
+
ry[previous],
|
|
4113
|
+
rz[previous]
|
|
4114
|
+
];
|
|
4115
|
+
const e = eulerFromQuat(this.sim.rotation(), prev);
|
|
4116
|
+
rx[index] = e[0];
|
|
4117
|
+
ry[index] = e[1];
|
|
4118
|
+
rz[index] = e[2];
|
|
4119
|
+
const [vx, vy, vz] = this.sim.velocity();
|
|
4120
|
+
rec.channel("vx")[index] = vx * ppm;
|
|
4121
|
+
rec.channel("vy")[index] = vy * ppm;
|
|
4122
|
+
rec.channel("vz")[index] = vz * ppm;
|
|
4123
|
+
rec.channel("speed")[index] = Math.hypot(vx, vy, vz) * ppm;
|
|
4124
|
+
rec.channel("sleeping")[index] = this.sim.sleeping() ? 1 : 0;
|
|
4125
|
+
}
|
|
4126
|
+
/** @internal */
|
|
4127
|
+
boundChannels() {
|
|
4128
|
+
const out = [
|
|
4129
|
+
["x", "x"],
|
|
4130
|
+
["y", "y"],
|
|
4131
|
+
["z", "z"]
|
|
4132
|
+
];
|
|
4133
|
+
if (this.options.lockRotation !== true) out.push(["rotateX", "rotateX"], ["rotateY", "rotateY"], ["rotateZ", "rotateZ"]);
|
|
4134
|
+
return out;
|
|
4135
|
+
}
|
|
4136
|
+
/** @internal World point (px) → local frame (meters) at the start pose. */
|
|
4137
|
+
localAnchor(point) {
|
|
4138
|
+
if (!point) return [
|
|
4139
|
+
0,
|
|
4140
|
+
0,
|
|
4141
|
+
0
|
|
4142
|
+
];
|
|
4143
|
+
const ppm = this.world.pixelsPerMeter;
|
|
4144
|
+
const p = this.sim.position();
|
|
4145
|
+
const [qx, qy, qz, qw] = this.sim.rotation();
|
|
4146
|
+
return rotateByQuat([
|
|
4147
|
+
point[0] / ppm - p[0],
|
|
4148
|
+
point[1] / ppm - p[1],
|
|
4149
|
+
point[2] / ppm - p[2]
|
|
4150
|
+
], [
|
|
4151
|
+
-qx,
|
|
4152
|
+
-qy,
|
|
4153
|
+
-qz,
|
|
4154
|
+
qw
|
|
4155
|
+
]);
|
|
4156
|
+
}
|
|
4157
|
+
/** @internal Start-pose center, px. */
|
|
4158
|
+
center() {
|
|
4159
|
+
const ppm = this.world.pixelsPerMeter;
|
|
4160
|
+
const [x, y, z] = this.sim.position();
|
|
4161
|
+
return [
|
|
4162
|
+
x * ppm,
|
|
4163
|
+
y * ppm,
|
|
4164
|
+
z * ppm
|
|
4165
|
+
];
|
|
4166
|
+
}
|
|
4167
|
+
};
|
|
4168
|
+
function rotateByQuat(v, q) {
|
|
4169
|
+
const r = quatMul(quatMul(q, [
|
|
4170
|
+
v[0],
|
|
4171
|
+
v[1],
|
|
4172
|
+
v[2],
|
|
4173
|
+
0
|
|
4174
|
+
]), [
|
|
4175
|
+
-q[0],
|
|
4176
|
+
-q[1],
|
|
4177
|
+
-q[2],
|
|
4178
|
+
q[3]
|
|
4179
|
+
]);
|
|
4180
|
+
return [
|
|
4181
|
+
r[0],
|
|
4182
|
+
r[1],
|
|
4183
|
+
r[2]
|
|
4184
|
+
];
|
|
4185
|
+
}
|
|
4186
|
+
/**
|
|
4187
|
+
* A deterministic 3D rigid-body world (Rapier) for Layer3D primitives and
|
|
4188
|
+
* models, in the 3D scene's pixels (y down). Same flow as 2D: add bodies,
|
|
4189
|
+
* `simulate()`, and each layer's `x/y/z` and `rotateX/Y/Z` follow the
|
|
4190
|
+
* simulation; bodies expose signals for everything else.
|
|
4191
|
+
*/
|
|
4192
|
+
var PhysicsWorld3D = class extends WorldCore {
|
|
4193
|
+
/** @internal */
|
|
4194
|
+
constructor(backend, options) {
|
|
4195
|
+
const ppm = options.pixelsPerMeter ?? 100;
|
|
4196
|
+
const g = options.gravity ?? 2400;
|
|
4197
|
+
const gravity = typeof g === "number" ? [
|
|
4198
|
+
0,
|
|
4199
|
+
g / ppm,
|
|
4200
|
+
0
|
|
4201
|
+
] : [
|
|
4202
|
+
g[0] / ppm,
|
|
4203
|
+
g[1] / ppm,
|
|
4204
|
+
g[2] / ppm
|
|
4205
|
+
];
|
|
4206
|
+
super(backend.createWorld3D(gravity), gravity, options);
|
|
4207
|
+
}
|
|
4208
|
+
/** Adds a body for a Layer3D primitive or model (centered on its `x/y/z`), or a plain box. */
|
|
4209
|
+
body(target, options = {}) {
|
|
4210
|
+
this.assertOpen();
|
|
4211
|
+
const layer = typeof target.kind === "string" ? target : void 0;
|
|
4212
|
+
const t = target;
|
|
4213
|
+
const position = [
|
|
4214
|
+
num(t.x, 0),
|
|
4215
|
+
num(t.y, 0),
|
|
4216
|
+
num(t.z, 0)
|
|
4217
|
+
];
|
|
4218
|
+
const rotation = [
|
|
4219
|
+
num(t.rotateX, 0),
|
|
4220
|
+
num(t.rotateY, 0),
|
|
4221
|
+
num(t.rotateZ, 0)
|
|
4222
|
+
];
|
|
4223
|
+
const ppm = this.pixelsPerMeter;
|
|
4224
|
+
const type = options.type ?? "dynamic";
|
|
4225
|
+
const v = options.velocity ?? [
|
|
4226
|
+
0,
|
|
4227
|
+
0,
|
|
4228
|
+
0
|
|
4229
|
+
];
|
|
4230
|
+
const w = options.angularVelocity ?? [
|
|
4231
|
+
0,
|
|
4232
|
+
0,
|
|
4233
|
+
0
|
|
4234
|
+
];
|
|
4235
|
+
const spec$1 = {
|
|
4236
|
+
type,
|
|
4237
|
+
position: [
|
|
4238
|
+
position[0] / ppm,
|
|
4239
|
+
position[1] / ppm,
|
|
4240
|
+
position[2] / ppm
|
|
4241
|
+
],
|
|
4242
|
+
rotation: quatFromEuler(rotation[0], rotation[1], rotation[2]),
|
|
4243
|
+
velocity: [
|
|
4244
|
+
v[0] / ppm,
|
|
4245
|
+
v[1] / ppm,
|
|
4246
|
+
v[2] / ppm
|
|
4247
|
+
],
|
|
4248
|
+
angularVelocity: [
|
|
4249
|
+
w[0] * DEG,
|
|
4250
|
+
w[1] * DEG,
|
|
4251
|
+
w[2] * DEG
|
|
4252
|
+
],
|
|
4253
|
+
linearDamping: options.linearDamping ?? 0,
|
|
4254
|
+
angularDamping: options.angularDamping ?? 0,
|
|
4255
|
+
gravityScale: options.gravityScale ?? 1,
|
|
4256
|
+
lockRotation: options.lockRotation === true,
|
|
4257
|
+
...typeof options.lockRotation === "object" && { enabledRotations: [
|
|
4258
|
+
!options.lockRotation.x,
|
|
4259
|
+
!options.lockRotation.y,
|
|
4260
|
+
!options.lockRotation.z
|
|
4261
|
+
] },
|
|
4262
|
+
ccd: options.ccd ?? false,
|
|
4263
|
+
enabled: true,
|
|
4264
|
+
sensor: options.sensor ?? false,
|
|
4265
|
+
density: options.density ?? 1,
|
|
4266
|
+
friction: options.friction ?? .6,
|
|
4267
|
+
restitution: options.restitution ?? .3,
|
|
4268
|
+
shape: scaleShape(colliderShape3D(t, options), 1 / ppm)
|
|
4269
|
+
};
|
|
4270
|
+
const simBody = this.sim.addBody(spec$1);
|
|
4271
|
+
const body = new PhysicsBody3D(this, simBody, type, layer, {
|
|
4272
|
+
position,
|
|
4273
|
+
rotation
|
|
4274
|
+
}, options, this.newRecording());
|
|
4275
|
+
return this.addBody(body);
|
|
4276
|
+
}
|
|
4277
|
+
/** A fixed, very large floor whose top face sits at `y`. */
|
|
4278
|
+
ground(options) {
|
|
4279
|
+
const t = 2e3;
|
|
4280
|
+
return this.body({
|
|
4281
|
+
x: 0,
|
|
4282
|
+
y: options.y + t / 2,
|
|
4283
|
+
z: 0,
|
|
4284
|
+
width: 1e6,
|
|
4285
|
+
height: t,
|
|
4286
|
+
depth: 1e6
|
|
4287
|
+
}, {
|
|
4288
|
+
type: "fixed",
|
|
4289
|
+
friction: options.friction,
|
|
4290
|
+
restitution: options.restitution ?? .2
|
|
4291
|
+
});
|
|
4292
|
+
}
|
|
4293
|
+
/** Fixed floor and four walls around a box centered on `x`/`z`, floor at `y + height/2`. */
|
|
4294
|
+
walls(options) {
|
|
4295
|
+
const { width: W, height: H, depth: D } = options;
|
|
4296
|
+
const [x, y, z] = [
|
|
4297
|
+
options.x ?? 0,
|
|
4298
|
+
options.y ?? 0,
|
|
4299
|
+
options.z ?? 0
|
|
4300
|
+
];
|
|
4301
|
+
const t = options.thickness ?? 400;
|
|
4302
|
+
const mat = {
|
|
4303
|
+
type: "fixed",
|
|
4304
|
+
friction: options.friction,
|
|
4305
|
+
restitution: options.restitution ?? .2
|
|
4306
|
+
};
|
|
4307
|
+
const tall = H * 4;
|
|
4308
|
+
const wy = y + H / 2 - tall / 2;
|
|
4309
|
+
return [
|
|
4310
|
+
this.body({
|
|
4311
|
+
x,
|
|
4312
|
+
y: y + H / 2 + t / 2,
|
|
4313
|
+
z,
|
|
4314
|
+
width: W + 2 * t,
|
|
4315
|
+
height: t,
|
|
4316
|
+
depth: D + 2 * t
|
|
4317
|
+
}, mat),
|
|
4318
|
+
this.body({
|
|
4319
|
+
x: x - W / 2 - t / 2,
|
|
4320
|
+
y: wy,
|
|
4321
|
+
z,
|
|
4322
|
+
width: t,
|
|
4323
|
+
height: tall,
|
|
4324
|
+
depth: D + 2 * t
|
|
4325
|
+
}, mat),
|
|
4326
|
+
this.body({
|
|
4327
|
+
x: x + W / 2 + t / 2,
|
|
4328
|
+
y: wy,
|
|
4329
|
+
z,
|
|
4330
|
+
width: t,
|
|
4331
|
+
height: tall,
|
|
4332
|
+
depth: D + 2 * t
|
|
4333
|
+
}, mat),
|
|
4334
|
+
this.body({
|
|
4335
|
+
x,
|
|
4336
|
+
y: wy,
|
|
4337
|
+
z: z - D / 2 - t / 2,
|
|
4338
|
+
width: W,
|
|
4339
|
+
height: tall,
|
|
4340
|
+
depth: t
|
|
4341
|
+
}, mat),
|
|
4342
|
+
this.body({
|
|
4343
|
+
x,
|
|
4344
|
+
y: wy,
|
|
4345
|
+
z: z + D / 2 + t / 2,
|
|
4346
|
+
width: W,
|
|
4347
|
+
height: tall,
|
|
4348
|
+
depth: t
|
|
4349
|
+
}, mat)
|
|
4350
|
+
];
|
|
4351
|
+
}
|
|
4352
|
+
/** Pins a body to the world at a point (default its center); it can still turn around it. */
|
|
4353
|
+
pin(body, at) {
|
|
4354
|
+
const point = at ?? body.center();
|
|
4355
|
+
const anchor = this.anchorBody(point);
|
|
4356
|
+
this.joint(body, anchor, {
|
|
4357
|
+
type: "spherical",
|
|
4358
|
+
anchorA: body.localAnchor(point),
|
|
4359
|
+
anchorB: [
|
|
4360
|
+
0,
|
|
4361
|
+
0,
|
|
4362
|
+
0
|
|
4363
|
+
]
|
|
4364
|
+
});
|
|
4365
|
+
return this;
|
|
4366
|
+
}
|
|
4367
|
+
/** Joins two bodies at a world point, turning only around `axis` (default z, facing the camera). */
|
|
4368
|
+
hinge(a, b, at, axis = [
|
|
4369
|
+
0,
|
|
4370
|
+
0,
|
|
4371
|
+
1
|
|
4372
|
+
]) {
|
|
4373
|
+
this.joint(a, b, {
|
|
4374
|
+
type: "revolute",
|
|
4375
|
+
anchorA: a.localAnchor(at),
|
|
4376
|
+
anchorB: b.localAnchor(at),
|
|
4377
|
+
axis
|
|
4378
|
+
});
|
|
4379
|
+
return this;
|
|
4380
|
+
}
|
|
4381
|
+
/** Glues two bodies together rigidly. */
|
|
4382
|
+
weld(a, b) {
|
|
4383
|
+
this.joint(a, b, {
|
|
4384
|
+
type: "fixed",
|
|
4385
|
+
anchorA: [
|
|
4386
|
+
0,
|
|
4387
|
+
0,
|
|
4388
|
+
0
|
|
4389
|
+
],
|
|
4390
|
+
anchorB: b.localAnchor(a.center())
|
|
4391
|
+
});
|
|
4392
|
+
return this;
|
|
4393
|
+
}
|
|
4394
|
+
spring(a, b, options = {}) {
|
|
4395
|
+
const { other, pa, pb } = this.endpoints(a, b, options.anchorA, options.anchorB);
|
|
4396
|
+
const ppm = this.pixelsPerMeter;
|
|
4397
|
+
const length = (options.length ?? dist(pa, pb)) / ppm;
|
|
4398
|
+
const ma = a.type === "dynamic" ? a.sim.mass() : 0;
|
|
4399
|
+
const mb = other.type === "dynamic" ? other.sim.mass() : 0;
|
|
4400
|
+
const mass = ma > 0 && mb > 0 ? ma * mb / (ma + mb) : Math.max(ma, mb, .001);
|
|
4401
|
+
const w = 2 * Math.PI * (options.frequency ?? 2);
|
|
4402
|
+
this.joint(a, other, {
|
|
4403
|
+
type: "spring",
|
|
4404
|
+
anchorA: a.localAnchor(pa),
|
|
4405
|
+
anchorB: other.localAnchor(pb),
|
|
4406
|
+
restLength: length,
|
|
4407
|
+
stiffness: mass * w * w,
|
|
4408
|
+
damping: 2 * (options.dampingRatio ?? .2) * mass * w
|
|
4409
|
+
});
|
|
4410
|
+
return this;
|
|
4411
|
+
}
|
|
4412
|
+
rope(a, b, options = {}) {
|
|
4413
|
+
const { other, pa, pb } = this.endpoints(a, b, options.anchorA, options.anchorB);
|
|
4414
|
+
this.joint(a, other, {
|
|
4415
|
+
type: "rope",
|
|
4416
|
+
anchorA: a.localAnchor(pa),
|
|
4417
|
+
anchorB: other.localAnchor(pb),
|
|
4418
|
+
length: (options.length ?? dist(pa, pb)) / this.pixelsPerMeter
|
|
4419
|
+
});
|
|
4420
|
+
return this;
|
|
4421
|
+
}
|
|
4422
|
+
/** A steady push, px/s² (mass-independent). Drivers are read every frame. */
|
|
4423
|
+
force(body, accel) {
|
|
4424
|
+
const ppm = this.pixelsPerMeter;
|
|
4425
|
+
const read = (d, frame) => d === void 0 ? 0 : sampleDriver(d, frame, this.fps) / ppm;
|
|
4426
|
+
this.addForce(body, (frame) => [
|
|
4427
|
+
read(accel.x, frame),
|
|
4428
|
+
read(accel.y, frame),
|
|
4429
|
+
read(accel.z, frame)
|
|
4430
|
+
]);
|
|
4431
|
+
return this;
|
|
4432
|
+
}
|
|
4433
|
+
/** A force field, as in 2D: returns `[ax, ay, az]` px/s² for a body at `x/y/z` px. */
|
|
4434
|
+
field(accel, options = {}) {
|
|
4435
|
+
const ppm = this.pixelsPerMeter;
|
|
4436
|
+
this.addField((body, frame) => {
|
|
4437
|
+
const [x, y, z] = body.sim.position();
|
|
4438
|
+
const [vx, vy, vz] = body.sim.velocity();
|
|
4439
|
+
const a = accel({
|
|
4440
|
+
x: x * ppm,
|
|
4441
|
+
y: y * ppm,
|
|
4442
|
+
z: z * ppm,
|
|
4443
|
+
vx: vx * ppm,
|
|
4444
|
+
vy: vy * ppm,
|
|
4445
|
+
vz: vz * ppm,
|
|
4446
|
+
frame,
|
|
4447
|
+
body
|
|
4448
|
+
});
|
|
4449
|
+
return a ? [
|
|
4450
|
+
a[0] / ppm,
|
|
4451
|
+
a[1] / ppm,
|
|
4452
|
+
a[2] / ppm
|
|
4453
|
+
] : null;
|
|
4454
|
+
}, options.bodies);
|
|
4455
|
+
return this;
|
|
4456
|
+
}
|
|
4457
|
+
/** An instant kick at a simulation frame: adds `[vx, vy, vz]` px/s. */
|
|
4458
|
+
impulse(body, frame, velocity) {
|
|
4459
|
+
const ppm = this.pixelsPerMeter;
|
|
4460
|
+
this.addImpulse(body, frame, [
|
|
4461
|
+
velocity[0] / ppm,
|
|
4462
|
+
velocity[1] / ppm,
|
|
4463
|
+
velocity[2] / ppm
|
|
4464
|
+
]);
|
|
4465
|
+
return this;
|
|
4466
|
+
}
|
|
4467
|
+
lerpRotation(a, b, t) {
|
|
4468
|
+
const sign = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3] < 0 ? -1 : 1;
|
|
4469
|
+
const q = a.map((v, i) => v + (b[i] * sign - v) * t);
|
|
4470
|
+
const len = Math.hypot(...q) || 1;
|
|
4471
|
+
return q.map((v) => v / len);
|
|
4472
|
+
}
|
|
4473
|
+
joint(a, b, spec$1) {
|
|
4474
|
+
this.assertOpen();
|
|
4475
|
+
this.sim.addJoint(a.sim, b.sim, spec$1);
|
|
4476
|
+
}
|
|
4477
|
+
anchorBody(point) {
|
|
4478
|
+
return this.body({
|
|
4479
|
+
x: point[0],
|
|
4480
|
+
y: point[1],
|
|
4481
|
+
z: point[2],
|
|
4482
|
+
width: 1,
|
|
4483
|
+
height: 1,
|
|
4484
|
+
depth: 1
|
|
4485
|
+
}, {
|
|
4486
|
+
type: "fixed",
|
|
4487
|
+
output: "none",
|
|
4488
|
+
sensor: true
|
|
4489
|
+
});
|
|
4490
|
+
}
|
|
4491
|
+
endpoints(a, b, anchorA, anchorB) {
|
|
4492
|
+
const pa = anchorA ?? a.center();
|
|
4493
|
+
if (Array.isArray(b)) {
|
|
4494
|
+
const pb = anchorB ?? b;
|
|
4495
|
+
return {
|
|
4496
|
+
other: this.anchorBody(pb),
|
|
4497
|
+
pa,
|
|
4498
|
+
pb
|
|
4499
|
+
};
|
|
4500
|
+
}
|
|
4501
|
+
return {
|
|
4502
|
+
other: b,
|
|
4503
|
+
pa,
|
|
4504
|
+
pb: anchorB ?? b.center()
|
|
4505
|
+
};
|
|
4506
|
+
}
|
|
4507
|
+
};
|
|
4508
|
+
function dist(a, b) {
|
|
4509
|
+
return Math.hypot(b[0] - a[0], b[1] - a[1], b[2] - a[2]);
|
|
4510
|
+
}
|
|
4511
|
+
function num(v, fallback) {
|
|
4512
|
+
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
4513
|
+
}
|
|
4514
|
+
/** Collider in px around the layer's center, with the layer's scale applied. */
|
|
4515
|
+
function colliderShape3D(t, options) {
|
|
4516
|
+
const id = typeof t.id === "string" ? ` "${t.id}"` : "";
|
|
4517
|
+
const s = num(t.scale, 1);
|
|
4518
|
+
const scale = [
|
|
4519
|
+
s * num(t.scaleX, 1),
|
|
4520
|
+
s * num(t.scaleY, 1),
|
|
4521
|
+
s * num(t.scaleZ, 1)
|
|
4522
|
+
];
|
|
4523
|
+
const uniform = scale[0] === scale[1] && scale[1] === scale[2];
|
|
4524
|
+
const prim = t.primitive3d ?? {};
|
|
4525
|
+
const mode = options.shape ?? "auto";
|
|
4526
|
+
const hull = () => {
|
|
4527
|
+
const pts = meshPoints(t.modelData);
|
|
4528
|
+
if (!pts) return void 0;
|
|
4529
|
+
return {
|
|
4530
|
+
kind: "hull",
|
|
4531
|
+
points: pts.map((v, i) => v * scale[i % 3])
|
|
4532
|
+
};
|
|
4533
|
+
};
|
|
4534
|
+
if (options.size || mode === "box") {
|
|
4535
|
+
const [w, h$1, d] = options.size ?? boxSize(t, prim) ?? [
|
|
4536
|
+
0,
|
|
4537
|
+
0,
|
|
4538
|
+
0
|
|
4539
|
+
];
|
|
4540
|
+
if (w > 0 && h$1 > 0 && d > 0) return {
|
|
4541
|
+
kind: "box",
|
|
4542
|
+
hx: w * scale[0] / 2,
|
|
4543
|
+
hy: h$1 * scale[1] / 2,
|
|
4544
|
+
hz: d * scale[2] / 2,
|
|
4545
|
+
radius: prim.type === "roundedBox" ? num(prim.radius, 0) * Math.min(...scale) : 0
|
|
4546
|
+
};
|
|
4547
|
+
}
|
|
4548
|
+
if (mode === "hull") {
|
|
4549
|
+
const h$1 = hull();
|
|
4550
|
+
if (h$1) return h$1;
|
|
4551
|
+
}
|
|
4552
|
+
if ((mode === "sphere" || mode === "auto" && prim.type === "sphere") && (uniform || mode === "sphere")) {
|
|
4553
|
+
const r = num(prim.radius, 0) || Math.max(...boxSize(t, prim) ?? [0]) / 2;
|
|
4554
|
+
if (r > 0) return {
|
|
4555
|
+
kind: "ball",
|
|
4556
|
+
radius: r * Math.max(...scale)
|
|
4557
|
+
};
|
|
4558
|
+
}
|
|
4559
|
+
const rTop = num(prim.radiusTop, num(prim.radius, 0));
|
|
4560
|
+
const rBottom = num(prim.radiusBottom, num(prim.radius, 0));
|
|
4561
|
+
if ((mode === "cylinder" || mode === "auto" && prim.type === "cylinder") && rTop === rBottom && rTop > 0 && scale[0] === scale[2]) return {
|
|
4562
|
+
kind: "cylinder",
|
|
4563
|
+
halfHeight: num(prim.height, 0) * scale[1] / 2,
|
|
4564
|
+
radius: rTop * scale[0]
|
|
4565
|
+
};
|
|
4566
|
+
if (mode === "auto" && prim.type === "roundedBox" && (prim.taper ?? 1) === 1) {
|
|
4567
|
+
const [w, h$1, d] = [
|
|
4568
|
+
num(prim.width, 0),
|
|
4569
|
+
num(prim.height, 0),
|
|
4570
|
+
num(prim.depth, 0)
|
|
4571
|
+
];
|
|
4572
|
+
return {
|
|
4573
|
+
kind: "box",
|
|
4574
|
+
hx: w * scale[0] / 2,
|
|
4575
|
+
hy: h$1 * scale[1] / 2,
|
|
4576
|
+
hz: d * scale[2] / 2,
|
|
4577
|
+
radius: num(prim.radius, 0) * Math.min(...scale)
|
|
4578
|
+
};
|
|
4579
|
+
}
|
|
4580
|
+
const h = hull();
|
|
4581
|
+
if (h) return h;
|
|
4582
|
+
const size$1 = boxSize(t, prim);
|
|
4583
|
+
if (size$1) return {
|
|
4584
|
+
kind: "box",
|
|
4585
|
+
hx: size$1[0] * scale[0] / 2,
|
|
4586
|
+
hy: size$1[1] * scale[1] / 2,
|
|
4587
|
+
hz: size$1[2] * scale[2] / 2
|
|
4588
|
+
};
|
|
4589
|
+
throw new Error(`Physics: layer${id} has no mesh or size to build a 3D collider from; pass \`size: [w, h, d]\`.`);
|
|
4590
|
+
}
|
|
4591
|
+
function boxSize(t, prim) {
|
|
4592
|
+
if (prim.type === "roundedBox") return [
|
|
4593
|
+
num(prim.width, 0),
|
|
4594
|
+
num(prim.height, 0),
|
|
4595
|
+
num(prim.depth, 0)
|
|
4596
|
+
];
|
|
4597
|
+
if (prim.type === "sphere") {
|
|
4598
|
+
const d = num(prim.radius, 0) * 2;
|
|
4599
|
+
return [
|
|
4600
|
+
d,
|
|
4601
|
+
d,
|
|
4602
|
+
d
|
|
4603
|
+
];
|
|
4604
|
+
}
|
|
4605
|
+
if (prim.type === "cylinder") {
|
|
4606
|
+
const d = Math.max(num(prim.radiusTop, num(prim.radius, 0)), num(prim.radiusBottom, num(prim.radius, 0))) * 2;
|
|
4607
|
+
return [
|
|
4608
|
+
d,
|
|
4609
|
+
num(prim.height, 0),
|
|
4610
|
+
d
|
|
4611
|
+
];
|
|
4612
|
+
}
|
|
4613
|
+
if (typeof t.width === "number" && typeof t.height === "number" && typeof t.depth === "number") return [
|
|
4614
|
+
t.width,
|
|
4615
|
+
t.height,
|
|
4616
|
+
t.depth
|
|
4617
|
+
];
|
|
4618
|
+
}
|
|
4619
|
+
/** Every vertex of a model, flat xyz, at most ~4k points (the hull needs no more). */
|
|
4620
|
+
function meshPoints(modelData) {
|
|
4621
|
+
const meshes = modelData?.meshes;
|
|
4622
|
+
if (!meshes?.length) return void 0;
|
|
4623
|
+
let total = 0;
|
|
4624
|
+
for (const m of meshes) total += (m.positions?.length ?? 0) / 3;
|
|
4625
|
+
if (total < 4) return void 0;
|
|
4626
|
+
const stride = Math.max(1, Math.floor(total / 4096));
|
|
4627
|
+
const out = [];
|
|
4628
|
+
let i = 0;
|
|
4629
|
+
for (const m of meshes) {
|
|
4630
|
+
const p = m.positions;
|
|
4631
|
+
if (!p) continue;
|
|
4632
|
+
for (let v = 0; v < p.length; v += 3, i++) if (i % stride === 0) out.push(p[v], p[v + 1], p[v + 2]);
|
|
4633
|
+
}
|
|
4634
|
+
return out;
|
|
4635
|
+
}
|
|
4636
|
+
function scaleShape(shape, s) {
|
|
4637
|
+
switch (shape.kind) {
|
|
4638
|
+
case "ball": return {
|
|
4639
|
+
kind: "ball",
|
|
4640
|
+
radius: shape.radius * s
|
|
4641
|
+
};
|
|
4642
|
+
case "box": return {
|
|
4643
|
+
kind: "box",
|
|
4644
|
+
hx: shape.hx * s,
|
|
4645
|
+
hy: shape.hy * s,
|
|
4646
|
+
hz: shape.hz * s,
|
|
4647
|
+
radius: (shape.radius ?? 0) * s
|
|
4648
|
+
};
|
|
4649
|
+
case "cylinder": return {
|
|
4650
|
+
kind: "cylinder",
|
|
4651
|
+
halfHeight: shape.halfHeight * s,
|
|
4652
|
+
radius: shape.radius * s
|
|
4653
|
+
};
|
|
4654
|
+
case "hull": return {
|
|
4655
|
+
kind: "hull",
|
|
4656
|
+
points: shape.points.map((v) => v * s)
|
|
4657
|
+
};
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
|
|
4661
|
+
//#endregion
|
|
4662
|
+
//#region src/physics/index.ts
|
|
4663
|
+
/**
|
|
4664
|
+
* Rigid-body physics baked into signals. A world simulates once, while the
|
|
4665
|
+
* composition is built (Rapier's deterministic build, so every machine gets
|
|
4666
|
+
* the same motion), and replays as per-frame signals: layers follow their
|
|
4667
|
+
* bodies, and `impact`, `speed`, … drive any other prop or effect.
|
|
4668
|
+
*
|
|
4669
|
+
* ```ts
|
|
4670
|
+
* const world = await Physics.world2d({ fps: 30, frames: 150 });
|
|
4671
|
+
* world.walls({ width: W, height: H });
|
|
4672
|
+
* const card = world.body(cardLayer, { restitution: 0.4 });
|
|
4673
|
+
* world.simulate();
|
|
4674
|
+
* flash.animate(LayerAnimation.create().signal("opacity", card.impact));
|
|
4675
|
+
* ```
|
|
4676
|
+
*/
|
|
4677
|
+
const Physics = {
|
|
4678
|
+
async world2d(options) {
|
|
4679
|
+
return new PhysicsWorld2D(await rapierBackend(2), options);
|
|
4680
|
+
},
|
|
4681
|
+
async world3d(options) {
|
|
4682
|
+
return new PhysicsWorld3D(await rapierBackend(3), options);
|
|
4683
|
+
}
|
|
4684
|
+
};
|
|
4685
|
+
|
|
4686
|
+
//#endregion
|
|
4687
|
+
//#region src/styles.ts
|
|
4688
|
+
const look = (...effects) => effects;
|
|
4689
|
+
const Styles = {
|
|
4690
|
+
pixelArt: (o = {}) => look(new Posterize({
|
|
4691
|
+
palette: o.palette ?? "pico8",
|
|
4692
|
+
pixelSize: o.pixelSize ?? 6,
|
|
4693
|
+
dither: .4
|
|
4694
|
+
})),
|
|
4695
|
+
gameBoy: (o = {}) => look(new Posterize({
|
|
4696
|
+
palette: "gameboy",
|
|
4697
|
+
pixelSize: o.pixelSize ?? 4,
|
|
4698
|
+
dither: .6
|
|
4699
|
+
})),
|
|
4700
|
+
teletext: (o = {}) => look(new Posterize({
|
|
4701
|
+
palette: "teletext",
|
|
4702
|
+
pixelSize: o.pixelSize ?? 10
|
|
4703
|
+
})),
|
|
4704
|
+
ledWall: (o = {}) => look(new Pixelate({
|
|
4705
|
+
size: o.size ?? 14,
|
|
4706
|
+
shape: "circle",
|
|
4707
|
+
gap: .18
|
|
4708
|
+
})),
|
|
4709
|
+
ascii: (o = {}) => look(new Ascii({
|
|
4710
|
+
cellSize: o.cellSize ?? 10,
|
|
4711
|
+
colorMode: o.matrix ? "green" : "source",
|
|
4712
|
+
bg: o.matrix ? "#020a04" : "#0b0b0b"
|
|
4713
|
+
})),
|
|
4714
|
+
oneBit: (o = {}) => look(new Dither({
|
|
4715
|
+
mode: "bayer",
|
|
4716
|
+
scale: o.scale ?? 2,
|
|
4717
|
+
dark: o.dark ?? "#111111",
|
|
4718
|
+
light: o.light ?? "#f2efe6"
|
|
4719
|
+
})),
|
|
4720
|
+
risograph: (o = {}) => {
|
|
4721
|
+
const a = o.inkA ?? "#1d3fbb";
|
|
4722
|
+
const b = o.inkB ?? "#ff5fa2";
|
|
4723
|
+
const paper = o.paper ?? "#f4efe4";
|
|
4724
|
+
return look(new GradientMap({ stops: [
|
|
4725
|
+
{
|
|
4726
|
+
position: 0,
|
|
4727
|
+
color: a
|
|
4728
|
+
},
|
|
4729
|
+
{
|
|
4730
|
+
position: .55,
|
|
4731
|
+
color: b
|
|
4732
|
+
},
|
|
4733
|
+
{
|
|
4734
|
+
position: 1,
|
|
4735
|
+
color: paper
|
|
4736
|
+
}
|
|
4737
|
+
] }), new Posterize({
|
|
4738
|
+
palette: "custom",
|
|
4739
|
+
colors: [
|
|
4740
|
+
a,
|
|
4741
|
+
b,
|
|
4742
|
+
paper
|
|
4743
|
+
],
|
|
4744
|
+
dither: .8,
|
|
4745
|
+
pixelSize: 2
|
|
4746
|
+
}), new FilmGrain({ strength: .2 }));
|
|
4747
|
+
},
|
|
4748
|
+
cartoon: (o = {}) => look(new Toon({
|
|
4749
|
+
levels: o.levels ?? 4,
|
|
4750
|
+
edgeColor: o.ink ?? "#111111",
|
|
4751
|
+
saturation: 1.3
|
|
4752
|
+
})),
|
|
4753
|
+
comic: (o = {}) => look(new Toon({
|
|
4754
|
+
levels: 4,
|
|
4755
|
+
edgeWidth: 1.5,
|
|
4756
|
+
saturation: 1.4
|
|
4757
|
+
}), new HalftoneScreen({
|
|
4758
|
+
frequency: o.dotFrequency ?? 70,
|
|
4759
|
+
opacity: .35
|
|
4760
|
+
})),
|
|
4761
|
+
thermal: () => look(new GradientMap({ stops: [
|
|
4762
|
+
{
|
|
4763
|
+
position: 0,
|
|
4764
|
+
color: "#000010"
|
|
4765
|
+
},
|
|
4766
|
+
{
|
|
4767
|
+
position: .25,
|
|
4768
|
+
color: "#3b0a7a"
|
|
4769
|
+
},
|
|
4770
|
+
{
|
|
4771
|
+
position: .5,
|
|
4772
|
+
color: "#c4205f"
|
|
4773
|
+
},
|
|
4774
|
+
{
|
|
4775
|
+
position: .75,
|
|
4776
|
+
color: "#ff9a1f"
|
|
4777
|
+
},
|
|
4778
|
+
{
|
|
4779
|
+
position: 1,
|
|
4780
|
+
color: "#fffbe0"
|
|
4781
|
+
}
|
|
4782
|
+
] })),
|
|
4783
|
+
vhs: (o = {}) => look(new CRT({
|
|
4784
|
+
tracking: o.tracking ?? .5,
|
|
4785
|
+
chroma: 4,
|
|
4786
|
+
curvature: .04,
|
|
4787
|
+
scanlines: .25,
|
|
4788
|
+
mask: .05,
|
|
4789
|
+
noise: .15
|
|
4790
|
+
}), new RGBSplit({ amount: 3 })),
|
|
4791
|
+
crt: (o = {}) => look(new CRT({
|
|
4792
|
+
curvature: o.curvature ?? .15,
|
|
4793
|
+
scanlines: .5,
|
|
4794
|
+
mask: .35
|
|
4795
|
+
})),
|
|
4796
|
+
glitch: (o = {}) => look(new RGBSplit({ amount: o.amount ?? 18 }), new CRT({
|
|
4797
|
+
tracking: 1,
|
|
4798
|
+
chroma: 6,
|
|
4799
|
+
curvature: 0,
|
|
4800
|
+
scanlines: .1,
|
|
4801
|
+
mask: 0,
|
|
4802
|
+
vignette: 0
|
|
4803
|
+
})),
|
|
4804
|
+
oilPainting: (o = {}) => look(new Painterly({
|
|
4805
|
+
style: "oil",
|
|
4806
|
+
radius: o.brush ?? 5
|
|
4807
|
+
})),
|
|
4808
|
+
watercolor: (o = {}) => look(new Painterly({
|
|
4809
|
+
style: "watercolor",
|
|
4810
|
+
radius: o.brush ?? 6,
|
|
4811
|
+
paper: .5
|
|
4812
|
+
})),
|
|
4813
|
+
topographic: (o = {}) => look(new Contour({
|
|
4814
|
+
levels: o.levels ?? 16,
|
|
4815
|
+
fill: "flat",
|
|
4816
|
+
lineColor: o.ink ?? "#5a3a1a",
|
|
4817
|
+
fillColor: o.paper ?? "#f3ead8"
|
|
4818
|
+
})),
|
|
4819
|
+
kaleidoscope: (o = {}) => look(new Kaleidoscope({
|
|
4820
|
+
segments: o.segments ?? 8,
|
|
4821
|
+
rotation: o.rotation ?? 0
|
|
4822
|
+
})),
|
|
4823
|
+
opArt: (o = {}) => look(new OpArt({
|
|
4824
|
+
pattern: o.pattern ?? "stripes",
|
|
4825
|
+
frequency: o.frequency ?? 60
|
|
4826
|
+
})),
|
|
4827
|
+
holographic: (o = {}) => look(new Iridescence({
|
|
4828
|
+
strength: o.strength ?? .7,
|
|
4829
|
+
shift: o.shift ?? 0
|
|
4830
|
+
})),
|
|
4831
|
+
noir: () => look(new GradientMap({ stops: [
|
|
4832
|
+
{
|
|
4833
|
+
position: 0,
|
|
4834
|
+
color: "#000000"
|
|
4835
|
+
},
|
|
4836
|
+
{
|
|
4837
|
+
position: .55,
|
|
4838
|
+
color: "#8a8a8a"
|
|
4839
|
+
},
|
|
4840
|
+
{
|
|
4841
|
+
position: 1,
|
|
4842
|
+
color: "#ffffff"
|
|
4843
|
+
}
|
|
4844
|
+
] }), new FilmGrain({ strength: .35 }), new Vignette({ strength: .6 })),
|
|
4845
|
+
film: () => look(new FilmGrain({ strength: .25 }), new Vignette({ strength: .35 }))
|
|
4846
|
+
};
|
|
4847
|
+
|
|
4848
|
+
//#endregion
|
|
4849
|
+
//#region src/index.ts
|
|
4850
|
+
function decorateTrackSignals(track, source) {
|
|
4851
|
+
if (!track) return track;
|
|
4852
|
+
if (source && !track.source) Object.defineProperty(track, "source", {
|
|
4853
|
+
value: source,
|
|
4854
|
+
enumerable: true,
|
|
4855
|
+
writable: true,
|
|
4856
|
+
configurable: true
|
|
4857
|
+
});
|
|
4858
|
+
if (typeof track.section !== "function") Object.defineProperty(track, "section", {
|
|
4859
|
+
value: (sourceOrOptions, options) => {
|
|
4860
|
+
if (typeof sourceOrOptions === "object" && sourceOrOptions !== null && !("kind" in sourceOrOptions) && !("inputHandleId" in sourceOrOptions) && !("src" in sourceOrOptions)) {
|
|
4861
|
+
const resolvedSource$1 = sourceOrOptions.source ?? track.source ?? source;
|
|
4862
|
+
return Layer.section(resolvedSource$1, track, sourceOrOptions);
|
|
4863
|
+
}
|
|
4864
|
+
const resolvedSource = sourceOrOptions ?? track.source ?? source;
|
|
4865
|
+
return Layer.section(resolvedSource, track, options);
|
|
4866
|
+
},
|
|
4867
|
+
enumerable: false,
|
|
1976
4868
|
writable: true,
|
|
1977
4869
|
configurable: true
|
|
1978
4870
|
});
|
|
@@ -2120,6 +5012,8 @@ var Composition = class Composition {
|
|
|
2120
5012
|
audioTracks = [];
|
|
2121
5013
|
signals = {};
|
|
2122
5014
|
_frameHooks = [];
|
|
5015
|
+
_prepareHooks = [];
|
|
5016
|
+
_clipVision = /* @__PURE__ */ new Map();
|
|
2123
5017
|
_visionConfig;
|
|
2124
5018
|
_visionBundle;
|
|
2125
5019
|
_effects = [];
|
|
@@ -2153,6 +5047,66 @@ var Composition = class Composition {
|
|
|
2153
5047
|
return this._visionBundle;
|
|
2154
5048
|
}
|
|
2155
5049
|
/**
|
|
5050
|
+
* Vision for a clip, analysed per frame on demand and shared by everything
|
|
5051
|
+
* that reads it (see {@link ClipVision}): `comp.vision("dancer.mp4").person`
|
|
5052
|
+
* gives pose keypoints and a silhouette at any frame. Each frame is
|
|
5053
|
+
* analysed just before it renders, so nothing runs up front. Results
|
|
5054
|
+
* persist on disk in Node. The same clip and options return one instance.
|
|
5055
|
+
*/
|
|
5056
|
+
vision(source, options = {}) {
|
|
5057
|
+
const key = `${source}\u0000${JSON.stringify(options)}`;
|
|
5058
|
+
let clip = this._clipVision.get(key);
|
|
5059
|
+
if (!clip) {
|
|
5060
|
+
clip = new ClipVision(source, this.fps, options);
|
|
5061
|
+
this._clipVision.set(key, clip);
|
|
5062
|
+
}
|
|
5063
|
+
return clip;
|
|
5064
|
+
}
|
|
5065
|
+
/**
|
|
5066
|
+
* @internal Analyses every `comp.vision` clip up to a composition frame
|
|
5067
|
+
* (frames already analysed are skipped), so consumers that step frame by
|
|
5068
|
+
* frame — physics, particles — always find every frame before it.
|
|
5069
|
+
*/
|
|
5070
|
+
async prepareVision(frame) {
|
|
5071
|
+
this.linkClipMattes();
|
|
5072
|
+
await Promise.all([...this._clipVision.values()].map((clip) => clip.ready(0, frame)));
|
|
5073
|
+
}
|
|
5074
|
+
/**
|
|
5075
|
+
* Points matte-family `withVision` layers at the `comp.vision` clip they
|
|
5076
|
+
* show, so the cutout reuses that clip's per-frame subject instead of
|
|
5077
|
+
* segmenting the frame again during render. Only layers whose settings
|
|
5078
|
+
* would give the same subject are linked; the rest keep their own pass.
|
|
5079
|
+
*/
|
|
5080
|
+
linkClipMattes() {
|
|
5081
|
+
if (this._clipVision.size === 0) return;
|
|
5082
|
+
const walk = (node) => {
|
|
5083
|
+
if (!node || typeof node !== "object") return;
|
|
5084
|
+
const n = node;
|
|
5085
|
+
if (n.kind === "media" && n.dataType === "Video" && typeof n.inputHandleId === "string" && n.vision instanceof Vision && !(CLIP_SUBJECT in n.vision)) {
|
|
5086
|
+
const clip = this.clipForMatte(n.inputHandleId, n.vision);
|
|
5087
|
+
if (clip) {
|
|
5088
|
+
clip.keepSubjects();
|
|
5089
|
+
Object.defineProperty(n.vision, CLIP_SUBJECT, {
|
|
5090
|
+
value: clip,
|
|
5091
|
+
enumerable: false,
|
|
5092
|
+
configurable: true
|
|
5093
|
+
});
|
|
5094
|
+
}
|
|
5095
|
+
}
|
|
5096
|
+
if (Array.isArray(n.children)) for (const c of n.children) walk(c);
|
|
5097
|
+
};
|
|
5098
|
+
for (const child of this.children) walk(child);
|
|
5099
|
+
}
|
|
5100
|
+
clipForMatte(source, v) {
|
|
5101
|
+
const mode = v.mode ?? "passthrough";
|
|
5102
|
+
if (mode !== "matte" && mode !== "mask" && mode !== "crop") return;
|
|
5103
|
+
if (v.matteSource === "selfie" || v.keyBackground === true || v.classes !== void 0 || v.enablePose === true || v.enableMatte === true || v.visionBundle !== void 0 || (v.maskThreshold ?? .5) !== .5 || v.featherRadius !== void 0) return;
|
|
5104
|
+
for (const clip of this._clipVision.values()) {
|
|
5105
|
+
const o = clip.options;
|
|
5106
|
+
if (clip.source === source && (o.variant ?? "s") === (v.variant ?? "s") && (o.confidence ?? .3) === (v.confidence ?? .3)) return clip;
|
|
5107
|
+
}
|
|
5108
|
+
}
|
|
5109
|
+
/**
|
|
2156
5110
|
* One-shot, ffmpeg-free vision analysis of a media source.
|
|
2157
5111
|
*
|
|
2158
5112
|
* Decodes frames through the same mediabunny pipeline the renderer uses, runs the requested
|
|
@@ -2204,8 +5158,25 @@ var Composition = class Composition {
|
|
|
2204
5158
|
this._frameHooks.push(callback);
|
|
2205
5159
|
return this;
|
|
2206
5160
|
}
|
|
5161
|
+
/**
|
|
5162
|
+
* Async work a frame needs before it is evaluated (vision, a live physics
|
|
5163
|
+
* world). Every render path awaits these, in order, before drawing the
|
|
5164
|
+
* frame; they may run more than once per frame, so keep them idempotent.
|
|
5165
|
+
*/
|
|
5166
|
+
onPrepareFrame(callback) {
|
|
5167
|
+
this._prepareHooks.push(callback);
|
|
5168
|
+
return this;
|
|
5169
|
+
}
|
|
2207
5170
|
notifyFrame(ctx) {
|
|
2208
5171
|
for (const hook of this._frameHooks) hook(ctx);
|
|
5172
|
+
if (this._clipVision.size === 0 && this._prepareHooks.length === 0) return;
|
|
5173
|
+
return (async () => {
|
|
5174
|
+
await this.prepareVision(ctx.frame);
|
|
5175
|
+
for (const hook of this._prepareHooks) await hook(ctx);
|
|
5176
|
+
})();
|
|
5177
|
+
}
|
|
5178
|
+
hasFrameHooks() {
|
|
5179
|
+
return this._frameHooks.length > 0 || this._prepareHooks.length > 0 || this._clipVision.size > 0;
|
|
2209
5180
|
}
|
|
2210
5181
|
constructor(options) {
|
|
2211
5182
|
this.width = options.width;
|
|
@@ -2538,7 +5509,7 @@ var Composition = class Composition {
|
|
|
2538
5509
|
signals: this.signals,
|
|
2539
5510
|
...this._effects.length > 0 && { effects: this._effects },
|
|
2540
5511
|
...this._visionConfig && { vision: this._visionConfig },
|
|
2541
|
-
...this.
|
|
5512
|
+
...this.hasFrameHooks() && { onRequestFrame: (ctx) => this.notifyFrame(ctx) }
|
|
2542
5513
|
};
|
|
2543
5514
|
}
|
|
2544
5515
|
/**
|
|
@@ -2576,9 +5547,9 @@ var Composition = class Composition {
|
|
|
2576
5547
|
* Can render either by timestamp in milliseconds (`atMs`) or by frame index (`frame`).
|
|
2577
5548
|
*/
|
|
2578
5549
|
async renderFrame(options = {}) {
|
|
2579
|
-
if (this.
|
|
5550
|
+
if (this.hasFrameHooks()) {
|
|
2580
5551
|
const targetFrame = options.frame ?? (options.atMs !== void 0 ? Math.round(options.atMs / 1e3 * this.fps) : 0);
|
|
2581
|
-
this.notifyFrame({
|
|
5552
|
+
await this.notifyFrame({
|
|
2582
5553
|
frame: targetFrame,
|
|
2583
5554
|
fps: this.fps,
|
|
2584
5555
|
time: this.fps > 0 ? targetFrame / this.fps : 0,
|
|
@@ -2641,7 +5612,13 @@ function withAnimation(node) {
|
|
|
2641
5612
|
Object.defineProperty(target, "animate", {
|
|
2642
5613
|
value: (...anims) => {
|
|
2643
5614
|
const incoming = anims.flat().flatMap((a) => a instanceof LayerAnimation ? a.tracks : a.tracks ?? []);
|
|
2644
|
-
|
|
5615
|
+
const current = target.animation;
|
|
5616
|
+
const existing = current?.tracks ?? [];
|
|
5617
|
+
const frameStep = anims.flat().map((a) => a.frameStep).filter((n) => n !== void 0).at(-1) ?? current?.frameStep;
|
|
5618
|
+
target.animation = {
|
|
5619
|
+
tracks: mergeAnimationTracks([...existing, ...incoming]),
|
|
5620
|
+
...frameStep !== void 0 && frameStep > 1 && { frameStep }
|
|
5621
|
+
};
|
|
2645
5622
|
return target;
|
|
2646
5623
|
},
|
|
2647
5624
|
enumerable: false,
|
|
@@ -2910,6 +5887,68 @@ const Layer = {
|
|
|
2910
5887
|
children: options.children ?? [],
|
|
2911
5888
|
...options
|
|
2912
5889
|
}),
|
|
5890
|
+
particles: (options) => {
|
|
5891
|
+
const system = new Particles(options);
|
|
5892
|
+
const source = {
|
|
5893
|
+
metadata: {
|
|
5894
|
+
width: options.width,
|
|
5895
|
+
height: options.height
|
|
5896
|
+
},
|
|
5897
|
+
operation: {
|
|
5898
|
+
...system.toOperation(),
|
|
5899
|
+
effect: system,
|
|
5900
|
+
dataType: "Video"
|
|
5901
|
+
},
|
|
5902
|
+
children: []
|
|
5903
|
+
};
|
|
5904
|
+
return withAnimation({
|
|
5905
|
+
id: options.id ?? autoId("particles"),
|
|
5906
|
+
kind: "media",
|
|
5907
|
+
dataType: "Video",
|
|
5908
|
+
src: source,
|
|
5909
|
+
position: "absolute",
|
|
5910
|
+
x: options.x ?? 0,
|
|
5911
|
+
y: options.y ?? 0,
|
|
5912
|
+
width: options.width,
|
|
5913
|
+
height: options.height,
|
|
5914
|
+
...options.startFrame !== void 0 && { startFrame: options.startFrame },
|
|
5915
|
+
...options.durationFrames !== void 0 && { durationFrames: options.durationFrames },
|
|
5916
|
+
...options.zIndex !== void 0 && { zIndex: options.zIndex }
|
|
5917
|
+
});
|
|
5918
|
+
},
|
|
5919
|
+
vfx: (options) => {
|
|
5920
|
+
const { x, y, id, startFrame, durationFrames, zIndex, ...props } = options;
|
|
5921
|
+
const vfx = new ProceduralVFX({
|
|
5922
|
+
outputType: "Video",
|
|
5923
|
+
...props
|
|
5924
|
+
});
|
|
5925
|
+
const source = {
|
|
5926
|
+
metadata: {
|
|
5927
|
+
width: options.width,
|
|
5928
|
+
height: options.height
|
|
5929
|
+
},
|
|
5930
|
+
operation: {
|
|
5931
|
+
...vfx.toOperation(),
|
|
5932
|
+
effect: vfx,
|
|
5933
|
+
dataType: "Video"
|
|
5934
|
+
},
|
|
5935
|
+
children: []
|
|
5936
|
+
};
|
|
5937
|
+
return withAnimation({
|
|
5938
|
+
id: id ?? autoId("vfx"),
|
|
5939
|
+
kind: "media",
|
|
5940
|
+
dataType: "Video",
|
|
5941
|
+
src: source,
|
|
5942
|
+
position: "absolute",
|
|
5943
|
+
x: x ?? 0,
|
|
5944
|
+
y: y ?? 0,
|
|
5945
|
+
width: options.width,
|
|
5946
|
+
height: options.height,
|
|
5947
|
+
...startFrame !== void 0 && { startFrame },
|
|
5948
|
+
...durationFrames !== void 0 && { durationFrames },
|
|
5949
|
+
...zIndex !== void 0 && { zIndex }
|
|
5950
|
+
});
|
|
5951
|
+
},
|
|
2913
5952
|
section: (source, targetOrOptions = {}, options = {}) => {
|
|
2914
5953
|
let realSource = source;
|
|
2915
5954
|
if (source && typeof source === "object" && "toSpec" in source && "children" in source) {
|
|
@@ -3216,5 +6255,5 @@ const Effect = Object.assign(Effect$1, effectFactories, {
|
|
|
3216
6255
|
});
|
|
3217
6256
|
|
|
3218
6257
|
//#endregion
|
|
3219
|
-
export { AETextAnimatorSchema, AdvancedTextAnimatorBuilder, AdvancedTextAnimatorSchema, AnchorGroupingSchema, AnchorPresetSchema, AnimatableGlyphPropsSchema, AnimationTypeSchema, AnyOutputUnionSchema, ApplyLUT, AudioCompressor, AudioDelay, AudioExtractorSignalDataSchema, AudioFade, AudioLatentEngine, AudioLatentTrackCache, AudioMeshDeformPipeline, AudioNoiseGate, AudioParametricEq, AudioReverb, AudioSignal, AudioSignalExtractor, AudioSpectrumTypographySchema, BaseMediaOperationPropsSchema, BaseVideoLayerPropsSchema, BloomConfigSchema, Blur, BoxNodeSchema, CAPTION_LAYER_DEFAULTS, COCO17_BONES, COCO17_KEYPOINTS, COCO17_KEYPOINT_NAMES, COCO_CLASSES, COMPOSITE_OPERATIONS, COMPOSITOR_DEFAULTS, Camera3D, CameraAnimation, CameraNodeSchema, CameraParallax3D, CannyComputePipeline, CarrierHandoffConfigSchema, ChannelMerger, ChannelSplitter, ChromaticGlitchConfigSchema, Color, ColorBalance, ColorKey, ColorStopSchema, Composition, CompositorProgramSchema, ConfigHandleSchema, ConfigSchemaRegistry, ControlNetMultiplexer, CornerPin, Crop, CubicBezierSegmentSchema, Curves, CustomEffect, DATA_TYPE_EXTENSIONS, DEFAULT_CHART_COLORS, DEFAULT_DURATION_MS, DEFAULT_FPS, DEFAULT_MEDIA_DIMENSION, DataType, DataTypeVal, DataTypes, DepthNormalsComputePipeline, DepthOfField, DisplacementMap, DynamicStrokePropertiesSchema, ENVIRONMENT_PRESETS, Easing, Effect, EffectPipeline, EnhancedTextNodeSchema, EnvironmentFogSchema, EnvironmentPresetSchema, ExportResultSchema, ExpressionSelectorSchema, ExtendedGlyphPropertiesSchema, ExtendedLayerSchema, ExtractLUT, FileAssetSchema, FileDataSchema, FilmGrain, FlexNodeSchema, Flip, FontManager, GeneratorSignalDataSchema, GlassPipeline, GlobalCompositeOperation, GlyphAnchorGroupingSchema, GlyphAnchorPointSchema, Gradient, GradientMap, HalftoneScreen, HandleTypeVal, HeadlessMediaRenderer, HeadlessWebGPURenderer, HighPass, JsonValueSchema, KenBurns, KineticMarqueeConfigSchema, LIGHT_UNIFORM_SIZE_BYTES, Layer, Layer3D, LayerAnimation, LayerStyle, LayoutNodeSchema, Levels, Light, Light3D, LightNodeSchema, LightTypeSchema, Lighting, LineSpacingDynamicsSchema, Liquify, LongShadowConfigSchema, MAX_GRADIENT_STOPS, MAX_LIGHTS, MIME_TYPES, MaskMath, MaterialTypeSchema, Matrix4Math, MatteModeSchema, Media, MediaMetadataSchema, MediaNodeSchema, MediaOperationSchema, MediaSourceSchema, MeshWarp, Model3DNodeSchema, ModerationError, ModerationUnrefundableError, Modulate, MotionBlur, MotionBlurPipeline, MultiOutputGenericSchema, NodeMetadataSchema, NodeResultSchema, NodeVisualConfigSchema, NoiseGenerator, OpticalFlowComputePipeline, OutputItemSchema, PBRGlass, Paint, ParticleDissolveConfigSchema, PatchHeal, Path3D, PoseSkeletonRenderer, ProceduralVFX, ProgrammaticSignal, RefineEdge, RefractionCaustics3D, Relight3D, Renderer2D, Renderer3D, ResizerScaler, Rotate, SSAO, SSAOPipeline, Scene, SceneDefinitionSchema, SceneTransitionTypeSchema, SegmentationTexturePool, SelectiveColor, SelectorBasedOnSchema, SelectorModeSchema, SelectorShapeSchema, SelectorUnitsSchema, Sequence, ShadowPipeline, ShadowsHighlights, ShapeNodeSchema, Signal, SignalChannelMetaSchema, SignalDataSchema, SignalGateDataSchema, SignalMathDataSchema, SignalStatsSchema, SingleOutputGenericSchema, SkewPropertiesSchema, SlugFontCache, SlugPipeline, SourceOperationSchema, Spatial3DPropertiesSchema, SpatialLandmarkTransformer, SpringPhysicsSchema, StereoPanning, TEXT_LAYER_DEFAULTS, TemporalDeflicker, TemporalDeflickerPipeline, TemporalObjectTracker, TensorPipeline, TextAnchorConfigSchema, TextAnimator, TextAnimatorBuilder, TextAnimatorSchema, TextCursorConfigSchema, TextCursorStyleSchema, TextNodeSchema, TextOperationSchema, TextPathBuilder, TextPathOptionsSchema, TextPathSourceSchema, TextRangeSelectorSchema, TextScrambleConfigSchema, TextSelectorSchema, TextSignalSelectorSchema, TextSpanMarkSchema, TextSpanSchema, TextWigglySelectorSchema, TileOffset, TimelineSegmentSchema, TimelineTransformSchema, ToneMappingSchema, Transition, TransitionSchema, TransitionTypeSchema, TypewriterAnimatorSchema, TypingCadenceSchema, UnsharpMask, VariableFontAxesSchema, Vec2Schema, Vector3Math, VideoAnimationSchema, VideoLayerTypeSchema, VideoSpatialPropsSchema, VideoStylePropsSchema, VideoTextPropsSchema, VideoTimingPropsSchema, Vignette, VirtualMediaDataSchema, Vision, VisionBundle, VisionNode, VisionRunner, VolumetricFormationConfigSchema, VolumetricFormationModeSchema, accent, analyzeSequence, appendOperation, applyStaggerToChildren, bezier, blurRegion, bold, buildPathLUT, calculateSequenceTimeline, checkNodeResultCache, code, collectNodeOps, color, compositorToProgram, computeAnchorPivotOffset, computeCarrierContinuity, computeGridLayout, computeLayout, computeLayoutSync, computeLineLeadingDisplacement, computeRangeSelectorWeight, computeStaggerDelay, computeStrokeRect, computeVideoCropRenderProps, computed, cosineSignal, createOutputItemSchema, createVirtualMedia, dataTypeColors, describeGpu, deserializeSignalData, deterministicWigglyNoise, distanceToCurveParameter, editTrackedRegion, effectFactories, effectMeta, ellipseToCubicBeziers, encodeStereoWav, ensureDevice, evaluateAnimationAtFrame, evaluateCubicBezier, evaluateCubicBezierDerivative, evaluateExpressionSelector, evaluateMarqueeOffset, evaluateSelectorCombination, evaluateSkewMatrix, evaluateSpringProgress, evaluateTrackAtFrame, evaluateTypewriterState, evaluateVolumetricFormation, extractSvgDimensions, findSourceAsset, formatHex, formatQaReport, frameArraySignal, frameSignal, generateArcLengthLUT, generateId, generatePathSegments, getActiveMediaMetadata, getFileDataMetadata, getFingerprint, getLeafMediaType, getMediaType, getRenderFingerprint, getScramblePool, getShuffledOrder, gradientStopsToOklab, hasOnlySingleSource, hoistMattes, inputStore, interpolate, isCssGradient, isEqual, isSignal, isVirtualMediaData, italic, limitGradientStops, lintComposition, loadFBX, loadModel3D, loadOBJ, lutStore, mapBlendModeToSkia, mark, measureSpring, measureText, measureTextAsync, mergeAnimationTracks, mixAudioTracks, mixColors, mixSfxInto, normalizeColorStops, normalizeTextSpans, oklabToRgb, parseColorToRgb, parseCssColor, parseCssGradient, parseFBX, parseMTL, parseMarkup, parseOBJ, parseSvgPathToCubicBeziers, pinNodeToLandmark, pinNodeToObject, preloadCompositionFonts, preloadFont, programmaticSignal, progressSignal, registerSignalDependency, renderFrameGrid, renderSemaphore, renderSfx, reportRenderDiagnostic, requiresIsolation, resetRenderCaches, resolveBoolean, resolveEnvironment, resolveMediaMimeType, resolveNumber, resolveParam, resolveSamplePoints, resolveString, rgbToOklab, rich, sampleAudioSpectrumScale, sampleColorStops, sampleCurveGeometry, samplePathChainGeometry, section, serializeSignalData, shaderStore, signal, signalBuilder, signalRegistry, sineSignal, size, softLimit, span, spring, springCalculation, stampResult, startPreview, sub, sup, takeRenderDiagnostics, textureCache, timeSignal, updateClockSignals, waveToCubicBeziers };
|
|
6258
|
+
export { AETextAnimatorSchema, AdvancedTextAnimatorBuilder, AdvancedTextAnimatorSchema, AnchorGroupingSchema, AnchorPresetSchema, AnimatableGlyphPropsSchema, AnimationTypeSchema, AnyOutputUnionSchema, ApplyLUT, Ascii, AudioCompressor, AudioDelay, AudioExtractorSignalDataSchema, AudioFade, AudioLatentEngine, AudioLatentTrackCache, AudioMeshDeformPipeline, AudioNoiseGate, AudioParametricEq, AudioReverb, AudioSignal, AudioSignalExtractor, AudioSpectrumTypographySchema, BaseMediaOperationPropsSchema, BaseVideoLayerPropsSchema, BloomConfigSchema, Blur, BoxNodeSchema, CAPTION_LAYER_DEFAULTS, COCO17_BONES, COCO17_KEYPOINTS, COCO17_KEYPOINT_NAMES, COCO_CLASSES, COMPOSITE_OPERATIONS, COMPOSITOR_DEFAULTS, CRT, Camera3D, CameraAnimation, CameraNodeSchema, CameraParallax3D, CannyComputePipeline, CarrierHandoffConfigSchema, ChannelMerger, ChannelSplitter, ChromaticGlitchConfigSchema, ClipVision, Color, ColorBalance, ColorKey, ColorStopSchema, Composition, CompositorProgramSchema, ConfigHandleSchema, ConfigSchemaRegistry, Contour, ControlNetMultiplexer, CornerPin, Crop, CubicBezierSegmentSchema, Curves, CustomEffect, DATA_TYPE_EXTENSIONS, DEFAULT_CHART_COLORS, DEFAULT_DURATION_MS, DEFAULT_FPS, DEFAULT_MEDIA_DIMENSION, DataType, DataTypeVal, DataTypes, Datamosh, DepthNormalsComputePipeline, DepthOfField, DisplacementMap, Dither, DynamicStrokePropertiesSchema, ENVIRONMENT_PRESETS, Easing, Effect, EffectPipeline, EnhancedTextNodeSchema, EnvironmentFogSchema, EnvironmentPresetSchema, ExportResultSchema, ExpressionSelectorSchema, ExtendedGlyphPropertiesSchema, ExtendedLayerSchema, ExtractLUT, FileAssetSchema, FileDataSchema, FilmGrain, FlexNodeSchema, Flip, FontManager, GeneratorSignalDataSchema, GlassPipeline, GlobalCompositeOperation, GlyphAnchorGroupingSchema, GlyphAnchorPointSchema, Gooey, Gradient, GradientMap, HalftoneScreen, HandleTypeVal, HeadlessMediaRenderer, HeadlessWebGPURenderer, HighPass, Iridescence, JsonValueSchema, Kaleidoscope, KenBurns, KineticMarqueeConfigSchema, LIGHT_UNIFORM_SIZE_BYTES, Layer, Layer3D, LayerAnimation, LayerStyle, LayoutNodeSchema, Levels, Light, Light3D, LightNodeSchema, LightTypeSchema, Lighting, LineSpacingDynamicsSchema, Liquify, LongShadowConfigSchema, MAX_GRADIENT_STOPS, MAX_LIGHTS, MIME_TYPES, MaskMath, MaterialTypeSchema, Matrix4Math, MatteModeSchema, Media, MediaMetadataSchema, MediaNodeSchema, MediaOperationSchema, MediaSourceSchema, MeshWarp, Model3DNodeSchema, ModerationError, ModerationUnrefundableError, Modulate, MotionBlur, MotionBlurPipeline, MultiOutputGenericSchema, NodeMetadataSchema, NodeResultSchema, NodeVisualConfigSchema, NoiseGenerator, OpArt, OpticalFlowComputePipeline, OutputItemSchema, PBRGlass, Paint, Painterly, ParticleDissolveConfigSchema, Particles, PatchHeal, Path3D, PersonTrack, Physics, PhysicsBody2D, PhysicsBody3D, PhysicsWorld2D, PhysicsWorld3D, Pixelate, PoseSkeletonRenderer, Posterize, ProceduralVFX, ProgrammaticSignal, RGBSplit, RefineEdge, RefractionCaustics3D, Relight3D, Renderer2D, Renderer3D, ResizerScaler, Rotate, SSAO, SSAOPipeline, Scene, SceneDefinitionSchema, SceneTransitionTypeSchema, SegmentationTexturePool, SelectiveColor, SelectorBasedOnSchema, SelectorModeSchema, SelectorShapeSchema, SelectorUnitsSchema, Sequence, ShadowPipeline, ShadowsHighlights, ShapeNodeSchema, Signal, SignalChannelMetaSchema, SignalDataSchema, SignalGateDataSchema, SignalMathDataSchema, SignalStatsSchema, SingleOutputGenericSchema, SkewPropertiesSchema, SlugFontCache, SlugPipeline, SourceOperationSchema, Spatial3DPropertiesSchema, SpatialLandmarkTransformer, SpringPhysicsSchema, StereoPanning, Styles, TEXT_LAYER_DEFAULTS, TemporalDeflicker, TemporalDeflickerPipeline, TemporalObjectTracker, TensorPipeline, TextAnchorConfigSchema, TextAnimator, TextAnimatorBuilder, TextAnimatorSchema, TextCursorConfigSchema, TextCursorStyleSchema, TextNodeSchema, TextOperationSchema, TextPathBuilder, TextPathOptionsSchema, TextPathSourceSchema, TextRangeSelectorSchema, TextScrambleConfigSchema, TextSelectorSchema, TextSignalSelectorSchema, TextSpanMarkSchema, TextSpanSchema, TextWigglySelectorSchema, TileOffset, TimelineSegmentSchema, TimelineTransformSchema, ToneMappingSchema, Toon, Trails, Transition, TransitionSchema, TransitionTypeSchema, TypewriterAnimatorSchema, TypingCadenceSchema, UnsharpMask, VariableFontAxesSchema, Vec2Schema, Vector3Math, VideoAnimationSchema, VideoLayerTypeSchema, VideoSpatialPropsSchema, VideoStylePropsSchema, VideoTextPropsSchema, VideoTimingPropsSchema, Vignette, VirtualMediaDataSchema, Vision, VisionBundle, VisionNode, VisionRunner, VolumetricFormationConfigSchema, VolumetricFormationModeSchema, accent, analyzeSequence, appendOperation, applyStaggerToChildren, bezier, blurRegion, bold, buildPathLUT, calculateSequenceTimeline, checkNodeResultCache, code, collectNodeOps, color, compositorToProgram, computeAnchorPivotOffset, computeCarrierContinuity, computeGridLayout, computeLayout, computeLayoutSync, computeLineLeadingDisplacement, computeRangeSelectorWeight, computeStaggerDelay, computeStrokeRect, computeVideoCropRenderProps, computed, cosineSignal, createOutputItemSchema, createVirtualMedia, dataTypeColors, describeGpu, deserializeSignalData, deterministicWigglyNoise, distanceToCurveParameter, editTrackedRegion, effectFactories, effectMeta, ellipseToCubicBeziers, encodeStereoWav, ensureDevice, evaluateAnimationAtFrame, evaluateCubicBezier, evaluateCubicBezierDerivative, evaluateExpressionSelector, evaluateMarqueeOffset, evaluateSelectorCombination, evaluateSkewMatrix, evaluateSpringProgress, evaluateTrackAtFrame, evaluateTypewriterState, evaluateVolumetricFormation, extractSvgDimensions, findSourceAsset, formatHex, formatQaReport, frameArraySignal, frameSignal, generateArcLengthLUT, generateId, generatePathSegments, getActiveMediaMetadata, getFileDataMetadata, getFingerprint, getLeafMediaType, getMediaType, getRenderFingerprint, getScramblePool, getShuffledOrder, gradientStopsToOklab, hasOnlySingleSource, hoistMattes, inputStore, interpolate, isCssGradient, isEqual, isSignal, isVirtualMediaData, italic, limitGradientStops, lintComposition, loadFBX, loadModel3D, loadOBJ, lutStore, mapBlendModeToSkia, mark, measureSpring, measureText, measureTextAsync, mergeAnimationTracks, mixAudioTracks, mixColors, mixSfxInto, normalizeColorStops, normalizeTextSpans, oklabToRgb, parseColorToRgb, parseCssColor, parseCssGradient, parseFBX, parseMTL, parseMarkup, parseOBJ, parseSvgPathToCubicBeziers, pinNodeToLandmark, pinNodeToObject, preloadCompositionFonts, preloadFont, programmaticSignal, progressSignal, registerSignalDependency, renderFrameGrid, renderSemaphore, renderSfx, reportRenderDiagnostic, requiresIsolation, resetRenderCaches, resolveBoolean, resolveEnvironment, resolveMediaMimeType, resolveNumber, resolveParam, resolveSamplePoints, resolveString, rgbToOklab, rich, sampleAudioSpectrumScale, sampleColorStops, sampleCurveGeometry, samplePathChainGeometry, section, serializeSignalData, shaderStore, signal, signalBuilder, signalRegistry, sineSignal, size, softLimit, span, spring, springCalculation, stampResult, startPreview, sub, sup, takeRenderDiagnostics, textureCache, timeSignal, updateClockSignals, waveToCubicBeziers };
|
|
3220
6259
|
//# sourceMappingURL=index.mjs.map
|