1import { r as reactExports, j as jsxRuntimeExports } from "./vendor-react-Bay9Y7Lb.js"; 2import { s as synth } from "./synth-BE1TSTD4.js"; 3import { s as solve2BoneIK } from "./IkMath-D6fi1ppd.js"; 4import { g as gsapWithCSS } from "./vendor-gsap-50Lx19ak.js"; 5import { u as useAnimationFrame, m as motion } from "./vendor-framer-C8eEgkdL.js"; 6const family_0_pianosSVG = ` 7 <!-- Legs --> 8 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 9 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#5D4037" /> 10 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 11 </g> 12 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 13 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#5D4037" /> 14 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 15 </g> 16 <!-- Torso & Head --> 17 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 18 <g id="body"> 19 <!-- Torso --> 20 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#5D4037" /> 21 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#D7CCC8" /> 22 <!-- Head --> 23 <g id="head" transform="translate(0, -60)"> 24 <circle cx="-15" cy="-30" r="8" fill="#5D4037" /><circle cx="15" cy="-30" r="8" fill="#5D4037" /> 25 <circle cx="0" cy="-15" r="22" fill="#5D4037" /> 26 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 27 <circle cx="0" cy="-5" r="8" fill="#D7CCC8" /><path d="M0,-8 L0,-5 M-3,-5 Q0,-2 3,-5" stroke="#000" stroke-width="1.5" fill="none"/> 28 </g> 29 </g> 30 </g>
31 <g id="mount_front_body"></g> 32 <!-- Left Arm Hierarchy --> 33 <g id="shoulder_l" opacity="0"></g> 34 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#5D4037" /></g> 35 <g id="elbow_l" opacity="0"></g> 36 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#5D4037" /></g> 37 <g id="hand_l" stroke="#333" stroke-width="2"> 38 <circle cx="0" cy="0" r="8" fill="#D7CCC8" /> 39 <!-- Dynamic Instrument Injection Point --> 40 <g id="instrument" transform="translate(0,0)"></g> 41 </g> 42 <!-- Right Arm Hierarchy --> 43 <g id="shoulder_r" opacity="0"></g> 44 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#5D4037" /></g> 45 <g id="elbow_r" opacity="0"></g> 46 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#5D4037" /></g> 47 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#D7CCC8" /></g> 48`; 49const family_1_chromatic_percussionSVG = ` 50 <!-- Legs --> 51 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 52 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#E64A19" /> 53 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 54 </g> 55 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 56 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#E64A19" /> 57 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 58 </g> 59 <!-- Torso & Head --> 60 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 61 <g id="body"> 62 <!-- Torso --> 63 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#E64A19" /> 64 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFFFFF" /> 65 <!-- Head --> 66 <g id="head" transform="translate(0, -60)"> 67 <polygon points="-10,-20 -25,-40 -20,-15" fill="#E64A19" /><polygon points="10,-20 25,-40 20,-15" fill="#E64A19" /> 68 <circle cx="0" cy="-15" r="22" fill="#E64A19" /> 69 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 70 <polygon points="-15,-5 15,-5 0,10" fill="#FFFFFF" /><circle cx="0" cy="8" r="3" fill="#000" /> 71 </g> 72 </g> 73 </g>
74 <g id="mount_front_body"></g> 75 <!-- Left Arm Hierarchy --> 76 <g id="shoulder_l" opacity="0"></g> 77 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#E64A19" /></g> 78 <g id="elbow_l" opacity="0"></g> 79 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#E64A19" /></g> 80 <g id="hand_l" stroke="#333" stroke-width="2"> 81 <circle cx="0" cy="0" r="8" fill="#FFFFFF" /> 82 <!-- Dynamic Instrument Injection Point --> 83 <g id="instrument" transform="translate(0,0)"></g> 84 </g> 85 <!-- Right Arm Hierarchy --> 86 <g id="shoulder_r" opacity="0"></g> 87 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#E64A19" /></g> 88 <g id="elbow_r" opacity="0"></g> 89 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#E64A19" /></g> 90 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFFFFF" /></g> 91`; 92const family_2_organsSVG = ` 93 <!-- Legs --> 94 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 95 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#FBC02D" /> 96 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 97 </g> 98 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 99 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#FBC02D" /> 100 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 101 </g> 102 <!-- Torso & Head --> 103 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 104 <g id="body"> 105 <!-- Torso --> 106 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#FBC02D" /> 107 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFF9C4" /> 108 <!-- Head --> 109 <g id="head" transform="translate(0, -60)"> 110 <circle cx="0" cy="-15" r="30" fill="#E65100" /> 111 <circle cx="0" cy="-15" r="22" fill="#FBC02D" /> 112 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 113 <circle cx="0" cy="-5" r="10" fill="#FFF9C4" /><circle cx="0" cy="-8" r="3" fill="#000" /> 114 </g> 115 </g> 116 </g>
117 <g id="mount_front_body"></g> 118 <!-- Left Arm Hierarchy --> 119 <g id="shoulder_l" opacity="0"></g> 120 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#FBC02D" /></g> 121 <g id="elbow_l" opacity="0"></g> 122 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#FBC02D" /></g> 123 <g id="hand_l" stroke="#333" stroke-width="2"> 124 <circle cx="0" cy="0" r="8" fill="#FFF9C4" /> 125 <!-- Dynamic Instrument Injection Point --> 126 <g id="instrument" transform="translate(0,0)"></g> 127 </g> 128 <!-- Right Arm Hierarchy --> 129 <g id="shoulder_r" opacity="0"></g> 130 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#FBC02D" /></g> 131 <g id="elbow_r" opacity="0"></g> 132 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#FBC02D" /></g> 133 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFF9C4" /></g> 134`; 135const family_3_guitarsSVG = ` 136 <!-- Legs --> 137 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 138 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#78909C" /> 139 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 140 </g> 141 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 142 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#78909C" /> 143 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 144 </g> 145 <!-- Torso & Head --> 146 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 147 <g id="body"> 148 <!-- Torso --> 149 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#78909C" /> 150 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#CFD8DC" /> 151 <!-- Head --> 152 <g id="head" transform="translate(0, -60)"> 153 <ellipse cx="-25" cy="-10" rx="15" ry="25" fill="#78909C" /><ellipse cx="25" cy="-10" rx="15" ry="25" fill="#78909C" /> 154 <circle cx="0" cy="-15" r="22" fill="#78909C" /> 155 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 156 <path d="M-5,-5 L5,-5 L3,20 Q0,25 -5,15 Z" fill="#78909C" /> 157 </g> 158 </g> 159 </g>
160 <g id="mount_front_body"></g> 161 <!-- Left Arm Hierarchy --> 162 <g id="shoulder_l" opacity="0"></g> 163 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#78909C" /></g> 164 <g id="elbow_l" opacity="0"></g> 165 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#78909C" /></g> 166 <g id="hand_l" stroke="#333" stroke-width="2"> 167 <circle cx="0" cy="0" r="8" fill="#CFD8DC" /> 168 <!-- Dynamic Instrument Injection Point --> 169 <g id="instrument" transform="translate(0,0)"></g> 170 </g> 171 <!-- Right Arm Hierarchy --> 172 <g id="shoulder_r" opacity="0"></g> 173 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#78909C" /></g> 174 <g id="elbow_r" opacity="0"></g> 175 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#78909C" /></g> 176 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#CFD8DC" /></g> 177`; 178const family_4_bassesSVG = ` 179 <!-- Legs --> 180 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 181 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#212121" /> 182 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 183 </g> 184 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 185 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#212121" /> 186 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 187 </g> 188 <!-- Torso & Head --> 189 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 190 <g id="body"> 191 <!-- Torso --> 192 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#212121" /> 193 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFFFFF" /> 194 <!-- Head --> 195 <g id="head" transform="translate(0, -60)"> 196 197 <circle cx="0" cy="-15" r="22" fill="#212121" /> 198 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 199 <ellipse cx="0" cy="0" rx="15" ry="10" fill="#FFF" /><polygon points="-5,-5 5,-5 0,5" fill="#FFB300" /> 200 </g> 201 </g> 202 </g>
203 <g id="mount_front_body"></g> 204 <!-- Left Arm Hierarchy --> 205 <g id="shoulder_l" opacity="0"></g> 206 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#212121" /></g> 207 <g id="elbow_l" opacity="0"></g> 208 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#212121" /></g> 209 <g id="hand_l" stroke="#333" stroke-width="2"> 210 <circle cx="0" cy="0" r="8" fill="#FFFFFF" /> 211 <!-- Dynamic Instrument Injection Point --> 212 <g id="instrument" transform="translate(0,0)"></g> 213 </g> 214 <!-- Right Arm Hierarchy --> 215 <g id="shoulder_r" opacity="0"></g> 216 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#212121" /></g> 217 <g id="elbow_r" opacity="0"></g> 218 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#212121" /></g> 219 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFFFFF" /></g> 220`; 221const family_5_stringsSVG = ` 222 <!-- Legs --> 223 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 224 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#43A047" /> 225 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 226 </g> 227 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 228 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#43A047" /> 229 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 230 </g> 231 <!-- Torso & Head --> 232 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 233 <g id="body"> 234 <!-- Torso --> 235 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#43A047" /> 236 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#C8E6C9" /> 237 <!-- Head --> 238 <g id="head" transform="translate(0, -60)"> 239 <circle cx="-15" cy="-25" r="10" fill="#43A047" /><circle cx="15" cy="-25" r="10" fill="#43A047" /> 240 <circle cx="0" cy="-15" r="22" fill="#43A047" /> 241 <circle cx="-15" cy="-25" r="3" fill="#000" /><circle cx="15" cy="-25" r="3" fill="#000" /> 242 <path d="M-10,0 Q0,10 10,0" stroke="#000" stroke-width="2" fill="none" /> 243 </g> 244 </g> 245 </g>
246 <g id="mount_front_body"></g> 247 <!-- Left Arm Hierarchy --> 248 <g id="shoulder_l" opacity="0"></g> 249 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#43A047" /></g> 250 <g id="elbow_l" opacity="0"></g> 251 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#43A047" /></g> 252 <g id="hand_l" stroke="#333" stroke-width="2"> 253 <circle cx="0" cy="0" r="8" fill="#C8E6C9" /> 254 <!-- Dynamic Instrument Injection Point --> 255 <g id="instrument" transform="translate(0,0)"></g> 256 </g> 257 <!-- Right Arm Hierarchy --> 258 <g id="shoulder_r" opacity="0"></g> 259 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#43A047" /></g> 260 <g id="elbow_r" opacity="0"></g> 261 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#43A047" /></g> 262 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#C8E6C9" /></g> 263`; 264const family_6_ensembleSVG = ` 265 <!-- Legs --> 266 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 267 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#795548" /> 268 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 269 </g> 270 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 271 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#795548" /> 272 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 273 </g> 274 <!-- Torso & Head --> 275 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 276 <g id="body"> 277 <!-- Torso --> 278 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#795548" /> 279 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFCC80" /> 280 <!-- Head --> 281 <g id="head" transform="translate(0, -60)"> 282 <circle cx="-25" cy="-15" r="10" fill="#FFCC80" /><circle cx="25" cy="-15" r="10" fill="#FFCC80" /> 283 <circle cx="0" cy="-15" r="22" fill="#795548" /> 284 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 285 <ellipse cx="0" cy="0" rx="18" ry="12" fill="#FFCC80" /><circle cx="0" cy="-3" r="2" fill="#000" /><path d="M-8,3 Q0,8 8,3" stroke="#000" stroke-width="2" fill="none" /> 286 </g> 287 </g> 288 </g>
289 <g id="mount_front_body"></g> 290 <!-- Left Arm Hierarchy --> 291 <g id="shoulder_l" opacity="0"></g> 292 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#795548" /></g> 293 <g id="elbow_l" opacity="0"></g> 294 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#795548" /></g> 295 <g id="hand_l" stroke="#333" stroke-width="2"> 296 <circle cx="0" cy="0" r="8" fill="#FFCC80" /> 297 <!-- Dynamic Instrument Injection Point --> 298 <g id="instrument" transform="translate(0,0)"></g> 299 </g> 300 <!-- Right Arm Hierarchy --> 301 <g id="shoulder_r" opacity="0"></g> 302 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#795548" /></g> 303 <g id="elbow_r" opacity="0"></g> 304 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#795548" /></g> 305 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFCC80" /></g> 306`; 307const family_7_brassSVG = ` 308 <!-- Legs --> 309 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 310 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#FF9800" /> 311 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 312 </g> 313 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 314 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#FF9800" /> 315 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 316 </g> 317 <!-- Torso & Head --> 318 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 319 <g id="body"> 320 <!-- Torso --> 321 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#FF9800" /> 322 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFE082" /> 323 <!-- Head --> 324 <g id="head" transform="translate(0, -60)"> 325 <circle cx="-15" cy="-30" r="6" fill="#FF9800" /><circle cx="15" cy="-30" r="6" fill="#FF9800" /> 326 <circle cx="0" cy="-15" r="22" fill="#FF9800" /> 327 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 328 <circle cx="0" cy="-5" r="10" fill="#FFE082" /><circle cx="0" cy="-8" r="3" fill="#000" /><path d="M-15,-20 L-5,-20 M15,-20 L5,-20 M-20,-10 L-10,-10 M20,-10 L10,-10" stroke="#000" stroke-width="2" fill="none" /> 329 </g> 330 </g> 331 </g>
332 <g id="mount_front_body"></g> 333 <!-- Left Arm Hierarchy --> 334 <g id="shoulder_l" opacity="0"></g> 335 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#FF9800" /></g> 336 <g id="elbow_l" opacity="0"></g> 337 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#FF9800" /></g> 338 <g id="hand_l" stroke="#333" stroke-width="2"> 339 <circle cx="0" cy="0" r="8" fill="#FFE082" /> 340 <!-- Dynamic Instrument Injection Point --> 341 <g id="instrument" transform="translate(0,0)"></g> 342 </g> 343 <!-- Right Arm Hierarchy --> 344 <g id="shoulder_r" opacity="0"></g> 345 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#FF9800" /></g> 346 <g id="elbow_r" opacity="0"></g> 347 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#FF9800" /></g> 348 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFE082" /></g> 349`; 350const family_8_reedSVG = `
351 <!-- Legs --> 352 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 353 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#607D8B" /> 354 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 355 </g> 356 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 357 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#607D8B" /> 358 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 359 </g> 360 <!-- Torso & Head --> 361 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 362 <g id="body"> 363 <!-- Torso --> 364 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#607D8B" /> 365 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#ECEFF1" /> 366 <!-- Head --> 367 <g id="head" transform="translate(0, -60)"> 368 <polygon points="-10,-20 -20,-35 -20,-15" fill="#607D8B" /><polygon points="10,-20 20,-35 20,-15" fill="#607D8B" /> 369 <circle cx="0" cy="-15" r="22" fill="#607D8B" /> 370 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 371 <ellipse cx="0" cy="-2" rx="12" ry="8" fill="#ECEFF1" /><circle cx="0" cy="-5" r="3" fill="#000" /> 372 </g> 373 </g> 374 </g>
375 <g id="mount_front_body"></g> 376 <!-- Left Arm Hierarchy --> 377 <g id="shoulder_l" opacity="0"></g> 378 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#607D8B" /></g> 379 <g id="elbow_l" opacity="0"></g> 380 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#607D8B" /></g> 381 <g id="hand_l" stroke="#333" stroke-width="2"> 382 <circle cx="0" cy="0" r="8" fill="#ECEFF1" /> 383 <!-- Dynamic Instrument Injection Point --> 384 <g id="instrument" transform="translate(0,0)"></g> 385 </g> 386 <!-- Right Arm Hierarchy --> 387 <g id="shoulder_r" opacity="0"></g> 388 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#607D8B" /></g> 389 <g id="elbow_r" opacity="0"></g> 390 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#607D8B" /></g> 391 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#ECEFF1" /></g> 392`; 393const family_9_pipeSVG = ` 394 <!-- Legs --> 395 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 396 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#9C27B0" /> 397 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 398 </g> 399 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 400 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#9C27B0" /> 401 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 402 </g> 403 <!-- Torso & Head --> 404 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 405 <g id="body"> 406 <!-- Torso --> 407 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#9C27B0" /> 408 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#E1BEE7" /> 409 <!-- Head --> 410 <g id="head" transform="translate(0, -60)"> 411 <circle cx="-15" cy="-25" r="5" fill="#9C27B0" /><circle cx="15" cy="-25" r="5" fill="#9C27B0" /> 412 <circle cx="0" cy="-15" r="22" fill="#9C27B0" /> 413 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 414 <ellipse cx="0" cy="5" rx="20" ry="15" fill="#9C27B0" /><circle cx="-8" cy="2" r="2" fill="#000" /><circle cx="8" cy="2" r="2" fill="#000" /> 415 </g> 416 </g> 417 </g>
418 <g id="mount_front_body"></g> 419 <!-- Left Arm Hierarchy --> 420 <g id="shoulder_l" opacity="0"></g> 421 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#9C27B0" /></g> 422 <g id="elbow_l" opacity="0"></g> 423 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#9C27B0" /></g> 424 <g id="hand_l" stroke="#333" stroke-width="2"> 425 <circle cx="0" cy="0" r="8" fill="#E1BEE7" /> 426 <!-- Dynamic Instrument Injection Point --> 427 <g id="instrument" transform="translate(0,0)"></g> 428 </g> 429 <!-- Right Arm Hierarchy --> 430 <g id="shoulder_r" opacity="0"></g> 431 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#9C27B0" /></g> 432 <g id="elbow_r" opacity="0"></g> 433 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#9C27B0" /></g> 434 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#E1BEE7" /></g> 435`; 436const family_10_synth_leadSVG = ` 437 <!-- Legs --> 438 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 439 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#9E9E9E" /> 440 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 441 </g> 442 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 443 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#9E9E9E" /> 444 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 445 </g> 446 <!-- Torso & Head --> 447 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 448 <g id="body"> 449 <!-- Torso --> 450 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#9E9E9E" /> 451 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#F5F5F5" /> 452 <!-- Head --> 453 <g id="head" transform="translate(0, -60)"> 454 <polygon points="-10,-20 -15,-30 -20,-15" fill="#9E9E9E" /><polygon points="10,-20 15,-30 20,-15" fill="#9E9E9E" /> 455 <circle cx="0" cy="-15" r="22" fill="#9E9E9E" /> 456 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 457 <ellipse cx="0" cy="5" rx="15" ry="10" fill="#F5F5F5" /><polygon points="-3,0 3,0 0,-15" fill="#FFF" /> 458 </g> 459 </g> 460 </g>
461 <g id="mount_front_body"></g> 462 <!-- Left Arm Hierarchy --> 463 <g id="shoulder_l" opacity="0"></g> 464 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#9E9E9E" /></g> 465 <g id="elbow_l" opacity="0"></g> 466 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#9E9E9E" /></g> 467 <g id="hand_l" stroke="#333" stroke-width="2"> 468 <circle cx="0" cy="0" r="8" fill="#F5F5F5" /> 469 <!-- Dynamic Instrument Injection Point --> 470 <g id="instrument" transform="translate(0,0)"></g> 471 </g> 472 <!-- Right Arm Hierarchy --> 473 <g id="shoulder_r" opacity="0"></g> 474 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#9E9E9E" /></g> 475 <g id="elbow_r" opacity="0"></g> 476 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#9E9E9E" /></g> 477 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#F5F5F5" /></g> 478`; 479const family_11_synth_padSVG = ` 480 <!-- Legs --> 481 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 482 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#F06292" /> 483 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 484 </g> 485 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 486 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#F06292" /> 487 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 488 </g> 489 <!-- Torso & Head --> 490 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 491 <g id="body"> 492 <!-- Torso --> 493 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#F06292" /> 494 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#F8BBD0" /> 495 <!-- Head --> 496 <g id="head" transform="translate(0, -60)"> 497 <polygon points="-10,-20 -20,-30 -20,-15" fill="#F06292" /><polygon points="10,-20 20,-30 20,-15" fill="#F06292" /> 498 <circle cx="0" cy="-15" r="22" fill="#F06292" /> 499 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 500 <ellipse cx="0" cy="-5" rx="12" ry="8" fill="#F48FB1" /><circle cx="-4" cy="-5" r="2" fill="#000" /><circle cx="4" cy="-5" r="2" fill="#000" /> 501 </g> 502 </g> 503 </g>
504 <g id="mount_front_body"></g> 505 <!-- Left Arm Hierarchy --> 506 <g id="shoulder_l" opacity="0"></g> 507 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#F06292" /></g> 508 <g id="elbow_l" opacity="0"></g> 509 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#F06292" /></g> 510 <g id="hand_l" stroke="#333" stroke-width="2"> 511 <circle cx="0" cy="0" r="8" fill="#F8BBD0" /> 512 <!-- Dynamic Instrument Injection Point --> 513 <g id="instrument" transform="translate(0,0)"></g> 514 </g> 515 <!-- Right Arm Hierarchy --> 516 <g id="shoulder_r" opacity="0"></g> 517 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#F06292" /></g> 518 <g id="elbow_r" opacity="0"></g> 519 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#F06292" /></g> 520 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#F8BBD0" /></g> 521`; 522const family_12_synth_effectsSVG = ` 523 <!-- Legs --> 524 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 525 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#424242" /> 526 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 527 </g> 528 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 529 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#424242" /> 530 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 531 </g> 532 <!-- Torso & Head --> 533 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 534 <g id="body"> 535 <!-- Torso --> 536 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#424242" /> 537 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#BDBDBD" /> 538 <!-- Head --> 539 <g id="head" transform="translate(0, -60)"> 540 <polygon points="-10,-20 -20,-35 -20,-15" fill="#424242" /><polygon points="10,-20 20,-35 20,-15" fill="#424242" /> 541 <circle cx="0" cy="-15" r="22" fill="#424242" /> 542 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 543 <path d="M-15,-5 L-25,-8 M-15,-2 L-25,-2 M15,-5 L25,-8 M15,-2 L25,-2" stroke="#000" stroke-width="1" fill="none" /><circle cx="0" cy="-5" r="2" fill="#F48FB1" /> 544 </g> 545 </g> 546 </g>
547 <g id="mount_front_body"></g> 548 <!-- Left Arm Hierarchy --> 549 <g id="shoulder_l" opacity="0"></g> 550 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#424242" /></g> 551 <g id="elbow_l" opacity="0"></g> 552 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#424242" /></g> 553 <g id="hand_l" stroke="#333" stroke-width="2"> 554 <circle cx="0" cy="0" r="8" fill="#BDBDBD" /> 555 <!-- Dynamic Instrument Injection Point --> 556 <g id="instrument" transform="translate(0,0)"></g> 557 </g> 558 <!-- Right Arm Hierarchy --> 559 <g id="shoulder_r" opacity="0"></g> 560 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#424242" /></g> 561 <g id="elbow_r" opacity="0"></g> 562 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#424242" /></g> 563 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#BDBDBD" /></g> 564`; 565const family_13_ethnicSVG = `
566 <!-- Legs --> 567 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 568 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#8D6E63" /> 569 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 570 </g> 571 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 572 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#8D6E63" /> 573 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 574 </g> 575 <!-- Torso & Head --> 576 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 577 <g id="body"> 578 <!-- Torso --> 579 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#8D6E63" /> 580 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#EFEBE9" /> 581 <!-- Head --> 582 <g id="head" transform="translate(0, -60)"> 583 <ellipse cx="-20" cy="-15" rx="8" ry="20" fill="#8D6E63" /><ellipse cx="20" cy="-15" rx="8" ry="20" fill="#8D6E63" /> 584 <circle cx="0" cy="-15" r="22" fill="#8D6E63" /> 585 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 586 <ellipse cx="0" cy="-2" rx="14" ry="10" fill="#EFEBE9" /><circle cx="0" cy="-5" r="4" fill="#000" /><path d="M0,-5 L0,2 M-5,2 Q0,5 5,2" stroke="#000" stroke-width="1.5" fill="none"/> 587 </g> 588 </g> 589 </g>
590 <g id="mount_front_body"></g> 591 <!-- Left Arm Hierarchy --> 592 <g id="shoulder_l" opacity="0"></g> 593 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#8D6E63" /></g> 594 <g id="elbow_l" opacity="0"></g> 595 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#8D6E63" /></g> 596 <g id="hand_l" stroke="#333" stroke-width="2"> 597 <circle cx="0" cy="0" r="8" fill="#EFEBE9" /> 598 <!-- Dynamic Instrument Injection Point --> 599 <g id="instrument" transform="translate(0,0)"></g> 600 </g> 601 <!-- Right Arm Hierarchy --> 602 <g id="shoulder_r" opacity="0"></g> 603 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#8D6E63" /></g> 604 <g id="elbow_r" opacity="0"></g> 605 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#8D6E63" /></g> 606 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#EFEBE9" /></g> 607`; 608const family_14_percussiveSVG = ` 609 <!-- Legs --> 610 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 611 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#90CAF9" /> 612 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 613 </g> 614 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 615 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#90CAF9" /> 616 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 617 </g> 618 <!-- Torso & Head --> 619 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 620 <g id="body"> 621 <!-- Torso --> 622 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#90CAF9" /> 623 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFFFFF" /> 624 <!-- Head --> 625 <g id="head" transform="translate(0, -60)"> 626 <ellipse cx="-10" cy="-40" rx="6" ry="25" fill="#90CAF9" /><ellipse cx="10" cy="-40" rx="6" ry="25" fill="#90CAF9" /> 627 <circle cx="0" cy="-15" r="22" fill="#90CAF9" /> 628 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 629 <circle cx="0" cy="-5" r="3" fill="#F48FB1" /><rect x="-3" y="-2" width="6" height="6" fill="#FFF" /> 630 </g> 631 </g> 632 </g>
633 <g id="mount_front_body"></g> 634 <!-- Left Arm Hierarchy --> 635 <g id="shoulder_l" opacity="0"></g> 636 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#90CAF9" /></g> 637 <g id="elbow_l" opacity="0"></g> 638 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#90CAF9" /></g> 639 <g id="hand_l" stroke="#333" stroke-width="2"> 640 <circle cx="0" cy="0" r="8" fill="#FFFFFF" /> 641 <!-- Dynamic Instrument Injection Point --> 642 <g id="instrument" transform="translate(0,0)"></g> 643 </g> 644 <!-- Right Arm Hierarchy --> 645 <g id="shoulder_r" opacity="0"></g> 646 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#90CAF9" /></g> 647 <g id="elbow_r" opacity="0"></g> 648 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#90CAF9" /></g> 649 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFFFFF" /></g> 650`; 651const family_15_sound_effectsSVG = ` 652 <!-- Legs --> 653 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 654 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#F57C00" /> 655 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 656 </g> 657 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 658 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#F57C00" /> 659 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 660 </g> 661 <!-- Torso & Head --> 662 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 663 <g id="body"> 664 <!-- Torso --> 665 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#F57C00" /> 666 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#FFF" /> 667 <!-- Head --> 668 <g id="head" transform="translate(0, -60)"> 669 <circle cx="-15" cy="-30" r="8" fill="#F57C00" /><circle cx="15" cy="-30" r="8" fill="#F57C00" /> 670 <circle cx="0" cy="-15" r="22" fill="#F57C00" /> 671 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 672 <circle cx="0" cy="-5" r="10" fill="#FFF" /><circle cx="0" cy="-8" r="3" fill="#F48FB1" /> 673 </g> 674 </g> 675 </g>
676 <g id="mount_front_body"></g> 677 <!-- Left Arm Hierarchy --> 678 <g id="shoulder_l" opacity="0"></g> 679 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#F57C00" /></g> 680 <g id="elbow_l" opacity="0"></g> 681 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#F57C00" /></g> 682 <g id="hand_l" stroke="#333" stroke-width="2"> 683 <circle cx="0" cy="0" r="8" fill="#FFF" /> 684 <!-- Dynamic Instrument Injection Point --> 685 <g id="instrument" transform="translate(0,0)"></g> 686 </g> 687 <!-- Right Arm Hierarchy --> 688 <g id="shoulder_r" opacity="0"></g> 689 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#F57C00" /></g> 690 <g id="elbow_r" opacity="0"></g> 691 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#F57C00" /></g> 692 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#FFF" /></g> 693`; 694const percussionSVG = ` 695 <!-- Legs --> 696 <g id="leg_l" transform="translate(-10, 0)" stroke="#333" stroke-width="2"> 697 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#4E342E" /> 698 <g id="foot_l" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 699 </g> 700 <g id="leg_r" transform="translate(10, 0)" stroke="#333" stroke-width="2"> 701 <rect x="-6" y="0" width="12" height="30" rx="4" fill="#4E342E" /> 702 <g id="foot_r" transform="translate(0, 30)"><rect x="-12" y="-2" width="24" height="12" rx="6" fill="#212121" /></g> 703 </g> 704 <!-- Torso & Head --> 705 <g id="body_container" stroke="#333" stroke-width="2" stroke-linejoin="round"> 706 <g id="body"> 707 <!-- Torso --> 708 <ellipse cx="0" cy="-25" rx="28" ry="30" fill="#4E342E" /> 709 <ellipse cx="0" cy="-20" rx="20" ry="22" fill="#D7CCC8" /> 710 <!-- Head --> 711 <g id="head" transform="translate(0, -60)"> 712 713 <circle cx="0" cy="-15" r="22" fill="#4E342E" /> 714 <circle cx="-6" cy="-15" r="2" fill="#000" /><circle cx="6" cy="-15" r="2" fill="#000" /> 715 <ellipse cx="0" cy="-5" rx="15" ry="12" fill="#D7CCC8" /><circle cx="-6" cy="-8" r="2" fill="#000" /><circle cx="6" cy="-8" r="2" fill="#000" /> 716 </g> 717 </g> 718 </g>
719 <g id="mount_front_body"></g> 720 <!-- Left Arm Hierarchy --> 721 <g id="shoulder_l" opacity="0"></g> 722 <g id="upper_arm_l" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#4E342E" /></g> 723 <g id="elbow_l" opacity="0"></g> 724 <g id="lower_arm_l" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#4E342E" /></g> 725 <g id="hand_l" stroke="#333" stroke-width="2"> 726 <circle cx="0" cy="0" r="8" fill="#D7CCC8" /> 727 <!-- Dynamic Instrument Injection Point --> 728 <g id="instrument" transform="translate(0,0)"></g> 729 </g> 730 <!-- Right Arm Hierarchy --> 731 <g id="shoulder_r" opacity="0"></g> 732 <g id="upper_arm_r" stroke="#333" stroke-width="2"><rect x="-5" y="-5" width="40" height="10" rx="5" fill="#4E342E" /></g> 733 <g id="elbow_r" opacity="0"></g> 734 <g id="lower_arm_r" stroke="#333" stroke-width="2"><rect x="-4" y="-4" width="40" height="8" rx="4" fill="#4E342E" /></g> 735 <g id="hand_r" stroke="#333" stroke-width="2"><circle cx="0" cy="0" r="8" fill="#D7CCC8" /></g> 736`; 737const defaultPuppetConfigs = { 738 "family_0_pianos": { 739 id: "family_0_pianos", 740 name: "Bear (Pianos)", 741 family: "keys", 742 svgContent: family_0_pianosSVG, 743 anchorX: 0, 744 anchorY: 100, 745 scale: 0.5, 746 noteMap: { 747 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 748 }, 749 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 750 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 751 }, 752 "family_1_chromatic_percussion": { 753 id: "family_1_chromatic_percussion", 754 name: "Fox (Chromatic Perc.)", 755 family: "keys", 756 svgContent: family_1_chromatic_percussionSVG, 757 anchorX: 0, 758 anchorY: 100, 759 scale: 0.5, 760 noteMap: { 761 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 762 }, 763 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 764 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 765 }, 766 "family_2_organs": { 767 id: "family_2_organs", 768 name: "Lion (Organs)", 769 family: "keys", 770 svgContent: family_2_organsSVG, 771 anchorX: 0, 772 anchorY: 100, 773 scale: 0.5, 774 noteMap: { 775 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 776 }, 777 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 778 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 779 }, 780 "family_3_guitars": { 781 id: "family_3_guitars", 782 name: "Elephant (Guitars)", 783 family: "strings", 784 svgContent: family_3_guitarsSVG, 785 anchorX: 0, 786 anchorY: 100, 787 scale: 0.5, 788 noteMap: { 789 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 790 }, 791 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 792 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 793 }, 794 "family_4_basses": { 795 id: "family_4_basses", 796 name: "Penguin (Basses)", 797 family: "strings", 798 svgContent: family_4_bassesSVG, 799 anchorX: 0, 800 anchorY: 100, 801 scale: 0.5, 802 noteMap: { 803 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 804 }, 805 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 806 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 807 }, 808 "family_5_strings": { 809 id: "family_5_strings", 810 name: "Frog (Strings)", 811 family: "strings", 812 svgContent: family_5_stringsSVG, 813 anchorX: 0, 814 anchorY: 100, 815 scale: 0.5, 816 noteMap: { 817 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 818 }, 819 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 820 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 821 }, 822 "family_6_ensemble": { 823 id: "family_6_ensemble", 824 name: "Monkey (Ensemble)", 825 family: "other", 826 svgContent: family_6_ensembleSVG, 827 anchorX: 0, 828 anchorY: 100, 829 scale: 0.5, 830 noteMap: { 831 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 832 }, 833 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 834 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 835 }, 836 "family_7_brass": { 837 id: "family_7_brass", 838 name: "Tiger (Brass)", 839 family: "winds", 840 svgContent: family_7_brassSVG, 841 anchorX: 0, 842 anchorY: 100, 843 scale: 0.5, 844 noteMap: { 845 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 846 }, 847 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 848 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 849 }, 850 "family_8_reed": { 851 id: "family_8_reed", 852 name: "Wolf (Reed)", 853 family: "winds", 854 svgContent: family_8_reedSVG, 855 anchorX: 0, 856 anchorY: 100, 857 scale: 0.5, 858 noteMap: { 859 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 860 }, 861 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 862 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 863 }, 864 "family_9_pipe": { 865 id: "family_9_pipe", 866 name: "Hippo (Pipe)", 867 family: "winds", 868 svgContent: family_9_pipeSVG, 869 anchorX: 0, 870 anchorY: 100, 871 scale: 0.5, 872 noteMap: { 873 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 874 }, 875 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 876 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 877 }, 878 "family_10_synth_lead": { 879 id: "family_10_synth_lead", 880 name: "Rhino (Synth Lead)", 881 family: "keys", 882 svgContent: family_10_synth_leadSVG, 883 anchorX: 0, 884 anchorY: 100, 885 scale: 0.5, 886 noteMap: { 887 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 888 }, 889 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 890 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 891 }, 892 "family_11_synth_pad": { 893 id: "family_11_synth_pad", 894 name: "Pig (Synth Pad)", 895 family: "keys", 896 svgContent: family_11_synth_padSVG, 897 anchorX: 0, 898 anchorY: 100, 899 scale: 0.5, 900 noteMap: { 901 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 902 }, 903 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 904 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 905 }, 906 "family_12_synth_effects": { 907 id: "family_12_synth_effects", 908 name: "Cat (Synth Effects)", 909 family: "other", 910 svgContent: family_12_synth_effectsSVG, 911 anchorX: 0, 912 anchorY: 100, 913 scale: 0.5, 914 noteMap: { 915 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 916 }, 917 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 918 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 919 }, 920 "family_13_ethnic": { 921 id: "family_13_ethnic", 922 name: "Dog (Ethnic)", 923 family: "other", 924 svgContent: family_13_ethnicSVG, 925 anchorX: 0, 926 anchorY: 100, 927 scale: 0.5, 928 noteMap: { 929 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 930 }, 931 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 932 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 933 }, 934 "family_14_percussive": { 935 id: "family_14_percussive", 936 name: "Rabbit (Percussive)", 937 family: "keys", 938 svgContent: family_14_percussiveSVG, 939 anchorX: 0, 940 anchorY: 100, 941 scale: 0.5, 942 noteMap: { 943 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 944 }, 945 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 946 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 947 }, 948 "family_15_sound_effects": { 949 id: "family_15_sound_effects", 950 name: "Hamster (Sound FX)", 951 family: "other", 952 svgContent: family_15_sound_effectsSVG, 953 anchorX: 0, 954 anchorY: 100, 955 scale: 0.5, 956 noteMap: { 957 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 958 }, 959 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 960 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 } 961 }, 962 "percussion": { 963 id: "percussion", 964 name: "Gorilla (Percussion)", 965 family: "percussion", 966 svgContent: percussionSVG, 967 anchorX: 0, 968 anchorY: 100, 969 scale: 0.5, 970 noteMap: { 971 default: { target: "hand_l", mapping: "linear", minX: -60, maxX: 60, minY: -40, maxY: 40, baseZIndex: 1 } 972 }, 973 ikLeftArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: 1, originX: -20, originY: -50 }, 974 ikRightArm: { upperArmLength: 40, lowerArmLength: 40, bendDirection: -1, originX: 20, originY: -50 }, 975 sockets: { 976 mouth: { x: 0, y: -65 }, 977 shoulder_l: { x: -20, y: -50 }, 978 shoulder_r: { x: 20, y: -50 }, 979 elbow_l: { x: -50, y: -20 }, 980 elbow_r: { x: 50, y: -20 }, 981 hand_l: { x: -50, y: 15 }, 982 hand_r: { x: 50, y: 15 } 983 } 984 } 985}; 986function mergeWithGorillaConfig(config, allConfigs) { 987 if (!config) return config; 988 const gorillaFromAll = allConfigs && allConfigs["percussion"]; 989 const gorillaDefault = defaultPuppetConfigs["percussion"]; 990 const gorilla = gorillaFromAll ? { 991 ...gorillaDefault, 992 ...gorillaFromAll, 993 sockets: { 994 ...gorillaDefault.sockets || {}, 995 ...gorillaFromAll.sockets || {} 996 }, 997 ikLeftArm: { 998 ...gorillaDefault.ikLeftArm || {},
999 ...gorillaFromAll.ikLeftArm || {} 1000 }, 1001 ikRightArm: { 1002 ...gorillaDefault.ikRightArm || {}, 1003 ...gorillaFromAll.ikRightArm || {} 1004 } 1005 } : gorillaDefault; 1006 if (config.id === "percussion") return gorilla; 1007 return { 1008 ...config, 1009 anchorX: config.anchorX !== void 0 ? config.anchorX : (gorilla == null ? void 0 : gorilla.anchorX) ?? 0, 1010 anchorY: config.anchorY !== void 0 ? config.anchorY : (gorilla == null ? void 0 : gorilla.anchorY) ?? 100, 1011 scale: config.scale !== void 0 ? config.scale : (gorilla == null ? void 0 : gorilla.scale) ?? 0.5, 1012 instrumentScale: config.instrumentScale !== void 0 ? config.instrumentScale : gorilla == null ? void 0 : gorilla.instrumentScale, 1013 instrumentRotation: config.instrumentRotation !== void 0 ? config.instrumentRotation : gorilla == null ? void 0 : gorilla.instrumentRotation, 1014 ikLeftArm: (gorilla == null ? void 0 : gorilla.ikLeftArm) || config.ikLeftArm ? { 1015 ...(gorilla == null ? void 0 : gorilla.ikLeftArm) || {}, 1016 ...config.ikLeftArm || {} 1017 } : void 0, 1018 ikRightArm: (gorilla == null ? void 0 : gorilla.ikRightArm) || config.ikRightArm ? { 1019 ...(gorilla == null ? void 0 : gorilla.ikRightArm) || {}, 1020 ...config.ikRightArm || {} 1021 } : void 0, 1022 noteMap: config.noteMap && Object.keys(config.noteMap).length > 0 ? config.noteMap : (gorilla == null ? void 0 : gorilla.noteMap) || {}, 1023 transforms: (gorilla == null ? void 0 : gorilla.transforms) || config.transforms ? { 1024 ...(gorilla == null ? void 0 : gorilla.transforms) || {}, 1025 ...config.transforms || {} 1026 } : void 0, 1027 sockets: (gorilla == null ? void 0 : gorilla.sockets) || config.sockets ? { 1028 ...(gorilla == null ? void 0 : gorilla.sockets) || {}, 1029 ...config.sockets || {} 1030 } : void 0, 1031 baseZOrder: (gorilla == null ? void 0 : gorilla.baseZOrder) || config.baseZOrder ? { 1032 ...(gorilla == null ? void 0 : gorilla.baseZOrder) || {}, 1033 ...config.baseZOrder || {} 1034 } : void 0 1035 }; 1036} 1037function mergeAllConfigsWithGorilla(configs) { 1038 const merged = {}; 1039 const keys = Object.keys(configs); 1040 keys.forEach((id) => { 1041 merged[id] = mergeWithGorillaConfig(configs[id], configs); 1042 }); 1043 return merged; 1044} 1045const defaultBackgroundConfig = { 1046 type: "image", 1047 color: "#0c0c14", 1048 gradient: { 1049 type: "linear", 1050 angle: 135, 1051 stops: [ 1052 { color: "#0f172a", offset: 0 }, 1053 { color: "#1e1b4b", offset: 50 }, 1054 { color: "#311042", offset: 100 } 1055 ] 1056 }, 1057 image: { 1058 url: "/assets/visualizers/orchestra/background-forest.webp", 1059 size: "cover", 1060 position: "center center", 1061 repeat: "no-repeat", 1062 blur: 0 1063 }, 1064 particles: { 1065 enabled: false, 1066 type: "stars", 1067 count: 50, 1068 speed: 1, 1069 color: "#ffffff", 1070 size: 2 1071 }, 1072 overlay: { 1073 enabled: true, 1074 color: "#000000", 1075 opacity: 0.15 1076 } 1077}; 1078const defaultBackgroundPresets = [ 1079 { 1080 id: "alt_rock", 1081 name: "Alternative Rock", 1082 tags: ["alternative rock"], 1083 config: { 1084 type: "image", 1085 color: "#000000", 1086 gradient: { type: "linear", angle: 0, stops: [] }, 1087 image: { url: "/assets/visualizers/orchestra/bkg/alt_rock.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1088 particles: { enabled: true, type: "dust", count: 50, speed: 1, color: "#ffffff", size: 2 }, 1089 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1090 } 1091 }, 1092 { 1093 id: "anime", 1094 name: "Anime", 1095 tags: ["anime", "animation"], 1096 config: { 1097 type: "image", 1098 color: "#000000", 1099 gradient: { type: "linear", angle: 0, stops: [] }, 1100 image: { url: "/assets/visualizers/orchestra/bkg/anime.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1101 particles: { enabled: true, type: "fireflies", count: 80, speed: 1, color: "#ffffff", size: 2 }, 1102 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1103 } 1104 }, 1105 { 1106 id: "classical", 1107 name: "Classical & Orchestral", 1108 tags: ["classical", "neo-classical", "orchestral", "piano", "soundtrack", "piano solo"], 1109 config: { 1110 type: "image", 1111 color: "#000000", 1112 gradient: { type: "linear", angle: 0, stops: [] }, 1113 image: { url: "/assets/visualizers/orchestra/bkg/classical.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1114 particles: { enabled: true, type: "dust", count: 60, speed: 1, color: "#ffffff", size: 2 },
1115 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1116 } 1117 }, 1118 { 1119 id: "country", 1120 name: "Country", 1121 tags: ["country"], 1122 config: { 1123 type: "image", 1124 color: "#000000", 1125 gradient: { type: "linear", angle: 0, stops: [] }, 1126 image: { url: "/assets/visualizers/orchestra/bkg/country.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1127 particles: { enabled: true, type: "dust", count: 40, speed: 1, color: "#ffffff", size: 2 }, 1128 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1129 } 1130 }, 1131 { 1132 id: "folk", 1133 name: "Folk", 1134 tags: ["folk"], 1135 config: { 1136 type: "image", 1137 color: "#000000", 1138 gradient: { type: "linear", angle: 0, stops: [] }, 1139 image: { url: "/assets/visualizers/orchestra/bkg/folk.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1140 particles: { enabled: true, type: "fireflies", count: 60, speed: 1, color: "#ffffff", size: 2 }, 1141 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1142 } 1143 }, 1144 { 1145 id: "game_music", 1146 name: "Game Music", 1147 tags: ["game music", "chiptune", "8-bit"], 1148 config: { 1149 type: "image", 1150 color: "#000000", 1151 gradient: { type: "linear", angle: 0, stops: [] }, 1152 image: { url: "/assets/visualizers/orchestra/bkg/game_music.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1153 particles: { enabled: true, type: "floating_orbs", count: 30, speed: 1, color: "#ffffff", size: 2 }, 1154 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1155 } 1156 }, 1157 { 1158 id: "hip_hop", 1159 name: "Hip Hop", 1160 tags: ["hip hop"], 1161 config: { 1162 type: "image", 1163 color: "#000000", 1164 gradient: { type: "linear", angle: 0, stops: [] }, 1165 image: { url: "/assets/visualizers/orchestra/bkg/hip_hop.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1166 particles: { enabled: true, type: "rain", count: 100, speed: 1, color: "#ffffff", size: 2 }, 1167 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1168 } 1169 }, 1170 { 1171 id: "indie", 1172 name: "Indie", 1173 tags: ["indie"], 1174 config: { 1175 type: "image", 1176 color: "#000000", 1177 gradient: { type: "linear", angle: 0, stops: [] }, 1178 image: { url: "/assets/visualizers/orchestra/bkg/indie.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1179 particles: { enabled: true, type: "floating_orbs", count: 40, speed: 1, color: "#ffffff", size: 2 }, 1180 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1181 } 1182 }, 1183 { 1184 id: "jazz", 1185 name: "Jazz & Blues", 1186 tags: ["jazz", "blues"], 1187 config: { 1188 type: "image", 1189 color: "#000000", 1190 gradient: { type: "linear", angle: 0, stops: [] }, 1191 image: { url: "/assets/visualizers/orchestra/bkg/jazz.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1192 particles: { enabled: true, type: "dust", count: 50, speed: 1, color: "#ffffff", size: 2 }, 1193 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1194 } 1195 }, 1196 { 1197 id: "karaoke", 1198 name: "Karaoke", 1199 tags: ["karaoke"], 1200 config: { 1201 type: "image", 1202 color: "#000000", 1203 gradient: { type: "linear", angle: 0, stops: [] }, 1204 image: { url: "/assets/visualizers/orchestra/bkg/karaoke.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1205 particles: { enabled: true, type: "confetti", count: 100, speed: 1, color: "#ffffff", size: 2 }, 1206 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1207 } 1208 }, 1209 { 1210 id: "latin", 1211 name: "Latin", 1212 tags: ["latin"], 1213 config: { 1214 type: "image", 1215 color: "#000000", 1216 gradient: { type: "linear", angle: 0, stops: [] }, 1217 image: { url: "/assets/visualizers/orchestra/bkg/latin.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1218 particles: { enabled: true, type: "hearts", count: 30, speed: 1, color: "#ffffff", size: 2 }, 1219 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1220 } 1221 }, 1222 { 1223 id: "metal", 1224 name: "Metal", 1225 tags: ["metal"], 1226 config: { 1227 type: "image", 1228 color: "#000000", 1229 gradient: { type: "linear", angle: 0, stops: [] }, 1230 image: { url: "/assets/visualizers/orchestra/bkg/metal.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1231 particles: { enabled: true, type: "sparkles", count: 60, speed: 1, color: "#ffffff", size: 2 },
1232 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1233 } 1234 }, 1235 { 1236 id: "national", 1237 name: "National Anthem", 1238 tags: ["national anthem"], 1239 config: { 1240 type: "image", 1241 color: "#000000", 1242 gradient: { type: "linear", angle: 0, stops: [] }, 1243 image: { url: "/assets/visualizers/orchestra/bkg/national.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1244 particles: { enabled: true, type: "dust", count: 30, speed: 1, color: "#ffffff", size: 2 }, 1245 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1246 } 1247 }, 1248 { 1249 id: "occasions", 1250 name: "Occasions & Broadway", 1251 tags: ["occasions", "broadway musical"], 1252 config: { 1253 type: "image", 1254 color: "#000000", 1255 gradient: { type: "linear", angle: 0, stops: [] }, 1256 image: { url: "/assets/visualizers/orchestra/bkg/occasions.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1257 particles: { enabled: true, type: "sparkles", count: 80, speed: 1, color: "#ffffff", size: 2 }, 1258 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1259 } 1260 }, 1261 { 1262 id: "original", 1263 name: "Original Composition", 1264 tags: ["original composition"], 1265 config: { 1266 type: "image", 1267 color: "#000000", 1268 gradient: { type: "linear", angle: 0, stops: [] }, 1269 image: { url: "/assets/visualizers/orchestra/bkg/original.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1270 particles: { enabled: true, type: "dust", count: 40, speed: 1, color: "#ffffff", size: 2 }, 1271 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1272 } 1273 }, 1274 { 1275 id: "pop", 1276 name: "Pop", 1277 tags: ["pop"], 1278 config: { 1279 type: "image", 1280 color: "#000000", 1281 gradient: { type: "linear", angle: 0, stops: [] }, 1282 image: { url: "/assets/visualizers/orchestra/bkg/pop.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1283 particles: { enabled: true, type: "confetti", count: 80, speed: 1, color: "#ffffff", size: 2 }, 1284 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1285 } 1286 }, 1287 { 1288 id: "reggae", 1289 name: "Reggae", 1290 tags: ["reggae"], 1291 config: { 1292 type: "image", 1293 color: "#000000", 1294 gradient: { type: "linear", angle: 0, stops: [] }, 1295 image: { url: "/assets/visualizers/orchestra/bkg/reggae.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1296 particles: { enabled: true, type: "fireflies", count: 10, speed: 1, color: "#ffffff", size: 2 }, 1297 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1298 } 1299 }, 1300 { 1301 id: "reggaeton", 1302 name: "Reggaeton", 1303 tags: ["reggaeton"], 1304 config: { 1305 type: "image", 1306 color: "#000000", 1307 gradient: { type: "linear", angle: 0, stops: [] }, 1308 image: { url: "/assets/visualizers/orchestra/bkg/reggaeton.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1309 particles: { enabled: true, type: "confetti", count: 80, speed: 1, color: "#ffffff", size: 2 }, 1310 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1311 } 1312 }, 1313 { 1314 id: "religious", 1315 name: "Religious", 1316 tags: ["religious"], 1317 config: { 1318 type: "image", 1319 color: "#000000", 1320 gradient: { type: "linear", angle: 0, stops: [] }, 1321 image: { url: "/assets/visualizers/orchestra/bkg/religious.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1322 particles: { enabled: true, type: "dust", count: 50, speed: 1, color: "#ffffff", size: 2 }, 1323 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1324 } 1325 }, 1326 { 1327 id: "rnb", 1328 name: "R&B", 1329 tags: ["r&b", "rnb"], 1330 config: { 1331 type: "image", 1332 color: "#000000", 1333 gradient: { type: "linear", angle: 0, stops: [] }, 1334 image: { url: "/assets/visualizers/orchestra/bkg/rnb.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1335 particles: { enabled: true, type: "hearts", count: 40, speed: 1, color: "#ffffff", size: 2 },
1336 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1337 } 1338 }, 1339 { 1340 id: "rock", 1341 name: "Rock", 1342 tags: ["rock"], 1343 config: { 1344 type: "image", 1345 color: "#000000", 1346 gradient: { type: "linear", angle: 0, stops: [] }, 1347 image: { url: "/assets/visualizers/orchestra/bkg/rock.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1348 particles: { enabled: true, type: "laser_lines", count: 30, speed: 1, color: "#ffffff", size: 2 }, 1349 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1350 } 1351 }, 1352 { 1353 id: "scifi", 1354 name: "Electronic & Ambient", 1355 tags: ["electronic", "ambient"], 1356 config: { 1357 type: "image", 1358 color: "#000000", 1359 gradient: { type: "linear", angle: 0, stops: [] }, 1360 image: { url: "/assets/visualizers/orchestra/bkg/sci-fi.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1361 particles: { enabled: true, type: "matrix", count: 60, speed: 1, color: "#ffffff", size: 2 }, 1362 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1363 } 1364 }, 1365 { 1366 id: "techno", 1367 name: "Techno & EDM", 1368 tags: ["techno", "trance", "dance & edm", "house", "disco", "edm"], 1369 config: { 1370 type: "image", 1371 color: "#000000", 1372 gradient: { type: "linear", angle: 0, stops: [] }, 1373 image: { url: "/assets/visualizers/orchestra/bkg/techno.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1374 particles: { enabled: true, type: "laser_lines", count: 40, speed: 1, color: "#ffffff", size: 2 }, 1375 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1376 } 1377 }, 1378 { 1379 id: "trap", 1380 name: "Trap", 1381 tags: ["trap"], 1382 config: { 1383 type: "image", 1384 color: "#000000", 1385 gradient: { type: "linear", angle: 0, stops: [] }, 1386 image: { url: "/assets/visualizers/orchestra/bkg/trap.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1387 particles: { enabled: true, type: "rain", count: 120, speed: 1, color: "#ffffff", size: 2 }, 1388 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1389 } 1390 }, 1391 { 1392 id: "world", 1393 name: "World", 1394 tags: ["world"], 1395 config: { 1396 type: "image", 1397 color: "#000000", 1398 gradient: { type: "linear", angle: 0, stops: [] }, 1399 image: { url: "/assets/visualizers/orchestra/bkg/world.jpg", size: "cover", position: "center center", repeat: "no-repeat", blur: 0 }, 1400 particles: { enabled: true, type: "fireflies", count: 70, speed: 1, color: "#ffffff", size: 2 }, 1401 overlay: { enabled: true, color: "#000000", opacity: 0.1 } 1402 } 1403 } 1404]; 1405const defaultClothingGraphics = { 1406 tuxedo: { 1407 id: "tuxedo", 1408 parts: { 1409 // A black tuxedo jacket over a white shirt with a bowtie 1410 body: ` 1411 <g class="clothing-tuxedo-body"> 1412 <!-- White Shirt --> 1413 <path d="M-12,-45 L12,-45 L15,20 L-15,20 Z" fill="#F8FAFC" /> 1414 <!-- Bowtie --> 1415 <path d="M-8,-38 L0,-35 L8,-38 L8,-32 L0,-35 L-8,-32 Z" fill="#1E293B" /> 1416 <!-- Jacket Left Side --> 1417 <path d="M-20,-45 L-5,-10 L-15,25 L-25,20 Z" fill="#0F172A" /> 1418 <!-- Jacket Right Side --> 1419 <path d="M20,-45 L5,-10 L15,25 L25,20 Z" fill="#0F172A" /> 1420 <!-- Lapels --> 1421 <path d="M-15,-45 L-5,-10 L-10,-45 Z" fill="#1E293B" /> 1422 <path d="M15,-45 L5,-10 L10,-45 Z" fill="#1E293B" /> 1423 <!-- Buttons --> 1424 <circle cx="0" cy="-10" r="1.5" fill="#0F172A" /> 1425 <circle cx="0" cy="0" r="1.5" fill="#0F172A" /> 1426 <circle cx="0" cy="10" r="1.5" fill="#0F172A" /> 1427 </g> 1428 `, 1429 // Tuxedo sleeves (covers horizontal arms) 1430 upper_arm_l: ` 1431 <g class="clothing-tuxedo-sleeve"> 1432 <!-- Black sleeve covering upper arm (horizontal) --> 1433 <rect x="-6" y="-6" width="42" height="12" rx="6" fill="#0F172A" /> 1434 </g> 1435 `, 1436 lower_arm_l: `
1437 <g class="clothing-tuxedo-sleeve"> 1438 <!-- Black sleeve covering lower arm (horizontal) --> 1439 <rect x="-5" y="-5" width="33" height="10" rx="5" fill="#0F172A" /> 1440 <!-- White cuff peeking out --> 1441 <rect x="28" y="-5" width="8" height="10" rx="2" fill="#F8FAFC" /> 1442 </g> 1443 `, 1444 upper_arm_r: ` 1445 <g class="clothing-tuxedo-sleeve"> 1446 <!-- Black sleeve covering upper arm (horizontal) --> 1447 <rect x="-6" y="-6" width="42" height="12" rx="6" fill="#0F172A" /> 1448 </g> 1449 `, 1450 lower_arm_r: ` 1451 <g class="clothing-tuxedo-sleeve"> 1452 <!-- Black sleeve covering lower arm (horizontal) --> 1453 <rect x="-5" y="-5" width="33" height="10" rx="5" fill="#0F172A" /> 1454 <!-- White cuff peeking out --> 1455 <rect x="28" y="-5" width="8" height="10" rx="2" fill="#F8FAFC" /> 1456 </g> 1457 `, 1458 // Tuxedo pants (covers vertical legs of height 30) 1459 leg_l: ` 1460 <g class="clothing-tuxedo-pants"> 1461 <rect x="-7" y="-2" width="14" height="34" rx="4" fill="#0F172A" /> 1462 <!-- Satin stripe --> 1463 <rect x="-7" y="-2" width="3" height="34" fill="#1E293B" /> 1464 </g> 1465 `, 1466 leg_r: ` 1467 <g class="clothing-tuxedo-pants"> 1468 <rect x="-7" y="-2" width="14" height="34" rx="4" fill="#0F172A" /> 1469 <!-- Satin stripe --> 1470 <rect x="4" y="-2" width="3" height="34" fill="#1E293B" /> 1471 </g> 1472 ` 1473 }, 1474 gluing: { 1475 body: { type: "two-point", p1: { x: 0, y: -60 }, p2: { x: 0, y: 0 }, t1: "head", t2: "body" }, 1476 upper_arm_l: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 36, y: 0 }, t1: "shoulder_l", t2: "elbow_l" }, 1477 upper_arm_r: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 36, y: 0 }, t1: "shoulder_r", t2: "elbow_r" }, 1478 lower_arm_l: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 32, y: 0 }, t1: "elbow_l", t2: "hand_l" }, 1479 lower_arm_r: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 32, y: 0 }, t1: "elbow_r", t2: "hand_r" }, 1480 leg_l: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 0, y: 30 }, t1: "leg_l", t2: "foot_l" }, 1481 leg_r: { type: "two-point", p1: { x: 0, y: 0 }, p2: { x: 0, y: 30 }, t1: "leg_r", t2: "foot_r" } 1482 } 1483 }, 1484 dress: { 1485 id: "dress", 1486 parts: { 1487 // Elegant flowing red evening gown 1488 body: ` 1489 <g class="clothing-dress-body"> 1490 <!-- Dress Bodice --> 1491 <path d="M-15,-45 L15,-45 L12,0 L-12,0 Z" fill="#9F1239" /> 1492 <path d="M-15,-45 Q0,-35 15,-45 Z" fill="#BE123C" /> 1493 <!-- Dress Skirt (Flows down to cover top of legs) --> 1494 <path d="M-12,0 L12,0 L25,40 L-25,40 Z" fill="#9F1239" /> 1495 <path d="M-12,0 Q0,40 12,0 Z" fill="#7F1D1D" opacity="0.3" /> 1496 <!-- Belt/Sash --> 1497 <rect x="-12" y="-5" width="24" height="8" rx="2" fill="#FDA4AF" /> 1498 </g> 1499 `, 1500 // Short sleeves covering horizontal upper arms 1501 upper_arm_l: ` 1502 <g class="clothing-dress-sleeve"> 1503 <rect x="-6" y="-6" width="20" height="12" rx="6" fill="#9F1239" /> 1504 </g> 1505 `, 1506 upper_arm_r: ` 1507 <g class="clothing-dress-sleeve"> 1508 <rect x="-6" y="-6" width="20" height="12" rx="6" fill="#9F1239" /> 1509 </g> 1510 ` 1511 }, 1512 gluing: { 1513 body: { type: "two-point", p1: { x: 0, y: -60 }, p2: { x: 0, y: 0 }, t1: "head", t2: "body" }, 1514 upper_arm_l: { type: "one-point", p1: { x: 0, y: 0 }, t1: "upper_arm_l" }, 1515 upper_arm_r: { type: "one-point", p1: { x: 0, y: 0 }, t1: "upper_arm_r" } 1516 } 1517 } 1518}; 1519const grooveSway = { 1520 id: "groove_sway", 1521 name: "Groove Sway", 1522 category: "body", 1523 trigger: "playing", 1524 priority: 10, 1525 duration: 0, 1526 // continuous — driven by wave, not a fixed duration 1527 tempoSync: true, 1528 loop: true, 1529 blendInMs: 0, 1530 blendOutMs: 0, 1531 channels: [ 1532 { 1533 joint: "body", 1534 property: "y", 1535 type: "wave", 1536 waveform: "sine", 1537 frequency: 0.5, 1538 // cycles per beat (bps * PI → half-cycle per beat) 1539 amplitude: 10, 1540 amplitudeBase: 2, 1541 intensityScale: 1, 1542 phase: 0, 1543 offset: 0 1544 } 1545 ], 1546 affectedJoints: ["body"] 1547}; 1548const headRotation = { 1549 id: "head_rotation", 1550 name: "Head Rotation", 1551 category: "body", 1552 trigger: "playing", 1553 priority: 10, 1554 duration: 0, 1555 tempoSync: true, 1556 loop: true, 1557 blendInMs: 0, 1558 blendOutMs: 0, 1559 channels: [ 1560 { 1561 joint: "head", 1562 property: "rotation", 1563 type: "wave", 1564 waveform: "sine", 1565 frequency: 0.25, 1566 // cycles per beat 1567 amplitude: 15, 1568 amplitudeBase: 5, 1569 intensityScale: 1, 1570 phase: 0, 1571 offset: 0 1572 } 1573 ], 1574 affectedJoints: ["head"] 1575}; 1576const footTap = { 1577 id: "foot_tap", 1578 name: "Foot Tap", 1579 category: "body", 1580 trigger: "playing", 1581 priority: 10, 1582 duration: 0, 1583 tempoSync: true, 1584 loop: true, 1585 blendInMs: 0, 1586 blendOutMs: 0, 1587 channels: [ 1588 { 1589 joint: "foot_r", 1590 property: "y", 1591 type: "wave", 1592 waveform: "abs_sine", 1593 frequency: 1, 1594 // 1 cycle per beat 1595 amplitude: -10, 1596 // negative: foot lifts up (y = -footTap) 1597 amplitudeBase: 0, 1598 intensityScale: 1, 1599 phase: 0, 1600 offset: 0 1601 }, 1602 { 1603 joint: "foot_r", 1604 property: "rotation", 1605 type: "wave", 1606 waveform: "abs_sine", 1607 frequency: 1, 1608 amplitude: 20, 1609 // footTap * 2 1610 amplitudeBase: 0, 1611 intensityScale: 1, 1612 phase: 0, 1613 offset: 0 1614 } 1615 ], 1616 affectedJoints: ["foot_r"] 1617}; 1618const idleNod = { 1619 id: "idle_nod", 1620 name: "Idle Nod", 1621 category: "body", 1622 trigger: "idle", 1623 priority: 5, 1624 duration: 2e3, 1625 tempoSync: false, 1626 loop: false, 1627 blendInMs: 500, 1628 blendOutMs: 500, 1629 minInterval: 1e4, 1630 maxInterval: 3e4, 1631 channels: [ 1632 // Channel 1: head.rotation = sin(progress * PI * 6) * 8 1633 // 13 keyframes tracing 3 full sine cycles 1634 { 1635 joint: "head", 1636 property: "rotation", 1637 type: "keyframe", 1638 keyframes: [ 1639 { time: 0 / 12, value: 0, easing: "sine" }, 1640 { time: 1 / 12, value: 8, easing: "sine" }, 1641 // peak 1 1642 { time: 2 / 12, value: 0, easing: "sine" }, 1643 { time: 3 / 12, value: -8, easing: "sine" }, 1644 // trough 1 1645 { time: 4 / 12, value: 0, easing: "sine" }, 1646 { time: 5 / 12, value: 8, easing: "sine" }, 1647 // peak 2 1648 { time: 6 / 12, value: 0, easing: "sine" }, 1649 { time: 7 / 12, value: -8, easing: "sine" }, 1650 // trough 2 1651 { time: 8 / 12, value: 0, easing: "sine" }, 1652 { time: 9 / 12, value: 8, easing: "sine" }, 1653 // peak 3 1654 { time: 10 / 12, value: 0, easing: "sine" }, 1655 { time: 11 / 12, value: -8, easing: "sine" }, 1656 // trough 3 1657 { time: 1, value: 0, easing: "sine" } 1658 ] 1659 }, 1660 // Channel 2: head.y = max(0, nodCycle) * 3 1661 // Only positive half-cycles contribute (peaks → 3, everything else → 0) 1662 { 1663 joint: "head", 1664 property: "y", 1665 type: "keyframe", 1666 keyframes: [ 1667 { time: 0 / 12, value: 0, easing: "sine" }, 1668 { time: 1 / 12, value: 3, easing: "sine" }, 1669 // positive peak 1 1670 { time: 2 / 12, value: 0, easing: "sine" }, 1671 { time: 3 / 12, value: 0, easing: "linear" }, 1672 // trough (clamped to 0) 1673 { time: 4 / 12, value: 0, easing: "sine" }, 1674 { time: 5 / 12, value: 3, easing: "sine" }, 1675 // positive peak 2 1676 { time: 6 / 12, value: 0, easing: "sine" }, 1677 { time: 7 / 12, value: 0, easing: "linear" }, 1678 // trough (clamped to 0) 1679 { time: 8 / 12, value: 0, easing: "sine" }, 1680 { time: 9 / 12, value: 3, easing: "sine" }, 1681 // positive peak 3 1682 { time: 10 / 12, value: 0, easing: "sine" }, 1683 { time: 11 / 12, value: 0, easing: "linear" }, 1684 // trough (clamped to 0) 1685 { time: 1, value: 0, easing: "sine" } 1686 ] 1687 } 1688 ], 1689 affectedJoints: ["head"] 1690}; 1691const idleWave = { 1692 id: "idle_wave", 1693 name: "Idle Wave", 1694 category: "body", 1695 trigger: "idle", 1696 priority: 5, 1697 duration: 3e3, 1698 tempoSync: false, 1699 loop: false, 1700 blendInMs: 500, 1701 blendOutMs: 500, 1702 minInterval: 1e4, 1703 maxInterval: 3e4, 1704 controlsIk: true,
1705 // ikSide is determined at runtime based on instrument mount point 1706 channels: [ 1707 // Single wave channel encoding the forearm oscillation. 1708 // The action player interprets '_ik_wave' as a virtual joint that 1709 // drives the IK wave solver rather than a physical joint directly. 1710 // offset = 45° (base wave angle, midpoint of 30°–90° random range) 1711 // amplitude = 25° (oscillation ±25° around the base angle) 1712 // frequency = 2.39 Hz (absolute, not tempo-synced) 1713 { 1714 joint: "_ik_wave", 1715 property: "rotation", 1716 type: "wave", 1717 waveform: "sine", 1718 frequency: 2.39, 1719 // ~15/(2*PI) Hz — absolute (tempoSync=false) 1720 amplitude: 25, 1721 amplitudeBase: 0, 1722 intensityScale: 0, 1723 // wave oscillation is independent of intensity 1724 phase: 0, 1725 offset: 45 1726 // base angle in degrees 1727 } 1728 ], 1729 affectedJoints: [ 1730 "upper_arm_l", 1731 "elbow_l", 1732 "lower_arm_l", 1733 "hand_l", 1734 "upper_arm_r", 1735 "elbow_r", 1736 "lower_arm_r", 1737 "hand_r" 1738 ] 1739}; 1740const defaultBodyActions = [ 1741 grooveSway, 1742 headRotation, 1743 footTap, 1744 idleNod, 1745 idleWave 1746]; 1747function getDefaultBodyActions() { 1748 return JSON.parse(JSON.stringify(defaultBodyActions)); 1749} 1750const defaultActionRules = [ 1751 { 1752 id: "default_body_all", 1753 name: "Default Body Actions", 1754 actionIds: ["groove_sway", "head_rotation", "foot_tap", "idle_nod", "idle_wave"], 1755 enabled: true, 1756 condition: { scope: "all" } 1757 } 1758]; 1759function multiply(A, B) { 1760 return [ 1761 A[0] * B[0] + A[2] * B[1], 1762 A[1] * B[0] + A[3] * B[1], 1763 A[0] * B[2] + A[2] * B[3], 1764 A[1] * B[2] + A[3] * B[3], 1765 A[0] * B[4] + A[2] * B[5] + A[4], 1766 A[1] * B[4] + A[3] * B[5] + A[5] 1767 ]; 1768} 1769function transformPoint(pt, M) { 1770 return { 1771 x: M[0] * pt.x + M[2] * pt.y + M[4], 1772 y: M[1] * pt.x + M[3] * pt.y + M[5] 1773 }; 1774} 1775function transformPointWithStatic(pt, transform) { 1776 if (!transform) return pt; 1777 const sx = transform.scaleX ?? transform.scale ?? 1; 1778 const sy = transform.scaleY ?? transform.scale ?? 1; 1779 const rot = transform.rotation ?? 0; 1780 const tx = transform.x ?? 0; 1781 const ty = transform.y ?? 0; 1782 const rad = rot * Math.PI / 180; 1783 const cos = Math.cos(rad); 1784 const sin = Math.sin(rad); 1785 const rx = pt.x * cos - pt.y * sin; 1786 const ry = pt.x * sin + pt.y * cos; 1787 return { 1788 x: tx + rx * sx, 1789 y: ty + ry * sy 1790 }; 1791} 1792function parseTransformAttribute(transformStr) { 1793 let mat = [1, 0, 0, 1, 0, 0]; 1794 if (!transformStr) return mat; 1795 const regex = /([a-zA-Z]+)\s*\(([^)]+)\)/g; 1796 let match; 1797 while ((match = regex.exec(transformStr)) !== null) { 1798 const type = match[1].toLowerCase(); 1799 const args = match[2].split(/[\s,]+/).map(parseFloat).filter((v) => !isNaN(v)); 1800 let commandMat = [1, 0, 0, 1, 0, 0]; 1801 if (type === "translate") { 1802 const tx = args[0] || 0; 1803 const ty = args[1] || 0; 1804 commandMat = [1, 0, 0, 1, tx, ty]; 1805 } else if (type === "scale") { 1806 const sx = args[0] !== void 0 ? args[0] : 1; 1807 const sy = args[1] !== void 0 ? args[1] : sx; 1808 commandMat = [sx, 0, 0, sy, 0, 0]; 1809 } else if (type === "rotate") { 1810 const deg = args[0] || 0; 1811 const rad = deg * Math.PI / 180; 1812 const cos = Math.cos(rad); 1813 const sin = Math.sin(rad); 1814 if (args.length >= 3) { 1815 const cx = args[1]; 1816 const cy = args[2]; 1817 const m1 = [1, 0, 0, 1, cx, cy]; 1818 const m2 = [cos, sin, -sin, cos, 0, 0]; 1819 const m3 = [1, 0, 0, 1, -cx, -cy]; 1820 commandMat = multiply(m1, multiply(m2, m3)); 1821 } else { 1822 commandMat = [cos, sin, -sin, cos, 0, 0]; 1823 } 1824 } else if (type === "matrix") { 1825 if (args.length === 6) { 1826 commandMat = args; 1827 } 1828 } 1829 mat = multiply(mat, commandMat); 1830 } 1831 return mat; 1832} 1833function getSvgJointBasePositions(svgContent) { 1834 const bases = {}; 1835 if (!svgContent) return bases; 1836 try { 1837 const parser = new DOMParser(); 1838 const doc = parser.parseFromString(`<svg>${svgContent}</svg>`, "image/svg+xml"); 1839 const jointIds = ["head", "body", "shoulder_l", "elbow_l", "hand_l", "shoulder_r", "elbow_r", "hand_r"]; 1840 jointIds.forEach((id) => { 1841 const el = doc.querySelector(`#${id}`); 1842 if (el) { 1843 const transformMats = []; 1844 let current = el; 1845 while (current && current.tagName !== "svg") { 1846 const transformAttr = current.getAttribute("transform"); 1847 if (transformAttr) { 1848 transformMats.push(parseTransformAttribute(transformAttr)); 1849 } 1850 current = current.parentElement; 1851 } 1852 let M = [1, 0, 0, 1, 0, 0]; 1853 for (let i = transformMats.length - 1; i >= 0; i--) { 1854 M = multiply(M, transformMats[i]); 1855 } 1856 bases[id] = transformPoint({ x: 0, y: 0 }, M); 1857 } 1858 }); 1859 } catch (e) { 1860 console.warn("Failed to parse SVG joint base positions:", e); 1861 }
1862 return bases; 1863} 1864function parseSvgSocketsAndPlugs(svgContent) { 1865 const coords = {}; 1866 if (!svgContent) return coords; 1867 try { 1868 const parser = new DOMParser(); 1869 const doc = parser.parseFromString(`<svg>${svgContent}</svg>`, "image/svg+xml"); 1870 const elements = doc.querySelectorAll("*"); 1871 elements.forEach((el) => { 1872 const id = el.getAttribute("id"); 1873 if (id && (id.startsWith("socket_") || id.startsWith("plug_"))) { 1874 let localPt = null; 1875 const cxAttr = el.getAttribute("cx"); 1876 const cyAttr = el.getAttribute("cy"); 1877 if (cxAttr !== null && cyAttr !== null) { 1878 localPt = { x: parseFloat(cxAttr), y: parseFloat(cyAttr) }; 1879 } else { 1880 const xAttr = el.getAttribute("x"); 1881 const yAttr = el.getAttribute("y"); 1882 if (xAttr !== null && yAttr !== null) { 1883 localPt = { x: parseFloat(xAttr), y: parseFloat(yAttr) }; 1884 } else { 1885 localPt = { x: 0, y: 0 }; 1886 } 1887 } 1888 const transformMats = []; 1889 let current = el; 1890 while (current && current.tagName !== "svg") { 1891 const transformAttr = current.getAttribute("transform"); 1892 if (transformAttr) { 1893 transformMats.push(parseTransformAttribute(transformAttr)); 1894 } 1895 current = current.parentElement; 1896 } 1897 let M = [1, 0, 0, 1, 0, 0]; 1898 for (let i = transformMats.length - 1; i >= 0; i--) { 1899 M = multiply(M, transformMats[i]); 1900 } 1901 coords[id] = transformPoint(localPt, M); 1902 } 1903 }); 1904 } catch (e) { 1905 console.warn("Failed to parse SVG sockets/plugs:", e); 1906 } 1907 return coords; 1908} 1909class ActionEngine { 1910 constructor() { 1911 this.actions = []; 1912 this.activeActions = /* @__PURE__ */ new Map(); 1913 this.idleSchedules = /* @__PURE__ */ new Map(); 1914 this.initialized = false; 1915 this.forceActionId = null; 1916 this.disableRandomIdle = false; 1917 } 1918 /** Replace the current action set */ 1919 setActions(actions) { 1920 this.actions = actions; 1921 for (const [id] of this.activeActions) { 1922 if (!actions.find((a) => a.id === id)) { 1923 this.activeActions.delete(id); 1924 } 1925 } 1926 for (const action of actions) { 1927 if (action.trigger === "idle" && !this.idleSchedules.has(action.id)) { 1928 this.idleSchedules.set(action.id, { 1929 actionId: action.id, 1930 nextFireTime: 0 1931 // will be set on first tick 1932 }); 1933 } 1934 } 1935 } 1936 getActions() { 1937 return this.actions; 1938 } 1939 getActiveActions() { 1940 return this.activeActions; 1941 } 1942 /** 1943 * Evaluate all actions for the current frame. 1944 * Returns a map of joint.property → delta value to apply. 1945 */ 1946 tick(time, bps, intensity, isSongPlaying, hasActiveNotes, bodyBehaviorMults, idleBehaviorMults) { 1947 const deltas = {}; 1948 if (!this.initialized && this.actions.length > 0) { 1949 this.initialized = true; 1950 for (const action of this.actions) { 1951 if (action.trigger === "idle") { 1952 const schedule = this.idleSchedules.get(action.id); 1953 if (schedule && schedule.nextFireTime === 0) { 1954 schedule.nextFireTime = time + 2e3 + Math.random() * 8e3; 1955 } 1956 } 1957 } 1958 } 1959 if (this.forceActionId) { 1960 for (const [id] of this.activeActions) { 1961 if (id !== this.forceActionId) { 1962 this.activeActions.delete(id); 1963 } 1964 } 1965 const action = this.actions.find((a) => a.id === this.forceActionId); 1966 if (action) { 1967 let existing = this.activeActions.get(this.forceActionId); 1968 if (!existing) { 1969 const durMs = action.tempoSync ? action.duration / bps * 1e3 : action.duration; 1970 existing = { 1971 def: action, 1972 startTime: time, 1973 duration: durMs, 1974 isActive: true, 1975 blend: 1 1976 }; 1977 this.activeActions.set(this.forceActionId, existing); 1978 } else { 1979 existing.isActive = true; 1980 existing.def = action; 1981 existing.duration = action.tempoSync ? action.duration / bps * 1e3 : action.duration; 1982 } 1983 } 1984 } else { 1985 for (const action of this.actions) { 1986 const shouldBeActive = this.shouldActivate(action, time, isSongPlaying, hasActiveNotes); 1987 const existing = this.activeActions.get(action.id); 1988 if (shouldBeActive && !existing) { 1989 const durMs = action.tempoSync ? action.duration / bps * 1e3 : action.duration; 1990 this.activeActions.set(action.id, { 1991 def: action, 1992 startTime: time, 1993 duration: durMs, 1994 isActive: true, 1995 blend: 0 1996 }); 1997 } else if (!shouldBeActive && existing && existing.isActive) { 1998 existing.isActive = false;
1999 } 2000 } 2001 } 2002 const sorted = [...this.activeActions.entries()].sort((a, b) => a[1].def.priority - b[1].def.priority); 2003 for (const [id, active] of sorted) { 2004 const elapsed = time - active.startTime; 2005 const { def } = active; 2006 if (!def.loop && elapsed >= active.duration + def.blendOutMs) { 2007 this.activeActions.delete(id); 2008 if (def.trigger === "idle") { 2009 const schedule = this.idleSchedules.get(id); 2010 if (schedule) { 2011 const minInt = def.minInterval ?? 1e4; 2012 const maxInt = def.maxInterval ?? 3e4; 2013 schedule.nextFireTime = time + minInt + Math.random() * (maxInt - minInt); 2014 } 2015 } 2016 continue; 2017 } 2018 let blend = 1; 2019 if (active.isActive) { 2020 if (elapsed < def.blendInMs && def.blendInMs > 0) { 2021 blend = elapsed / def.blendInMs; 2022 } 2023 } else { 2024 blend = Math.max(0, active.blend - 1 / Math.max(1, def.blendOutMs) * 16.67); 2025 if (blend <= 0) { 2026 this.activeActions.delete(id); 2027 continue; 2028 } 2029 } 2030 active.blend = blend; 2031 let progress; 2032 if (def.loop) { 2033 progress = elapsed % active.duration / active.duration; 2034 } else { 2035 progress = Math.min(1, elapsed / active.duration); 2036 } 2037 let behaviorMult = 1; 2038 if (bodyBehaviorMults) { 2039 if (def.id === "groove_sway") behaviorMult = bodyBehaviorMults.grooveSway ?? 1; 2040 else if (def.id === "head_rotation") behaviorMult = bodyBehaviorMults.headRotation ?? 1; 2041 else if (def.id === "foot_tap") behaviorMult = bodyBehaviorMults.footTap ?? 1; 2042 } 2043 for (const channel of def.channels) { 2044 const key = `${channel.joint}.${channel.property}`; 2045 let value = 0; 2046 if (channel.type === "wave") { 2047 value = this.evaluateWaveChannel(channel, time, bps, intensity, def.tempoSync); 2048 } else if (channel.type === "keyframe") { 2049 value = this.evaluateKeyframeChannel(channel, progress); 2050 } 2051 let channelMult = behaviorMult; 2052 if (def.trigger === "idle" && idleBehaviorMults) { 2053 if (channel.joint === "head") channelMult *= idleBehaviorMults.headSway ?? 1; 2054 else if (channel.joint === "body") channelMult *= idleBehaviorMults.bodySway ?? 1; 2055 else if (channel.joint.includes("arm") || channel.joint.includes("hand")) channelMult *= idleBehaviorMults.armSway ?? 1; 2056 } 2057 value *= blend * channelMult; 2058 deltas[key] = (deltas[key] || 0) + value; 2059 } 2060 } 2061 return deltas; 2062 } 2063 /** Check whether an idle_wave action is currently active */ 2064 isIdleWaveActive() { 2065 for (const [, active] of this.activeActions) { 2066 if (active.def.controlsIk && active.isActive && active.blend > 0.01) { 2067 return true; 2068 } 2069 } 2070 return false; 2071 } 2072 /** Get the current wave oscillation data for IK wave actions */ 2073 getIdleWaveData(time) { 2074 for (const [, active] of this.activeActions) { 2075 if (!active.def.controlsIk || active.blend < 0.01) continue; 2076 const waveChannel = active.def.channels.find((c) => c.joint === "_ik_wave"); 2077 if (!waveChannel) continue; 2078 const elapsed = time - active.startTime; 2079 const baseAngle = (waveChannel.offset ?? 45) * Math.PI / 180; 2080 const amplitude = (waveChannel.amplitude ?? 25) * Math.PI / 180; 2081 const freq = waveChannel.frequency ?? 2.39; 2082 const waveTheta = baseAngle + Math.sin(elapsed * 1e-3 * freq * Math.PI * 2) * amplitude; 2083 return { 2084 blend: active.blend, 2085 waveTheta, 2086 side: active.def.ikSide || "r" 2087 }; 2088 } 2089 return null; 2090 } 2091 /** Force-start an action (used by forceWave in PuppetStudio) */ 2092 forceAction(actionId, time, bps) { 2093 const action = this.actions.find((a) => a.id === actionId); 2094 if (!action) return; 2095 const existing = this.activeActions.get(actionId); 2096 if (existing) return; 2097 const durMs = action.tempoSync ? action.duration / bps * 1e3 : action.duration; 2098 this.activeActions.set(actionId, { 2099 def: action, 2100 startTime: time, 2101 duration: durMs, 2102 isActive: true, 2103 blend: 0 2104 }); 2105 } 2106 /** Stop all actions (used when song state changes) */ 2107 clearActive() { 2108 for (const [, active] of this.activeActions) { 2109 active.isActive = false;
2110 } 2111 } 2112 shouldActivate(action, time, isSongPlaying, hasActiveNotes) { 2113 switch (action.trigger) { 2114 case "playing": 2115 return isSongPlaying; 2116 case "idle": 2117 if (isSongPlaying || this.disableRandomIdle) return false; 2118 const schedule = this.idleSchedules.get(action.id); 2119 if (!schedule) return false; 2120 if (time >= schedule.nextFireTime && !this.activeActions.has(action.id)) { 2121 return true; 2122 } 2123 return this.activeActions.has(action.id); 2124 case "note_on": 2125 return hasActiveNotes; 2126 case "always": 2127 return true; 2128 default: 2129 return false; 2130 } 2131 } 2132 evaluateWaveChannel(channel, time, bps, intensity, tempoSync) { 2133 const freq = channel.frequency ?? 1; 2134 const amplitude = channel.amplitude ?? 1; 2135 const amplitudeBase = channel.amplitudeBase ?? 0; 2136 const phase = channel.phase ?? 0; 2137 const offset = channel.offset ?? 0; 2138 const intensityScale = channel.intensityScale ?? 0; 2139 let t; 2140 if (tempoSync) { 2141 t = time / 1e3 * bps * Math.PI * 2 * freq + phase; 2142 } else { 2143 t = time / 1e3 * Math.PI * 2 * freq + phase; 2144 } 2145 let wave; 2146 switch (channel.waveform ?? "sine") { 2147 case "sine": 2148 wave = Math.sin(t); 2149 break; 2150 case "cosine": 2151 wave = Math.cos(t); 2152 break; 2153 case "triangle": 2154 wave = 2 / Math.PI * Math.asin(Math.sin(t)); 2155 break; 2156 case "abs_sine": 2157 wave = Math.max(0, Math.sin(t)); 2158 break; 2159 default: 2160 wave = Math.sin(t); 2161 } 2162 let effectiveAmplitude; 2163 if (intensityScale > 0) { 2164 effectiveAmplitude = amplitudeBase + amplitude * intensity * intensityScale; 2165 } else { 2166 effectiveAmplitude = amplitude; 2167 } 2168 return wave * effectiveAmplitude + offset; 2169 } 2170 evaluateKeyframeChannel(channel, progress) { 2171 const kfs = channel.keyframes; 2172 if (!kfs || kfs.length === 0) return 0; 2173 if (kfs.length === 1) return kfs[0].value; 2174 let prev = kfs[0]; 2175 let next = kfs[kfs.length - 1]; 2176 for (let i = 0; i < kfs.length - 1; i++) { 2177 if (progress >= kfs[i].time && progress <= kfs[i + 1].time) { 2178 prev = kfs[i]; 2179 next = kfs[i + 1]; 2180 break; 2181 } 2182 } 2183 if (prev === next) return prev.value; 2184 const segmentDuration = next.time - prev.time; 2185 if (segmentDuration <= 0) return prev.value; 2186 let t = (progress - prev.time) / segmentDuration; 2187 const easing = next.easing ?? "sine"; 2188 switch (easing) { 2189 case "linear": 2190 break; 2191 case "sine": 2192 t = 0.5 - 0.5 * Math.cos(t * Math.PI); 2193 break; 2194 case "ease-in-out": 2195 t = t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t); 2196 break; 2197 } 2198 return prev.value + (next.value - prev.value) * t; 2199 } 2200} 2201class BaseAnimator { 2202 // true when custom or default actions are loaded 2203 constructor(config) { 2204 var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t; 2205 this.baseSwayOffset = 0; 2206 this.sockets = {}; 2207 this.jointBases = {}; 2208 this.idleAction = "none"; 2209 this.idleActionStartTime = 0; 2210 this.idleActionDuration = 0; 2211 this.nextIdleActionTime = 0; 2212 this.idleWaveAngle = 0; 2213 this.lastInstrumentDef = null; 2214 this.resolvedTargetL = { x: -30, y: 50 }; 2215 this.resolvedTargetR = { x: 30, y: 50 }; 2216 this.actionEngine = new ActionEngine(); 2217 this.useActionEngine = false; 2218 this.config = config; 2219 this.jointBases = getSvgJointBasePositions(config.svgContent); 2220 const svgCoords = parseSvgSocketsAndPlugs(config.svgContent); 2221 const sockets = {};
2222 Object.entries(svgCoords).forEach(([key, pt]) => { 2223 if (key.startsWith("socket_")) { 2224 sockets[key.replace("socket_", "")] = pt; 2225 } 2226 }); 2227 if (config.sockets) { 2228 Object.entries(config.sockets).forEach(([key, pt]) => { 2229 sockets[key] = pt; 2230 }); 2231 } 2232 this.sockets = sockets; 2233 const shoulderL = sockets.shoulder_l || { x: ((_a = config.ikLeftArm) == null ? void 0 : _a.originX) ?? -30, y: ((_b = config.ikLeftArm) == null ? void 0 : _b.originY) ?? -20 }; 2234 const shoulderR = sockets.shoulder_r || { x: ((_c = config.ikRightArm) == null ? void 0 : _c.originX) ?? 30, y: ((_d = config.ikRightArm) == null ? void 0 : _d.originY) ?? -20 }; 2235 let upperArmLengthL = ((_e = config.ikLeftArm) == null ? void 0 : _e.upperArmLength) || 40; 2236 let lowerArmLengthL = ((_f = config.ikLeftArm) == null ? void 0 : _f.lowerArmLength) || 40; 2237 if (sockets.shoulder_l && sockets.elbow_l) { 2238 const dx = sockets.elbow_l.x - sockets.shoulder_l.x; 2239 const dy = sockets.elbow_l.y - sockets.shoulder_l.y; 2240 upperArmLengthL = Math.sqrt(dx * dx + dy * dy); 2241 } 2242 if (sockets.elbow_l && sockets.hand_l) { 2243 const dx = sockets.hand_l.x - sockets.elbow_l.x; 2244 const dy = sockets.hand_l.y - sockets.elbow_l.y; 2245 lowerArmLengthL = Math.sqrt(dx * dx + dy * dy); 2246 } 2247 let upperArmLengthR = ((_g = config.ikRightArm) == null ? void 0 : _g.upperArmLength) || 40; 2248 let lowerArmLengthR = ((_h = config.ikRightArm) == null ? void 0 : _h.lowerArmLength) || 40; 2249 if (sockets.shoulder_r && sockets.elbow_r) { 2250 const dx = sockets.elbow_r.x - sockets.shoulder_r.x; 2251 const dy = sockets.elbow_r.y - sockets.shoulder_r.y; 2252 upperArmLengthR = Math.sqrt(dx * dx + dy * dy); 2253 } 2254 if (sockets.elbow_r && sockets.hand_r) { 2255 const dx = sockets.hand_r.x - sockets.elbow_r.x; 2256 const dy = sockets.hand_r.y - sockets.elbow_r.y; 2257 lowerArmLengthR = Math.sqrt(dx * dx + dy * dy); 2258 } 2259 this.config.ikLeftArm = { 2260 ...config.ikLeftArm, 2261 originX: shoulderL.x, 2262 originY: shoulderL.y, 2263 upperArmLength: upperArmLengthL, 2264 lowerArmLength: lowerArmLengthL 2265 }; 2266 this.config.ikRightArm = { 2267 ...config.ikRightArm, 2268 originX: shoulderR.x, 2269 originY: shoulderR.y, 2270 upperArmLength: upperArmLengthR, 2271 lowerArmLength: lowerArmLengthR 2272 }; 2273 this.state = { 2274 intensity: 0, 2275 isPlaying: false, 2276 joints: { 2277 head: { x: 0, y: 0, rotation: 0 }, 2278 body: { x: 0, y: 0, rotation: 0 }, 2279 shoulder_l: { x: shoulderL.x, y: shoulderL.y, rotation: 0 }, 2280 upper_arm_l: { x: shoulderL.x, y: shoulderL.y, rotation: 0 }, 2281 elbow_l: { x: ((_i = sockets.elbow_l) == null ? void 0 : _i.x) ?? -30, y: ((_j = sockets.elbow_l) == null ? void 0 : _j.y) ?? 20, rotation: 0 }, 2282 lower_arm_l: { x: ((_k = sockets.elbow_l) == null ? void 0 : _k.x) ?? -30, y: ((_l = sockets.elbow_l) == null ? void 0 : _l.y) ?? 20, rotation: 0 }, 2283 hand_l: { x: ((_m = sockets.hand_l) == null ? void 0 : _m.x) ?? -30, y: ((_n = sockets.hand_l) == null ? void 0 : _n.y) ?? 50, rotation: 0 }, 2284 shoulder_r: { x: shoulderR.x, y: shoulderR.y, rotation: 0 }, 2285 upper_arm_r: { x: shoulderR.x, y: shoulderR.y, rotation: 0 }, 2286 elbow_r: { x: ((_o = sockets.elbow_r) == null ? void 0 : _o.x) ?? 30, y: ((_p = sockets.elbow_r) == null ? void 0 : _p.y) ?? 20, rotation: 0 }, 2287 lower_arm_r: { x: ((_q = sockets.elbow_r) == null ? void 0 : _q.x) ?? 30, y: ((_r = sockets.elbow_r) == null ? void 0 : _r.y) ?? 20, rotation: 0 }, 2288 hand_r: { x: ((_s = sockets.hand_r) == null ? void 0 : _s.x) ?? 30, y: ((_t = sockets.hand_r) == null ? void 0 : _t.y) ?? 50, rotation: 0 }, 2289 leg_l: { x: 0, y: 0, rotation: 0 }, 2290 foot_l: { x: 0, y: 0, rotation: 0 }, 2291 leg_r: { x: 0, y: 0, rotation: 0 }, 2292 foot_r: { x: 0, y: 0, rotation: 0 } 2293 } 2294 }; 2295 const customActions = config.actions; 2296 if (customActions && customActions.length > 0) { 2297 this.actionEngine.setActions(customActions); 2298 this.useActionEngine = true; 2299 } else { 2300 this.actionEngine.setActions(getDefaultBodyActions()); 2301 this.useActionEngine = true; 2302 } 2303 } 2304 getState() { 2305 return this.state; 2306 } 2307 syncShoulders() { 2308 var _a, _b, _c, _d, _e, _f, _g, _h; 2309 this.state.joints.shoulder_l.x = ((_a = this.config.ikLeftArm) == null ? void 0 : _a.originX) ?? -30; 2310 this.state.joints.shoulder_l.y = ((_b = this.config.ikLeftArm) == null ? void 0 : _b.originY) ?? -20; 2311 this.state.joints.upper_arm_l.x = ((_c = this.config.ikLeftArm) == null ? void 0 : _c.originX) ?? -30; 2312 this.state.joints.upper_arm_l.y = ((_d = this.config.ikLeftArm) == null ? void 0 : _d.originY) ?? -20; 2313 this.state.joints.shoulder_r.x = ((_e = this.config.ikRightArm) == null ? void 0 : _e.originX) ?? 30; 2314 this.state.joints.shoulder_r.y = ((_f = this.config.ikRightArm) == null ? void 0 : _f.originY) ?? -20; 2315 this.state.joints.upper_arm_r.x = ((_g = this.config.ikRightArm) == null ? void 0 : _g.originX) ?? 30; 2316 this.state.joints.upper_arm_r.y = ((_h = this.config.ikRightArm) == null ? void 0 : _h.originY) ?? -20; 2317 } 2318 updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef) { 2319 var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k; 2320 this.syncShoulders(); 2321 this.state.isPlaying = activeNotes.length > 0; 2322 if (instrumentDef !== this.lastInstrumentDef) { 2323 this.prevOffsetL = void 0; 2324 this.prevOffsetR = void 0; 2325 } 2326 this.lastInstrumentDef = instrumentDef || null; 2327 if (this.state.intensity > 0) { 2328 this.state.intensity -= 0.05; 2329 if (this.state.intensity < 0) this.state.intensity = 0; 2330 }
2331 activeNotes.forEach((note) => { 2332 const vel = note.velocity / 127; 2333 if (vel > this.state.intensity) { 2334 this.state.intensity = vel; 2335 } 2336 }); 2337 this.state.joints.head.x = 0; 2338 this.state.joints.head.y = 0; 2339 this.state.joints.body.y = 0; 2340 this.state.joints.leg_l.x = 0; 2341 this.state.joints.leg_l.y = 0; 2342 this.state.joints.leg_r.x = 0; 2343 this.state.joints.leg_r.y = 0; 2344 this.state.joints.foot_l.x = 0; 2345 this.state.joints.foot_l.y = 0; 2346 this.state.joints.foot_r.x = 0; 2347 this.state.joints.foot_r.y = 0; 2348 this.state.joints.foot_r.rotation = 0; 2349 this.state.joints.head.rotation = 0; 2350 if (this.useActionEngine) { 2351 this.actionEngine.disableRandomIdle = !!this.disableRandomIdle; 2352 const bodyBehaviorMults = { 2353 grooveSway: ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.grooveSway) ?? 1, 2354 headRotation: ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headRotation) ?? 1, 2355 footTap: ((_c = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _c.footTap) ?? 1 2356 }; 2357 const idleBehaviorMults = { 2358 bodySway: ((_d = instrumentDef == null ? void 0 : instrumentDef.idleBehavior) == null ? void 0 : _d.bodySway) ?? 1, 2359 headSway: ((_e = instrumentDef == null ? void 0 : instrumentDef.idleBehavior) == null ? void 0 : _e.headSway) ?? 1, 2360 armSway: ((_f = instrumentDef == null ? void 0 : instrumentDef.idleBehavior) == null ? void 0 : _f.armSway) ?? 1 2361 }; 2362 const previewIntensity = this.actionEngine.forceActionId ? 1 : isSongPlaying ? Math.max(0.5, this.state.intensity) : this.state.intensity; 2363 const deltas = this.actionEngine.tick( 2364 time, 2365 bps, 2366 previewIntensity, 2367 isSongPlaying, 2368 activeNotes.length > 0, 2369 bodyBehaviorMults, 2370 idleBehaviorMults 2371 ); 2372 for (const [key, value] of Object.entries(deltas)) { 2373 const [jointName, prop] = key.split("."); 2374 if (jointName.startsWith("_")) continue; 2375 const joint = this.state.joints[jointName]; 2376 if (joint && (prop === "x" || prop === "y" || prop === "rotation")) { 2377 joint[prop] += value; 2378 } 2379 } 2380 } else { 2381 const grooveSwayMult = ((_g = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _g.grooveSway) ?? 1; 2382 const headRotationMult = ((_h = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _h.headRotation) ?? 1; 2383 const footTapMult = ((_i = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _i.footTap) ?? 1; 2384 if (isSongPlaying) { 2385 const grooveSway2 = Math.sin(time / 1e3 * bps * Math.PI) * (2 + this.state.intensity * 10) * grooveSwayMult; 2386 const headRotation2 = Math.sin(time / 1e3 * bps * Math.PI * 0.5) * (5 + this.state.intensity * 15) * headRotationMult; 2387 const footTap2 = Math.max(0, Math.sin(time / 1e3 * bps * Math.PI * 2)) * 10 * this.state.intensity * footTapMult; 2388 this.state.joints.body.y = grooveSway2; 2389 this.state.joints.head.rotation = headRotation2; 2390 this.state.joints.foot_r.y = -footTap2; 2391 this.state.joints.foot_r.rotation = footTap2 * 2; 2392 } 2393 } 2394 this.state.joints.shoulder_l.x += this.state.joints.body.x; 2395 this.state.joints.shoulder_l.y += this.state.joints.body.y; 2396 this.state.joints.shoulder_r.x += this.state.joints.body.x; 2397 this.state.joints.shoulder_r.y += this.state.joints.body.y; 2398 this.state.joints.head.x += this.state.joints.body.x; 2399 this.state.joints.head.y += this.state.joints.body.y; 2400 this.state.joints.leg_l.x += this.state.joints.body.x; 2401 this.state.joints.leg_l.y += this.state.joints.body.y; 2402 this.state.joints.leg_r.x += this.state.joints.body.x; 2403 this.state.joints.leg_r.y += this.state.joints.body.y; 2404 this.state.joints.foot_l.x += this.state.joints.body.x; 2405 this.state.joints.foot_l.y += this.state.joints.body.y; 2406 this.state.joints.foot_r.x += this.state.joints.body.x; 2407 this.state.joints.foot_r.y += this.state.joints.body.y; 2408 if (activeNotes.length > 0) { 2409 const now = time; 2410 const bowGapMs = ((_j = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _j.bowGapMs) ?? 500; 2411 const bowPhaseStep = ((_k = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _k.bowPhaseStep) ?? 0.05; 2412 if (!this.state.lastNoteTime || now - this.state.lastNoteTime > bowGapMs) { 2413 this.state.bowDirection = (this.state.bowDirection || 1) === 1 ? -1 : 1; 2414 this.state.bowPhase = this.state.bowDirection === 1 ? 0 : 1; 2415 } else { 2416 this.state.bowPhase = (this.state.bowPhase || 0) + (this.state.bowDirection === 1 ? bowPhaseStep : -bowPhaseStep); 2417 if (this.state.bowPhase > 1) { 2418 this.state.bowPhase = 1; 2419 this.state.bowDirection = -1; 2420 } 2421 if (this.state.bowPhase < 0) { 2422 this.state.bowPhase = 0; 2423 this.state.bowDirection = 1; 2424 } 2425 }
2426 this.state.lastNoteTime = now; 2427 } 2428 } 2429 getTargetFromMotion(motion2, hand, lowest, highest, bps, time, instrumentDef) { 2430 var _a, _b; 2431 if (motion2.logic === "none") return { ...motion2.start }; 2432 let t = 0; 2433 if (this.state.isPlaying) { 2434 if (motion2.logic === "oscillate") { 2435 t = (Math.sin(time / 1e3 * 0.01 * bps) + 1) / 2; 2436 } else if (motion2.logic === "strike") { 2437 t = this.state.intensity; 2438 } else if (motion2.logic === "pitch_map") { 2439 const minNote = ((_a = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _a.minNote) ?? 40; 2440 const maxNote = ((_b = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _b.maxNote) ?? 88; 2441 const noteVal = hand === "l" ? lowest : highest; 2442 t = Math.max(0, Math.min(1, (noteVal - minNote) / (maxNote - minNote))); 2443 } else if (motion2.logic === "phrase_bow") { 2444 t = this.state.bowPhase || 0; 2445 } 2446 } else { 2447 if (motion2.logic === "phrase_bow") t = this.state.bowPhase || 0; 2448 else t = 0; 2449 } 2450 return { 2451 x: this.lerp(motion2.start.x, motion2.end.x, t), 2452 y: this.lerp(motion2.start.y, motion2.end.y, t) 2453 }; 2454 } 2455 resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, defaultLerpL = 0.3, defaultLerpR = 0.3) { 2456 var _a, _b, _c, _d, _e, _f, _g, _h, _i; 2457 targetL = { ...targetL }; 2458 targetR = { ...targetR }; 2459 const lerpL = ((_a = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _a.ikLerpL) ?? defaultLerpL; 2460 const lerpR = ((_b = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _b.ikLerpR) ?? defaultLerpR; 2461 let anchorWorldL = { x: this.state.joints.shoulder_l.x, y: this.state.joints.shoulder_l.y }; 2462 let anchorWorldR = { x: this.state.joints.shoulder_r.x, y: this.state.joints.shoulder_r.y }; 2463 if (instrumentDef) { 2464 const instPlugs = {}; 2465 if (instrumentDef.svgContent) { 2466 const svgCoords = parseSvgSocketsAndPlugs(instrumentDef.svgContent);
2467 Object.entries(svgCoords).forEach(([key, pt]) => { 2468 if (key.startsWith("plug_")) { 2469 instPlugs[key.replace("plug_", "")] = pt; 2470 } 2471 }); 2472 } 2473 if (instrumentDef.plugs) { 2474 Object.entries(instrumentDef.plugs).forEach(([key, pt]) => { 2475 instPlugs[key] = pt; 2476 }); 2477 } 2478 let gluingArray = Array.isArray(instrumentDef.gluing) ? instrumentDef.gluing : instrumentDef.gluing ? [instrumentDef.gluing] : []; 2479 const mouthpiecePlug = instPlugs.mouthpiece || instrumentDef.headAnchor; 2480 const mouthpieceSocketName = (mouthpiecePlug == null ? void 0 : mouthpiecePlug.socket) || "mouth"; 2481 const mouthSocket = this.sockets[mouthpieceSocketName] || this.sockets.mouth; 2482 if (gluingArray.length === 0 && (instrumentDef.headAnchor || instPlugs.mouthpiece)) { 2483 gluingArray = [{ type: "one-point", ip: instrumentDef.headAnchor || instPlugs.mouthpiece, t1: "head", tp: mouthSocket || { x: 0, y: -45 } }]; 2484 } 2485 const firstGlue = gluingArray.length > 0 ? gluingArray[0] : null; 2486 const hasOnePoint = (firstGlue == null ? void 0 : firstGlue.type) === "one-point"; 2487 if (hasOnePoint && firstGlue) { 2488 let targetWorld = { x: 0, y: 0 }; 2489 let targetRot = 0; 2490 const t1Key = firstGlue.t1 || "head"; 2491 const defaultTp = t1Key === "head" ? mouthSocket || { x: 0, y: -45 } : { x: 0, y: 0 }; 2492 const tpVal = ((_c = firstGlue.tp) == null ? void 0 : _c.x) !== void 0 ? firstGlue.tp : defaultTp; 2493 const targetState = this.state.joints[t1Key]; 2494 if (targetState) { 2495 targetRot = targetState.rotation || 0; 2496 const rad2 = targetRot * Math.PI / 180; 2497 const cos2 = Math.cos(rad2); 2498 const sin2 = Math.sin(rad2); 2499 const targetBase = this.jointBases[t1Key] || { x: 0, y: 0 }; 2500 targetWorld = { 2501 x: targetBase.x + targetState.x + (tpVal.x * cos2 - tpVal.y * sin2), 2502 y: targetBase.y + targetState.y + (tpVal.x * sin2 + tpVal.y * cos2) 2503 }; 2504 } 2505 anchorWorldL = targetWorld; 2506 anchorWorldR = targetWorld; 2507 const instRot = targetRot + (this.config.instrumentRotation || 0); 2508 const instScaleX = this.config.instrumentScale ?? 1; 2509 const instScaleY = this.config.instrumentScale ?? 1; 2510 let ipVal = { x: 0, y: 0 }; 2511 if ((firstGlue == null ? void 0 : firstGlue.plugName) && instPlugs[firstGlue.plugName]) { 2512 ipVal = instPlugs[firstGlue.plugName]; 2513 } else if (firstGlue && !firstGlue.plugName && firstGlue.t1 === "head" && (instPlugs.mouthpiece || instrumentDef.headAnchor)) { 2514 ipVal = instPlugs.mouthpiece || instrumentDef.headAnchor; 2515 } else { 2516 ipVal = (firstGlue == null ? void 0 : firstGlue.ip) || instrumentDef.headAnchor || instPlugs.mouthpiece || { x: 0, y: 0 }; 2517 } 2518 const ipValTransformed = transformPointWithStatic(ipVal, instrumentDef.transform); 2519 const rad = instRot * Math.PI / 180; 2520 const cos = Math.cos(rad); 2521 const sin = Math.sin(rad); 2522 const leftGlue = gluingArray.find((g) => g.t1 === "hand_l"); 2523 const rightGlue = gluingArray.find((g) => g.t1 === "hand_r"); 2524 const leftHandPlug = ((leftGlue == null ? void 0 : leftGlue.plugName) ? instPlugs[leftGlue.plugName] : null) || ((leftGlue == null ? void 0 : leftGlue.t1) ? instPlugs[leftGlue.t1] : null) || ((_d = Object.entries(instPlugs).find(([_, pt]) => pt.socket === "hand_l")) == null ? void 0 : _d[1]) || instPlugs.hand_l || (leftGlue == null ? void 0 : leftGlue.ip); 2525 const rightHandPlug = ((rightGlue == null ? void 0 : rightGlue.plugName) ? instPlugs[rightGlue.plugName] : null) || ((rightGlue == null ? void 0 : rightGlue.t1) ? instPlugs[rightGlue.t1] : null) || ((_e = Object.entries(instPlugs).find(([_, pt]) => pt.socket === "hand_r")) == null ? void 0 : _e[1]) || instPlugs.hand_r || (rightGlue == null ? void 0 : rightGlue.ip); 2526 const leftHandPlugTransformed = leftHandPlug ? transformPointWithStatic(leftHandPlug, instrumentDef.transform) : null; 2527 const rightHandPlugTransformed = rightHandPlug ? transformPointWithStatic(rightHandPlug, instrumentDef.transform) : null; 2528 const armReachL = (((_f = this.config.ikLeftArm) == null ? void 0 : _f.upperArmLength) ?? 40) + (((_g = this.config.ikLeftArm) == null ? void 0 : _g.lowerArmLength) ?? 40); 2529 const armReachR = (((_h = this.config.ikRightArm) == null ? void 0 : _h.upperArmLength) ?? 40) + (((_i = this.config.ikRightArm) == null ? void 0 : _i.lowerArmLength) ?? 40); 2530 const shoulderL = { x: this.state.joints.shoulder_l.x, y: this.state.joints.shoulder_l.y }; 2531 const shoulderR = { x: this.state.joints.shoulder_r.x, y: this.state.joints.shoulder_r.y }; 2532 if (instrumentDef.mountPoint !== "hand_l") { 2533 if (leftHandPlugTransformed) { 2534 const txL = (leftHandPlugTransformed.x - ipValTransformed.x) * instScaleX; 2535 const tyL = (leftHandPlugTransformed.y - ipValTransformed.y) * instScaleY; 2536 const worldLx = targetWorld.x + (txL * cos - tyL * sin); 2537 const worldLy = targetWorld.y + (txL * sin + tyL * cos); 2538 const dxL = worldLx - shoulderL.x; 2539 const dyL = worldLy - shoulderL.y; 2540 if (Math.sqrt(dxL * dxL + dyL * dyL) <= armReachL * 1.2) { 2541 targetL = { x: worldLx, y: worldLy }; 2542 } else if ((leftGlue == null ? void 0 : leftGlue.tp) !== void 0) { 2543 const rx = leftGlue.tp.x * cos - leftGlue.tp.y * sin; 2544 const ry = leftGlue.tp.x * sin + leftGlue.tp.y * cos; 2545 targetL = { x: targetWorld.x + rx, y: targetWorld.y + ry }; 2546 } 2547 } else if ((leftGlue == null ? void 0 : leftGlue.tp) !== void 0) { 2548 const rx = leftGlue.tp.x * cos - leftGlue.tp.y * sin; 2549 const ry = leftGlue.tp.x * sin + leftGlue.tp.y * cos; 2550 targetL = { x: targetWorld.x + rx, y: targetWorld.y + ry }; 2551 } 2552 } 2553 if (instrumentDef.mountPoint !== "hand_r") { 2554 if (rightHandPlugTransformed) { 2555 const txR = (rightHandPlugTransformed.x - ipValTransformed.x) * instScaleX; 2556 const tyR = (rightHandPlugTransformed.y - ipValTransformed.y) * instScaleY; 2557 const worldRx = targetWorld.x + (txR * cos - tyR * sin); 2558 const worldRy = targetWorld.y + (txR * sin + tyR * cos); 2559 const dxR = worldRx - shoulderR.x; 2560 const dyR = worldRy - shoulderR.y; 2561 if (Math.sqrt(dxR * dxR + dyR * dyR) <= armReachR * 1.2) { 2562 targetR = { x: worldRx, y: worldRy }; 2563 } else if ((rightGlue == null ? void 0 : rightGlue.tp) !== void 0) { 2564 const rx = rightGlue.tp.x * cos - rightGlue.tp.y * sin; 2565 const ry = rightGlue.tp.x * sin + rightGlue.tp.y * cos; 2566 targetR = { x: targetWorld.x + rx, y: targetWorld.y + ry }; 2567 } 2568 } else if ((rightGlue == null ? void 0 : rightGlue.tp) !== void 0) { 2569 const rx = rightGlue.tp.x * cos - rightGlue.tp.y * sin; 2570 const ry = rightGlue.tp.x * sin + rightGlue.tp.y * cos; 2571 targetR = { x: targetWorld.x + rx, y: targetWorld.y + ry }; 2572 } 2573 } 2574 } else if (instrumentDef.mountPoint && instrumentDef.mountPoint !== "root" && instrumentDef.mountPoint !== "front") { 2575 const mountState = this.state.joints[instrumentDef.mountPoint]; 2576 if (mountState) { 2577 let dx = mountState.x || 0; 2578 let dy = mountState.y || 0; 2579 let rot = mountState.rotation || 0; 2580 const pivotX = 0; 2581 const pivotY = 40; 2582 const rad = rot * Math.PI / 180; 2583 const cos = Math.cos(rad); 2584 const sin = Math.sin(rad); 2585 if (instrumentDef.mountPoint !== "hand_l") { 2586 const tx = targetL.x - pivotX; 2587 const ty = targetL.y - pivotY; 2588 targetL.x = pivotX + (tx * cos - ty * sin) + dx; 2589 targetL.y = pivotY + (tx * sin + ty * cos) + dy; 2590 } 2591 if (instrumentDef.mountPoint !== "hand_r") { 2592 const tx = targetR.x - pivotX; 2593 const ty = targetR.y - pivotY; 2594 targetR.x = pivotX + (tx * cos - ty * sin) + dx; 2595 targetR.y = pivotY + (tx * sin + ty * cos) + dy; 2596 } 2597 const mountBase = this.jointBases[instrumentDef.mountPoint] || { x: 0, y: 0 }; 2598 const mountWorld = { 2599 x: mountBase.x + dx, 2600 y: mountBase.y + dy 2601 }; 2602 anchorWorldL = mountWorld; 2603 anchorWorldR = mountWorld; 2604 } 2605 } 2606 } 2607 this.resolvedTargetL = { ...targetL }; 2608 this.resolvedTargetR = { ...targetR }; 2609 const offsetL = { 2610 x: targetL.x - anchorWorldL.x, 2611 y: targetL.y - anchorWorldL.y 2612 }; 2613 const offsetR = { 2614 x: targetR.x - anchorWorldR.x, 2615 y: targetR.y - anchorWorldR.y 2616 }; 2617 if (!this.prevOffsetL) { 2618 this.prevOffsetL = { ...offsetL }; 2619 } 2620 if (!this.prevOffsetR) { 2621 this.prevOffsetR = { ...offsetR }; 2622 } 2623 const smoothOffsetL = { 2624 x: this.lerp(this.prevOffsetL.x, offsetL.x, lerpL), 2625 y: this.lerp(this.prevOffsetL.y, offsetL.y, lerpL) 2626 }; 2627 const smoothOffsetR = { 2628 x: this.lerp(this.prevOffsetR.x, offsetR.x, lerpR), 2629 y: this.lerp(this.prevOffsetR.y, offsetR.y, lerpR) 2630 }; 2631 this.prevOffsetL = { ...smoothOffsetL }; 2632 this.prevOffsetR = { ...smoothOffsetR }; 2633 const smoothL = { 2634 x: anchorWorldL.x + smoothOffsetL.x, 2635 y: anchorWorldL.y + smoothOffsetL.y 2636 }; 2637 const smoothR = { 2638 x: anchorWorldR.x + smoothOffsetR.x, 2639 y: anchorWorldR.y + smoothOffsetR.y 2640 }; 2641 this.applyIkToArm("l", smoothL); 2642 this.applyIkToArm("r", smoothR); 2643 this.applyIdleAction(time, isSongPlaying); 2644 } 2645 getFamilyDefaultRestPose(family) { 2646 switch (family) { 2647 case "percussion": 2648 return { leftHand: { x: -40, y: 50 }, rightHand: { x: 40, y: 50 } }; 2649 case "keys": 2650 return { leftHand: { x: -40, y: 40 }, rightHand: { x: 40, y: 40 } }; 2651 case "strings": 2652 return { leftHand: { x: -50, y: 10 }, rightHand: { x: 10, y: 40 } }; 2653 case "winds": 2654 return { leftHand: { x: -10, y: 30 }, rightHand: { x: 10, y: 35 } }; 2655 default: 2656 return { leftHand: { x: -30, y: 50 }, rightHand: { x: 30, y: 50 } }; 2657 } 2658 } 2659 tick(time, activeNotes, bps, isSongPlaying, instrumentDef) { 2660 var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n; 2661 this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef); 2662 let lowest = 128; 2663 let highest = -1;
2664 activeNotes.forEach((n) => { 2665 if (n.noteNumber < lowest) lowest = n.noteNumber; 2666 if (n.noteNumber > highest) highest = n.noteNumber; 2667 }); 2668 const familyDefault = this.getFamilyDefaultRestPose(this.config.family); 2669 let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_a = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _a.leftHand) || familyDefault.leftHand }; 2670 let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_b = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _b.rightHand) || familyDefault.rightHand }; 2671 if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) && activeNotes.length > 0 && ((_c = instrumentDef == null ? void 0 : instrumentDef.playAnimation) == null ? void 0 : _c.leftHandMode)) { 2672 const mode = instrumentDef.playAnimation.leftHandMode; 2673 let avgNote = 0; 2674 activeNotes.forEach((n) => avgNote += n.noteNumber); 2675 avgNote /= activeNotes.length; 2676 if (mode === "fret_neck") { 2677 const minNote = ((_d = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _d.minNote) ?? 40; 2678 const maxNote = ((_e = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _e.maxNote) ?? 88; 2679 const neckLength = ((_f = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _f.physicalLength) ?? 80; 2680 targetL.y += (avgNote - minNote) / (maxNote - minNote) * neckLength; 2681 } else if (mode === "strike") { 2682 const strikeFrequency = ((_g = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _g.strikeFrequency) ?? 0.02; 2683 const strikeAmplitude = ((_h = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _h.strikeAmplitude) ?? 10; 2684 targetL.y += Math.sin(time / 1e3 * strikeFrequency) * strikeAmplitude * this.state.intensity; 2685 } 2686 } 2687 if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) && activeNotes.length > 0 && ((_i = instrumentDef == null ? void 0 : instrumentDef.playAnimation) == null ? void 0 : _i.rightHandMode)) { 2688 const mode = instrumentDef.playAnimation.rightHandMode; 2689 if (mode === "bow") targetR.x += Math.sin(time / 1e3 * 0.01 * (bps * 0.5)) * 30; 2690 else if (mode === "strum") { 2691 const strumSpeedMult = ((_j = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _j.strumSpeedMult) ?? 0.03; 2692 const strumVerticalAmp = ((_k = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _k.strumVerticalAmp) ?? 15; 2693 const strumHorizontalAmp = ((_l = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _l.strumHorizontalAmp) ?? 5; 2694 const strumSpd = time / 1e3 * strumSpeedMult * (bps * 0.5); 2695 targetR.y += Math.sin(strumSpd) * strumVerticalAmp * this.state.intensity; 2696 targetR.x += Math.cos(strumSpd) * strumHorizontalAmp; 2697 } else if (mode === "strike") { 2698 const strikeFrequency = ((_m = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _m.strikeFrequency) ?? 0.02; 2699 const strikeAmplitude = ((_n = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _n.strikeAmplitude) ?? 10; 2700 targetR.y += Math.sin(time / 1e3 * strikeFrequency) * strikeAmplitude * this.state.intensity; 2701 } 2702 } 2703 this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying); 2704 } 2705 lerp(start, end, t) { 2706 return start * (1 - t) + end * t; 2707 } 2708 lerpAngle(start, end, t) { 2709 const delta = (end - start + 540) % 360 - 180; 2710 return start + delta * t; 2711 } 2712 getBlendFactor(elapsed, duration) { 2713 const transitionTime = 500; 2714 if (elapsed < transitionTime) { 2715 return elapsed / transitionTime; 2716 } else if (elapsed > duration - transitionTime) { 2717 return Math.max(0, (duration - elapsed) / transitionTime); 2718 } else { 2719 return 1; 2720 } 2721 } 2722 updateIdleState(time) { 2723 if (this.nextIdleActionTime === 0) { 2724 this.nextIdleActionTime = time + 2e3 + Math.random() * 8e3; 2725 } 2726 if (this.idleAction === "none") { 2727 if (time >= this.nextIdleActionTime) { 2728 const rand = Math.random(); 2729 if (rand < 0.4) { 2730 this.idleAction = "nod"; 2731 this.idleActionDuration = 1500 + Math.random() * 1e3; 2732 } else {
2733 let waveChoice = Math.random() < 0.5 ? "wave_l" : "wave_r"; 2734 if (this.lastInstrumentDef) { 2735 if (this.lastInstrumentDef.mountPoint === "hand_l") { 2736 waveChoice = "wave_r"; 2737 } else if (this.lastInstrumentDef.mountPoint === "hand_r") { 2738 waveChoice = "wave_l"; 2739 } 2740 } 2741 this.idleAction = waveChoice; 2742 this.idleActionDuration = 1e3 + Math.random() * 5e3; 2743 this.idleWaveAngle = (30 + Math.random() * 60) * (Math.PI / 180); 2744 } 2745 this.idleActionStartTime = time; 2746 } 2747 } else { 2748 if (time >= this.idleActionStartTime + this.idleActionDuration) { 2749 this.idleAction = "none"; 2750 const minInterval = 1e4; 2751 const maxInterval = 3e4; 2752 this.nextIdleActionTime = time + minInterval + Math.random() * (maxInterval - minInterval); 2753 } 2754 } 2755 } 2756 applyIdleAction(time, isSongPlaying) { 2757 var _a; 2758 if (this.forceWave) { 2759 if (this.useActionEngine) { 2760 let waveSide = "r"; 2761 if (((_a = this.lastInstrumentDef) == null ? void 0 : _a.mountPoint) === "hand_r") { 2762 waveSide = "l"; 2763 } 2764 const waveAction = this.actionEngine.getActions().find((a) => a.id === "idle_wave"); 2765 if (waveAction) { 2766 waveAction.ikSide = waveSide; 2767 this.actionEngine.forceAction("idle_wave", time, 1); 2768 } 2769 const waveData = this.actionEngine.getIdleWaveData(time); 2770 if (waveData) { 2771 this.applyWaveIk(waveData.side, waveData.waveTheta, waveData.blend, time); 2772 } 2773 return; 2774 } 2775 if (this.idleAction !== "wave_r" && this.idleAction !== "wave_l") { 2776 let waveChoice = "wave_r"; 2777 if (this.lastInstrumentDef) { 2778 if (this.lastInstrumentDef.mountPoint === "hand_r") { 2779 waveChoice = "wave_l"; 2780 } 2781 } 2782 this.idleAction = waveChoice; 2783 this.idleActionStartTime = time; 2784 this.idleActionDuration = 99999999; 2785 this.idleWaveAngle = 45 * Math.PI / 180; 2786 } 2787 } else if (!this.forceWave && this.idleActionDuration === 99999999) { 2788 this.idleAction = "none"; 2789 this.nextIdleActionTime = time + 2e3; 2790 } 2791 if (this.useActionEngine && !this.forceWave) { 2792 const waveData = this.actionEngine.getIdleWaveData(time); 2793 if (waveData) { 2794 let waveSide = waveData.side; 2795 if (this.lastInstrumentDef) { 2796 if (this.lastInstrumentDef.mountPoint === "hand_l") waveSide = "r"; 2797 else if (this.lastInstrumentDef.mountPoint === "hand_r") waveSide = "l"; 2798 } 2799 this.applyWaveIk(waveSide, waveData.waveTheta, waveData.blend, time); 2800 } 2801 return; 2802 } 2803 if (!this.forceWave) { 2804 if (this.disableRandomIdle) { 2805 this.idleAction = "none"; 2806 return; 2807 } else if (isSongPlaying) { 2808 this.idleAction = "none"; 2809 return; 2810 } 2811 } 2812 if (!this.forceWave) { 2813 this.updateIdleState(time); 2814 } 2815 if (this.idleAction === "nod") { 2816 const elapsed = time - this.idleActionStartTime; 2817 const progress = elapsed / this.idleActionDuration; 2818 const nodCycle = Math.sin(progress * Math.PI * 6); 2819 this.state.joints.head.rotation = nodCycle * 8; 2820 this.state.joints.head.y += Math.max(0, nodCycle) * 3; 2821 } else if (this.idleAction === "wave_l") { 2822 const elapsed = time - this.idleActionStartTime; 2823 const blend = this.getBlendFactor(elapsed, this.idleActionDuration); 2824 const waveTheta = this.idleWaveAngle + Math.sin(elapsed * 0.015) * (25 * Math.PI / 180); 2825 this.applyWaveIk("l", waveTheta, blend, time); 2826 } else if (this.idleAction === "wave_r") { 2827 const elapsed = time - this.idleActionStartTime; 2828 const blend = this.getBlendFactor(elapsed, this.idleActionDuration); 2829 const waveTheta = this.idleWaveAngle + Math.sin(elapsed * 0.015) * (25 * Math.PI / 180); 2830 this.applyWaveIk("r", waveTheta, blend, time); 2831 } 2832 } 2833 /** Shared IK geometry for the waving arm animation */ 2834 applyWaveIk(side, waveTheta, blend, _time) { 2835 const ikConfig = side === "l" ? this.config.ikLeftArm : this.config.ikRightArm; 2836 const L1 = (ikConfig == null ? void 0 : ikConfig.upperArmLength) || 40; 2837 const L2 = (ikConfig == null ? void 0 : ikConfig.lowerArmLength) || 40; 2838 const shoulderX = this.state.joints[`shoulder_${side}`].x; 2839 const shoulderY = this.state.joints[`shoulder_${side}`].y; 2840 if (side === "l") { 2841 const waveShoulderAngle = 180; 2842 const waveElbowX = shoulderX - L1; 2843 const waveElbowY = shoulderY; 2844 const waveElbowAngle = -90 - waveTheta * 180 / Math.PI;
2845 const waveHandX = waveElbowX + Math.cos(waveElbowAngle * Math.PI / 180) * L2; 2846 const waveHandY = waveElbowY + Math.sin(waveElbowAngle * Math.PI / 180) * L2; 2847 this.state.joints.upper_arm_l.rotation = this.lerpAngle(this.state.joints.upper_arm_l.rotation, waveShoulderAngle, blend); 2848 this.state.joints.elbow_l.x = this.lerp(this.state.joints.elbow_l.x, waveElbowX, blend); 2849 this.state.joints.elbow_l.y = this.lerp(this.state.joints.elbow_l.y, waveElbowY, blend); 2850 this.state.joints.lower_arm_l.rotation = this.lerpAngle(this.state.joints.lower_arm_l.rotation, waveElbowAngle, blend); 2851 this.state.joints.lower_arm_l.x = this.state.joints.elbow_l.x; 2852 this.state.joints.lower_arm_l.y = this.state.joints.elbow_l.y; 2853 this.state.joints.hand_l.x = this.lerp(this.state.joints.hand_l.x, waveHandX, blend); 2854 this.state.joints.hand_l.y = this.lerp(this.state.joints.hand_l.y, waveHandY, blend); 2855 this.state.joints.hand_l.rotation = this.lerpAngle(this.state.joints.hand_l.rotation || 0, waveElbowAngle, blend); 2856 } else { 2857 const waveShoulderAngle = 0; 2858 const waveElbowX = shoulderX + L1; 2859 const waveElbowY = shoulderY; 2860 const waveElbowAngle = -90 + waveTheta * 180 / Math.PI; 2861 const waveHandX = waveElbowX + Math.cos(waveElbowAngle * Math.PI / 180) * L2;
2862 const waveHandY = waveElbowY + Math.sin(waveElbowAngle * Math.PI / 180) * L2; 2863 this.state.joints.upper_arm_r.rotation = this.lerpAngle(this.state.joints.upper_arm_r.rotation, waveShoulderAngle, blend); 2864 this.state.joints.elbow_r.x = this.lerp(this.state.joints.elbow_r.x, waveElbowX, blend); 2865 this.state.joints.elbow_r.y = this.lerp(this.state.joints.elbow_r.y, waveElbowY, blend); 2866 this.state.joints.lower_arm_r.rotation = this.lerpAngle(this.state.joints.lower_arm_r.rotation, waveElbowAngle, blend); 2867 this.state.joints.lower_arm_r.x = this.state.joints.elbow_r.x; 2868 this.state.joints.lower_arm_r.y = this.state.joints.elbow_r.y; 2869 this.state.joints.hand_r.x = this.lerp(this.state.joints.hand_r.x, waveHandX, blend); 2870 this.state.joints.hand_r.y = this.lerp(this.state.joints.hand_r.y, waveHandY, blend); 2871 this.state.joints.hand_r.rotation = this.lerpAngle(this.state.joints.hand_r.rotation || 0, waveElbowAngle, blend); 2872 } 2873 } 2874 applyIkToArm(side, target) { 2875 const shoulder = this.state.joints[`shoulder_${side}`]; 2876 const ikConfig = side === "l" ? this.config.ikLeftArm : this.config.ikRightArm; 2877 const L1 = (ikConfig == null ? void 0 : ikConfig.upperArmLength) || 40; 2878 const L2 = (ikConfig == null ? void 0 : ikConfig.lowerArmLength) || 40; 2879 const bendDir = (ikConfig == null ? void 0 : ikConfig.bendDirection) || (side === "l" ? 1 : -1); 2880 const result = solve2BoneIK(shoulder, target, L1, L2, bendDir); 2881 this.state.joints[`upper_arm_${side}`].rotation = result.shoulderAngle; 2882 this.state.joints[`elbow_${side}`].x = result.elbow.x; 2883 this.state.joints[`elbow_${side}`].y = result.elbow.y; 2884 this.state.joints[`lower_arm_${side}`].rotation = result.elbowAngle; 2885 this.state.joints[`lower_arm_${side}`].x = result.elbow.x; 2886 this.state.joints[`lower_arm_${side}`].y = result.elbow.y; 2887 this.state.joints[`hand_${side}`].x = result.hand.x; 2888 this.state.joints[`hand_${side}`].y = result.hand.y; 2889 this.state.joints[`hand_${side}`].rotation = result.elbowAngle; 2890 } 2891} 2892class PercussionAnimator extends BaseAnimator { 2893 tick(time, activeNotes, bps, isSongPlaying, instrumentDef) { 2894 var _a, _b, _c, _d, _e, _f, _g; 2895 this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef); 2896 const bodySway = ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.bodySway) ?? 0; 2897 const headSway = ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headSway) ?? 0; 2898 if (bodySway > 0 && isSongPlaying) { 2899 const swayFrequency = ((_c = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _c.swayFrequency) ?? 5e-3; 2900 const sway = Math.sin(time / 1e3 * swayFrequency * bps) * bodySway * Math.max(0.5, this.state.intensity); 2901 this.state.joints.body.x = this.lerp(this.state.joints.body.x, sway, 0.1); 2902 this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, sway * headSway, 0.1); 2903 } else { 2904 this.state.joints.body.x = this.lerp(this.state.joints.body.x, 0, 0.05); 2905 this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, 0, 0.05); 2906 } 2907 let lowest = 128; 2908 let highest = -1; 2909 activeNotes.forEach((n) => { 2910 if (n.noteNumber < lowest) lowest = n.noteNumber; 2911 if (n.noteNumber > highest) highest = n.noteNumber; 2912 }); 2913 const familyDefault = this.getFamilyDefaultRestPose("percussion"); 2914 let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_d = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _d.leftHand) || familyDefault.leftHand }; 2915 let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_e = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _e.rightHand) || familyDefault.rightHand }; 2916 if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) || !(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) { 2917 if (activeNotes.length > 0) { 2918 const strikeFrequency = ((_f = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _f.strikeFrequency) ?? 0.02; 2919 const strikeAmplitude = ((_g = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _g.strikeAmplitude) ?? 10;
2920 activeNotes.forEach((note, idx) => { 2921 const map = this.config.noteMap[note.noteNumber]; 2922 const hitPos = map ? { x: map.x, y: map.y } : { x: (Math.random() - 0.5) * 60, y: 50 }; 2923 const strikeBounce = Math.sin(time / 1e3 * strikeFrequency) * strikeAmplitude * this.state.intensity; 2924 if ((map == null ? void 0 : map.hand) === "l") { 2925 if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) targetL = { x: hitPos.x, y: hitPos.y + strikeBounce }; 2926 } else if ((map == null ? void 0 : map.hand) === "r") { 2927 if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) targetR = { x: hitPos.x, y: hitPos.y + strikeBounce }; 2928 } else if (idx % 2 === 0) { 2929 if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) targetR = { x: hitPos.x, y: hitPos.y + strikeBounce }; 2930 } else { 2931 if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) targetL = { x: hitPos.x, y: hitPos.y + strikeBounce }; 2932 } 2933 }); 2934 } 2935 } 2936 this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.3, 0.3); 2937 } 2938} 2939class KeysAnimator extends BaseAnimator { 2940 tick(time, activeNotes, bps, isSongPlaying, instrumentDef) { 2941 var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m; 2942 this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef); 2943 const bodySway = ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.bodySway) ?? 0; 2944 const headSway = ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headSway) ?? 0; 2945 if (bodySway > 0 && isSongPlaying) { 2946 const swayFrequency = ((_c = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _c.swayFrequency) ?? 5e-3; 2947 const sway = Math.sin(time / 1e3 * swayFrequency * bps) * bodySway * Math.max(0.5, this.state.intensity); 2948 this.state.joints.body.x = this.lerp(this.state.joints.body.x, sway, 0.1); 2949 this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, sway * headSway, 0.1); 2950 } else { 2951 this.state.joints.body.x = this.lerp(this.state.joints.body.x, 0, 0.05); 2952 this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, 0, 0.05); 2953 } 2954 let lowest = 128; 2955 let highest = -1; 2956 activeNotes.forEach((n) => { 2957 if (n.noteNumber < lowest) lowest = n.noteNumber; 2958 if (n.noteNumber > highest) highest = n.noteNumber; 2959 }); 2960 const familyDefault = this.getFamilyDefaultRestPose("keys"); 2961 let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_d = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _d.leftHand) || familyDefault.leftHand }; 2962 let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_e = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _e.rightHand) || familyDefault.rightHand }; 2963 if ((!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) || !(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) && activeNotes.length > 0) { 2964 const keyboardWidth = ((_f = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _f.physicalLength) ?? 140; 2965 const minNote = ((_g = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _g.minNote) ?? 21; 2966 const maxNote = ((_h = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _h.maxNote) ?? 108; 2967 const lx = (lowest - minNote) / (maxNote - minNote) * keyboardWidth - keyboardWidth / 2; 2968 const hx = (highest - minNote) / (maxNote - minNote) * keyboardWidth - keyboardWidth / 2; 2969 const pressBounce = (((_i = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _i.strikeAmplitude) ?? 5) * this.state.intensity; 2970 const restY_L = ((_k = (_j = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _j.leftHand) == null ? void 0 : _k.y) ?? familyDefault.leftHand.y; 2971 const restY_R = ((_m = (_l = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _l.rightHand) == null ? void 0 : _m.y) ?? familyDefault.rightHand.y; 2972 let useL = { x: lx, y: restY_L + pressBounce }; 2973 let useR = { x: hx, y: restY_R + pressBounce }; 2974 if (activeNotes.length === 1) { 2975 const noteMap = this.config.noteMap[activeNotes[0].noteNumber]; 2976 if (noteMap) { 2977 if (noteMap.hand === "l") useL = { x: noteMap.x, y: noteMap.y + pressBounce }; 2978 if (noteMap.hand === "r") useR = { x: noteMap.x, y: noteMap.y + pressBounce }; 2979 } 2980 } 2981 if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) targetL = useL; 2982 if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) targetR = useR; 2983 } 2984 this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.2,
29840.2); 2985 } 2986} 2987class StringsAnimator extends BaseAnimator { 2988 tick(time, activeNotes, bps, isSongPlaying, instrumentDef) { 2989 var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m; 2990 this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef); 2991 const bodySway = ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.bodySway) ?? 0; 2992 const headSway = ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headSway) ?? 0; 2993 if (isSongPlaying) { 2994 const swayFrequency = ((_c = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _c.swayFrequency) ?? 5e-3; 2995 const sway = Math.sin(time / 1e3 * swayFrequency * bps) * bodySway * Math.max(0.5, this.state.intensity); 2996 this.state.joints.body.x = this.lerp(this.state.joints.body.x, sway, 0.1); 2997 this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, sway * headSway, 0.1); 2998 } else { 2999 this.state.joints.body.x = this.lerp(this.state.joints.body.x, 0, 0.05); 3000 this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, 0, 0.05); 3001 } 3002 let lowest = 128; 3003 let highest = -1; 3004 activeNotes.forEach((n) => { 3005 if (n.noteNumber < lowest) lowest = n.noteNumber; 3006 if (n.noteNumber > highest) highest = n.noteNumber; 3007 }); 3008 const familyDefault = this.getFamilyDefaultRestPose("strings"); 3009 let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_d = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _d.leftHand) || familyDefault.leftHand }; 3010 let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_e = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _e.rightHand) || familyDefault.rightHand }; 3011 if ((!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) || !(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) && activeNotes.length > 0) { 3012 const minNote = ((_f = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _f.minNote) ?? 40; 3013 const maxNote = ((_g = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _g.maxNote) ?? 88; 3014 const neckLength = ((_h = instrumentDef == null ? void 0 : instrumentDef.noteRange) == null ? void 0 : _h.physicalLength) ?? 80; 3015 let avgNote = 0; 3016 activeNotes.forEach((n) => avgNote += n.noteNumber); 3017 avgNote /= activeNotes.length; 3018 if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) { 3019 const fretPos = (avgNote - minNote) / (maxNote - minNote) * neckLength; 3020 const rest_L = ((_i = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _i.leftHand) || familyDefault.leftHand; 3021 targetL = { x: rest_L.x + fretPos * 0.5, y: rest_L.y - fretPos * 0.2 }; 3022 } 3023 if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) { 3024 const strumSpeedMult = ((_j = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _j.strumSpeedMult) ?? 0.03; 3025 const strumVerticalAmp = ((_k = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _k.strumVerticalAmp) ?? 15; 3026 const strumHorizontalAmp = ((_l = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _l.strumHorizontalAmp) ?? 5; 3027 const strumSpd = time / 1e3 * strumSpeedMult * (bps * 0.5); 3028 const rest_R = ((_m = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _m.rightHand) || familyDefault.rightHand; 3029 targetR = { 3030 x: rest_R.x + Math.cos(strumSpd) * strumHorizontalAmp, 3031 y: rest_R.y + Math.sin(strumSpd) * strumVerticalAmp * this.state.intensity 3032 }; 3033 } 3034 } 3035 this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.15, 0.4); 3036 } 3037} 3038class WindsAnimator extends BaseAnimator { 3039 tick(time, activeNotes, bps, isSongPlaying, instrumentDef) { 3040 var _a, _b, _c, _d, _e, _f; 3041 this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef); 3042 const bodySway = ((_a = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _a.bodySway) ?? 20; 3043 const headSway = ((_b = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _b.headSway) ?? 1; 3044 const fingerFlutter = ((_c = instrumentDef == null ? void 0 : instrumentDef.bodyBehavior) == null ? void 0 : _c.fingerFlutter) ?? 2; 3045 if (isSongPlaying) { 3046 const swayFrequency = ((_d = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _d.swayFrequency) ?? 5e-3; 3047 const sway = Math.sin(time / 1e3 * swayFrequency * bps) * bodySway * Math.max(0.5, this.state.intensity); 3048 this.state.joints.body.x = this.lerp(this.state.joints.body.x, sway, 0.1); 3049 this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, sway * 0.5 * headSway, 0.1); 3050 } else { 3051 this.state.joints.body.x = this.lerp(this.state.joints.body.x, 0, 0.05); 3052 this.state.joints.head.rotation = this.lerp(this.state.joints.head.rotation, 0, 0.05); 3053 } 3054 let lowest = 128; 3055 let highest = -1;
3056 activeNotes.forEach((n) => { 3057 if (n.noteNumber < lowest) lowest = n.noteNumber; 3058 if (n.noteNumber > highest) highest = n.noteNumber; 3059 }); 3060 const familyDefault = this.getFamilyDefaultRestPose("winds"); 3061 let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_e = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _e.leftHand) || familyDefault.leftHand }; 3062 let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_f = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _f.rightHand) || familyDefault.rightHand }; 3063 if (activeNotes.length > 0 && fingerFlutter > 0) { 3064 if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) { 3065 targetL.y += Math.random() * fingerFlutter * this.state.intensity; 3066 } 3067 if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) { 3068 targetR.y += Math.random() * fingerFlutter * this.state.intensity; 3069 } 3070 } 3071 this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.3, 0.3); 3072 } 3073} 3074class GenericAnimator extends BaseAnimator { 3075 tick(time, activeNotes, bps, isSongPlaying, instrumentDef) { 3076 var _a, _b, _c, _d; 3077 this.updateBaseState(time, activeNotes, bps, isSongPlaying, instrumentDef); 3078 let lowest = 128; 3079 let highest = -1; 3080 activeNotes.forEach((n) => { 3081 if (n.noteNumber < lowest) lowest = n.noteNumber; 3082 if (n.noteNumber > highest) highest = n.noteNumber; 3083 }); 3084 const familyDefault = this.getFamilyDefaultRestPose("other"); 3085 let targetL = (instrumentDef == null ? void 0 : instrumentDef.leftHandMotion) ? this.getTargetFromMotion(instrumentDef.leftHandMotion, "l", lowest, highest, bps, time, instrumentDef) : { ...((_a = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _a.leftHand) || familyDefault.leftHand }; 3086 let targetR = (instrumentDef == null ? void 0 : instrumentDef.rightHandMotion) ? this.getTargetFromMotion(instrumentDef.rightHandMotion, "r", lowest, highest, bps, time, instrumentDef) : { ...((_b = instrumentDef == null ? void 0 : instrumentDef.restPose) == null ? void 0 : _b.rightHand) || familyDefault.rightHand }; 3087 if (activeNotes.length > 0) { 3088 const strikeFrequency = ((_c = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _c.strikeFrequency) ?? 0.02; 3089 const strikeAmplitude = ((_d = instrumentDef == null ? void 0 : instrumentDef.motionPhysics) == null ? void 0 : _d.strikeAmplitude) ?? 10; 3090 if (!(instrumentDef == null ? void 0 : instrumentDef.leftHandMotion)) { 3091 targetL.y += Math.sin(time / 1e3 * strikeFrequency) * strikeAmplitude * this.state.intensity; 3092 } 3093 if (!(instrumentDef == null ? void 0 : instrumentDef.rightHandMotion)) { 3094 targetR.y += Math.sin(time / 1e3 * strikeFrequency + Math.PI) * strikeAmplitude * this.state.intensity; 3095 } 3096 } 3097 this.resolveAndApplyIk(targetL, targetR, instrumentDef, time, isSongPlaying, 0.2, 0.2); 3098 } 3099} 3100function createAnimator(config) { 3101 switch (config.family) { 3102 case "percussion": 3103 return new PercussionAnimator(config); 3104 case "keys": 3105 return new KeysAnimator(config); 3106 case "strings": 3107 return new StringsAnimator(config); 3108 case "winds": 3109 return new WindsAnimator(config); 3110 default: 3111 return new GenericAnimator(config); 3112 } 3113} 3114function sanitizeSvgContent(svg) { 3115 if (!svg) return svg; 3116 return svg.replace(/<sodipodi:[^>]*\/>/gi, "").replace(/<sodipodi:[\s\S]*?<\/sodipodi:[^>]*>/gi, "").replace(/<inkscape:[^>]*\/>/gi, "").replace(/<inkscape:[\s\S]*?<\/inkscape:[^>]*>/gi, "").replace(/<(dc|cc|rdf):[\s\S]*?<\/(?:dc|cc|rdf):[^>]*>/gi, "").replace(/<metadata[\s\S]*?<\/metadata>/gi, "").replace(/\s(sodipodi|inkscape|dc|cc|rdf):[a-z-]+\s*=\s*"[^"]*"/gi, "").replace(/\s(sodipodi|inkscape|dc|cc|rdf):[a-z-]+\s*=\s*'[^']*'/gi, "").replace(/\s xlink:(?!href)[a-z-]+\s*=\s*"[^"]*"/gi, "").replace(/\s xmlns:(sodipodi|inkscape|dc|cc|rdf|xlink)="[^"]*"/gi, ""); 3117} 3118function parseSvgTranslate(transformStr) { 3119 if (!transformStr) return { x: 0, y: 0 }; 3120 const match = transformStr.match(/translate\(([^,)]+)[,\s]+([^)]+)\)/); 3121 if (match) { 3122 return { 3123 x: parseFloat(match[1]) || 0, 3124 y: parseFloat(match[2]) || 0 3125 }; 3126 } 3127 return { x: 0, y: 0 }; 3128} 3129function parseSvgRotation(transformStr) { 3130 if (!transformStr) return 0; 3131 const match = transformStr.match(/rotate\(([^)]+)\)/); 3132 if (match) { 3133 const deg = parseFloat(match[1].split(/[\s,]+/)[0]) || 0; 3134 return deg; 3135 } 3136 return 0; 3137} 3138const PuppetCharacter = ({ 3139 config, 3140 state: propsState, 3141 x, 3142 y, 3143 isSoloed, 3144 isMuted = false, 3145 isHidden, 3146 instrumentName, 3147 onClick, 3148 onContextMenu, 3149 onHover, 3150 showSkeleton, 3151 visibility, 3152 specificGraphic, 3153 clothingGraphic, 3154 onClothingMouseDown, 3155 onBodyPartMouseDown, 3156 onInstrumentMouseDown, 3157 isCharacterTab, 3158 showActionPaths, 3159 activeTab, 3160 selectedActionId, 3161 globalActions, 3162 onActionOverlayMouseDown,
3163 viewZoom, 3164 isPlaying 3165}) => { 3166 const state = reactExports.useMemo(() => { 3167 var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l; 3168 if (!isCharacterTab || isPlaying) return propsState; 3169 const svgCoords = parseSvgSocketsAndPlugs(config.svgContent); 3170 const sockets = {}; 3171 Object.entries(svgCoords).forEach(([key, pt]) => { 3172 if (key.startsWith("socket_")) { 3173 sockets[key.replace("socket_", "")] = pt; 3174 } 3175 }); 3176 if (config.sockets) { 3177 Object.entries(config.sockets).forEach(([key, pt]) => { 3178 sockets[key] = pt; 3179 }); 3180 } 3181 const shoulderL = sockets.shoulder_l || { x: ((_a = config.ikLeftArm) == null ? void 0 : _a.originX) ?? -30, y: ((_b = config.ikLeftArm) == null ? void 0 : _b.originY) ?? -20 }; 3182 const shoulderR = sockets.shoulder_r || { x: ((_c = config.ikRightArm) == null ? void 0 : _c.originX) ?? 30, y: ((_d = config.ikRightArm) == null ? void 0 : _d.originY) ?? -20 }; 3183 const elbowL = sockets.elbow_l || { x: ((_e = config.ikLeftArm) == null ? void 0 : _e.originX) ?? -30, y: (((_f = config.ikLeftArm) == null ? void 0 : _f.originY) ?? -20) + 40 }; 3184 const elbowR = sockets.elbow_r || { x: ((_g = config.ikRightArm) == null ? void 0 : _g.originX) ?? 30, y: (((_h = config.ikRightArm) == null ? void 0 : _h.originY) ?? -20) + 40 }; 3185 const handL = sockets.hand_l || { x: ((_i = config.ikLeftArm) == null ? void 0 : _i.originX) ?? -30, y: (((_j = config.ikLeftArm) == null ? void 0 : _j.originY) ?? -20) + 70 }; 3186 const handR = sockets.hand_r || { x: ((_k = config.ikRightArm) == null ? void 0 : _k.originX) ?? 30, y: (((_l = config.ikRightArm) == null ? void 0 : _l.originY) ?? -20) + 70 }; 3187 const rotL1 = Math.atan2(elbowL.y - shoulderL.y, elbowL.x - shoulderL.x) * 180 / Math.PI; 3188 const rotL2 = Math.atan2(handL.y - elbowL.y, handL.x - elbowL.x) * 180 / Math.PI; 3189 const rotR1 = Math.atan2(elbowR.y - shoulderR.y, elbowR.x - shoulderR.x) * 180 / Math.PI; 3190 const rotR2 = Math.atan2(handR.y - elbowR.y, handR.x - elbowR.x) * 180 / Math.PI; 3191 return { 3192 ...propsState, 3193 joints: { 3194 ...propsState.joints, 3195 head: { x: 0, y: 0, rotation: 0 }, 3196 body: { x: 0, y: 0, rotation: 0 }, 3197 shoulder_l: { x: shoulderL.x, y: shoulderL.y, rotation: 0 }, 3198 upper_arm_l: { x: shoulderL.x, y: shoulderL.y, rotation: rotL1 }, 3199 elbow_l: { x: elbowL.x, y: elbowL.y, rotation: 0 }, 3200 lower_arm_l: { x: elbowL.x, y: elbowL.y, rotation: rotL2 }, 3201 hand_l: { x: handL.x, y: handL.y, rotation: rotL2 }, 3202 shoulder_r: { x: shoulderR.x, y: shoulderR.y, rotation: 0 }, 3203 upper_arm_r: { x: shoulderR.x, y: shoulderR.y, rotation: rotR1 }, 3204 elbow_r: { x: elbowR.x, y: elbowR.y, rotation: 0 }, 3205 lower_arm_r: { x: elbowR.x, y: elbowR.y, rotation: rotR2 }, 3206 hand_r: { x: handR.x, y: handR.y, rotation: rotR2 }, 3207 leg_l: { x: 0, y: 0, rotation: 0 }, 3208 foot_l: { x: 0, y: 0, rotation: 0 }, 3209 leg_r: { x: 0, y: 0, rotation: 0 }, 3210 foot_r: { x: 0, y: 0, rotation: 0 } 3211 } 3212 }; 3213 }, [propsState, config, isCharacterTab, isPlaying]); 3214 const clothingPartsJson = clothingGraphic && clothingGraphic.parts ? JSON.stringify(clothingGraphic.parts) : ""; 3215 const onClothingMouseDownRef = reactExports.useRef(onClothingMouseDown); 3216 onClothingMouseDownRef.current = onClothingMouseDown; 3217 const onBodyPartMouseDownRef = reactExports.useRef(onBodyPartMouseDown); 3218 onBodyPartMouseDownRef.current = onBodyPartMouseDown; 3219 const onInstrumentMouseDownRef = reactExports.useRef(onInstrumentMouseDown); 3220 onInstrumentMouseDownRef.current = onInstrumentMouseDown; 3221 const containerRef = reactExports.useRef(null); 3222 const svgRef = reactExports.useRef(null); 3223 const initialTransforms = reactExports.useRef({}); 3224 const mergedVisibility = reactExports.useMemo(() => { 3225 if (!config.visibility && !visibility) return null; 3226 return { 3227 ...config.visibility || {}, 3228 ...visibility || {} 3229 }; 3230 }, [config.visibility, visibility]); 3231 const visibilityRef = reactExports.useRef(mergedVisibility); 3232 const clothingGraphicRef = reactExports.useRef(clothingGraphic); 3233 clothingGraphicRef.current = clothingGraphic; 3234 const configRef = reactExports.useRef(config); 3235 configRef.current = config; 3236 const stateRef = reactExports.useRef(state); 3237 stateRef.current = state; 3238 const specificGraphicRef = reactExports.useRef(specificGraphic); 3239 specificGraphicRef.current = specificGraphic; 3240 const showSkeletonRef = reactExports.useRef(showSkeleton); 3241 showSkeletonRef.current = showSkeleton; 3242 reactExports.useEffect(() => { 3243 visibilityRef.current = mergedVisibility; 3244 }, [mergedVisibility]); 3245 const getBase = (id) => initialTransforms.current[id] || { x: 0, y: 0, rotation: 0 }; 3246 const getJointCoordinate = (jointId, useCenter = false) => { 3247 var _a, _b, _c, _d, _e, _f; 3248 if (jointId === "shoulder_l") return stateRef.current.joints.shoulder_l || { x: 0, y: 0 }; 3249 if (jointId === "elbow_l") return stateRef.current.joints.elbow_l || { x: 0, y: 0 }; 3250 if (jointId === "hand_l") return stateRef.current.joints.hand_l || { x: 0, y: 0 }; 3251 if (jointId === "shoulder_r") return stateRef.current.joints.shoulder_r || { x: 0, y: 0 }; 3252 if (jointId === "elbow_r") return stateRef.current.joints.elbow_r || { x: 0, y: 0 }; 3253 if (jointId === "hand_r") return stateRef.current.joints.hand_r || { x: 0, y: 0 }; 3254 if (svgRef.current) { 3255 const targetNode = svgRef.current.querySelector(`#${jointId}`); 3256 if (targetNode) { 3257 const ctm = targetNode.getCTM(); 3258 const parentCtm = svgRef.current.getCTM(); 3259 if (ctm && parentCtm) { 3260 try { 3261 const relativeCtm = parentCtm.inverse().multiply(ctm); 3262 const ownerSvg = svgRef.current.ownerSVGElement || svgRef.current.closest("svg"); 3263 if (ownerSvg) { 3264 const pt = ownerSvg.createSVGPoint(); 3265 if (useCenter) { 3266 const bbox = targetNode.getBBox(); 3267 pt.x = bbox.x + bbox.width / 2; 3268 pt.y = bbox.y + bbox.height / 2; 3269 } else { 3270 pt.x = 0; 3271 pt.y = 0; 3272 } 3273 const localP = pt.matrixTransform(relativeCtm); 3274 return { x: localP.x, y: localP.y }; 3275 } 3276 } catch (e) {
3277 console.error("Error calculating relative CTM coordinate:", e); 3278 } 3279 } 3280 } 3281 } 3282 if (jointId === "head") { 3283 const headBase = getBase("head"); 3284 const headUt = ((_a = configRef.current.transforms) == null ? void 0 : _a["head"]) || { x: 0, y: 0 }; 3285 let targetX = stateRef.current.joints.head.x; 3286 let targetY = stateRef.current.joints.head.y; 3287 if (specificGraphicRef.current && typeof specificGraphicRef.current === "object" && specificGraphicRef.current.headAnchor) { 3288 let dx = 0; 3289 let dy = 0; 3290 if (specificGraphicRef.current.mountPoint && specificGraphicRef.current.mountPoint !== "root" && specificGraphicRef.current.mountPoint !== "front") { 3291 const mountState = stateRef.current.joints[specificGraphicRef.current.mountPoint]; 3292 if (mountState) { 3293 dx = mountState.x || 0; 3294 dy = mountState.y || 0; 3295 } 3296 } 3297 targetX = specificGraphicRef.current.headAnchor.x - headBase.x + dx; 3298 targetY = specificGraphicRef.current.headAnchor.y - headBase.y + dy; 3299 } 3300 return { 3301 x: headBase.x + targetX + (headUt.x || 0), 3302 y: headBase.y + targetY + (headUt.y || 0) 3303 }; 3304 } 3305 if (jointId === "body") { 3306 const bodyBase = getBase("body"); 3307 const bodyUt = ((_b = configRef.current.transforms) == null ? void 0 : _b["body"]) || { x: 0, y: 0 }; 3308 return { 3309 x: bodyBase.x + stateRef.current.joints.body.x + (bodyUt.x || 0), 3310 y: bodyBase.y + stateRef.current.joints.body.y + (bodyUt.y || 0) 3311 }; 3312 } 3313 if ((jointId == null ? void 0 : jointId.startsWith("leg_")) || (jointId == null ? void 0 : jointId.startsWith("foot_"))) { 3314 const side = jointId.endsWith("_l") ? "l" : "r"; 3315 const type = jointId.startsWith("leg_") ? "leg" : "foot"; 3316 const baseJoint = getBase(`${type}_${side}`); 3317 const jointState = stateRef.current.joints[`${type}_${side}`]; 3318 if (jointState) { 3319 const ut = ((_c = configRef.current.transforms) == null ? void 0 : _c[`${type}_${side}`]) || { x: 0, y: 0 }; 3320 return { 3321 x: baseJoint.x + jointState.x + (ut.x || 0), 3322 y: baseJoint.y + jointState.y + (ut.y || 0) 3323 }; 3324 } 3325 } 3326 let socketPt = (_d = configRef.current.sockets) == null ? void 0 : _d[jointId]; 3327 if (!socketPt && svgRef.current) { 3328 const socketEl = svgRef.current.querySelector(`#socket_${jointId}`); 3329 if (socketEl) { 3330 const cxAttr = socketEl.getAttribute("cx"); 3331 const cyAttr = socketEl.getAttribute("cy"); 3332 if (cxAttr !== null && cyAttr !== null) { 3333 socketPt = { x: parseFloat(cxAttr), y: parseFloat(cyAttr) }; 3334 } else { 3335 const bbox = socketEl.getBBox(); 3336 socketPt = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 }; 3337 } 3338 } 3339 } 3340 if (socketPt) { 3341 if (jointId === "mouth" || jointId === "head") { 3342 const headBase = getBase("head"); 3343 const headUt = ((_e = configRef.current.transforms) == null ? void 0 : _e["head"]) || { x: 0, y: 0 }; 3344 const rad2 = (stateRef.current.joints.head.rotation || 0) * Math.PI / 180; 3345 const cos2 = Math.cos(rad2); 3346 const sin2 = Math.sin(rad2); 3347 const dx2 = socketPt.x - headBase.x; 3348 const dy2 = socketPt.y - headBase.y; 3349 return { 3350 x: headBase.x + stateRef.current.joints.head.x + (dx2 * cos2 - dy2 * sin2) + (headUt.x || 0), 3351 y: headBase.y + stateRef.current.joints.head.y + (dx2 * sin2 + dy2 * cos2) + (headUt.y || 0) 3352 }; 3353 } 3354 const bodyBase = getBase("body"); 3355 const bodyUt = ((_f = configRef.current.transforms) == null ? void 0 : _f["body"]) || { x: 0, y: 0 }; 3356 const rad = (stateRef.current.joints.body.rotation || 0) * Math.PI / 180; 3357 const cos = Math.cos(rad); 3358 const sin = Math.sin(rad); 3359 const dx = socketPt.x - bodyBase.x; 3360 const dy = socketPt.y - bodyBase.y; 3361 return { 3362 x: bodyBase.x + stateRef.current.joints.body.x + (dx * cos - dy * sin) + (bodyUt.x || 0), 3363 y: bodyBase.y + stateRef.current.joints.body.y + (dx * sin + dy * cos) + (bodyUt.y || 0) 3364 }; 3365 } 3366 return { x: 0, y: 0 }; 3367 }; 3368 reactExports.useEffect(() => { 3369 if (!svgRef.current) return; 3370 ["l", "r"].forEach((side) => { 3371 var _a, _b, _c, _d; 3372 const leg = (_a = svgRef.current) == null ? void 0 : _a.querySelector(`#leg_${side}`); 3373 const foot = (_b = svgRef.current) == null ? void 0 : _b.querySelector(`#foot_${side}`); 3374 if (leg && foot && leg.contains(foot)) { 3375 const footCtm = foot.getCTM(); 3376 const svgCtm = (_c = svgRef.current) == null ? void 0 : _c.getCTM(); 3377 if (footCtm && svgCtm) { 3378 try { 3379 const relMatrix = svgCtm.inverse().multiply(footCtm); 3380 foot.setAttribute("transform", `translate(${relMatrix.e}, ${relMatrix.f})`); 3381 (_d = svgRef.current) == null ? void 0 : _d.appendChild(foot); 3382 } catch (e) { 3383 console.error("Error un-nesting foot:", e); 3384 } 3385 } 3386 } 3387 }); 3388 const parts = ["head", "body", "leg_l", "leg_r", "foot_l", "foot_r", "upper_arm_l", "lower_arm_l", "hand_l", "upper_arm_r", "lower_arm_r", "hand_r"];
3389 parts.forEach((part) => { 3390 var _a; 3391 const node = (_a = svgRef.current) == null ? void 0 : _a.querySelector(`#${part}`); 3392 if (node) { 3393 node.style.cursor = "grab"; 3394 const listener = (e) => { 3395 if (onBodyPartMouseDownRef.current) { 3396 onBodyPartMouseDownRef.current(part, e); 3397 } 3398 }; 3399 node.addEventListener("mousedown", listener); 3400 if (["head", "body", "leg_l", "leg_r", "foot_l", "foot_r"].includes(part)) { 3401 const bbox = node.getBBox(); 3402 const transformAttr = node.getAttribute("transform"); 3403 const translate = parseSvgTranslate(transformAttr); 3404 const rotation = parseSvgRotation(transformAttr); 3405 initialTransforms.current[part] = { 3406 x: translate.x, 3407 y: translate.y, 3408 rotation, 3409 originCenterTop: `${bbox.x + bbox.width / 2}px ${bbox.y}px`, 3410 originCenterCenter: `${bbox.x + bbox.width / 2}px ${bbox.y + bbox.height / 2}px`, 3411 originCenterBottom: `${bbox.x + bbox.width / 2}px ${bbox.y + bbox.height}px` 3412 }; 3413 } 3414 } 3415 }); 3416 }, [config.svgContent]); 3417 reactExports.useEffect(() => { 3418 if (!svgRef.current) return; 3419 const svgs = svgRef.current.querySelectorAll("svg"); 3420 svgs.forEach((nestedSvg) => { 3421 const viewBox = nestedSvg.getAttribute("viewBox"); 3422 if (viewBox) { 3423 const parts = viewBox.trim().split(/\s+/); 3424 if (parts.length === 4) { 3425 const w = parseFloat(parts[2]); 3426 const h = parseFloat(parts[3]); 3427 if (!isNaN(w) && !isNaN(h)) { 3428 const widthAttr = nestedSvg.getAttribute("width"); 3429 const heightAttr = nestedSvg.getAttribute("height"); 3430 if (!widthAttr || widthAttr.includes("%") || parseFloat(widthAttr) !== w) { 3431 nestedSvg.setAttribute("width", w.toString()); 3432 } 3433 if (!heightAttr || heightAttr.includes("%") || parseFloat(heightAttr) !== h) { 3434 nestedSvg.setAttribute("height", h.toString()); 3435 } 3436 } 3437 } 3438 } 3439 nestedSvg.setAttribute("overflow", "visible"); 3440 }); 3441 }, [config.svgContent, specificGraphic, clothingPartsJson]); 3442 reactExports.useEffect(() => { 3443 if (!svgRef.current) return; 3444 svgRef.current.querySelectorAll(".custom-instrument-mount").forEach((el) => el.remove()); 3445 const legacyInstrumentGroup = svgRef.current.querySelector("#instrument"); 3446 if (legacyInstrumentGroup) { 3447 legacyInstrumentGroup.innerHTML = ""; 3448 } 3449 if (!specificGraphic) return; 3450 if (typeof specificGraphic === "string") { 3451 if (legacyInstrumentGroup) { 3452 legacyInstrumentGroup.innerHTML = specificGraphic; 3453 const scale = config.instrumentScale ?? 1; 3454 const rotation = config.instrumentRotation ?? 0; 3455 if (scale !== 1 || rotation !== 0) { 3456 legacyInstrumentGroup.setAttribute("transform", `scale(${scale}) rotate(${rotation})`); 3457 } else { 3458 legacyInstrumentGroup.removeAttribute("transform"); 3459 } 3460 } 3461 } else { 3462 const mountInstrument = (svgContent, mountPoint, partType, transformObj) => { 3463 if (svgRef.current) { 3464 const wrapper = document.createElementNS("http://www.w3.org/2000/svg", "g"); 3465 wrapper.classList.add("custom-instrument-mount"); 3466 wrapper.setAttribute("data-target-mount", mountPoint); 3467 wrapper.setAttribute("data-instrument-part", partType); 3468 const inner = document.createElementNS("http://www.w3.org/2000/svg", "g"); 3469 inner.innerHTML = sanitizeSvgContent(svgContent); 3470 inner.classList.add("custom-instrument-svg"); 3471 const baseScaleX = (transformObj == null ? void 0 : transformObj.scaleX) ?? (transformObj == null ? void 0 : transformObj.scale) ?? 1; 3472 const baseScaleY = (transformObj == null ? void 0 : transformObj.scaleY) ?? (transformObj == null ? void 0 : transformObj.scale) ?? 1; 3473 const baseRot = (transformObj == null ? void 0 : transformObj.rotation) ?? 0; 3474 const baseX = (transformObj == null ? void 0 : transformObj.x) ?? 0; 3475 const baseY = (transformObj == null ? void 0 : transformObj.y) ?? 0; 3476 inner.setAttribute("transform", `translate(${baseX}, ${baseY}) scale(${baseScaleX}, ${baseScaleY}) rotate(${baseRot})`); 3477 wrapper.appendChild(inner); 3478 if (onInstrumentMouseDownRef.current) { 3479 wrapper.style.cursor = "grab"; 3480 wrapper.addEventListener("mousedown", (e) => { 3481 if (onInstrumentMouseDownRef.current) { 3482 onInstrumentMouseDownRef.current(partType, e); 3483 } 3484 }); 3485 } 3486 svgRef.current.appendChild(wrapper); 3487 } 3488 }; 3489 mountInstrument(specificGraphic.svgContent, specificGraphic.mountPoint, "main", specificGraphic.transform); 3490 if (specificGraphic.accessorySvg && specificGraphic.accessoryMountPoint) { 3491 mountInstrument(specificGraphic.accessorySvg, specificGraphic.accessoryMountPoint, "accessory", specificGraphic.accessoryTransform); 3492 } 3493 } 3494 }, [config.svgContent, specificGraphic, config.instrumentScale, config.instrumentRotation]); 3495 reactExports.useEffect(() => { 3496 if (!svgRef.current) return; 3497 svgRef.current.querySelectorAll(".custom-clothing-mount").forEach((el) => el.remove()); 3498 if (!clothingGraphic || !clothingGraphic.parts) return; 3499 const parts = Object.entries(clothingGraphic.parts); 3500 for (const [jointId, svgString] of parts) { 3501 if (!svgString) continue; 3502 const wrapper = document.createElementNS("http://www.w3.org/2000/svg", "g"); 3503 wrapper.classList.add("custom-clothing-mount"); 3504 wrapper.setAttribute("data-target-joint", jointId); 3505 const offsetGroup = document.createElementNS("http://www.w3.org/2000/svg", "g"); 3506 offsetGroup.classList.add("custom-clothing-offset"); 3507 offsetGroup.innerHTML = sanitizeSvgContent(svgString); 3508 wrapper.appendChild(offsetGroup); 3509 if (onClothingMouseDownRef.current) { 3510 wrapper.style.cursor = "grab"; 3511 wrapper.addEventListener("mousedown", (e) => { 3512 if (onClothingMouseDownRef.current) { 3513 onClothingMouseDownRef.current(jointId, e); 3514 } 3515 }); 3516 } 3517 svgRef.current.appendChild(wrapper); 3518 } 3519 }, [config.svgContent, clothingPartsJson]); 3520 reactExports.useEffect(() => { 3521 if (!svgRef.current) return; 3522 svgRef.current.querySelectorAll(".custom-bodypart-mount").forEach((el) => el.remove()); 3523 const allParts = ["head", "body", "upper_arm_l", "lower_arm_l", "hand_l", "upper_arm_r", "lower_arm_r", "hand_r", "leg_l", "foot_l", "leg_r", "foot_r"];
3524 allParts.forEach((partId) => { 3525 var _a; 3526 const baseNode = (_a = svgRef.current) == null ? void 0 : _a.querySelector(`#${partId}`); 3527 if (baseNode) { 3528 baseNode.style.display = ""; 3529 } 3530 }); 3531 if (!config.parts) return; 3532 const parts = Object.entries(config.parts); 3533 for (const [jointId, svgString] of parts) { 3534 if (!svgString) continue; 3535 const wrapper = document.createElementNS("http://www.w3.org/2000/svg", "g"); 3536 wrapper.classList.add("custom-bodypart-mount"); 3537 wrapper.setAttribute("data-target-joint", jointId); 3538 const offsetGroup = document.createElementNS("http://www.w3.org/2000/svg", "g"); 3539 offsetGroup.classList.add("custom-bodypart-offset"); 3540 offsetGroup.innerHTML = sanitizeSvgContent(svgString); 3541 wrapper.appendChild(offsetGroup); 3542 if (onBodyPartMouseDownRef.current) { 3543 wrapper.style.cursor = "grab"; 3544 wrapper.addEventListener("mousedown", (e) => { 3545 if (onBodyPartMouseDownRef.current) { 3546 onBodyPartMouseDownRef.current(jointId, e); 3547 } 3548 }); 3549 } 3550 const baseNode = svgRef.current.querySelector(`#${jointId}`); 3551 if (baseNode) { 3552 baseNode.style.display = "none"; 3553 } 3554 svgRef.current.appendChild(wrapper); 3555 } 3556 }, [config.svgContent, config.parts ? JSON.stringify(config.parts) : ""]); 3557 reactExports.useEffect(() => { 3558 if (!svgRef.current) return; 3559 const defaults = { 3560 leg_l: 1, 3561 clothing_leg_l: 1.5, 3562 leg_r: 2, 3563 clothing_leg_r: 2.5, 3564 foot_l: 3, 3565 foot_r: 4, 3566 body: 5, 3567 clothing_body: 5.5, 3568 head: 6, 3569 clothing_head: 6.5, 3570 upper_arm_r: 7, 3571 clothing_upper_arm_r: 7.2, 3572 lower_arm_r: 8, 3573 clothing_lower_arm_r: 8.2, 3574 hand_r: 9, 3575 clothing_hand_r: 9.2, 3576 instrument: 10, 3577 upper_arm_l: 11, 3578 clothing_upper_arm_l: 11.2, 3579 lower_arm_l: 12, 3580 clothing_lower_arm_l: 12.2, 3581 hand_l: 13, 3582 clothing_hand_l: 13.2 3583 }; 3584 const customZOrder = specificGraphic && typeof specificGraphic === "object" ? specificGraphic.zOrder : void 0; 3585 const getZIndex = (el) => { 3586 let id = el.id; 3587 if (el.classList.contains("custom-instrument-mount")) { 3588 id = "instrument"; 3589 } 3590 if (el.classList.contains("custom-clothing-mount")) { 3591 const jointId = el.getAttribute("data-target-joint"); 3592 id = `clothing_${jointId}`; 3593 } 3594 if (el.classList.contains("custom-bodypart-mount")) { 3595 id = el.getAttribute("data-target-joint") || id; 3596 } 3597 if (id && customZOrder && customZOrder[id] !== void 0) { 3598 return customZOrder[id]; 3599 } 3600 if (id && config.baseZOrder && config.baseZOrder[id] !== void 0) { 3601 return config.baseZOrder[id]; 3602 } 3603 return defaults[id] ?? 0; 3604 }; 3605 const children = Array.from(svgRef.current.childNodes).filter((el) => el instanceof SVGElement); 3606 const sorted = [...children].sort((a, b) => getZIndex(a) - getZIndex(b)); 3607 sorted.forEach((el) => { 3608 var _a; 3609 (_a = svgRef.current) == null ? void 0 : _a.appendChild(el); 3610 }); 3611 }, [config.svgContent, specificGraphic, clothingGraphic == null ? void 0 : clothingGraphic.id, clothingPartsJson, config.baseZOrder, config.parts ? JSON.stringify(config.parts) : ""]); 3612 reactExports.useEffect(() => { 3613 var _a; 3614 if (!svgRef.current || !clothingGraphic) return; 3615 const parts = Object.keys(clothingGraphic.parts || {}); 3616 for (const jointId of parts) { 3617 const wrapper = svgRef.current.querySelector(`.custom-clothing-mount[data-target-joint="${jointId}"]`); 3618 if (!wrapper) continue; 3619 const offsetGroup = wrapper.querySelector(".custom-clothing-offset"); 3620 if (!offsetGroup) continue; 3621 const transform = (_a = clothingGraphic.transforms) == null ? void 0 : _a[jointId]; 3622 const x2 = (transform == null ? void 0 : transform.x) || 0; 3623 const y2 = (transform == null ? void 0 : transform.y) || 0; 3624 const scaleX = (transform == null ? void 0 : transform.scaleX) ?? 1; 3625 const scaleY = (transform == null ? void 0 : transform.scaleY) ?? 1; 3626 const rotation = (transform == null ? void 0 : transform.rotation) || 0; 3627 offsetGroup.setAttribute("transform", `translate(${x2}, ${y2}) scale(${scaleX}, ${scaleY}) rotate(${rotation})`); 3628 } 3629 }, [clothingGraphic == null ? void 0 : clothingGraphic.transforms]); 3630 useAnimationFrame(() => { 3631 var _a, _b, _c, _d, _e; 3632 if (!svgRef.current) return; 3633 const clothingGraphic2 = clothingGraphicRef.current; 3634 const config2 = configRef.current; 3635 const state2 = stateRef.current; 3636 const specificGraphic2 = specificGraphicRef.current; 3637 if (visibilityRef.current) { 3638 const parts = [ 3639 "head", 3640 "body", 3641 "upper_arm_l", 3642 "lower_arm_l", 3643 "hand_l", 3644 "upper_arm_r", 3645 "lower_arm_r", 3646 "hand_r", 3647 "leg_l", 3648 "leg_r", 3649 "foot_l", 3650 "foot_r", 3651 "instrument", 3652 "clothing_body", 3653 "clothing_head", 3654 "clothing_upper_arm_l", 3655 "clothing_lower_arm_l", 3656 "clothing_hand_l", 3657 "clothing_upper_arm_r", 3658 "clothing_lower_arm_r", 3659 "clothing_hand_r", 3660 "clothing_leg_l", 3661 "clothing_leg_r", 3662 "clothing_foot_l", 3663 "clothing_foot_r" 3664 ];
3665 parts.forEach((partId) => { 3666 var _a2, _b2, _c2, _d2; 3667 const node = ((_a2 = svgRef.current) == null ? void 0 : _a2.querySelector(`.custom-bodypart-mount[data-target-joint="${partId}"]`)) || ((_b2 = svgRef.current) == null ? void 0 : _b2.querySelector(`#${partId}`)); 3668 if (node) { 3669 gsapWithCSS.set(node, { autoAlpha: visibilityRef.current[partId] === false ? 0 : 1 }); 3670 } 3671 if (partId === "instrument") { 3672 (_c2 = svgRef.current) == null ? void 0 : _c2.querySelectorAll(".custom-instrument-mount").forEach((instNode) => { 3673 gsapWithCSS.set(instNode, { autoAlpha: visibilityRef.current[partId] === false ? 0 : 1 }); 3674 }); 3675 } 3676 if (partId.startsWith("clothing_")) { 3677 const jointId = partId.replace("clothing_", ""); 3678 (_d2 = svgRef.current) == null ? void 0 : _d2.querySelectorAll(`.custom-clothing-mount[data-target-joint="${jointId}"]`).forEach((clothNode) => { 3679 gsapWithCSS.set(clothNode, { autoAlpha: visibilityRef.current[partId] === false ? 0 : 1 }); 3680 }); 3681 } 3682 }); 3683 } 3684 const head = svgRef.current.querySelector('.custom-bodypart-mount[data-target-joint="head"]') || svgRef.current.querySelector("#head"); 3685 const body = svgRef.current.querySelector('.custom-bodypart-mount[data-target-joint="body"]') || svgRef.current.querySelector("#body"); 3686 const getJointRotation = (jointId) => { 3687 var _a2; 3688 if (jointId === "shoulder_l" || jointId === "elbow_l" || jointId === "hand_l" || jointId === "shoulder_r" || jointId === "elbow_r" || jointId === "hand_r") { 3689 const jointState2 = state2.joints[jointId]; 3690 return (jointState2 == null ? void 0 : jointState2.rotation) || 0; 3691 } 3692 if (svgRef.current) { 3693 const targetNode = svgRef.current.querySelector(`#${jointId}`); 3694 if (targetNode) { 3695 const ctm = targetNode.getCTM(); 3696 const parentCtm = svgRef.current.getCTM(); 3697 if (ctm && parentCtm) { 3698 try { 3699 const relativeCtm = parentCtm.inverse().multiply(ctm); 3700 return Math.atan2(relativeCtm.b, relativeCtm.a) * 180 / Math.PI; 3701 } catch (e) { 3702 console.error("Error calculating relative CTM rotation:", e); 3703 } 3704 } 3705 } 3706 } 3707 if (jointId === "head") return getBase("head").rotation + state2.joints.head.rotation; 3708 if (jointId === "body") return getBase("body").rotation + state2.joints.body.rotation; 3709 if ((jointId == null ? void 0 : jointId.startsWith("leg_")) || (jointId == null ? void 0 : jointId.startsWith("foot_"))) { 3710 const side = jointId.endsWith("_l") ? "l" : "r"; 3711 const type = jointId.startsWith("leg_") ? "leg" : "foot"; 3712 const baseJoint = getBase(`${type}_${side}`); 3713 return baseJoint.rotation + (((_a2 = state2.joints[`${type}_${side}`]) == null ? void 0 : _a2.rotation) || 0); 3714 } 3715 const jointState = state2.joints[jointId]; 3716 if (jointState) return jointState.rotation || 0; 3717 return 0; 3718 }; 3719 if (head) { 3720 const base = getBase("head"); 3721 let targetX = state2.joints.head.x; 3722 let targetY = state2.joints.head.y; 3723 if (specificGraphic2 && typeof specificGraphic2 === "object" && specificGraphic2.headAnchor) { 3724 let dx = 0; 3725 let dy = 0; 3726 if (specificGraphic2.mountPoint && specificGraphic2.mountPoint !== "root" && specificGraphic2.mountPoint !== "front") { 3727 const mountState = state2.joints[specificGraphic2.mountPoint]; 3728 if (mountState) { 3729 dx = mountState.x || 0; 3730 dy = mountState.y || 0; 3731 } 3732 } 3733 targetX = specificGraphic2.headAnchor.x - base.x + dx; 3734 targetY = specificGraphic2.headAnchor.y - base.y + dy; 3735 } 3736 const ut = ((_a = config2.transforms) == null ? void 0 : _a["head"]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 3737 gsapWithCSS.set(head, { 3738 rotation: base.rotation + state2.joints.head.rotation + (ut.rotation || 0), 3739 x: base.x + targetX + (ut.x || 0), 3740 y: base.y + targetY + (ut.y || 0), 3741 scaleX: ut.scaleX ?? 1, 3742 scaleY: ut.scaleY ?? 1, 3743 transformOrigin: base.originCenterBottom || "center bottom" 3744 }); 3745 } 3746 if (body) { 3747 const base = getBase("body"); 3748 const ut = ((_b = config2.transforms) == null ? void 0 : _b["body"]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 3749 gsapWithCSS.set(body, { 3750 rotation: base.rotation + state2.joints.body.rotation + (ut.rotation || 0), 3751 x: base.x + state2.joints.body.x + (ut.x || 0), 3752 y: base.y + state2.joints.body.y + (ut.y || 0), 3753 scaleX: ut.scaleX ?? 1, 3754 scaleY: ut.scaleY ?? 1, 3755 transformOrigin: base.originCenterCenter || "center center" 3756 }); 3757 } 3758 const updateArm = (side) => { 3759 var _a2, _b2, _c2, _d2, _e2, _f, _g, _h, _i, _j, _k; 3760 const shoulderNode = (_a2 = svgRef.current) == null ? void 0 : _a2.querySelector(`#shoulder_${side}`); 3761 const upperArmNode = ((_b2 = svgRef.current) == null ? void 0 : _b2.querySelector(`.custom-bodypart-mount[data-target-joint="upper_arm_${side}"]`)) || ((_c2 = svgRef.current) == null ? void 0 : _c2.querySelector(`#upper_arm_${side}`)); 3762 const elbowNode = (_d2 = svgRef.current) == null ? void 0 : _d2.querySelector(`#elbow_${side}`); 3763 const lowerArmNode = ((_e2 = svgRef.current) == null ? void 0 : _e2.querySelector(`.custom-bodypart-mount[data-target-joint="lower_arm_${side}"]`)) || ((_f = svgRef.current) == null ? void 0 : _f.querySelector(`#lower_arm_${side}`)); 3764 const handNode = ((_g = svgRef.current) == null ? void 0 : _g.querySelector(`.custom-bodypart-mount[data-target-joint="hand_${side}"]`)) || ((_h = svgRef.current) == null ? void 0 : _h.querySelector(`#hand_${side}`)); 3765 const shoulderState = state2.joints[`shoulder_${side}`]; 3766 const upperArmState = state2.joints[`upper_arm_${side}`]; 3767 const elbowState = state2.joints[`elbow_${side}`]; 3768 const lowerArmState = state2.joints[`lower_arm_${side}`]; 3769 const handState = state2.joints[`hand_${side}`]; 3770 if (shoulderNode && shoulderState) { 3771 shoulderNode.setAttribute("transform", `translate(${shoulderState.x}, ${shoulderState.y})`); 3772 } 3773 if (upperArmNode && upperArmState && shoulderState) { 3774 const ut = ((_i = config2.transforms) == null ? void 0 : _i[`upper_arm_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 3775 upperArmNode.setAttribute("transform", `translate(${shoulderState.x + (ut.x || 0)}, ${shoulderState.y + (ut.y || 0)}) scale(${ut.scaleX ?? 1}, ${ut.scaleY ?? 1}) rotate(${(upperArmState.rotation || 0) + (ut.rotation || 0)})`); 3776 } 3777 if (elbowNode && elbowState) { 3778 elbowNode.setAttribute("transform", `translate(${elbowState.x}, ${elbowState.y})`); 3779 } 3780 if (lowerArmNode && lowerArmState && elbowState) { 3781 const ut = ((_j = config2.transforms) == null ? void 0 : _j[`lower_arm_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 3782 lowerArmNode.setAttribute("transform", `translate(${elbowState.x + (ut.x || 0)}, ${elbowState.y + (ut.y || 0)}) scale(${ut.scaleX ?? 1}, ${ut.scaleY ?? 1}) rotate(${(lowerArmState.rotation || 0) + (ut.rotation || 0)})`); 3783 } 3784 if (handNode && handState) { 3785 const ut = ((_k = config2.transforms) == null ? void 0 : _k[`hand_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 3786 handNode.setAttribute("transform", `translate(${handState.x + (ut.x || 0)}, ${handState.y + (ut.y || 0)}) scale(${ut.scaleX ?? 1}, ${ut.scaleY ?? 1}) rotate(${(handState.rotation || 0) + (ut.rotation || 0)})`); 3787 } 3788 }; 3789 const updateLeg = (side) => { 3790 var _a2, _b2, _c2, _d2, _e2, _f; 3791 const legNode = ((_a2 = svgRef.current) == null ? void 0 : _a2.querySelector(`.custom-bodypart-mount[data-target-joint="leg_${side}"]`)) || ((_b2 = svgRef.current) == null ? void 0 : _b2.querySelector(`#leg_${side}`)); 3792 const footNode = ((_c2 = svgRef.current) == null ? void 0 : _c2.querySelector(`.custom-bodypart-mount[data-target-joint="foot_${side}"]`)) || ((_d2 = svgRef.current) == null ? void 0 : _d2.querySelector(`#foot_${side}`)); 3793 if (legNode) { 3794 const baseLeg = getBase(`leg_${side}`); 3795 const legState = state2.joints[`leg_${side}`]; 3796 if (legState) { 3797 const ut = ((_e2 = config2.transforms) == null ? void 0 : _e2[`leg_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 3798 gsapWithCSS.set(legNode, { 3799 x: baseLeg.x + legState.x + (ut.x || 0), 3800 y: baseLeg.y + legState.y + (ut.y || 0), 3801 rotation: baseLeg.rotation + legState.rotation + (ut.rotation || 0), 3802 scaleX: ut.scaleX ?? 1, 3803 scaleY: ut.scaleY ?? 1, 3804 transformOrigin: baseLeg.originCenterTop || "center top" 3805 }); 3806 } 3807 } 3808 if (footNode) { 3809 const baseFoot = getBase(`foot_${side}`); 3810 const footState = state2.joints[`foot_${side}`]; 3811 if (footState) { 3812 const ut = ((_f = config2.transforms) == null ? void 0 : _f[`foot_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 3813 gsapWithCSS.set(footNode, { 3814 x: baseFoot.x + footState.x + (ut.x || 0), 3815 y: baseFoot.y + footState.y + (ut.y || 0), 3816 rotation: baseFoot.rotation + footState.rotation + (ut.rotation || 0), 3817 scaleX: ut.scaleX ?? 1, 3818 scaleY: ut.scaleY ?? 1, 3819 transformOrigin: baseFoot.originCenterCenter || "center center" 3820 }); 3821 } 3822 } 3823 }; 3824 updateArm("l"); 3825 updateArm("r"); 3826 updateLeg("l"); 3827 updateLeg("r"); 3828 (_c = svgRef.current) == null ? void 0 : _c.querySelectorAll(".custom-instrument-mount").forEach((instNode) => { 3829 var _a2, _b2, _c2, _d2; 3830 const mountPoint = instNode.getAttribute("data-target-mount") || "root";
3831 if (specificGraphic2 && typeof specificGraphic2 === "object") { 3832 const mouthpiecePlug = (_a2 = specificGraphic2.plugs) == null ? void 0 : _a2.mouthpiece; 3833 let gluingArray = Array.isArray(specificGraphic2.gluing) ? specificGraphic2.gluing : specificGraphic2.gluing ? [specificGraphic2.gluing] : []; 3834 if (gluingArray.length === 0 && (specificGraphic2.headAnchor || mouthpiecePlug)) { 3835 gluingArray = [{ type: "one-point", ip: specificGraphic2.headAnchor || mouthpiecePlug, t1: "head", tp: void 0 }]; 3836 } 3837 const firstGlue = gluingArray.length > 0 ? gluingArray[0] : null; 3838 const gluingType = (firstGlue == null ? void 0 : firstGlue.type) || "none"; 3839 const instScaleX = config2.instrumentScale ?? 1; 3840 const instScaleY = config2.instrumentScale ?? 1; 3841 if (gluingType === "one-point" && firstGlue) { 3842 const t1Key = firstGlue.t1 || "head"; 3843 let tpVal = firstGlue.tp; 3844 if (!tpVal) { 3845 const mSocketName = (mouthpiecePlug == null ? void 0 : mouthpiecePlug.socket) || "mouth"; 3846 tpVal = ((_b2 = config2.sockets) == null ? void 0 : _b2[mSocketName]) || ((_c2 = config2.sockets) == null ? void 0 : _c2.mouth) || { x: 0, y: 0 }; 3847 } 3848 const targetBase = getJointCoordinate(t1Key); 3849 const targetRot = getJointRotation(t1Key); 3850 const rad = targetRot * Math.PI / 180; 3851 const cos = Math.cos(rad); 3852 const sin = Math.sin(rad); 3853 const targetWorld = { 3854 x: targetBase.x + (tpVal.x * cos - tpVal.y * sin), 3855 y: targetBase.y + (tpVal.x * sin + tpVal.y * cos) 3856 }; 3857 const instRot = targetRot + (config2.instrumentRotation || 0); 3858 let ipVal = { x: 0, y: 0 }; 3859 if (firstGlue.plugName && ((_d2 = specificGraphic2.plugs) == null ? void 0 : _d2[firstGlue.plugName])) { 3860 ipVal = specificGraphic2.plugs[firstGlue.plugName]; 3861 } else if (!firstGlue.plugName && firstGlue.t1 === "head" && (mouthpiecePlug || specificGraphic2.headAnchor)) { 3862 ipVal = mouthpiecePlug || specificGraphic2.headAnchor; 3863 } else { 3864 ipVal = firstGlue.ip || specificGraphic2.headAnchor || mouthpiecePlug || { x: 0, y: 0 }; 3865 } 3866 const ipValTransformed = transformPointWithStatic(ipVal, specificGraphic2.transform); 3867 gsapWithCSS.set(instNode, { 3868 x: targetWorld.x - ipValTransformed.x, 3869 y: targetWorld.y - ipValTransformed.y, 3870 rotation: instRot, 3871 scaleX: instScaleX, 3872 scaleY: instScaleY, 3873 svgOrigin: `${ipValTransformed.x} ${ipValTransformed.y}` 3874 }); 3875 } else { 3876 if (mountPoint === "body") { 3877 const bodyBase = getBase("body"); 3878 gsapWithCSS.set(instNode, { 3879 x: bodyBase.x + state2.joints.body.x, 3880 y: bodyBase.y + state2.joints.body.y, 3881 rotation: bodyBase.rotation + state2.joints.body.rotation, 3882 scaleX: instScaleX, 3883 scaleY: instScaleY, 3884 transformOrigin: bodyBase.originCenterCenter || "center center" 3885 }); 3886 } else if (mountPoint === "head") { 3887 const headBase = getBase("head"); 3888 gsapWithCSS.set(instNode, { 3889 x: headBase.x + state2.joints.head.x, 3890 y: headBase.y + state2.joints.head.y, 3891 rotation: headBase.rotation + state2.joints.head.rotation, 3892 scaleX: instScaleX, 3893 scaleY: instScaleY, 3894 transformOrigin: headBase.originCenterBottom || "center bottom" 3895 }); 3896 } else if (mountPoint === "hand_l" || mountPoint === "hand_r") { 3897 const handState = state2.joints[mountPoint]; 3898 if (handState) { 3899 gsapWithCSS.set(instNode, { 3900 x: handState.x, 3901 y: handState.y, 3902 rotation: handState.rotation || 0, 3903 scaleX: instScaleX, 3904 scaleY: instScaleY, 3905 transformOrigin: "center center" 3906 }); 3907 } 3908 } else { 3909 gsapWithCSS.set(instNode, { 3910 x: 0, 3911 y: 0, 3912 rotation: 0, 3913 scaleX: instScaleX, 3914 scaleY: instScaleY, 3915 transformOrigin: "center center" 3916 }); 3917 } 3918 } 3919 } else { 3920 const instScaleXLegacy = config2.instrumentScale ?? 1; 3921 const instScaleYLegacy = config2.instrumentScale ?? 1; 3922 gsapWithCSS.set(instNode, { 3923 x: 0, 3924 y: 0, 3925 rotation: 0, 3926 scaleX: instScaleXLegacy, 3927 scaleY: instScaleYLegacy, 3928 transformOrigin: "center center" 3929 }); 3930 } 3931 }); 3932 (_d = svgRef.current) == null ? void 0 : _d.querySelectorAll(".custom-clothing-mount").forEach((clothNode) => { 3933 var _a2, _b2, _c2, _d2, _e2, _f; 3934 const jointId = clothNode.getAttribute("data-target-joint"); 3935 if (!jointId) return; 3936 const glueCfg = (_a2 = clothingGraphic2 == null ? void 0 : clothingGraphic2.gluing) == null ? void 0 : _a2[jointId]; 3937 let p1 = null; 3938 let p2 = null; 3939 const p1El = clothNode.querySelector("#plug_p1"); 3940 if (p1El) { 3941 const cxAttr = p1El.getAttribute("cx"); 3942 const cyAttr = p1El.getAttribute("cy"); 3943 if (cxAttr !== null && cyAttr !== null) { 3944 p1 = { x: parseFloat(cxAttr), y: parseFloat(cyAttr) }; 3945 } else { 3946 const bbox = p1El.getBBox(); 3947 p1 = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 }; 3948 } 3949 } 3950 const p2El = clothNode.querySelector("#plug_p2"); 3951 if (p2El) { 3952 const cxAttr = p2El.getAttribute("cx"); 3953 const cyAttr = p2El.getAttribute("cy"); 3954 if (cxAttr !== null && cyAttr !== null) { 3955 p2 = { x: parseFloat(cxAttr), y: parseFloat(cyAttr) }; 3956 } else { 3957 const bbox = p2El.getBBox(); 3958 p2 = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 }; 3959 } 3960 } 3961 const dbPlugs = (_b2 = clothingGraphic2 == null ? void 0 : clothingGraphic2.plugs) == null ? void 0 : _b2[jointId]; 3962 if (dbPlugs) { 3963 if (dbPlugs.p1) p1 = dbPlugs.p1; 3964 if (dbPlugs.p2) p2 = dbPlugs.p2; 3965 } 3966 if (glueCfg) { 3967 if (!p1 && glueCfg.p1) p1 = glueCfg.p1; 3968 if (!p2 && glueCfg.p2) p2 = glueCfg.p2; 3969 } 3970 if (glueCfg && glueCfg.type === "two-point") { 3971 const side = jointId.endsWith("_l") ? "l" : "r"; 3972 const t1Key = glueCfg.t1 || (jointId.startsWith("upper_arm_") ? "shoulder_" + side : jointId.startsWith("lower_arm_") ? "elbow_" + side : "shoulder_l"); 3973 const t2Key = glueCfg.t2 || (jointId.startsWith("upper_arm_") ? "elbow_" + side : jointId.startsWith("lower_arm_") ? "hand_" + side : "elbow_l"); 3974 const resolvedP1 = p1 || { x: 0, y: 0 }; 3975 const resolvedP2 = p2 || { x: 0, y: 40 }; 3976 const T1 = getJointCoordinate(t1Key); 3977 const T2 = getJointCoordinate(t2Key); 3978 const dx_s = resolvedP2.x - resolvedP1.x; 3979 const dy_s = resolvedP2.y - resolvedP1.y; 3980 const L_s = Math.sqrt(dx_s * dx_s + dy_s * dy_s); 3981 const theta_s = Math.atan2(dy_s, dx_s) * 180 / Math.PI; 3982 const dx_t = T2.x - T1.x; 3983 const dy_t = T2.y - T1.y; 3984 const L_t = Math.sqrt(dx_t * dx_t + dy_t * dy_t); 3985 const theta_t = Math.atan2(dy_t, dx_t) * 180 / Math.PI; 3986 const s = L_s > 1e-3 ? L_t / L_s : 1; 3987 const rot = theta_t - theta_s; 3988 const rotRad = rot * Math.PI / 180; 3989 const cosR = Math.cos(rotRad); 3990 const sinR = Math.sin(rotRad); 3991 const tx = T1.x - s * (resolvedP1.x * cosR - resolvedP1.y * sinR); 3992 const ty = T1.y - s * (resolvedP1.x * sinR + resolvedP1.y * cosR); 3993 clothNode.style.transform = ""; 3994 clothNode.setAttribute( 3995 "transform", 3996 `matrix(${s * cosR},${s * sinR},${-s * sinR},${s * cosR},${tx},${ty})` 3997 ); 3998 } else if (glueCfg && glueCfg.type === "one-point") { 3999 const t1Key = glueCfg.t1 || "body"; 4000 const resolvedP1 = p1 || { x: 0, y: 0 }; 4001 const T1 = getJointCoordinate(t1Key); 4002 const rot = getJointRotation(t1Key); 4003 const rotRad1 = rot * Math.PI / 180; 4004 const cosR1 = Math.cos(rotRad1); 4005 const sinR1 = Math.sin(rotRad1); 4006 const tx1 = T1.x - (resolvedP1.x * cosR1 - resolvedP1.y * sinR1); 4007 const ty1 = T1.y - (resolvedP1.x * sinR1 + resolvedP1.y * cosR1); 4008 clothNode.style.transform = ""; 4009 clothNode.setAttribute( 4010 "transform", 4011 `matrix(${cosR1},${sinR1},${-sinR1},${cosR1},${tx1},${ty1})` 4012 ); 4013 } else { 4014 if (jointId === "body") { 4015 const bodyBase = getBase("body"); 4016 const ut = ((_c2 = config2.transforms) == null ? void 0 : _c2["body"]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 4017 gsapWithCSS.set(clothNode, { 4018 x: bodyBase.x + state2.joints.body.x + (ut.x || 0), 4019 y: bodyBase.y + state2.joints.body.y + (ut.y || 0), 4020 rotation: bodyBase.rotation + state2.joints.body.rotation + (ut.rotation || 0), 4021 scaleX: ut.scaleX ?? 1, 4022 scaleY: ut.scaleY ?? 1, 4023 transformOrigin: bodyBase.originCenterCenter || "center center" 4024 }); 4025 } else if (jointId === "head") { 4026 const headBase = getBase("head"); 4027 const ut = ((_d2 = config2.transforms) == null ? void 0 : _d2["head"]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 4028 let targetX = state2.joints.head.x; 4029 let targetY = state2.joints.head.y; 4030 if (specificGraphic2 && typeof specificGraphic2 === "object" && specificGraphic2.headAnchor) { 4031 let dx = 0; 4032 let dy = 0; 4033 if (specificGraphic2.mountPoint && specificGraphic2.mountPoint !== "root" && specificGraphic2.mountPoint !== "front") { 4034 const mountState = state2.joints[specificGraphic2.mountPoint]; 4035 if (mountState) { 4036 dx = mountState.x || 0; 4037 dy = mountState.y || 0; 4038 } 4039 } 4040 targetX = specificGraphic2.headAnchor.x - headBase.x + dx; 4041 targetY = specificGraphic2.headAnchor.y - headBase.y + dy; 4042 } 4043 gsapWithCSS.set(clothNode, { 4044 x: headBase.x + targetX + (ut.x || 0), 4045 y: headBase.y + targetY + (ut.y || 0), 4046 rotation: headBase.rotation + state2.joints.head.rotation + (ut.rotation || 0), 4047 scaleX: ut.scaleX ?? 1, 4048 scaleY: ut.scaleY ?? 1, 4049 transformOrigin: headBase.originCenterBottom || "center bottom" 4050 }); 4051 } else if ((jointId == null ? void 0 : jointId.startsWith("upper_arm_")) || (jointId == null ? void 0 : jointId.startsWith("lower_arm_")) || (jointId == null ? void 0 : jointId.startsWith("hand_"))) { 4052 const side = jointId.endsWith("_l") ? "l" : "r"; 4053 const type = jointId.startsWith("upper_arm_") ? "upper_arm" : jointId.startsWith("lower_arm_") ? "lower_arm" : "hand"; 4054 const jointState = state2.joints[`${type}_${side}`]; 4055 if (jointState) { 4056 const ut = ((_e2 = config2.transforms) == null ? void 0 : _e2[`${type}_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 4057 if (type === "upper_arm") { 4058 const shoulderState = state2.joints[`shoulder_${side}`]; 4059 if (shoulderState) { 4060 gsapWithCSS.set(clothNode, { 4061 x: shoulderState.x + (ut.x || 0), 4062 y: shoulderState.y + (ut.y || 0), 4063 rotation: (jointState.rotation || 0) + (ut.rotation || 0), 4064 scaleX: ut.scaleX ?? 1, 4065 scaleY: ut.scaleY ?? 1, 4066 transformOrigin: "0px 0px" 4067 }); 4068 } 4069 } else if (type === "lower_arm") { 4070 const elbowState = state2.joints[`elbow_${side}`]; 4071 if (elbowState) { 4072 gsapWithCSS.set(clothNode, { 4073 x: elbowState.x + (ut.x || 0), 4074 y: elbowState.y + (ut.y || 0), 4075 rotation: (jointState.rotation || 0) + (ut.rotation || 0), 4076 scaleX: ut.scaleX ?? 1, 4077 scaleY: ut.scaleY ?? 1, 4078 transformOrigin: "0px 0px" 4079 }); 4080 } 4081 } else { 4082 gsapWithCSS.set(clothNode, { 4083 x: jointState.x + (ut.x || 0), 4084 y: jointState.y + (ut.y || 0), 4085 rotation: (jointState.rotation || 0) + (ut.rotation || 0), 4086 scaleX: ut.scaleX ?? 1, 4087 scaleY: ut.scaleY ?? 1, 4088 transformOrigin: "center center" 4089 }); 4090 } 4091 } 4092 } else if ((jointId == null ? void 0 : jointId.startsWith("leg_")) || (jointId == null ? void 0 : jointId.startsWith("foot_"))) { 4093 const side = jointId.endsWith("_l") ? "l" : "r"; 4094 const type = jointId.startsWith("leg_") ? "leg" : "foot"; 4095 const baseJoint = getBase(`${type}_${side}`); 4096 const jointState = state2.joints[`${type}_${side}`]; 4097 if (jointState) { 4098 const ut = ((_f = config2.transforms) == null ? void 0 : _f[`${type}_${side}`]) || { x: 0, y: 0, scaleX: 1, scaleY: 1, rotation: 0 }; 4099 gsapWithCSS.set(clothNode, { 4100 x: baseJoint.x + jointState.x + (ut.x || 0),
4101 y: baseJoint.y + jointState.y + (ut.y || 0), 4102 rotation: baseJoint.rotation + jointState.rotation + (ut.rotation || 0), 4103 scaleX: ut.scaleX ?? 1, 4104 scaleY: ut.scaleY ?? 1, 4105 transformOrigin: type === "leg" ? baseJoint.originCenterTop || "center top" : baseJoint.originCenterCenter || "center center" 4106 }); 4107 } 4108 } 4109 } 4110 }); 4111 const innerSvg = (_e = svgRef.current) == null ? void 0 : _e.querySelector("svg"); 4112 if (innerSvg) { 4113 let diagG = innerSvg.querySelector("#diagnostic-skeleton-inner"); 4114 if (!showSkeletonRef.current) { 4115 if (diagG) diagG.remove(); 4116 } else { 4117 if (!diagG) { 4118 diagG = document.createElementNS("http://www.w3.org/2000/svg", "g"); 4119 diagG.setAttribute("id", "diagnostic-skeleton-inner"); 4120 diagG.setAttribute("pointer-events", "none"); 4121 diagG.setAttribute("opacity", "0.8"); 4122 innerSvg.appendChild(diagG); 4123 } 4124 const sL = state2.joints.shoulder_l || { x: 0, y: 0 }; 4125 const eL = state2.joints.elbow_l || { x: 0, y: 0 }; 4126 const hL = state2.joints.hand_l || { x: 0, y: 0 }; 4127 const sR = state2.joints.shoulder_r || { x: 0, y: 0 }; 4128 const eR = state2.joints.elbow_r || { x: 0, y: 0 }; 4129 const hR = state2.joints.hand_r || { x: 0, y: 0 }; 4130 diagG.innerHTML = ` 4131 <!-- Left Arm Skeleton --> 4132 <line x1="${sL.x}" y1="${sL.y}" x2="${eL.x}" y2="${eL.y}" stroke="red" stroke-width="2" stroke-dasharray="4" /> 4133 <line x1="${eL.x}" y1="${eL.y}" x2="${hL.x}" y2="${hL.y}" stroke="red" stroke-width="2" stroke-dasharray="4" /> 4134 <circle cx="${sL.x}" cy="${sL.y}" r="4" fill="yellow" /> 4135 <circle cx="${eL.x}" cy="${eL.y}" r="4" fill="yellow" /> 4136 <circle cx="${hL.x}" cy="${hL.y}" r="4" fill="yellow" /> 4137 4138 <!-- Right Arm Skeleton --> 4139 <line x1="${sR.x}" y1="${sR.y}" x2="${eR.x}" y2="${eR.y}" stroke="red" stroke-width="2" stroke-dasharray="4" /> 4140 <line x1="${eR.x}" y1="${eR.y}" x2="${hR.x}" y2="${hR.y}" stroke="red" stroke-width="2" stroke-dasharray="4" /> 4141 <circle cx="${sR.x}" cy="${sR.y}" r="4" fill="yellow" /> 4142 <circle cx="${eR.x}" cy="${eR.y}" r="4" fill="yellow" /> 4143 <circle cx="${hR.x}" cy="${hR.y}" r="4" fill="yellow" /> 4144 `; 4145 } 4146 } 4147 }); 4148 const renderSkeleton = () => { 4149 return null; 4150 }; 4151 const renderActionPaths = () => { 4152 if (!showActionPaths || !globalActions) return null; 4153 const actionsToShow = activeTab === "actions" && selectedActionId ? globalActions.filter((a) => a.id === selectedActionId) : globalActions.filter((a) => a.trigger === "playing"); 4154 const CHANNEL_COLORS = { 4155 "body": "#818cf8", 4156 "head": "#f472b6", 4157 "foot_r": "#34d399", 4158 "foot_l": "#34d399", 4159 "hand_l": "#f59e0b", 4160 "hand_r": "#f59e0b", 4161 "upper_arm_l": "#60a5fa", 4162 "upper_arm_r": "#60a5fa", 4163 "lower_arm_l": "#a78bfa", 4164 "lower_arm_r": "#a78bfa", 4165 "leg_l": "#fb923c", 4166 "leg_r": "#fb923c" 4167 }; 4168 const zoom = viewZoom || 1; 4169 return /* @__PURE__ */ jsxRuntimeExports.jsx("g", { id: "action-paths-overlay", transform: `translate(${config.anchorX}, ${config.anchorY})`, pointerEvents: "auto", opacity: "0.8", children: actionsToShow.flatMap( 4170 (action) => action.channels.map((ch, ci) => { 4171 const jointPt = getJointCoordinate(ch.joint, true); 4172 const delta = state.joints[ch.joint] || { x: 0, y: 0 }; 4173 const jx = jointPt.x - (delta.x || 0); 4174 const jy = jointPt.y - (delta.y || 0); 4175 const color = CHANNEL_COLORS[ch.joint] || "#818cf8"; 4176 if (ch.type === "wave") { 4177 const amp = Math.min(20, Math.abs(ch.amplitude || 10)); 4178 const isVertical = ch.property === "y"; 4179 const pts = []; 4180 for (let i = 0; i <= 20; i++) { 4181 const t = i / 20; 4182 const wave = Math.sin(t * Math.PI * 2 * (ch.frequency || 1)) * amp * 0.3; 4183 if (isVertical) { 4184 pts.push(`${jx + (t - 0.5) * 30},${jy + wave}`); 4185 } else if (ch.property === "x") { 4186 pts.push(`${jx + wave},${jy + (t - 0.5) * 30}`); 4187 } else { 4188 pts.push(`${jx + (t - 0.5) * 30},${jy - 15 + wave}`); 4189 } 4190 } 4191 const handleX = isVertical ? jx : ch.property === "x" ? j
4191x - amp * 0.3 : jx; 4192 const handleY = isVertical ? jy - amp * 0.3 : ch.property === "x" ? jy : jy - 15 - amp * 0.3; 4193 return /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [ 4194 /* @__PURE__ */ jsxRuntimeExports.jsx("polyline", { points: pts.join(" "), fill: "none", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", pointerEvents: "none" }), 4195 /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: jx, cy: jy, r: "3", fill: color, opacity: "0.3", pointerEvents: "none" }), 4196 /* @__PURE__ */ jsxRuntimeExports.jsx( 4197 "circle", 4198 { 4199 cx: handleX, 4200 cy: handleY, 4201 r: 3 / zoom, 4202 fill: "white", 4203 stroke: color, 4204 strokeWidth: 1 / zoom, 4205 className: "cursor-grab active:cursor-grabbing", 4206 pointerEvents: "auto", 4207 onMouseDown: (e) => { 4208 e.stopPropagation(); 4209 e.preventDefault(); 4210 onActionOverlayMouseDown == null ? void 0 : onActionOverlayMouseDown({ 4211 target: "action_wave_amp", 4212 actionId: action.id, 4213 channelIdx: ci, 4214 isVertical, 4215 isX: ch.property === "x", 4216 jx, 4217 jy 4218 }); 4219 } 4220 } 4221 ),
4222 /* @__PURE__ */ jsxRuntimeExports.jsxs("text", { x: jx + 5, y: jy - 5, fill: color, fontSize: "4", fontFamily: "monospace", pointerEvents: "none", children: [ 4223 ch.joint, 4224 ".", 4225 ch.property 4226 ] }) 4227 ] }, `${action.id}-${ci}`); 4228 } else if (ch.type === "keyframe" && ch.keyframes) { 4229 const maxV = Math.max(20, ...(ch.keyframes || []).map((k) => Math.abs(k.value))); 4230 return /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [ 4231 /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: jx - 15, y1: jy - 12, x2: jx + 15, y2: jy - 12, stroke: color, strokeWidth: "0.5", opacity: "0.4", pointerEvents: "none" }), 4232 ch.keyframes.map((kf, ki) => /* @__PURE__ */ jsxRuntimeExports.jsx( 4233 "circle", 4234 { 4235 cx: jx - 15 + kf.time * 30, 4236 cy: jy - 12 - kf.value / maxV * 8, 4237 r: 2.5 / zoom, 4238 fill: color, 4239 stroke: "white", 4240 strokeWidth: 0.5 / zoom, 4241 className: "cursor-grab active:cursor-grabbing", 4242 pointerEvents: "auto", 4243 onMouseDown: (e) => { 4244 e.stopPropagation(); 4245 e.preventDefault(); 4246 onActionOverlayMouseDown == null ? void 0 : onActionOverlayMouseDown({ 4247 target: "action_keyframe", 4248 actionId: action.id, 4249 channelIdx: ci, 4250 keyframeIdx: ki, 4251 startX: kf.time, 4252 startY: kf.value, 4253 maxV, 4254 jx, 4255 jy 4256 }); 4257 } 4258 }, 4259 ki 4260 )),
4261 /* @__PURE__ */ jsxRuntimeExports.jsxs("text", { x: jx + 5, y: jy - 18, fill: color, fontSize: "4", fontFamily: "monospace", pointerEvents: "none", children: [ 4262 ch.joint, 4263 ".", 4264 ch.property 4265 ] }) 4266 ] }, `${action.id}-${ci}`); 4267 } 4268 return null; 4269 }) 4270 ) }); 4271 }; 4272 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 4273 "g", 4274 { 4275 ref: containerRef, 4276 transform: `translate(${x}, ${y}) scale(${config.scale})`, 4277 opacity: isHidden ? 0.3 : 1, 4278 onClick, 4279 onContextMenu, 4280 onMouseEnter: () => onHover && onHover(true), 4281 onMouseLeave: () => onHover && onHover(false), 4282 onDragStart: (e) => e.preventDefault(), 4283 className: onClick ? "cursor-pointer group" : "group", 4284 children: [ 4285 isSoloed && /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: config.anchorX, cy: config.anchorY - 100, r: 100, fill: "url(#soloGradient)", className: "pointer-events-none" }), 4286 /* @__PURE__ */ jsxRuntimeExports.jsx( 4287 "g", 4288 { 4289 ref: svgRef, 4290 className: "custom-character-svg", 4291 dangerouslySetInnerHTML: { __html: sanitizeSvgContent(config.svgContent) }, 4292 transform: `translate(${config.anchorX}, ${config.anchorY})` 4293 } 4294 ), 4295 renderSkeleton(), 4296 renderActionPaths(),
4297 isSoloed && /* @__PURE__ */ jsxRuntimeExports.jsx( 4298 "text", 4299 { 4300 x: config.anchorX, 4301 y: config.anchorY - 220, 4302 textAnchor: "middle", 4303 fill: "#F59E0B", 4304 fontSize: "24", 4305 fontWeight: "bold", 4306 className: "pointer-events-none", 4307 children: "SOLO" 4308 } 4309 ), 4310 isMuted && !isSoloed && /* @__PURE__ */ jsxRuntimeExports.jsx( 4311 "text", 4312 { 4313 x: config.anchorX, 4314 y: config.anchorY - 220, 4315 textAnchor: "middle", 4316 fill: "#EF4444", 4317 fontSize: "24", 4318 fontWeight: "bold", 4319 className: "pointer-events-none", 4320 children: "MUTE" 4321 } 4322 ), 4323 instrumentName && /* @__PURE__ */ jsxRuntimeExports.jsx("foreignObject", { x: config.anchorX - 100, y: config.anchorY + 50, width: "200", height: "40", className: "overflow-visible pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `flex justify-center w-full transition-opacity duration-300 ${state.isPlaying || isSoloed ? "opacity-100" : "opacity-40"}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-black/70 rounded-full px-3 py-1 text-white text-sm font-bold text-center whitespace-nowrap", children: instrumentName }) }) }) 4324 ] 4325 } 4326 ); 4327}; 4328const BackgroundParticlesCanvas = ({ config, isPlaying }) => { 4329 const canvasRef = reactExports.useRef(null); 4330 reactExports.useEffect(() => { 4331 const canvas = canvasRef.current; 4332 if (!canvas) return; 4333 const ctx = canvas.getContext("2d"); 4334 if (!ctx) return; 4335 let animationFrameId; 4336 let width = canvas.width = canvas.offsetWidth || window.innerWidth; 4337 let height = canvas.height = canvas.offsetHeight || window.innerHeight; 4338 const handleResize = () => { 4339 if (!canvas) return; 4340 width = canvas.width = canvas.offsetWidth || window.innerWidth; 4341 height = canvas.height = canvas.offsetHeight || window.innerHeight; 4342 }; 4343 window.addEventListener("resize", handleResize); 4344 class Particle { 4345 constructor() { 4346 this.x = Math.random() * width; 4347 this.y = Math.random() * height; 4348 let speed = (0.5 + Math.random() * 1.5) * config.speed; 4349 let angle = -Math.PI / 2; 4350 let drift = (Math.random() - 0.5) * 0.5; 4351 if (config.type === "snow" || config.type === "rain" || config.type === "confetti" || config.type === "autumn_leaves" || config.type === "matrix") { 4352 angle = Math.PI / 2; 4353 } 4354 if (config.type === "rain") { 4355 speed *= 3; 4356 drift = 0.1; 4357 this.length = 10 + Math.random() * 10; 4358 } else if (config.type === "matrix") { 4359 speed *= 1.5; 4360 drift = 0; 4361 } else if (config.type === "laser_lines") { 4362 angle = Math.random() > 0.5 ? 0 : Math.PI; 4363 speed *= 4; 4364 this.length = 50 + Math.random() * 100; 4365 drift = 0; 4366 } else if (config.type === "fireflies" || config.type === "dust" || config.type === "floating_orbs") { 4367 angle = Math.random() * Math.PI * 2; 4368 speed *= 0.3; 4369 } 4370 this.vx = Math.sin(angle + drift) * speed; 4371 this.vy = Math.cos(angle + drift) * speed; 4372 this.size = Math.random() * config.size + 1; 4373 this.alpha = Math.random() * 0.5 + 0.2; 4374 this.fadeSpeed = 2e-3 + Math.random() * 5e-3; 4375 this.angleMult = Math.random() * Math.PI * 2; 4376 if (config.type === "notes") { 4377 const notes = ["♩", "♪", "♫", "♬", "â™Â", "♮", "♯"]; 4378 this.noteChar = notes[Math.floor(Math.random() * notes.length)]; 4379 } else if (config.type === "matrix") { 4380 this.noteChar = Math.random() > 0.5 ? "0" : "1"; 4381 } else if (config.type === "confetti") { 4382 const colors = ["#f44336", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#2196f3", "#03a9f4", "#00bcd4", "#009688", "#4caf50", "#8bc34a", "#cddc39", "#ffeb3b", "#ffc107", "#ff9800", "#ff5722"]; 4383 this.color = colors[Math.floor(Math.random() * colors.length)]; 4384 } else if (config.type === "autumn_leaves") { 4385 const colors = ["#d84315", "#e65100", "#ef6c00", "#f57c00", "#ff9800", "#ffb300", "#fbc02d", "#8d6e63"]; 4386 this.color = colors[Math.floor(Math.random() * colors.length)]; 4387 } 4388 } 4389 update() { 4390 if (config.type === "autumn_leaves" || config.type === "confetti") { 4391 this.vx = Math.sin(this.angleMult) * 2; 4392 this.angleMult += 0.05; 4393 } else if (config.type === "fireflies") { 4394 this.vx += (Math.random() - 0.5) * 0.2; 4395 this.vy += (Math.random() - 0.5) * 0.2; 4396 const currentSpeed = Math.sqrt(this.vx * this.vx + this.vy * this.vy); 4397 if (currentSpeed > 2) { 4398 this.vx *= 0.9; 4399 this.vy *= 0.9; 4400 } 4401 } 4402 this.x += this.vx; 4403 this.y += this.vy; 4404 const fallingTypes = ["snow", "rain", "confetti", "autumn_leaves", "matrix"]; 4405 if (fallingTypes.includes(config.type)) { 4406 if (this.y > height + 20) {
4407 this.y = -20; 4408 this.x = Math.random() * width; 4409 if (config.type === "matrix") this.noteChar = Math.random() > 0.5 ? "0" : "1"; 4410 } 4411 } else { 4412 if (this.y < -20) { 4413 this.y = height + 20; 4414 this.x = Math.random() * width; 4415 } else if (this.y > height + 20) { 4416 this.y = -20; 4417 } 4418 } 4419 if (this.x < -20) this.x = width + 20; 4420 if (this.x > width + 20) this.x = -20; 4421 if (["stars", "fireflies", "sparkles"].includes(config.type)) { 4422 this.alpha += this.fadeSpeed; 4423 if (this.alpha > (config.type === "fireflies" ? 1 : 0.8) || this.alpha < 0.1) { 4424 this.fadeSpeed = -this.fadeSpeed; 4425 } 4426 } else if (config.type === "matrix") { 4427 if (Math.random() < 0.05) this.noteChar = Math.random() > 0.5 ? "0" : "1"; 4428 } 4429 } 4430 draw(c) { 4431 c.save(); 4432 c.globalAlpha = Math.max(0, Math.min(1, this.alpha)); 4433 c.fillStyle = this.color || config.color; 4434 c.strokeStyle = this.color || config.color; 4435 if (config.type === "notes" && this.noteChar) { 4436 c.font = `${this.size * 5 + 10}px sans-serif`; 4437 c.fillText(this.noteChar, this.x, this.y); 4438 } else if (config.type === "matrix" && this.noteChar) { 4439 c.font = `${this.size * 5 + 10}px monospace`; 4440 c.fillStyle = "#0f0"; 4441 c.fillText(this.noteChar, this.x, this.y); 4442 } else if (config.type === "bubbles") { 4443 c.beginPath(); 4444 c.arc(this.x, this.y, this.size, 0, Math.PI * 2); 4445 c.lineWidth = 1.5; 4446 c.stroke(); 4447 } else if (config.type === "rain" || config.type === "laser_lines") { 4448 c.beginPath(); 4449 c.moveTo(this.x, this.y); 4450 c.lineTo(this.x - this.vx * (this.length || 10) * 0.1, this.y - this.vy * (this.length || 10) * 0.1); 4451 c.lineWidth = this.size; 4452 c.stroke(); 4453 } else if (config.type === "hearts") { 4454 c.beginPath(); 4455 const s = this.size * 2; 4456 c.moveTo(this.x, this.y + s / 4); 4457 c.quadraticCurveTo(this.x, this.y, this.x - s / 2, this.y); 4458 c.quadraticCurveTo(this.x - s, this.y, this.x - s, this.y + s / 2.5); 4459 c.quadraticCurveTo(this.x - s, this.y + s, this.x, this.y + s * 1.5); 4460 c.quadraticCurveTo(this.x + s, this.y + s, this.x + s, this.y + s / 2.5); 4461 c.quadraticCurveTo(this.x + s, this.y, this.x + s / 2, this.y); 4462 c.quadraticCurveTo(this.x, this.y, this.x, this.y + s / 4); 4463 c.fill(); 4464 } else if (config.type === "confetti" || config.type === "autumn_leaves") { 4465 c.translate(this.x, this.y); 4466 c.rotate(this.angleMult); 4467 c.fillRect(-this.size, -this.size / 2, this.size * 2, this.size); 4468 } else if (config.type === "sparkles") { 4469 c.beginPath(); 4470 c.moveTo(this.x, this.y - this.size * 2); 4471 c.lineTo(this.x + this.size / 2, this.y - this.size / 2); 4472 c.lineTo(this.x + this.size * 2, this.y); 4473 c.lineTo(this.x + this.size / 2, this.y + this.size / 2); 4474 c.lineTo(this.x, this.y + this.size * 2); 4475 c.lineTo(this.x - this.size / 2, this.y + this.size / 2); 4476 c.lineTo(this.x - this.size * 2, this.y); 4477 c.lineTo(this.x - this.size / 2, this.y - this.size / 2); 4478 c.closePath(); 4479 c.fill(); 4480 } else if (config.type === "floating_orbs") { 4481 c.beginPath(); 4482 c.arc(this.x, this.y, this.size * 3, 0, Math.PI * 2); 4483 c.fill(); 4484 } else { 4485 c.beginPath(); 4486 c.arc(this.x, this.y, this.size, 0, Math.PI * 2); 4487 c.fill(); 4488 } 4489 c.restore(); 4490 } 4491 } 4492 const particles = []; 4493 for (let i = 0; i < config.count; i++) { 4494 particles.push(new Particle()); 4495 } 4496 const render = () => { 4497 ctx.clearRect(0, 0, width, height); 4498 particles.forEach((p) => { 4499 p.update(); 4500 p.draw(ctx); 4501 }); 4502 animationFrameId = requestAnimationFrame(render); 4503 }; 4504 render(); 4505 return () => { 4506 cancelAnimationFrame(animationFrameId); 4507 window.removeEventListener("resize", handleResize); 4508 }; 4509 }, [config]); 4510 return /* @__PURE__ */ jsxRuntimeExports.jsx( 4511 "canvas", 4512 { 4513 ref: canvasRef, 4514 className: "absolute inset-0 w-full h-full pointer-events-none", 4515 style: { zIndex: 3 } 4516 } 4517 ); 4518}; 4519const StagePuppetEngine = ({ 4520 track,
4521 currentTime, 4522 isPlaying, 4523 pianoRollData, 4524 processedInstruments, 4525 uniqueNames, 4526 soloedIndex, 4527 onSolo, 4528 mutedIndices = /* @__PURE__ */ new Set(), 4529 onMute = () => { 4530 }, 4531 onPanChange, 4532 puppetConfigs, 4533 instrumentConfigs, 4534 clothingConfigs, 4535 backgroundConfig, 4536 hiddenTracks = /* @__PURE__ */ new Set() 4537}) => { 4538 var _a, _b, _c, _d; 4539 const musicians = reactExports.useMemo(() => { 4540 if (!processedInstruments) return []; 4541 const activeTrackIndicesWithNotes = /* @__PURE__ */ new Set(); 4542 const activeTrackNamesWithNotes = /* @__PURE__ */ new Set(); 4543 if (Array.isArray(pianoRollData) && pianoRollData.length > 0) { 4544 for (let i = 0; i < pianoRollData.length; i++) { 4545 const n = pianoRollData[i]; 4546 if (n) { 4547 if (typeof n.trackIdx === "number" && n.trackIdx >= 0) { 4548 activeTrackIndicesWithNotes.add(n.trackIdx); 4549 } 4550 if (typeof n.trackId === "number" && n.trackId >= 0) { 4551 activeTrackIndicesWithNotes.add(n.trackId); 4552 } 4553 if (typeof n.i === "
4553number") { 4554 activeTrackIndicesWithNotes.add(n.i); 4555 } else if (typeof n.i === "string" && n.i) { 4556 activeTrackNamesWithNotes.add(n.i); 4557 } 4558 } 4559 } 4560 } 4561 return processedInstruments.map((inst, idx) => ({ ...inst, index: idx })).filter((inst) => { 4562 if (inst.midiChannel === -1 || inst.channel === -1) { 4563 return false; 4564 } 4565 if (inst.notesCount === 0 || inst.notes_count === 0) { 4566 return false; 4567 } 4568 const name = ((inst == null ? void 0 : inst.displayName) || (inst == null ? void 0 : inst.name) || (inst == null ? void 0 : inst.originalName) || "").toLowerCase(); 4569 if (name.includes("informational") || name.includes("marker") || name.includes("conductor") || name.includes("tempo") || name === "informational track") { 4570 return false; 4571 } 4572 const uniqueName = uniqueNames[inst.index] || inst.displayName || inst.name; 4573 if (hiddenTracks && hiddenTracks.has(uniqueName)) { 4574 return false; 4575 } 4576 if (Array.isArray(pianoRollData) && pianoRollData.length > 0) { 4577 const hasNoteIndex = activeTrackIndicesWithNotes.has(inst.index) || inst.trackId !== void 0 && activeTrackIndicesWithNotes.has(inst.trackId); 4578 const hasNoteName = activeTrackNamesWithNotes.has(uniqueName) || activeTrackNamesWithNotes.has(inst.displayName) || activeTrackNamesWithNotes.has(inst.name) || activeTrackNamesWithNotes.has(inst.originalName); 4579 if (!hasNoteIndex && !hasNoteName) { 4580 return false; 4581 } 4582 } 4583 return true; 4584 }).map((inst) => { 4585 const program = inst.program; 4586 const channel = inst.midiChannel; 4587 let specificGraphic = null; 4588 if (instrumentConfigs) { 4589 if (channel === 9) { 4590 specificGraphic = instrumentConfigs.percussion[35] || null; 4591 } else { 4592 specificGraphic = instrumentConfigs.melodic[program] || null; 4593 } 4594 } 4595 let familyId = "family_0_pianos"; 4596 if (specificGraphic && typeof specificGraphic !== "string" && specificGraphic.characterId) { 4597 familyId = specificGraphic.characterId; 4598 } else if (channel === 9) { 4599 familyId = "percussion"; 4600 } else { 4601 const familyIndex = Math.floor(program / 8); 4602 const familyKeys = [ 4603 "family_0_pianos", 4604 "family_1_chromatic_percussion", 4605 "family_2_organs", 4606 "family_3_guitars", 4607 "family_4_basses", 4608 "family_5_strings", 4609 "family_6_ensemble", 4610 "family_7_brass", 4611 "family_8_reed", 4612 "family_9_pipe", 4613 "family_10_synth_lead", 4614 "family_11_synth_pad", 4615 "family_12_synth_effects", 4616 "family_13_ethnic", 4617 "family_14_percussive", 4618 "family_15_sound_effects" 4619 ]; 4620 familyId = familyKeys[familyIndex] || "family_0_pianos"; 4621 } 4622 const rawConfig = puppetConfigs ? puppetConfigs[familyId] || puppetConfigs["family_0_pianos"] || Object.values(puppetConfigs)[0] : null; 4623 if (!rawConfig) return null; 4624 const config = mergeWithGorillaConfig(rawConfig, puppetConfigs); 4625 const animator = createAnimator(config); 4626 const allActions = config.actions && config.actions.length > 0 ? config.actions : getDefaultBodyActions(); 4627 const allRules = config.actionRules && config.actionRules.length > 0 ? config.actionRules : [...defaultActionRules]; 4628 const musicianFamily = config.family || "other"; 4629 const matchedActionIds = /* @__PURE__ */ new Set(); 4630 for (const rule of allRules) { 4631 if (!rule.enabled) continue; 4632 let matches = false; 4633 switch (rule.condition.scope) { 4634 case "all": 4635 matches = true; 4636 break; 4637 case "family": 4638 matches = (rule.condition.families || []).includes(musicianFamily); 4639 break; 4640 case "selected": 4641 matches = (rule.condition.characterIds || []).includes(familyId); 4642 break; 4643 } 4644 if (matches) { 4645 rule.actionIds.forEach((id) => matchedActionIds.add(id)); 4646 } 4647 } 4648 if (matchedActionIds.size > 0) { 4649 const filteredActions = allActions.filter((a) => matchedActionIds.has(a.id)); 4650 animator.actionEngine.setActions(filteredActions); 4651 } 4652 let clothingGraphic = null; 4653 if (specificGraphic && typeof specificGraphic !== "string" && specificGraphic.clothingId === "
4653none") { 4654 clothingGraphic = null; 4655 } else if (specificGraphic && typeof specificGraphic !== "string" && specificGraphic.clothingId && clothingConfigs && clothingConfigs[specificGraphic.clothingId]) { 4656 clothingGraphic = clothingConfigs[specificGraphic.clothingId]; 4657 } else if (specificGraphic && typeof specificGraphic !== "string" && specificGraphic.clothingId && defaultClothingGraphics[specificGraphic.clothingId]) { 4658 clothingGraphic = defaultClothingGraphics[specificGraphic.clothingId]; 4659 } else { 4660 const availableClothes = clothingConfigs ? Object.values(clothingConfigs) : Object.values(defaultClothingGraphics); 4661 if (availableClothes.length > 0) { 4662 const randomIdx = inst.index * 137 % availableClothes.length; 4663 clothingGraphic = availableClothes[randomIdx]; 4664 } 4665 } 4666 return { 4667 index: inst.index, 4668 instrumentName: uniqueNames[inst.index] || inst.displayName || inst.name, 4669 uniqueName: uniqueNames[inst.index] || inst.displayName || inst.name, 4670 config, 4671 animator, 4672 specificGraphic, 4673 clothingGraphic, 4674 program, 4675 isPercussion: channel === 9, 4676 familyIndex: channel === 9 ? -1 : Math.floor(program / 8) 4677 }; 4678 }).filter(Boolean); 4679 }, [processedInstruments, uniqueNames, puppetConfigs, instrumentConfigs, clothingConfigs, hiddenTracks]); 4680 const bpm = ((_a = track.midiMetadata) == null ? void 0 : _a.tempo) || 120; 4681 const bps = bpm / 60; 4682 useAnimationFrame((time) => { 4683 const currentActive = {}; 4684 if (isPlaying && pianoRollData.length > 0) { 4685 let left = 0; 4686 let right = pianoRollData.length - 1; 4687 let startIdx = 0; 4688 const searchTime = Math.max(0, currentTime - 4); 4689 while (left <= right) { 4690 const mid = Math.floor((left + right) / 2); 4691 if (pianoRollData[mid].t < searchTime) { 4692 left = mid + 1; 4693 startIdx = left; 4694 } else { 4695 right = mid - 1; 4696 } 4697 } 4698 for (let i = startIdx; i < pianoRollData.length; i++) { 4699 const note = pianoRollData[i]; 4700 if (note.t > currentTime + 0.1) break; 4701 if (currentTime >= note.t && currentTime <= note.t + note.d) { 4702 if (!currentActive[note.i]) currentActive[note.i] = []; 4703 currentActive[note.i].push({ 4704 noteNumber: note.n, 4705 velocity: note.v, 4706 startTime: note.t, 4707 duration: note.d 4708 }); 4709 } 4710 } 4711 } 4712 musicians.forEach((m) => { 4713 const notes = currentActive[m.uniqueName] || []; 4714 m.animator.tick(time, notes, bps, isPlaying, m.specificGraphic ?? null); 4715 }); 4716 }); 4717 const handleMusicianClick = (e, index) => { 4718 e.stopPropagation(); 4719 onSolo(index); 4720 }; 4721 const handleMusicianContextMenu = (e, index) => { 4722 e.preventDefault(); 4723 e.stopPropagation(); 4724 onMute(index); 4725 }; 4726 const stageWidth = 1e3; 4727 const stageHeight = 600; 4728 const renderStage = () => { 4729 const count = musicians.length; 4730 if (count === 0) return /* @__PURE__ */ jsxRuntimeExports.jsx("text", { x: stageWidth / 2, y: stageHeight / 2, fill: "white", textAnchor: "middle", className: "text-sm opacity-50", children: "Awaiting Musicians..." }); 4731 if (count === 1) { 4732 const m = musicians[0]; 4733 return /* @__PURE__ */ jsxRuntimeExports.jsx( 4734 PuppetCharacter, 4735 { 4736 config: { ...m.config, scale: m.config.scale * 1.5 }, 4737 state: m.animator.getState(), 4738 x: stageWidth / 2, 4739 y: stageHeight * 0.7, 4740 isSoloed: soloedIndex === m.index, 4741 isMuted: mutedIndices.has(m.index), 4742 isHidden: soloedIndex !== null && soloedIndex !== m.index, 4743 instrumentName: m.instrumentName, 4744 specificGraphic: m.specificGraphic, 4745 clothingGraphic: m.clothingGraphic, 4746 onClick: (e) => handleMusicianClick(e, m.index), 4747 onContextMenu: (e) => handleMusicianContextMenu(e, m.index), 4748 onHover: (h) => synth.setHoverBoost(m.index, h) 4749 }, 4750 m.index 4751 ); 4752 } 4753 const backRow = []; 4754 const middleRow = []; 4755 const frontRow = []; 4756 const familySortWeights = { 4757 0: 10, 4758 // Pianos -> Left (Back) 4759 1: 20, 4760 // Chrom Perc 4761 2: 30, 4762 // Organ 4763 // Drums are 50 (Back center) 4764 10: 60, 4765 // Synth Lead 4766 11: 70, 4767 // Synth Pad 4768 12: 80, 4769 // Synth FX 4770 13: 90, 4771 // Ethnic 4772 14: 100, 4773 // Percussive 4774 15: 110, 4775 // FX 4776 // Middle Row: 4777 3: 10, 4778 // Guitar -> Left 4779 4: 20, 4780 // Bass -> Mid-left
4781 9: 30, 4782 // Pipe (Flutes) -> Center 4783 8: 40, 4784 // Reed (Oboe) -> Center 4785 6: 50, 4786 // Ensemble -> Center/Right 4787 7: 60, 4788 // Brass -> Right 4789 // Front Row: 4790 5: 10 4791 // Strings (Violin -> 10, Cello -> 12, etc.) 4792 }; 4793 musicians.forEach((m) => { 4794 if (m.isPercussion) { 4795 m.sortWeight = 50 * 100; 4796 backRow.push(m); 4797 } else { 4798 const weightBase = familySortWeights[m.familyIndex] || 50; 4799 m.sortWeight = weightBase * 100 + m.program; 4800 if ([0, 1, 2, 10, 11, 12, 13, 14, 15].includes(m.familyIndex)) { 4801 backRow.push(m); 4802 } else if ([3, 4, 6, 7, 8, 9].includes(m.familyIndex)) { 4803 middleRow.push(m); 4804 } else if ([5].includes(m.familyIndex)) { 4805 frontRow.push(m); 4806 } else { 4807 middleRow.push(m); 4808 } 4809 } 4810 }); 4811 backRow.sort((a, b) => a.sortWeight - b.sortWeight); 4812 middleRow.sort((a, b) => a.sortWeight - b.sortWeight); 4813 frontRow.sort((a, b) => a.sortWeight - b.sortWeight); 4814 const rows = [backRow, middleRow, frontRow].filter((r) => r.length > 0); 4815 const renderRow = (rowMusicians, rIndex) => { 4816 const rowDepth = 3 - rows.length + rIndex; 4817 return rowMusicians.map((m, i) => { 4818 const rowCount = rowMusicians.length; 4819 let col = i; 4820 let actualRowCount = rowCount; 4821 let stagger = 0; 4822 if (rowCount > 8) { 4823 const subRows = Math.ceil(rowCount / 8); 4824 const subRowIndex = i % subRows; 4825 col = Math.floor(i / subRows); 4826 actualRowCount = Math.ceil(rowCount / subRows); 4827 stagger = subRowIndex * 30; 4828 } else if (rowCount > 5) { 4829 stagger = i % 2 === 0 ? -15 : 15; 4830 } 4831 const stageSpread = 800; 4832 const xOffset = (stageWidth - stageSpread) / 2; 4833 const x = xOffset + stageSpread / (actualRowCount + 1) * (col + 1); 4834 const baseY = stageHeight * 0.35 + rowDepth * 110; 4835 const y = baseY + stagger; 4836 const scaleAdj = 0.9 + rowDepth * 0.3; 4837 return /* @__PURE__ */ jsxRuntimeExports.jsx( 4838 motion.g, 4839 { 4840 drag: true, 4841 dragMomentum: false, 4842 initial: { x, y }, 4843 onClick: (e) => e.stopPropagation(), 4844 onDragStart: () => { 4845 window.isPuppetDragging = true; 4846 }, 4847 onDrag: (event, info) => { 4848 const target = event.target; 4849 if (!target || !target.getBoundingClientRect) return; 4850 const rect = target.getBoundingClientRect(); 4851 const centerX = rect.left + rect.width / 2; 4852 const windowWidth = window.innerWidth; 4853 let pan = centerX / windowWidth * 2 - 1; 4854 pan = Math.max(-1, Math.min(1, pan)); 4855 synth.setPan(m.index, pan); 4856 }, 4857 onDragEnd: (event, info) => { 4858 const target = event.target; 4859 if (!target || !target.getBoundingClientRect) return; 4860 const rect = target.getBoundingClientRect(); 4861 const centerX = rect.left + rect.width / 2; 4862 const windowWidth = window.innerWidth; 4863 let pan = centerX / windowWidth * 2 - 1; 4864 pan = Math.max(-1, Math.min(1, pan)); 4865 if (onPanChange) { 4866 onPanChange(m.index, pan); 4867 } 4868 setTimeout(() => { 4869 window.isPuppetDragging = false; 4870 }, 50); 4871 }, 4872 style: { cursor: "grab" }, 4873 whileDrag: { cursor: "grabbing", scale: 1.1 }, 4874 children: /* @__PURE__ */ jsxRuntimeExports.jsx( 4875 PuppetCharacter, 4876 { 4877 config: { ...m.config, scale: m.config.scale * scaleAdj }, 4878 state: m.animator.getState(), 4879 x: 0, 4880 y: 0, 4881 isSoloed: soloedIndex === m.index, 4882 isMuted: mutedIndices.has(m.index), 4883 isHidden: soloedIndex !== null && soloedIndex !== m.index, 4884 instrumentName: m.instrumentName, 4885 specificGraphic: m.specificGraphic, 4886 clothingGraphic: m.clothingGraphic, 4887 onClick: (e) =>
4887 handleMusicianClick(e, m.index), 4888 onContextMenu: (e) => handleMusicianContextMenu(e, m.index), 4889 onHover: (h) => synth.setHoverBoost(m.index, h) 4890 }, 4891 m.index 4892 ) 4893 }, 4894 `drag-${m.index}` 4895 ); 4896 }); 4897 }; 4898 return rows.flatMap((rowMusicians, rIndex) => renderRow(rowMusicians, rIndex)); 4899 }; 4900 const bg = backgroundConfig || defaultBackgroundConfig; 4901 let bgStyle = {}; 4902 if (bg.type === "color") { 4903 bgStyle = { backgroundColor: bg.color }; 4904 } else if (bg.type === "gradient") { 4905 const { type, angle, stops } = bg.gradient; 4906 const stopStr = stops.slice().sort((a, b) => a.offset - b.offset).map((s) => `${s.color} ${s.offset}%`).join(", "); 4907 if (type === "radial") { 4908 bgStyle = { background: `radial-gradient(circle, ${stopStr})` }; 4909 } else { 4910 bgStyle = { background: `linear-gradient(${angle}deg, ${stopStr})` }; 4911 } 4912 } else if (bg.type === "image") { 4913 bgStyle = { backgroundColor: bg.color || "#050505" }; 4914 } 4915 const imageStyle = bg.type === "image" ? { 4916 backgroundImage: `url("${bg.image.url}")`, 4917 backgroundSize: bg.image.size, 4918 backgroundPosition: bg.image.position, 4919 backgroundRepeat: bg.image.repeat, 4920 filter: bg.image.blur ? `blur(${bg.image.blur}px)` : void 0, 4921 transform: bg.image.blur ? "scale(1.05)" : void 0 4922 } : {}; 4923 return /* @__PURE__ */ jsxRuntimeExports.jsxs( 4924 "div", 4925 { 4926 className: "w-full h-full flex items-center justify-center bg-[#050505] overflow-hidden relative", 4927 onContextMenu: (e) => e.preventDefault(), 4928 style: bgStyle, 4929 children: [ 4930 bg.type === "image" && ((_b = bg.image) == null ? void 0 : _b.url) && /* @__P
4930URE__ */ jsxRuntimeExports.jsx( 4931 "div", 4932 { 4933 className: "absolute inset-0 bg-cover bg-center", 4934 style: { 4935 ...imageStyle, 4936 zIndex: 1 4937 } 4938 } 4939 ), 4940 ((_c = bg.overlay) == null ? void 0 : _c.enabled) && /* @__PURE__ */ jsxRuntimeExports.jsx( 4941 "div", 4942 { 4943 className: "absolute inset-0 pointer-events-none", 4944 style: { 4945 backgroundColor: bg.overlay.color, 4946 opacity: bg.overlay.opacity, 4947 zIndex: 2 4948 } 4949 } 4950 ), 4951 ((_d = bg.particles) == null ? void 0 : _d.enabled) && /* @__PURE__ */ jsxRuntimeExports.jsx(BackgroundParticlesCanvas, { config: bg.particles, isPlaying }), 4952 /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 z-[4] pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute bottom-0 left-0 right-0 h-1/2 bg-gradient-to-t from-black/80 to-transparent" }) }),
4953 /* @__PURE__ */ jsxRuntimeExports.jsxs( 4954 "svg", 4955 { 4956 viewBox: `0 0 ${stageWidth} ${stageHeight}`, 4957 className: "w-full h-full max-w-6xl relative z-[10]", 4958 preserveAspectRatio: "xMidYMid meet", 4959 children: [ 4960 /* @__PURE__ */ jsxRuntimeExports.jsx("defs", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("radialGradient", { id: "soloGradient", children: [ 4961 /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: "#F59E0B", stopOpacity: "0.6" }), 4962 /* @__PURE__ */ jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: "#F59E0B", stopOpacity: "0" }) 4963 ] }) }), 4964 renderStage() 4965 ] 4966 } 4967 ) 4968 ] 4969 } 4970 ); 4971}; 4972export { 4973 BackgroundParticlesCanvas as B, 4974 PuppetCharacter as P, 4975 StagePuppetEngine as S, 4976 defaultBackgroundPresets as a, 4977 defaultClothingGraphics as b, 4978 createAnimator as c, 4979 defaultBackgroundConfig as d, 4980 defaultActionRules as e, 4981 defaultPuppetConfigs as f, 4982 getDefaultBodyActions as g, 4983 mergeAllConfigsWithGorilla as m, 4984 parseSvgSocketsAndPlugs as p 4985};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.