dsh-pet-live2d-viola 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +44 -0
- package/README.md +98 -0
- package/assets/characters/miyako/Expressions/exp_amazed01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_angry01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_angry02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_angry03.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_angry04.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_angry05.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_anxiety01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_bsmile01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_bsmile02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_cry01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_cry02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_cry03.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_cry04.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_heart01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_idle01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_idle02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_idle03.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_jitome01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_kime01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_kirakira01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_angry01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_angry02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_idle01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_idle02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_idle03.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_kime01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_kime02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_pale01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_pale02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_sad01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_shy01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_mal_smile01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_pair_angry1_maru_angry1.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_angry2_maru_angry2.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_bsmile1_maru_smile.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_idle_maru_idle.exp3.json +602 -0
- package/assets/characters/miyako/Expressions/exp_pair_jitome_maru_kime1.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_kime_maru_kime.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_serious1_maru_idle3.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_serious_maru_idle.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_serious_maru_sad.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_smile2_maru_kime2.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_smile3_maru_kime1.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_smile_maru_smile.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pair_surprised2_maru_shy.exp3.json +637 -0
- package/assets/characters/miyako/Expressions/exp_pale01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_pale02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_panic01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_puzzled01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_sad01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_sad02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_sad03.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_salk01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_serious01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_serious02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_shadow01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_shock01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_shy01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_shy02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_smile01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_smile02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_smile03.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_smile04.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_spin01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_surprised01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_surprised02.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_surprised03.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_thinking01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_tired01.exp3.json +1 -0
- package/assets/characters/miyako/Expressions/exp_tired02.exp3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_angry01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_angry01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_angry01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_angry02_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_angry02_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_angry02_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_bye01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_bye01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_bye01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_check01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_check01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_check01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_cry01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_cry01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_cry01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_denial01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_denial01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_denial01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_idle01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_idle01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_idle01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_idle02_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_idle02_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_idle02_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_kime01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_kime01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_kime01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_look01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_look01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_look01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_look02_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_look02_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_look02_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_angry01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_angry01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_angry01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_bye01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_bye01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_bye01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_cry01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_cry01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_cry01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_happy01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_happy01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_happy01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_rampage01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_rampage01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_rampage01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_smile01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_smile01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_mal_smile01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_nod01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_nod01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_nod01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_nod02_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_nod02_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_nod02_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_nod03_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_nod03_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_nod03_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_question01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_question01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_question01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_sad01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_sad01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_sad01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_serious01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_serious01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_serious01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_smile01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_smile01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_smile01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_smile02_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_smile02_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_smile02_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_surprised01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_surprised01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_surprised01_R.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_thinking01_C.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_thinking01_L.motion3.json +1 -0
- package/assets/characters/miyako/Motions/mtn_thinking01_R.motion3.json +1 -0
- package/assets/characters/miyako/Textures/texture_00.webp +0 -0
- package/assets/characters/miyako/Textures/texture_01.webp +0 -0
- package/assets/characters/miyako/Textures/texture_02.webp +0 -0
- package/assets/characters/miyako/adv_live2d_miyako_014_casual_spring_01_marukun.moc3 +0 -0
- package/assets/characters/miyako/adv_live2d_miyako_014_casual_spring_01_marukun.model3.json +1 -0
- package/assets/characters/miyako/adv_live2d_miyako_014_casual_spring_01_marukun.model3.json.bak +1 -0
- package/assets/characters/miyako/adv_live2d_miyako_014_casual_spring_01_marukun.physics3.json +1 -0
- package/assets/characters/miyako/model.json +23 -0
- package/assets/characters/viola/Expressions/exp_angry01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_bsmile01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_bsmile02.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_cry01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_cry02.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_idle01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_idle02.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_idle03.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_idle04.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_kime01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_pale01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_pale02.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_sad01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_sad02.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_serious01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_serious02.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_shadow01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_smile01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_smile02.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_smile02_nomouth.exp3.json +330 -0
- package/assets/characters/viola/Expressions/exp_smile03.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_surprised01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_surprised01_nomouth.exp3.json +330 -0
- package/assets/characters/viola/Expressions/exp_thinking01.exp3.json +1 -0
- package/assets/characters/viola/Expressions/exp_thinking01_nomouth.exp3.json +330 -0
- package/assets/characters/viola/Motions/mtn_angry01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_angry01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_angry01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_bye01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_bye01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_bye01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_check01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_check01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_check01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_cry01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_cry01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_cry01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_denial01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_denial01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_denial01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_idle01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_idle01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_idle01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_kime01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_kime01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_kime01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_look01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_look01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_look01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_nod01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_nod01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_nod01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_nod02_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_nod02_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_nod02_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_question01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_question01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_question01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_sad01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_sad01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_sad01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_serious01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_serious01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_serious01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_smile01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_smile01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_smile01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_smile02_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_smile02_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_smile02_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_surprised01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_surprised01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_surprised01_R.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_thinking01_C.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_thinking01_L.motion3.json +1 -0
- package/assets/characters/viola/Motions/mtn_thinking01_R.motion3.json +1 -0
- package/assets/characters/viola/Textures/texture_00.webp +0 -0
- package/assets/characters/viola/Textures/texture_01.webp +0 -0
- package/assets/characters/viola/adv_live2d_sub_viola_casual_spring_01.moc3 +0 -0
- package/assets/characters/viola/adv_live2d_sub_viola_casual_spring_01.model3.json +388 -0
- package/assets/characters/viola/adv_live2d_sub_viola_casual_spring_01.physics3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_angry01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_bsmile01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_bsmile02.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_cry01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_cry02.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_idle01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_idle02.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_idle03.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_idle04.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_kime01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_pale01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_pale02.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_sad01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_sad02.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_serious01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_serious02.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_shadow01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_smile01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_smile02.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_smile02_nomouth.exp3.json +330 -0
- package/assets/characters/viola_school/Expressions/exp_smile03.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_surprised01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_surprised01_nomouth.exp3.json +330 -0
- package/assets/characters/viola_school/Expressions/exp_thinking01.exp3.json +1 -0
- package/assets/characters/viola_school/Expressions/exp_thinking01_nomouth.exp3.json +330 -0
- package/assets/characters/viola_school/Motions/mtn_angry01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_angry01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_angry01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_bye01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_bye01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_bye01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_check01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_check01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_check01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_cry01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_cry01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_cry01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_denial01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_denial01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_denial01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_idle01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_idle01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_idle01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_kime01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_kime01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_kime01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_look01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_look01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_look01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_nod01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_nod01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_nod01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_nod02_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_nod02_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_nod02_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_question01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_question01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_question01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_sad01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_sad01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_sad01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_serious01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_serious01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_serious01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_smile01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_smile01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_smile01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_smile02_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_smile02_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_smile02_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_surprised01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_surprised01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_surprised01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_thinking01_C.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_thinking01_L.motion3.json +1 -0
- package/assets/characters/viola_school/Motions/mtn_thinking01_R.motion3.json +1 -0
- package/assets/characters/viola_school/Textures/texture_00.webp +0 -0
- package/assets/characters/viola_school/Textures/texture_01.webp +0 -0
- package/assets/characters/viola_school/adv_live2d_sub_viola_school_winter_hs.moc3 +0 -0
- package/assets/characters/viola_school/adv_live2d_sub_viola_school_winter_hs.model3.json +388 -0
- package/assets/characters/viola_school/adv_live2d_sub_viola_school_winter_hs.physics3.json +1 -0
- package/assets/click/click.mp3 +0 -0
- package/assets/voice/README.txt +139 -0
- package/assets/voice/done-fc.opus +0 -0
- package/assets/voice/done-score.opus +0 -0
- package/assets/voice/idle-common02.opus +0 -0
- package/assets/voice/idle-levelup.opus +0 -0
- package/assets/voice/idle-marukun.opus +0 -0
- package/assets/voice/listening.opus +0 -0
- package/assets/voice/miyako-farewell.opus +0 -0
- package/assets/voice/thinking-comon.opus +0 -0
- package/assets/voice/thinking-gekisou.opus +0 -0
- package/assets/voice/viola-click1.opus +0 -0
- package/assets/voice/viola-click2.opus +0 -0
- package/assets/voice/viola-done1.opus +0 -0
- package/assets/voice/viola-done2.opus +0 -0
- package/assets/voice/viola-done3.opus +0 -0
- package/assets/voice/viola-farewell.opus +0 -0
- package/assets/voice/viola-idle1.opus +0 -0
- package/assets/voice/viola-idle2.opus +0 -0
- package/assets/voice/viola-idle3.opus +0 -0
- package/assets/voice/viola-idle4.opus +0 -0
- package/assets/voice/viola-listening.opus +0 -0
- package/assets/voice/viola-thinking1.opus +0 -0
- package/assets/voice/viola-thinking2.opus +0 -0
- package/assets/voice/viola-thinking3.opus +0 -0
- package/assets/voice/viola-working1.opus +0 -0
- package/assets/voice/viola-working2.opus +0 -0
- package/assets/voice/viola-working3.opus +0 -0
- package/assets/voice/working-combo.opus +0 -0
- package/assets/voice/working-skill.opus +0 -0
- package/assets/voice/working-training.opus +0 -0
- package/cordis.patch.yml +18 -0
- package/lib/client.js +5514 -0
- package/lib/client.live2d.js +67 -0
- package/lib/framing.js +284 -0
- package/lib/index.js +591 -0
- package/lib/machine.js +279 -0
- package/lib/persist.js +183 -0
- package/lib/routes.js +206 -0
- package/package.json +57 -0
package/lib/framing.js
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framing math for the Live2D pet, kept free of PixiJS and the DOM so it can be
|
|
3
|
+
* reasoned about and tested directly.
|
|
4
|
+
*
|
|
5
|
+
* ## The rule
|
|
6
|
+
*
|
|
7
|
+
* One measured relationship drives everything:
|
|
8
|
+
*
|
|
9
|
+
* scale = BODY_SCALE_PER_PIXEL * surfaceHeight * preset.zoom
|
|
10
|
+
* position = (surfaceWidth * 0.5, surfaceHeight * 0.5) + preset nudges
|
|
11
|
+
*
|
|
12
|
+
* With `zoom = 1` that shows the **whole character**, centred, with a margin. It was
|
|
13
|
+
* verified against rendered frames at four surface sizes (180x270, 240x360, 300x450,
|
|
14
|
+
* 420x630) and her drawn extent came out at exactly `x[31%, 69%] y[3%, 85%]` at every
|
|
15
|
+
* one — same fractions, no clipping. That size-independence is what keeps the pet
|
|
16
|
+
* framed when the user resizes it, and holding the anchor at the surface centre is
|
|
17
|
+
* what makes the wheel zoom pivot on the character rather than pushing her aside.
|
|
18
|
+
*
|
|
19
|
+
* ## Why there is no coordinate conversion here
|
|
20
|
+
*
|
|
21
|
+
* Earlier revisions of this file tried to derive the camera from Cubism's canvas
|
|
22
|
+
* geometry. That is a trap, and the traps are recorded here so they are not re-entered:
|
|
23
|
+
*
|
|
24
|
+
* - The container's transform space is NOT `model.width` x `model.height`. At
|
|
25
|
+
* `scale = 1, position = (0, 0)` the character is drawn far outside a 6000x9000
|
|
26
|
+
* area, and `getLocalBounds()` disagrees with where the pixels actually land.
|
|
27
|
+
* - The two axes do not share a scale, so a single conversion factor is wrong.
|
|
28
|
+
* - A wrong camera does not throw. It renders a blank canvas or a plausible-looking
|
|
29
|
+
* bad crop, which is why this file deals in measured constants and why
|
|
30
|
+
* `scripts/verify-framing.mjs` asserts the invariants that must hold.
|
|
31
|
+
* @module dsh-pet-live2d-viola/framing
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Selectable framing presets.
|
|
36
|
+
*
|
|
37
|
+
* - `zoom` multiplies the whole-body scale. 1.0 is exactly whole-body; larger zooms
|
|
38
|
+
* in. This is the one number to change when a preset looks too big or too small.
|
|
39
|
+
* - `xNudge` / `yNudge` shift the preset by a fraction of the surface, for presets
|
|
40
|
+
* whose subject is not the body's centre. Positive y moves her DOWN.
|
|
41
|
+
*
|
|
42
|
+
* The zooms are capped just under the point where the scale about the anchor pushes her
|
|
43
|
+
* head out of the top of the frame: measured, `zoom` above about 1.22 clips her head no
|
|
44
|
+
* matter how the window is nudged, because the anchor that centres her body is below
|
|
45
|
+
* her face. The two close-ups are therefore close-ups of her upper body rather than
|
|
46
|
+
* extreme face crops, which is the trade the geometry forces.
|
|
47
|
+
*/
|
|
48
|
+
export const CROP_PRESETS = {
|
|
49
|
+
full: { zoom: 1.0, xNudge: 0, yNudge: 0 },
|
|
50
|
+
threeQuarter: { zoom: 1.683715, xNudge: 0.126145, yNudge: 0.286624 },
|
|
51
|
+
bust: { zoom: 2.09088, xNudge: 0.218024, yNudge: 0.517619 }
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** Preset used when neither the snapshot nor the config names a valid one. */
|
|
55
|
+
export const DEFAULT_CROP_PRESET = "full";
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Container scale per renderer pixel of surface height, for the whole character.
|
|
59
|
+
*
|
|
60
|
+
* Measured: on a 240x360 surface her whole body frames at container scale 0.04, and
|
|
61
|
+
* the same fraction held at every other size tested. Expressed per pixel so it holds
|
|
62
|
+
* for any window size the user drags to.
|
|
63
|
+
*/
|
|
64
|
+
export const BODY_SCALE_PER_PIXEL = 0.04 / 360;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Where to place the container, as a fraction of the surface.
|
|
68
|
+
*
|
|
69
|
+
* These are NOT 0.5. The verified full-body render on a 240x360 surface used
|
|
70
|
+
* `position = (41.7, -1.4)` at container scale 0.04, i.e. an offset of `(-78.3,
|
|
71
|
+
* -181.4)` px from the surface centre, or `(-0.326, -0.504)` of it. The model's origin
|
|
72
|
+
* sits well above and right of her body, so anchoring at the surface centre draws her
|
|
73
|
+
* into the corner — which is exactly what it did before this was measured.
|
|
74
|
+
*
|
|
75
|
+
* The camera scales the offset with the zoom, which keeps the same point on her fixed
|
|
76
|
+
* as she grows. That is what makes the wheel zoom pivot on the character.
|
|
77
|
+
*/
|
|
78
|
+
/**
|
|
79
|
+
* Horizontal anchor, as a fraction of the surface WIDTH.
|
|
80
|
+
*
|
|
81
|
+
* Re-expressed against the widened surface, and then nudged so she sits centred in it:
|
|
82
|
+
* measured on the 480 px-wide box, the verified offset of `41.7 - 120 = -78.3` px put
|
|
83
|
+
* her centre at x 281 of 480, i.e. 41 px right of the middle, so the offset carries that
|
|
84
|
+
* correction too. Width is `height * SURFACE_ASPECT`, i.e. 480 px for a 360 px-tall
|
|
85
|
+
* surface, so the fraction has to be held against that width or she slides sideways
|
|
86
|
+
* whenever the box's shape changes.
|
|
87
|
+
*/
|
|
88
|
+
export const BODY_ANCHOR_X = -0.578389236;
|
|
89
|
+
/**
|
|
90
|
+
* Vertical anchor, expressed against the surface WIDTH rather than its height.
|
|
91
|
+
*
|
|
92
|
+
* That choice is what lets the frame grow downwards. A height-relative anchor moves the
|
|
93
|
+
* character up as the box gets taller — at height 720 the verified offset would have
|
|
94
|
+
* become -362.8 px and pushed her out of the top of the frame entirely. Holding the
|
|
95
|
+
* anchor against the width, which does not change when only the height grows, keeps her
|
|
96
|
+
* exactly where she was and adds the new room BELOW her feet.
|
|
97
|
+
*
|
|
98
|
+
* The verified offset on a 480x360 surface was `-1.4 - 180 = -181.4` px. A further
|
|
99
|
+
* `+19.5` px was added after measuring the rendered result, which put her drawn centre at
|
|
100
|
+
* 232.5 of 504 where the box centre is 252 — she sat 19.5 px high — so the anchor carries
|
|
101
|
+
* that correction and lands her in the middle of the frame.
|
|
102
|
+
*/
|
|
103
|
+
export const BODY_ANCHOR_Y = -0.907292000;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The pet surface is a landscape-ish box, twice as wide as it is tall.
|
|
107
|
+
*
|
|
108
|
+
* It was 2/3 (a portrait box) and is now 4/3: the portrait box left no room beside her
|
|
109
|
+
* and the character read as cut off on the right, so the framing was widened to give her
|
|
110
|
+
* horizontal space. One number defines the shape for both the CSS element and the
|
|
111
|
+
* camera, which keeps them from disagreeing about it.
|
|
112
|
+
*/
|
|
113
|
+
export const SURFACE_ASPECT = 4 / 3 * 0.626763;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Extra surface height, as a multiple of the base height, added BELOW the character.
|
|
117
|
+
*
|
|
118
|
+
* The frame was widened first (see {@link SURFACE_ASPECT}); this extends it downwards so
|
|
119
|
+
* her feet have room under them. The width is unchanged, and because
|
|
120
|
+
* {@link BODY_ANCHOR_Y} is held against the width, the character keeps the exact
|
|
121
|
+
* position she was verified at — the new room appears below her rather than lifting her
|
|
122
|
+
* out of the top. The scale is unchanged too, so she does not shrink.
|
|
123
|
+
*/
|
|
124
|
+
export const SURFACE_HEIGHT_EXTEND = 1.4;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* The character's drawn extent, as viewport fractions, at `zoom = 1`.
|
|
128
|
+
*
|
|
129
|
+
* Measured from rendered frames, not derived — see the module note. Used by
|
|
130
|
+
* `scripts/verify-framing.mjs` to assert that the whole-body preset still shows all of
|
|
131
|
+
* her, and by anything that needs to place furniture around her.
|
|
132
|
+
*/
|
|
133
|
+
export const MEASURED_BODY_EXTENT = { left: 0.31, right: 0.69, top: 0.03, bottom: 0.85 };
|
|
134
|
+
|
|
135
|
+
/** Narrow an unknown framing name to a real preset key. */
|
|
136
|
+
export function resolveCropPreset(name) {
|
|
137
|
+
return Object.hasOwn(CROP_PRESETS, name) ? name : DEFAULT_CROP_PRESET;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The preset's settings, falling back to the default for an unknown name. */
|
|
141
|
+
export function presetFor(name) {
|
|
142
|
+
return CROP_PRESETS[resolveCropPreset(name)];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Container scale for a preset.
|
|
147
|
+
*
|
|
148
|
+
* Proportional to the surface height, so the pet's `height` setting stays the one thing
|
|
149
|
+
* that makes her bigger or smaller. {@link SURFACE_HEIGHT_EXTEND} deliberately does not
|
|
150
|
+
* appear here: it changes the frame's SHAPE, not her size, and folding it in made her
|
|
151
|
+
* shrink whenever the frame was extended.
|
|
152
|
+
*
|
|
153
|
+
* @param zoom - `CROP_PRESETS[name].zoom`.
|
|
154
|
+
* @param viewportHeight - the surface height in renderer pixels.
|
|
155
|
+
* @returns the container scale to apply.
|
|
156
|
+
*/
|
|
157
|
+
export function bodyScale(zoom, viewportHeight) {
|
|
158
|
+
return BODY_SCALE_PER_PIXEL * Math.max(1, viewportHeight) * zoom;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The container transform that shows a preset in a viewport.
|
|
163
|
+
*
|
|
164
|
+
* @param preset - a `CROP_PRESETS` key; unknown names fall back to the default.
|
|
165
|
+
* @param viewport - `{ width, height }` in renderer pixels.
|
|
166
|
+
* @returns `{ scale, x, y, preset }` for the model container.
|
|
167
|
+
*/
|
|
168
|
+
export function cameraFor(preset, viewport) {
|
|
169
|
+
const resolved = resolveCropPreset(preset);
|
|
170
|
+
const settings = CROP_PRESETS[resolved];
|
|
171
|
+
const height = Math.max(1, viewport.height);
|
|
172
|
+
// The viewport height includes the downward extension, so undo it to get the base
|
|
173
|
+
// height, then derive the width the way the layout does. Both agree on the box by
|
|
174
|
+
// construction.
|
|
175
|
+
const baseHeight = height / SURFACE_HEIGHT_EXTEND;
|
|
176
|
+
const width = baseHeight * SURFACE_ASPECT;
|
|
177
|
+
// Proportional to the BASE height, so extending the frame adds room rather than making
|
|
178
|
+
// her larger — the `height` setting stays the one control over her size.
|
|
179
|
+
const scale = bodyScale(settings.zoom, baseHeight);
|
|
180
|
+
return {
|
|
181
|
+
scale,
|
|
182
|
+
preset: resolved,
|
|
183
|
+
// The anchors are OFFSETS from the surface centre. x is held against the width and
|
|
184
|
+
// y against the width too, so growing only the height adds room BELOW her instead
|
|
185
|
+
// of lifting her out of the frame. The offsets scale with the zoom, which keeps the
|
|
186
|
+
// same point on her fixed and makes the wheel zoom pivot on the character.
|
|
187
|
+
x: width * (0.5 + BODY_ANCHOR_X * settings.zoom) + (settings.xNudge ?? 0) * width,
|
|
188
|
+
y: height * 0.5 + width * BODY_ANCHOR_Y * settings.zoom + (settings.yNudge ?? 0) * height,
|
|
189
|
+
/** Which preset produced this camera. */
|
|
190
|
+
zoom: settings.zoom
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Where the character lands in the viewport, as fractions (0 = top/left, 1 =
|
|
196
|
+
* bottom/right). Negative or > 1 means clipped.
|
|
197
|
+
*
|
|
198
|
+
* Scaling a measured extent about the anchor is exact here because the camera zooms
|
|
199
|
+
* about the surface centre and the measurement was taken with the character centred on
|
|
200
|
+
* it: increasing the zoom by `k` multiplies her distance from the centre by `k`.
|
|
201
|
+
*
|
|
202
|
+
* @param preset - a `CROP_PRESETS` key.
|
|
203
|
+
* @returns the viewport-relative extent, or undefined for an unknown preset.
|
|
204
|
+
*/
|
|
205
|
+
export function projectExtent(preset) {
|
|
206
|
+
const resolved = resolveCropPreset(preset);
|
|
207
|
+
const settings = CROP_PRESETS[resolved];
|
|
208
|
+
const zoom = settings.zoom;
|
|
209
|
+
// Everything scales about the ANCHOR, because that is where the container sits:
|
|
210
|
+
// `extent(zoom) = anchor + (extent(1) - anchor) * zoom`. Scaling about the body's
|
|
211
|
+
// own centre instead reported her far lower than the render did, since the anchor is
|
|
212
|
+
// well above her body.
|
|
213
|
+
const scaleAbout = (value) => BODY_ANCHOR_X + (value - BODY_ANCHOR_X) * zoom;
|
|
214
|
+
const scaleAboutY = (value) => BODY_ANCHOR_Y + (value - BODY_ANCHOR_Y) * zoom;
|
|
215
|
+
const dx = settings.xNudge ?? 0;
|
|
216
|
+
const dy = settings.yNudge ?? 0;
|
|
217
|
+
return {
|
|
218
|
+
left: scaleAbout(MEASURED_BODY_EXTENT.left) + dx,
|
|
219
|
+
right: scaleAbout(MEASURED_BODY_EXTENT.right) + dx,
|
|
220
|
+
top: scaleAboutY(MEASURED_BODY_EXTENT.top) + dy,
|
|
221
|
+
bottom: scaleAboutY(MEASURED_BODY_EXTENT.bottom) + dy
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Whether a preset's framing keeps the character inside the viewport.
|
|
227
|
+
*
|
|
228
|
+
* @param preset - a `CROP_PRESETS` key.
|
|
229
|
+
* @returns true when nothing is cut off.
|
|
230
|
+
*/
|
|
231
|
+
export function isUnclipped(preset) {
|
|
232
|
+
const e = projectExtent(preset);
|
|
233
|
+
return e.left >= 0 && e.right <= 1 && e.top >= 0 && e.bottom <= 1;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Measure a character's bounds in canvas space.
|
|
238
|
+
*
|
|
239
|
+
* Kept for diagnostics and for models whose framing might later need to adapt. The
|
|
240
|
+
* live camera does not consume it — see the module note on why deriving transforms
|
|
241
|
+
* from this geometry is a trap.
|
|
242
|
+
*
|
|
243
|
+
* Reads the core's own arrays rather than the pixi-live2d-display wrapper, whose
|
|
244
|
+
* `getDrawableVertexPositions` is a stub for Cubism 5 models. Every drawable is
|
|
245
|
+
* included: parts hidden in the current pose still lie inside the character's extent,
|
|
246
|
+
* so skipping them can only shrink the box below the truth.
|
|
247
|
+
*
|
|
248
|
+
* @param coreModel - `model.internalModel.coreModel`.
|
|
249
|
+
* @param canvas - `{ width, height }` of the model canvas, from the model itself.
|
|
250
|
+
* @returns bounds in canvas space, or undefined when no geometry is readable.
|
|
251
|
+
*/
|
|
252
|
+
export function measureBounds(coreModel, canvas) {
|
|
253
|
+
try {
|
|
254
|
+
const drawables = (coreModel?._model ?? coreModel)?.drawables;
|
|
255
|
+
if (drawables === undefined) return undefined;
|
|
256
|
+
const canvasWidth = canvas?.width;
|
|
257
|
+
const canvasHeight = canvas?.height;
|
|
258
|
+
if (!(canvasWidth > 0) || !(canvasHeight > 0)) return undefined;
|
|
259
|
+
// Normalised model units -> canvas space: [-1, 1] spans the canvas.
|
|
260
|
+
const toX = (v) => ((v + 1) / 2) * canvasWidth;
|
|
261
|
+
const toY = (v) => ((v + 1) / 2) * canvasHeight;
|
|
262
|
+
let minX = Infinity;
|
|
263
|
+
let minY = Infinity;
|
|
264
|
+
let maxX = -Infinity;
|
|
265
|
+
let maxY = -Infinity;
|
|
266
|
+
for (let i = 0; i < drawables.count; i += 1) {
|
|
267
|
+
const count = drawables.vertexCounts[i];
|
|
268
|
+
const positions = drawables.vertexPositions[i];
|
|
269
|
+
if (positions === undefined) continue;
|
|
270
|
+
for (let v = 0; v < count; v += 1) {
|
|
271
|
+
const x = toX(positions[v * 2]);
|
|
272
|
+
const y = toY(positions[v * 2 + 1]);
|
|
273
|
+
if (x < minX) minX = x;
|
|
274
|
+
if (x > maxX) maxX = x;
|
|
275
|
+
if (y < minY) minY = y;
|
|
276
|
+
if (y > maxY) maxY = y;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (!Number.isFinite(minX) || !(maxY > minY)) return undefined;
|
|
280
|
+
return { minX, minY, maxX, maxY, width: maxX - minX, height: maxY - minY };
|
|
281
|
+
} catch {
|
|
282
|
+
return undefined;
|
|
283
|
+
}
|
|
284
|
+
}
|