@nice2dev/ui-3d 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/dist/cjs/collaborative/collaborativeScene.js +16 -8
  2. package/dist/cjs/collaborative/collaborativeScene.js.map +1 -1
  3. package/dist/cjs/core/i18n.js +3 -3
  4. package/dist/cjs/dance/DanceBridge.js +65 -30
  5. package/dist/cjs/dance/DanceBridge.js.map +1 -1
  6. package/dist/cjs/dance/DanceScoreEngine.js +37 -23
  7. package/dist/cjs/dance/DanceScoreEngine.js.map +1 -1
  8. package/dist/cjs/dance/PoseDetector.js +92 -49
  9. package/dist/cjs/dance/PoseDetector.js.map +1 -1
  10. package/dist/cjs/material/NiceMaterialEditor.js +134 -91
  11. package/dist/cjs/material/NiceMaterialEditor.js.map +1 -1
  12. package/dist/cjs/material/materialEditorUtils.js +86 -55
  13. package/dist/cjs/material/materialEditorUtils.js.map +1 -1
  14. package/dist/cjs/model/ModelEditor.js +16 -14
  15. package/dist/cjs/model/ModelEditor.js.map +1 -1
  16. package/dist/cjs/model/ModelEditorLeftPanel.js +21 -15
  17. package/dist/cjs/model/ModelEditorLeftPanel.js.map +1 -1
  18. package/dist/cjs/model/ModelEditorMenuBar.js +23 -10
  19. package/dist/cjs/model/ModelEditorMenuBar.js.map +1 -1
  20. package/dist/cjs/model/ModelEditorRightPanel.js +32 -27
  21. package/dist/cjs/model/ModelEditorRightPanel.js.map +1 -1
  22. package/dist/cjs/model/ModelEditorSubComponents.js +20 -20
  23. package/dist/cjs/model/ModelEditorSubComponents.js.map +1 -1
  24. package/dist/cjs/model/ModelEditorTimeline.js +12 -11
  25. package/dist/cjs/model/ModelEditorTimeline.js.map +1 -1
  26. package/dist/cjs/model/ModelEditorToolbar.js +5 -5
  27. package/dist/cjs/model/ModelEditorToolbar.js.map +1 -1
  28. package/dist/cjs/model/ModelEditorViewport.js +12 -8
  29. package/dist/cjs/model/ModelEditorViewport.js.map +1 -1
  30. package/dist/cjs/model/ModelViewer.js +16 -15
  31. package/dist/cjs/model/ModelViewer.js.map +1 -1
  32. package/dist/cjs/model/NiceArmatureEditor.js +53 -30
  33. package/dist/cjs/model/NiceArmatureEditor.js.map +1 -1
  34. package/dist/cjs/model/NiceMorphTargetEditor.js +51 -30
  35. package/dist/cjs/model/NiceMorphTargetEditor.js.map +1 -1
  36. package/dist/cjs/model/NiceOctree.js +62 -37
  37. package/dist/cjs/model/NiceOctree.js.map +1 -1
  38. package/dist/cjs/model/NicePhysicsSimulation.js +74 -36
  39. package/dist/cjs/model/NicePhysicsSimulation.js.map +1 -1
  40. package/dist/cjs/model/NiceProceduralGeometry.js +101 -31
  41. package/dist/cjs/model/NiceProceduralGeometry.js.map +1 -1
  42. package/dist/cjs/model/NiceTerrainEditor.js +88 -38
  43. package/dist/cjs/model/NiceTerrainEditor.js.map +1 -1
  44. package/dist/cjs/model/NiceWeightPainter.js +46 -24
  45. package/dist/cjs/model/NiceWeightPainter.js.map +1 -1
  46. package/dist/cjs/model/NiceXRPreview.js +89 -43
  47. package/dist/cjs/model/NiceXRPreview.js.map +1 -1
  48. package/dist/cjs/model/cadModeUtils.js +2 -2
  49. package/dist/cjs/model/cadModeUtils.js.map +1 -1
  50. package/dist/cjs/model/editorShortcuts.js +69 -35
  51. package/dist/cjs/model/editorShortcuts.js.map +1 -1
  52. package/dist/cjs/model/modelEditorTypes.js +56 -52
  53. package/dist/cjs/model/modelEditorTypes.js.map +1 -1
  54. package/dist/cjs/model/modelEditorUtils.js +356 -131
  55. package/dist/cjs/model/modelEditorUtils.js.map +1 -1
  56. package/dist/cjs/model/simsModeUtils.js +819 -74
  57. package/dist/cjs/model/simsModeUtils.js.map +1 -1
  58. package/dist/cjs/model/useModelEditor.js +501 -394
  59. package/dist/cjs/model/useModelEditor.js.map +1 -1
  60. package/dist/cjs/model/useModelViewer.js +117 -86
  61. package/dist/cjs/model/useModelViewer.js.map +1 -1
  62. package/dist/cjs/particle/NiceParticleEditor.js +126 -69
  63. package/dist/cjs/particle/NiceParticleEditor.js.map +1 -1
  64. package/dist/cjs/particle/particleEditorUtils.js +64 -27
  65. package/dist/cjs/particle/particleEditorUtils.js.map +1 -1
  66. package/dist/cjs/rendering/NiceCascadedShadows.js +35 -23
  67. package/dist/cjs/rendering/NiceCascadedShadows.js.map +1 -1
  68. package/dist/cjs/rendering/NiceRenderExport.js +26 -12
  69. package/dist/cjs/rendering/NiceRenderExport.js.map +1 -1
  70. package/dist/cjs/rendering/NiceSSAO.js +28 -23
  71. package/dist/cjs/rendering/NiceSSAO.js.map +1 -1
  72. package/dist/cjs/rendering/NiceSSR.js +22 -18
  73. package/dist/cjs/rendering/NiceSSR.js.map +1 -1
  74. package/dist/cjs/rendering/NiceWebGPURenderer.js +60 -37
  75. package/dist/cjs/rendering/NiceWebGPURenderer.js.map +1 -1
  76. package/dist/cjs/ui/dist/NiceErrorBoundary-D75yr_WE.js +572 -0
  77. package/dist/cjs/ui/dist/NiceErrorBoundary-D75yr_WE.js.map +1 -0
  78. package/dist/cjs/ui/dist/NiceForm-CKoJA6NG.js +4979 -0
  79. package/dist/cjs/ui/dist/NiceForm-CKoJA6NG.js.map +1 -0
  80. package/dist/cjs/ui/dist/NiceModal-1QX7NhyH.js +98 -0
  81. package/dist/cjs/ui/dist/NiceModal-1QX7NhyH.js.map +1 -0
  82. package/dist/cjs/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js +5244 -0
  83. package/dist/cjs/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js.map +1 -0
  84. package/dist/cjs/ui/dist/NicePagination-DbE-uOkl.js +174 -0
  85. package/dist/cjs/ui/dist/NicePagination-DbE-uOkl.js.map +1 -0
  86. package/dist/cjs/ui/dist/NicePinCodeInput-B8cI8zu8.js +10459 -0
  87. package/dist/cjs/ui/dist/NicePinCodeInput-B8cI8zu8.js.map +1 -0
  88. package/dist/cjs/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js +6313 -0
  89. package/dist/cjs/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js.map +1 -0
  90. package/dist/cjs/ui/dist/NiceTabs-DzLHzwuA.js +1639 -0
  91. package/dist/cjs/ui/dist/NiceTabs-DzLHzwuA.js.map +1 -0
  92. package/dist/cjs/ui/dist/NiceWindow-DeC83yfE.js +807 -0
  93. package/dist/cjs/ui/dist/NiceWindow-DeC83yfE.js.map +1 -0
  94. package/dist/cjs/ui/dist/charts-BAS21jkC.js +3833 -0
  95. package/dist/cjs/ui/dist/charts-BAS21jkC.js.map +1 -0
  96. package/dist/cjs/ui/dist/core-Da43q-pO.js +11683 -0
  97. package/dist/cjs/ui/dist/core-Da43q-pO.js.map +1 -0
  98. package/dist/cjs/ui/dist/index-pAuGTbuB.js +28876 -0
  99. package/dist/cjs/ui/dist/index-pAuGTbuB.js.map +1 -0
  100. package/dist/cjs/uv/NiceUVEditor.js +115 -86
  101. package/dist/cjs/uv/NiceUVEditor.js.map +1 -1
  102. package/dist/cjs/uv/uvEditorUtils.js +41 -23
  103. package/dist/cjs/uv/uvEditorUtils.js.map +1 -1
  104. package/dist/esm/collaborative/collaborativeScene.js +16 -8
  105. package/dist/esm/collaborative/collaborativeScene.js.map +1 -1
  106. package/dist/esm/dance/DanceBridge.js +55 -20
  107. package/dist/esm/dance/DanceBridge.js.map +1 -1
  108. package/dist/esm/dance/DanceScoreEngine.js +29 -15
  109. package/dist/esm/dance/DanceScoreEngine.js.map +1 -1
  110. package/dist/esm/dance/PoseDetector.js +73 -30
  111. package/dist/esm/dance/PoseDetector.js.map +1 -1
  112. package/dist/esm/material/NiceMaterialEditor.js +115 -72
  113. package/dist/esm/material/NiceMaterialEditor.js.map +1 -1
  114. package/dist/esm/material/materialEditorUtils.js +86 -55
  115. package/dist/esm/material/materialEditorUtils.js.map +1 -1
  116. package/dist/esm/model/ModelEditor.js +16 -14
  117. package/dist/esm/model/ModelEditor.js.map +1 -1
  118. package/dist/esm/model/ModelEditorLeftPanel.js +21 -15
  119. package/dist/esm/model/ModelEditorLeftPanel.js.map +1 -1
  120. package/dist/esm/model/ModelEditorMenuBar.js +23 -10
  121. package/dist/esm/model/ModelEditorMenuBar.js.map +1 -1
  122. package/dist/esm/model/ModelEditorRightPanel.js +32 -27
  123. package/dist/esm/model/ModelEditorRightPanel.js.map +1 -1
  124. package/dist/esm/model/ModelEditorSubComponents.js +17 -17
  125. package/dist/esm/model/ModelEditorSubComponents.js.map +1 -1
  126. package/dist/esm/model/ModelEditorTimeline.js +12 -11
  127. package/dist/esm/model/ModelEditorTimeline.js.map +1 -1
  128. package/dist/esm/model/ModelEditorToolbar.js +5 -5
  129. package/dist/esm/model/ModelEditorToolbar.js.map +1 -1
  130. package/dist/esm/model/ModelEditorViewport.js +12 -8
  131. package/dist/esm/model/ModelEditorViewport.js.map +1 -1
  132. package/dist/esm/model/ModelViewer.js +8 -7
  133. package/dist/esm/model/ModelViewer.js.map +1 -1
  134. package/dist/esm/model/NiceArmatureEditor.js +36 -13
  135. package/dist/esm/model/NiceArmatureEditor.js.map +1 -1
  136. package/dist/esm/model/NiceMorphTargetEditor.js +34 -13
  137. package/dist/esm/model/NiceMorphTargetEditor.js.map +1 -1
  138. package/dist/esm/model/NiceOctree.js +47 -22
  139. package/dist/esm/model/NiceOctree.js.map +1 -1
  140. package/dist/esm/model/NicePhysicsSimulation.js +51 -13
  141. package/dist/esm/model/NicePhysicsSimulation.js.map +1 -1
  142. package/dist/esm/model/NiceProceduralGeometry.js +94 -24
  143. package/dist/esm/model/NiceProceduralGeometry.js.map +1 -1
  144. package/dist/esm/model/NiceTerrainEditor.js +70 -20
  145. package/dist/esm/model/NiceTerrainEditor.js.map +1 -1
  146. package/dist/esm/model/NiceWeightPainter.js +33 -11
  147. package/dist/esm/model/NiceWeightPainter.js.map +1 -1
  148. package/dist/esm/model/NiceXRPreview.js +72 -26
  149. package/dist/esm/model/NiceXRPreview.js.map +1 -1
  150. package/dist/esm/model/cadModeUtils.js +2 -2
  151. package/dist/esm/model/cadModeUtils.js.map +1 -1
  152. package/dist/esm/model/editorShortcuts.js +69 -35
  153. package/dist/esm/model/editorShortcuts.js.map +1 -1
  154. package/dist/esm/model/modelEditorTypes.js +56 -52
  155. package/dist/esm/model/modelEditorTypes.js.map +1 -1
  156. package/dist/esm/model/modelEditorUtils.js +356 -131
  157. package/dist/esm/model/modelEditorUtils.js.map +1 -1
  158. package/dist/esm/model/simsModeUtils.js +819 -74
  159. package/dist/esm/model/simsModeUtils.js.map +1 -1
  160. package/dist/esm/model/useModelEditor.js +384 -277
  161. package/dist/esm/model/useModelEditor.js.map +1 -1
  162. package/dist/esm/model/useModelViewer.js +56 -25
  163. package/dist/esm/model/useModelViewer.js.map +1 -1
  164. package/dist/esm/particle/NiceParticleEditor.js +116 -59
  165. package/dist/esm/particle/NiceParticleEditor.js.map +1 -1
  166. package/dist/esm/particle/particleEditorUtils.js +64 -27
  167. package/dist/esm/particle/particleEditorUtils.js.map +1 -1
  168. package/dist/esm/rendering/NiceCascadedShadows.js +24 -12
  169. package/dist/esm/rendering/NiceCascadedShadows.js.map +1 -1
  170. package/dist/esm/rendering/NiceRenderExport.js +22 -8
  171. package/dist/esm/rendering/NiceRenderExport.js.map +1 -1
  172. package/dist/esm/rendering/NiceSSAO.js +11 -6
  173. package/dist/esm/rendering/NiceSSAO.js.map +1 -1
  174. package/dist/esm/rendering/NiceSSR.js +9 -5
  175. package/dist/esm/rendering/NiceSSR.js.map +1 -1
  176. package/dist/esm/rendering/NiceWebGPURenderer.js +40 -17
  177. package/dist/esm/rendering/NiceWebGPURenderer.js.map +1 -1
  178. package/dist/esm/ui/dist/NiceErrorBoundary-D75yr_WE.js +563 -0
  179. package/dist/esm/ui/dist/NiceErrorBoundary-D75yr_WE.js.map +1 -0
  180. package/dist/esm/ui/dist/NiceForm-CKoJA6NG.js +4960 -0
  181. package/dist/esm/ui/dist/NiceForm-CKoJA6NG.js.map +1 -0
  182. package/dist/esm/ui/dist/NiceModal-1QX7NhyH.js +96 -0
  183. package/dist/esm/ui/dist/NiceModal-1QX7NhyH.js.map +1 -0
  184. package/dist/esm/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js +5201 -0
  185. package/dist/esm/ui/dist/NiceModuleLifecyclePanel-CMrPbSw9.js.map +1 -0
  186. package/dist/esm/ui/dist/NicePagination-DbE-uOkl.js +172 -0
  187. package/dist/esm/ui/dist/NicePagination-DbE-uOkl.js.map +1 -0
  188. package/dist/esm/ui/dist/NicePinCodeInput-B8cI8zu8.js +10408 -0
  189. package/dist/esm/ui/dist/NicePinCodeInput-B8cI8zu8.js.map +1 -0
  190. package/dist/esm/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js +6296 -0
  191. package/dist/esm/ui/dist/NiceSavedQueryPanel-CZ5bYOe_.js.map +1 -0
  192. package/dist/esm/ui/dist/NiceTabs-DzLHzwuA.js +1630 -0
  193. package/dist/esm/ui/dist/NiceTabs-DzLHzwuA.js.map +1 -0
  194. package/dist/esm/ui/dist/NiceWindow-DeC83yfE.js +796 -0
  195. package/dist/esm/ui/dist/NiceWindow-DeC83yfE.js.map +1 -0
  196. package/dist/esm/ui/dist/charts-BAS21jkC.js +3809 -0
  197. package/dist/esm/ui/dist/charts-BAS21jkC.js.map +1 -0
  198. package/dist/esm/ui/dist/core-Da43q-pO.js +11502 -0
  199. package/dist/esm/ui/dist/core-Da43q-pO.js.map +1 -0
  200. package/dist/esm/ui/dist/index-pAuGTbuB.js +28792 -0
  201. package/dist/esm/ui/dist/index-pAuGTbuB.js.map +1 -0
  202. package/dist/esm/uv/NiceUVEditor.js +91 -62
  203. package/dist/esm/uv/NiceUVEditor.js.map +1 -1
  204. package/dist/esm/uv/uvEditorUtils.js +41 -23
  205. package/dist/esm/uv/uvEditorUtils.js.map +1 -1
  206. package/package.json +2 -2
@@ -25,16 +25,18 @@ class UndoRedoStack {
25
25
  }
26
26
  undo() {
27
27
  const action = this.undoStack.pop();
28
- if (!action)
28
+ if (!action) {
29
29
  return null;
30
+ }
30
31
  action.undo();
31
32
  this.redoStack.push(action);
32
33
  return action.label;
33
34
  }
34
35
  redo() {
35
36
  const action = this.redoStack.pop();
36
- if (!action)
37
+ if (!action) {
37
38
  return null;
39
+ }
38
40
  action.redo();
39
41
  this.undoStack.push(action);
40
42
  return action.label;
@@ -67,11 +69,13 @@ const DEFAULT_LOD_LEVELS = [
67
69
  * Returns a new geometry with reduced vertex count.
68
70
  */
69
71
  function decimateGeometry(geometry, ratio) {
70
- if (ratio >= 1.0)
72
+ if (ratio >= 1.0) {
71
73
  return geometry.clone();
72
- const posAttr = geometry.getAttribute("position");
73
- if (!posAttr)
74
+ }
75
+ const posAttr = geometry.getAttribute('position');
76
+ if (!posAttr) {
74
77
  return geometry.clone();
78
+ }
75
79
  const positions = Array.from(posAttr.array);
76
80
  const vertexCount = posAttr.count;
77
81
  const targetCount = Math.max(3, Math.floor(vertexCount * ratio));
@@ -130,7 +134,7 @@ function decimateGeometry(geometry, ratio) {
130
134
  }
131
135
  }
132
136
  const result = new THREE.BufferGeometry();
133
- result.setAttribute("position", new THREE.BufferAttribute(newPositions, 3));
137
+ result.setAttribute('position', new THREE.BufferAttribute(newPositions, 3));
134
138
  result.setIndex(newIndices);
135
139
  result.computeVertexNormals();
136
140
  return result;
@@ -140,7 +144,7 @@ function decimateGeometry(geometry, ratio) {
140
144
  */
141
145
  function generateLOD(mesh, levels = DEFAULT_LOD_LEVELS) {
142
146
  const lod = new THREE.LOD();
143
- lod.name = mesh.name + "_LOD";
147
+ lod.name = mesh.name + '_LOD';
144
148
  lod.position.copy(mesh.position);
145
149
  lod.rotation.copy(mesh.rotation);
146
150
  lod.scale.copy(mesh.scale);
@@ -158,44 +162,233 @@ function generateLOD(mesh, levels = DEFAULT_LOD_LEVELS) {
158
162
  }
159
163
  const PBR_PRESETS = [
160
164
  // Metals
161
- { name: "Gold", category: "Metal", params: { color: 0xffd700, metalness: 1.0, roughness: 0.15 } },
162
- { name: "Silver", category: "Metal", params: { color: 0xc0c0c0, metalness: 1.0, roughness: 0.1 } },
163
- { name: "Copper", category: "Metal", params: { color: 0xb87333, metalness: 1.0, roughness: 0.25 } },
164
- { name: "Iron", category: "Metal", params: { color: 0x808080, metalness: 0.95, roughness: 0.6 } },
165
- { name: "Brushed Steel", category: "Metal", params: { color: 0xa0a0a0, metalness: 0.95, roughness: 0.4 } },
166
- { name: "Chrome", category: "Metal", params: { color: 0xdddddd, metalness: 1.0, roughness: 0.02 } },
167
- { name: "Bronze", category: "Metal", params: { color: 0xcd7f32, metalness: 0.9, roughness: 0.35 } },
168
- { name: "Aluminum", category: "Metal", params: { color: 0xd6d6d6, metalness: 0.85, roughness: 0.2 } },
165
+ { name: 'Gold', category: 'Metal', params: { color: 0xffd700, metalness: 1.0, roughness: 0.15 } },
166
+ {
167
+ name: 'Silver',
168
+ category: 'Metal',
169
+ params: { color: 0xc0c0c0, metalness: 1.0, roughness: 0.1 },
170
+ },
171
+ {
172
+ name: 'Copper',
173
+ category: 'Metal',
174
+ params: { color: 0xb87333, metalness: 1.0, roughness: 0.25 },
175
+ },
176
+ { name: 'Iron', category: 'Metal', params: { color: 0x808080, metalness: 0.95, roughness: 0.6 } },
177
+ {
178
+ name: 'Brushed Steel',
179
+ category: 'Metal',
180
+ params: { color: 0xa0a0a0, metalness: 0.95, roughness: 0.4 },
181
+ },
182
+ {
183
+ name: 'Chrome',
184
+ category: 'Metal',
185
+ params: { color: 0xdddddd, metalness: 1.0, roughness: 0.02 },
186
+ },
187
+ {
188
+ name: 'Bronze',
189
+ category: 'Metal',
190
+ params: { color: 0xcd7f32, metalness: 0.9, roughness: 0.35 },
191
+ },
192
+ {
193
+ name: 'Aluminum',
194
+ category: 'Metal',
195
+ params: { color: 0xd6d6d6, metalness: 0.85, roughness: 0.2 },
196
+ },
169
197
  // Non-metals
170
- { name: "Plastic (White)", category: "Plastic", params: { color: 0xf0f0f0, metalness: 0.0, roughness: 0.3 } },
171
- { name: "Plastic (Red)", category: "Plastic", params: { color: 0xcc2222, metalness: 0.0, roughness: 0.35 } },
172
- { name: "Rubber", category: "Plastic", params: { color: 0x333333, metalness: 0.0, roughness: 0.9 } },
198
+ {
199
+ name: 'Plastic (White)',
200
+ category: 'Plastic',
201
+ params: { color: 0xf0f0f0, metalness: 0.0, roughness: 0.3 },
202
+ },
203
+ {
204
+ name: 'Plastic (Red)',
205
+ category: 'Plastic',
206
+ params: { color: 0xcc2222, metalness: 0.0, roughness: 0.35 },
207
+ },
208
+ {
209
+ name: 'Rubber',
210
+ category: 'Plastic',
211
+ params: { color: 0x333333, metalness: 0.0, roughness: 0.9 },
212
+ },
173
213
  // Glass
174
- { name: "Clear Glass", category: "Glass", params: { color: 0xffffff, metalness: 0.0, roughness: 0.0, opacity: 0.15, transparent: true, ior: 1.5, transmission: 0.95 } },
175
- { name: "Frosted Glass", category: "Glass", params: { color: 0xeeeeff, metalness: 0.0, roughness: 0.5, opacity: 0.3, transparent: true, ior: 1.5, transmission: 0.7 } },
176
- { name: "Tinted Glass", category: "Glass", params: { color: 0x224488, metalness: 0.0, roughness: 0.05, opacity: 0.3, transparent: true, ior: 1.5, transmission: 0.8 } },
214
+ {
215
+ name: 'Clear Glass',
216
+ category: 'Glass',
217
+ params: {
218
+ color: 0xffffff,
219
+ metalness: 0.0,
220
+ roughness: 0.0,
221
+ opacity: 0.15,
222
+ transparent: true,
223
+ ior: 1.5,
224
+ transmission: 0.95,
225
+ },
226
+ },
227
+ {
228
+ name: 'Frosted Glass',
229
+ category: 'Glass',
230
+ params: {
231
+ color: 0xeeeeff,
232
+ metalness: 0.0,
233
+ roughness: 0.5,
234
+ opacity: 0.3,
235
+ transparent: true,
236
+ ior: 1.5,
237
+ transmission: 0.7,
238
+ },
239
+ },
240
+ {
241
+ name: 'Tinted Glass',
242
+ category: 'Glass',
243
+ params: {
244
+ color: 0x224488,
245
+ metalness: 0.0,
246
+ roughness: 0.05,
247
+ opacity: 0.3,
248
+ transparent: true,
249
+ ior: 1.5,
250
+ transmission: 0.8,
251
+ },
252
+ },
177
253
  // Natural
178
- { name: "Wood (Light)", category: "Natural", params: { color: 0xc19a6b, metalness: 0.0, roughness: 0.7 } },
179
- { name: "Wood (Dark)", category: "Natural", params: { color: 0x5c3317, metalness: 0.0, roughness: 0.65 } },
180
- { name: "Stone", category: "Natural", params: { color: 0x888888, metalness: 0.0, roughness: 0.85 } },
181
- { name: "Marble", category: "Natural", params: { color: 0xf0ece0, metalness: 0.0, roughness: 0.15 } },
182
- { name: "Clay", category: "Natural", params: { color: 0xb5651d, metalness: 0.0, roughness: 0.8 } },
183
- { name: "Concrete", category: "Natural", params: { color: 0x999999, metalness: 0.0, roughness: 0.95 } },
254
+ {
255
+ name: 'Wood (Light)',
256
+ category: 'Natural',
257
+ params: { color: 0xc19a6b, metalness: 0.0, roughness: 0.7 },
258
+ },
259
+ {
260
+ name: 'Wood (Dark)',
261
+ category: 'Natural',
262
+ params: { color: 0x5c3317, metalness: 0.0, roughness: 0.65 },
263
+ },
264
+ {
265
+ name: 'Stone',
266
+ category: 'Natural',
267
+ params: { color: 0x888888, metalness: 0.0, roughness: 0.85 },
268
+ },
269
+ {
270
+ name: 'Marble',
271
+ category: 'Natural',
272
+ params: { color: 0xf0ece0, metalness: 0.0, roughness: 0.15 },
273
+ },
274
+ {
275
+ name: 'Clay',
276
+ category: 'Natural',
277
+ params: { color: 0xb5651d, metalness: 0.0, roughness: 0.8 },
278
+ },
279
+ {
280
+ name: 'Concrete',
281
+ category: 'Natural',
282
+ params: { color: 0x999999, metalness: 0.0, roughness: 0.95 },
283
+ },
184
284
  // Fabric
185
- { name: "Cotton (White)", category: "Fabric", params: { color: 0xf5f5dc, metalness: 0.0, roughness: 0.9, sheen: 0.5, sheenRoughness: 0.8, sheenColor: 0xffffff } },
186
- { name: "Silk", category: "Fabric", params: { color: 0xe3dac9, metalness: 0.0, roughness: 0.3, sheen: 1.0, sheenRoughness: 0.3, sheenColor: 0xffeedd } },
187
- { name: "Velvet", category: "Fabric", params: { color: 0x800020, metalness: 0.0, roughness: 0.95, sheen: 0.9, sheenRoughness: 0.9, sheenColor: 0xff4060 } },
188
- { name: "Leather", category: "Fabric", params: { color: 0x553322, metalness: 0.0, roughness: 0.6 } },
189
- { name: "Denim", category: "Fabric", params: { color: 0x3b5998, metalness: 0.0, roughness: 0.85 } },
285
+ {
286
+ name: 'Cotton (White)',
287
+ category: 'Fabric',
288
+ params: {
289
+ color: 0xf5f5dc,
290
+ metalness: 0.0,
291
+ roughness: 0.9,
292
+ sheen: 0.5,
293
+ sheenRoughness: 0.8,
294
+ sheenColor: 0xffffff,
295
+ },
296
+ },
297
+ {
298
+ name: 'Silk',
299
+ category: 'Fabric',
300
+ params: {
301
+ color: 0xe3dac9,
302
+ metalness: 0.0,
303
+ roughness: 0.3,
304
+ sheen: 1.0,
305
+ sheenRoughness: 0.3,
306
+ sheenColor: 0xffeedd,
307
+ },
308
+ },
309
+ {
310
+ name: 'Velvet',
311
+ category: 'Fabric',
312
+ params: {
313
+ color: 0x800020,
314
+ metalness: 0.0,
315
+ roughness: 0.95,
316
+ sheen: 0.9,
317
+ sheenRoughness: 0.9,
318
+ sheenColor: 0xff4060,
319
+ },
320
+ },
321
+ {
322
+ name: 'Leather',
323
+ category: 'Fabric',
324
+ params: { color: 0x553322, metalness: 0.0, roughness: 0.6 },
325
+ },
326
+ {
327
+ name: 'Denim',
328
+ category: 'Fabric',
329
+ params: { color: 0x3b5998, metalness: 0.0, roughness: 0.85 },
330
+ },
190
331
  // Skin
191
- { name: "Skin (Light)", category: "Skin", params: { color: 0xffdbac, metalness: 0.0, roughness: 0.55 } },
192
- { name: "Skin (Medium)", category: "Skin", params: { color: 0xc68642, metalness: 0.0, roughness: 0.5 } },
193
- { name: "Skin (Dark)", category: "Skin", params: { color: 0x8d5524, metalness: 0.0, roughness: 0.5 } },
332
+ {
333
+ name: 'Skin (Light)',
334
+ category: 'Skin',
335
+ params: { color: 0xffdbac, metalness: 0.0, roughness: 0.55 },
336
+ },
337
+ {
338
+ name: 'Skin (Medium)',
339
+ category: 'Skin',
340
+ params: { color: 0xc68642, metalness: 0.0, roughness: 0.5 },
341
+ },
342
+ {
343
+ name: 'Skin (Dark)',
344
+ category: 'Skin',
345
+ params: { color: 0x8d5524, metalness: 0.0, roughness: 0.5 },
346
+ },
194
347
  // Special
195
- { name: "Car Paint", category: "Special", params: { color: 0xcc0000, metalness: 0.5, roughness: 0.15, clearcoat: 1.0, clearcoatRoughness: 0.05 } },
196
- { name: "Emissive (Blue)", category: "Special", params: { color: 0x111111, metalness: 0.0, roughness: 0.5, emissive: 0x0088ff, emissiveIntensity: 2.0 } },
197
- { name: "Emissive (Orange)", category: "Special", params: { color: 0x111111, metalness: 0.0, roughness: 0.5, emissive: 0xff6600, emissiveIntensity: 2.0 } },
198
- { name: "Neon", category: "Special", params: { color: 0x000000, metalness: 0.0, roughness: 0.1, emissive: 0x00ff88, emissiveIntensity: 5.0 } },
348
+ {
349
+ name: 'Car Paint',
350
+ category: 'Special',
351
+ params: {
352
+ color: 0xcc0000,
353
+ metalness: 0.5,
354
+ roughness: 0.15,
355
+ clearcoat: 1.0,
356
+ clearcoatRoughness: 0.05,
357
+ },
358
+ },
359
+ {
360
+ name: 'Emissive (Blue)',
361
+ category: 'Special',
362
+ params: {
363
+ color: 0x111111,
364
+ metalness: 0.0,
365
+ roughness: 0.5,
366
+ emissive: 0x0088ff,
367
+ emissiveIntensity: 2.0,
368
+ },
369
+ },
370
+ {
371
+ name: 'Emissive (Orange)',
372
+ category: 'Special',
373
+ params: {
374
+ color: 0x111111,
375
+ metalness: 0.0,
376
+ roughness: 0.5,
377
+ emissive: 0xff6600,
378
+ emissiveIntensity: 2.0,
379
+ },
380
+ },
381
+ {
382
+ name: 'Neon',
383
+ category: 'Special',
384
+ params: {
385
+ color: 0x000000,
386
+ metalness: 0.0,
387
+ roughness: 0.1,
388
+ emissive: 0x00ff88,
389
+ emissiveIntensity: 5.0,
390
+ },
391
+ },
199
392
  ];
200
393
  /**
201
394
  * Apply a PBR preset to a MeshStandardMaterial or MeshPhysicalMaterial.
@@ -209,39 +402,52 @@ function applyPBRPreset(material, preset) {
209
402
  material.color.setHex(p.color);
210
403
  material.metalness = p.metalness;
211
404
  material.roughness = p.roughness;
212
- if (p.emissive != null)
405
+ if (p.emissive != null) {
213
406
  material.emissive.setHex(p.emissive);
214
- if (p.emissiveIntensity != null)
407
+ }
408
+ if (p.emissiveIntensity != null) {
215
409
  material.emissiveIntensity = p.emissiveIntensity;
216
- if (p.opacity != null)
410
+ }
411
+ if (p.opacity != null) {
217
412
  material.opacity = p.opacity;
413
+ }
218
414
  material.transparent = (_a = p.transparent) !== null && _a !== void 0 ? _a : false;
219
- if (p.clearcoat != null)
415
+ if (p.clearcoat != null) {
220
416
  material.clearcoat = p.clearcoat;
221
- if (p.clearcoatRoughness != null)
417
+ }
418
+ if (p.clearcoatRoughness != null) {
222
419
  material.clearcoatRoughness = p.clearcoatRoughness;
223
- if (p.ior != null)
420
+ }
421
+ if (p.ior != null) {
224
422
  material.ior = p.ior;
225
- if (p.transmission != null)
423
+ }
424
+ if (p.transmission != null) {
226
425
  material.transmission = p.transmission;
227
- if (p.sheen != null)
426
+ }
427
+ if (p.sheen != null) {
228
428
  material.sheen = p.sheen;
229
- if (p.sheenRoughness != null)
429
+ }
430
+ if (p.sheenRoughness != null) {
230
431
  material.sheenRoughness = p.sheenRoughness;
231
- if (p.sheenColor != null)
432
+ }
433
+ if (p.sheenColor != null) {
232
434
  material.sheenColor.setHex(p.sheenColor);
435
+ }
233
436
  material.needsUpdate = true;
234
437
  }
235
438
  else if (material instanceof THREE.MeshStandardMaterial && !needsPhysical) {
236
439
  material.color.setHex(p.color);
237
440
  material.metalness = p.metalness;
238
441
  material.roughness = p.roughness;
239
- if (p.emissive != null)
442
+ if (p.emissive != null) {
240
443
  material.emissive.setHex(p.emissive);
241
- if (p.emissiveIntensity != null)
444
+ }
445
+ if (p.emissiveIntensity != null) {
242
446
  material.emissiveIntensity = p.emissiveIntensity;
243
- if (p.opacity != null)
447
+ }
448
+ if (p.opacity != null) {
244
449
  material.opacity = p.opacity;
450
+ }
245
451
  material.transparent = (_b = p.transparent) !== null && _b !== void 0 ? _b : false;
246
452
  material.needsUpdate = true;
247
453
  }
@@ -278,10 +484,10 @@ function convertToInstanced(parent) {
278
484
  var _a;
279
485
  const groups = new Map();
280
486
  parent.traverse((child) => {
281
- if (child instanceof THREE.Mesh && !child.name.startsWith("__")) {
487
+ if (child instanceof THREE.Mesh && !child.name.startsWith('__')) {
282
488
  const geoId = child.geometry.uuid;
283
489
  const matId = Array.isArray(child.material)
284
- ? child.material.map((m) => m.uuid).join(",")
490
+ ? child.material.map((m) => m.uuid).join(',')
285
491
  : child.material.uuid;
286
492
  const key = `${geoId}|${matId}`;
287
493
  let list = groups.get(key);
@@ -295,11 +501,12 @@ function convertToInstanced(parent) {
295
501
  let created = 0;
296
502
  let removed = 0;
297
503
  for (const [, meshes] of groups) {
298
- if (meshes.length < 2)
504
+ if (meshes.length < 2) {
299
505
  continue;
506
+ }
300
507
  const source = meshes[0];
301
508
  const instancedMesh = new THREE.InstancedMesh(source.geometry, source.material, meshes.length);
302
- instancedMesh.name = source.name + "_Instanced";
509
+ instancedMesh.name = source.name + '_Instanced';
303
510
  instancedMesh.castShadow = source.castShadow;
304
511
  instancedMesh.receiveShadow = source.receiveShadow;
305
512
  const matrix = new THREE.Matrix4();
@@ -331,9 +538,10 @@ function countTriangles(object) {
331
538
  count += geo.index.count / 3;
332
539
  }
333
540
  else {
334
- const pos = geo.getAttribute("position");
335
- if (pos)
541
+ const pos = geo.getAttribute('position');
542
+ if (pos) {
336
543
  count += pos.count / 3;
544
+ }
337
545
  }
338
546
  }
339
547
  });
@@ -367,8 +575,8 @@ function measureAngle(a, b, c) {
367
575
  const HDRI_PRESETS = [
368
576
  // Studio
369
577
  {
370
- name: "Studio Soft",
371
- category: "Studio",
578
+ name: 'Studio Soft',
579
+ category: 'Studio',
372
580
  bgColor: 0x1a1a2e,
373
581
  envIntensity: 1.0,
374
582
  ambientColor: 0xffffff,
@@ -379,8 +587,8 @@ const HDRI_PRESETS = [
379
587
  exposure: 1.0,
380
588
  },
381
589
  {
382
- name: "Studio High Key",
383
- category: "Studio",
590
+ name: 'Studio High Key',
591
+ category: 'Studio',
384
592
  bgColor: 0xf0f0f0,
385
593
  envIntensity: 1.2,
386
594
  ambientColor: 0xffffff,
@@ -391,8 +599,8 @@ const HDRI_PRESETS = [
391
599
  exposure: 1.2,
392
600
  },
393
601
  {
394
- name: "Studio Low Key",
395
- category: "Studio",
602
+ name: 'Studio Low Key',
603
+ category: 'Studio',
396
604
  bgColor: 0x0a0a0a,
397
605
  envIntensity: 0.5,
398
606
  ambientColor: 0x222244,
@@ -403,8 +611,8 @@ const HDRI_PRESETS = [
403
611
  exposure: 0.8,
404
612
  },
405
613
  {
406
- name: "Studio Rim",
407
- category: "Studio",
614
+ name: 'Studio Rim',
615
+ category: 'Studio',
408
616
  bgColor: 0x111111,
409
617
  envIntensity: 0.3,
410
618
  ambientColor: 0x111122,
@@ -416,8 +624,8 @@ const HDRI_PRESETS = [
416
624
  },
417
625
  // Outdoor
418
626
  {
419
- name: "Sunny Day",
420
- category: "Outdoor",
627
+ name: 'Sunny Day',
628
+ category: 'Outdoor',
421
629
  bgColor: 0x87ceeb,
422
630
  envIntensity: 1.0,
423
631
  ambientColor: 0x8ec8f0,
@@ -428,8 +636,8 @@ const HDRI_PRESETS = [
428
636
  exposure: 1.0,
429
637
  },
430
638
  {
431
- name: "Overcast",
432
- category: "Outdoor",
639
+ name: 'Overcast',
640
+ category: 'Outdoor',
433
641
  bgColor: 0xb0b0b8,
434
642
  envIntensity: 0.8,
435
643
  ambientColor: 0xccccdd,
@@ -441,8 +649,8 @@ const HDRI_PRESETS = [
441
649
  exposure: 1.0,
442
650
  },
443
651
  {
444
- name: "Golden Hour",
445
- category: "Outdoor",
652
+ name: 'Golden Hour',
653
+ category: 'Outdoor',
446
654
  bgColor: 0xff8844,
447
655
  envIntensity: 1.0,
448
656
  ambientColor: 0xffaa55,
@@ -453,8 +661,8 @@ const HDRI_PRESETS = [
453
661
  exposure: 1.1,
454
662
  },
455
663
  {
456
- name: "Blue Hour",
457
- category: "Outdoor",
664
+ name: 'Blue Hour',
665
+ category: 'Outdoor',
458
666
  bgColor: 0x1a2a4a,
459
667
  envIntensity: 0.6,
460
668
  ambientColor: 0x334466,
@@ -465,8 +673,8 @@ const HDRI_PRESETS = [
465
673
  exposure: 0.8,
466
674
  },
467
675
  {
468
- name: "Night",
469
- category: "Outdoor",
676
+ name: 'Night',
677
+ category: 'Outdoor',
470
678
  bgColor: 0x050510,
471
679
  envIntensity: 0.1,
472
680
  ambientColor: 0x111133,
@@ -478,8 +686,8 @@ const HDRI_PRESETS = [
478
686
  },
479
687
  // Interior
480
688
  {
481
- name: "Warm Interior",
482
- category: "Interior",
689
+ name: 'Warm Interior',
690
+ category: 'Interior',
483
691
  bgColor: 0x2a1a10,
484
692
  envIntensity: 0.7,
485
693
  ambientColor: 0xffddaa,
@@ -490,8 +698,8 @@ const HDRI_PRESETS = [
490
698
  exposure: 1.0,
491
699
  },
492
700
  {
493
- name: "Cool Office",
494
- category: "Interior",
701
+ name: 'Cool Office',
702
+ category: 'Interior',
495
703
  bgColor: 0x1a2230,
496
704
  envIntensity: 0.8,
497
705
  ambientColor: 0xddeeff,
@@ -502,8 +710,8 @@ const HDRI_PRESETS = [
502
710
  exposure: 1.0,
503
711
  },
504
712
  {
505
- name: "Gallery",
506
- category: "Interior",
713
+ name: 'Gallery',
714
+ category: 'Interior',
507
715
  bgColor: 0xfafafa,
508
716
  envIntensity: 1.0,
509
717
  ambientColor: 0xffffff,
@@ -515,8 +723,8 @@ const HDRI_PRESETS = [
515
723
  },
516
724
  // Dramatic
517
725
  {
518
- name: "Neon City",
519
- category: "Dramatic",
726
+ name: 'Neon City',
727
+ category: 'Dramatic',
520
728
  bgColor: 0x0a0015,
521
729
  envIntensity: 0.4,
522
730
  ambientColor: 0xff00ff,
@@ -527,8 +735,8 @@ const HDRI_PRESETS = [
527
735
  exposure: 1.0,
528
736
  },
529
737
  {
530
- name: "Cinematic",
531
- category: "Dramatic",
738
+ name: 'Cinematic',
739
+ category: 'Dramatic',
532
740
  bgColor: 0x0f0f1a,
533
741
  envIntensity: 0.5,
534
742
  ambientColor: 0x223344,
@@ -539,8 +747,8 @@ const HDRI_PRESETS = [
539
747
  exposure: 0.9,
540
748
  },
541
749
  {
542
- name: "Sunrise",
543
- category: "Dramatic",
750
+ name: 'Sunrise',
751
+ category: 'Dramatic',
544
752
  bgColor: 0xff6633,
545
753
  envIntensity: 0.8,
546
754
  ambientColor: 0xffaa66,
@@ -551,8 +759,8 @@ const HDRI_PRESETS = [
551
759
  exposure: 1.2,
552
760
  },
553
761
  {
554
- name: "Desert",
555
- category: "Dramatic",
762
+ name: 'Desert',
763
+ category: 'Dramatic',
556
764
  bgColor: 0xe8c888,
557
765
  envIntensity: 1.0,
558
766
  ambientColor: 0xddcc99,
@@ -575,10 +783,12 @@ function applyHDRIPreset(scene, renderer, preset) {
575
783
  let ambient;
576
784
  let directional;
577
785
  scene.traverse((child) => {
578
- if (child instanceof THREE.AmbientLight && !ambient)
786
+ if (child instanceof THREE.AmbientLight && !ambient) {
579
787
  ambient = child;
580
- if (child instanceof THREE.DirectionalLight && !directional)
788
+ }
789
+ if (child instanceof THREE.DirectionalLight && !directional) {
581
790
  directional = child;
791
+ }
582
792
  });
583
793
  if (ambient != null) {
584
794
  ambient.color.setHex(preset.ambientColor);
@@ -607,11 +817,13 @@ function snapToVertex(point, scene, maxDistance = Infinity) {
607
817
  let minDist = maxDistance;
608
818
  const tmpV = new THREE.Vector3();
609
819
  scene.traverse((child) => {
610
- if (!(child instanceof THREE.Mesh))
820
+ if (!(child instanceof THREE.Mesh)) {
611
821
  return;
612
- const posAttr = child.geometry.getAttribute("position");
613
- if (!posAttr)
822
+ }
823
+ const posAttr = child.geometry.getAttribute('position');
824
+ if (!posAttr) {
614
825
  return;
826
+ }
615
827
  child.updateWorldMatrix(true, false);
616
828
  for (let i = 0; i < posAttr.count; i++) {
617
829
  tmpV.fromBufferAttribute(posAttr, i);
@@ -619,7 +831,7 @@ function snapToVertex(point, scene, maxDistance = Infinity) {
619
831
  const d = point.distanceTo(tmpV);
620
832
  if (d < minDist) {
621
833
  minDist = d;
622
- closest = { point: tmpV.clone(), target: "vertex", object: child };
834
+ closest = { point: tmpV.clone(), target: 'vertex', object: child };
623
835
  }
624
836
  }
625
837
  });
@@ -632,11 +844,13 @@ function snapToEdge(point, scene, maxDistance = Infinity) {
632
844
  const a = new THREE.Vector3();
633
845
  const b = new THREE.Vector3();
634
846
  scene.traverse((child) => {
635
- if (!(child instanceof THREE.Mesh))
847
+ if (!(child instanceof THREE.Mesh)) {
636
848
  return;
637
- const posAttr = child.geometry.getAttribute("position");
638
- if (!posAttr)
849
+ }
850
+ const posAttr = child.geometry.getAttribute('position');
851
+ if (!posAttr) {
639
852
  return;
853
+ }
640
854
  const index = child.geometry.index;
641
855
  child.updateWorldMatrix(true, false);
642
856
  const processEdge = (i0, i1) => {
@@ -646,7 +860,7 @@ function snapToEdge(point, scene, maxDistance = Infinity) {
646
860
  const d = point.distanceTo(closestOnEdge);
647
861
  if (d < minDist) {
648
862
  minDist = d;
649
- closest = { point: closestOnEdge.clone(), target: "edge", object: child };
863
+ closest = { point: closestOnEdge.clone(), target: 'edge', object: child };
650
864
  }
651
865
  };
652
866
  if (index) {
@@ -678,23 +892,25 @@ function closestPointOnSegment(p, a, b) {
678
892
  * Multi-mode snap: tries vertex/edge/grid in priority order.
679
893
  * Returns the best snap within maxDistance, or grid snap as fallback.
680
894
  */
681
- function snapMulti(point, scene, gridSize, modes = ["vertex", "edge", "grid"], maxDistance = 0.5) {
895
+ function snapMulti(point, scene, gridSize, modes = ['vertex', 'edge', 'grid'], maxDistance = 0.5) {
682
896
  for (const mode of modes) {
683
- if (mode === "vertex") {
897
+ if (mode === 'vertex') {
684
898
  const result = snapToVertex(point, scene, maxDistance);
685
- if (result)
899
+ if (result) {
686
900
  return result;
901
+ }
687
902
  }
688
- else if (mode === "edge") {
903
+ else if (mode === 'edge') {
689
904
  const result = snapToEdge(point, scene, maxDistance);
690
- if (result)
905
+ if (result) {
691
906
  return result;
907
+ }
692
908
  }
693
- else if (mode === "grid") {
694
- return { point: snapToGrid(point, gridSize), target: "grid" };
909
+ else if (mode === 'grid') {
910
+ return { point: snapToGrid(point, gridSize), target: 'grid' };
695
911
  }
696
912
  }
697
- return { point: snapToGrid(point, gridSize), target: "grid" };
913
+ return { point: snapToGrid(point, gridSize), target: 'grid' };
698
914
  }
699
915
  /** Evaluate a cubic bezier segment at parameter t (0-1). */
700
916
  function cubicBezier(p0, p1, p2, p3, t) {
@@ -709,15 +925,19 @@ function cubicBezier(p0, p1, p2, p3, t) {
709
925
  function evaluateTrack(track, time) {
710
926
  var _a, _b;
711
927
  const { keyframes } = track;
712
- if (keyframes.length === 0)
928
+ if (keyframes.length === 0) {
713
929
  return 0;
714
- if (keyframes.length === 1)
930
+ }
931
+ if (keyframes.length === 1) {
715
932
  return keyframes[0].value;
933
+ }
716
934
  // Clamp to range
717
- if (time <= keyframes[0].time)
935
+ if (time <= keyframes[0].time) {
718
936
  return keyframes[0].value;
719
- if (time >= keyframes[keyframes.length - 1].time)
937
+ }
938
+ if (time >= keyframes[keyframes.length - 1].time) {
720
939
  return keyframes[keyframes.length - 1].value;
940
+ }
721
941
  // Find surrounding keyframes
722
942
  let left = 0;
723
943
  for (let i = 1; i < keyframes.length; i++) {
@@ -730,21 +950,22 @@ function evaluateTrack(track, time) {
730
950
  const kfL = keyframes[left];
731
951
  const kfR = keyframes[right];
732
952
  const dt = kfR.time - kfL.time;
733
- if (dt === 0)
953
+ if (dt === 0) {
734
954
  return kfL.value;
955
+ }
735
956
  const t = (time - kfL.time) / dt;
736
957
  switch (kfL.interpolation) {
737
- case "constant":
958
+ case 'constant':
738
959
  return kfL.value;
739
- case "linear":
960
+ case 'linear':
740
961
  return kfL.value + (kfR.value - kfL.value) * t;
741
- case "ease-in":
962
+ case 'ease-in':
742
963
  return kfL.value + (kfR.value - kfL.value) * (t * t);
743
- case "ease-out":
964
+ case 'ease-out':
744
965
  return kfL.value + (kfR.value - kfL.value) * (1 - (1 - t) * (1 - t));
745
- case "ease-in-out":
746
- return kfL.value + (kfR.value - kfL.value) * (t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t));
747
- case "bezier": {
966
+ case 'ease-in-out':
967
+ return (kfL.value + (kfR.value - kfL.value) * (t < 0.5 ? 2 * t * t : 1 - 2 * (1 - t) * (1 - t)));
968
+ case 'bezier': {
748
969
  const handleR = (_a = kfL.handleRight) !== null && _a !== void 0 ? _a : { y: 0 };
749
970
  const handleL = (_b = kfR.handleLeft) !== null && _b !== void 0 ? _b : { y: 0 };
750
971
  return cubicBezier(kfL.value, kfL.value + handleR.y, kfR.value + handleL.y, kfR.value, t);
@@ -754,7 +975,7 @@ function evaluateTrack(track, time) {
754
975
  }
755
976
  }
756
977
  /** Insert or update a keyframe at a given time. */
757
- function setKeyframe(track, time, value, interpolation = "bezier") {
978
+ function setKeyframe(track, time, value, interpolation = 'bezier') {
758
979
  // Find existing keyframe at same time (within epsilon)
759
980
  const epsilon = 0.001;
760
981
  const existingIdx = track.keyframes.findIndex((kf) => Math.abs(kf.time - time) < epsilon);
@@ -804,7 +1025,7 @@ function createAnimationClip(name, tracks, sampleRate = 30) {
804
1025
  /** Trigger a file download in the browser. */
805
1026
  function downloadBlob(blob, filename) {
806
1027
  const url = URL.createObjectURL(blob);
807
- const a = document.createElement("a");
1028
+ const a = document.createElement('a');
808
1029
  a.href = url;
809
1030
  a.download = filename;
810
1031
  document.body.appendChild(a);
@@ -829,9 +1050,10 @@ async function exportSceneUSDZ(scene) {
829
1050
  const { USDZExporter } = await import('three/examples/jsm/exporters/USDZExporter.js');
830
1051
  const exporter = new USDZExporter();
831
1052
  const arraybuffer = await new Promise((resolve, reject) => {
1053
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- USDZExporter.parse callback signature varies between three.js minor versions
832
1054
  exporter.parse(scene, resolve, reject);
833
1055
  });
834
- return new Blob([arraybuffer], { type: "model/vnd.usdz+zip" });
1056
+ return new Blob([arraybuffer], { type: 'model/vnd.usdz+zip' });
835
1057
  }
836
1058
  /**
837
1059
  * Setup crossfade between two animation actions.
@@ -862,8 +1084,9 @@ function makeAdditiveAction(mixer, clip, referenceClip) {
862
1084
  */
863
1085
  function blendAnimations(actions) {
864
1086
  const totalWeight = actions.reduce((sum, a) => sum + a.weight, 0);
865
- if (totalWeight === 0)
1087
+ if (totalWeight === 0) {
866
1088
  return;
1089
+ }
867
1090
  for (const { action, weight } of actions) {
868
1091
  const normalizedWeight = weight / totalWeight;
869
1092
  action.setEffectiveWeight(normalizedWeight);
@@ -887,12 +1110,14 @@ function solveIKChain(skeleton, chain) {
887
1110
  const bones = [];
888
1111
  for (const name of boneNames) {
889
1112
  const bone = skeleton.bones.find((b) => b.name === name);
890
- if (!bone)
1113
+ if (!bone) {
891
1114
  return false;
1115
+ }
892
1116
  bones.push(bone);
893
1117
  }
894
- if (bones.length < 2)
1118
+ if (bones.length < 2) {
895
1119
  return false;
1120
+ }
896
1121
  const endEffector = bones[bones.length - 1];
897
1122
  const endPos = new THREE.Vector3();
898
1123
  const toEnd = new THREE.Vector3();
@@ -942,10 +1167,10 @@ function solveIKChain(skeleton, chain) {
942
1167
  }
943
1168
  /** Standard quad viewport layout. */
944
1169
  const QUAD_VIEWPORT = [
945
- { preset: "perspective", x: 0.5, y: 0.5, width: 0.5, height: 0.5 },
946
- { preset: "top", x: 0, y: 0.5, width: 0.5, height: 0.5 },
947
- { preset: "front", x: 0, y: 0, width: 0.5, height: 0.5 },
948
- { preset: "right", x: 0.5, y: 0, width: 0.5, height: 0.5 },
1170
+ { preset: 'perspective', x: 0.5, y: 0.5, width: 0.5, height: 0.5 },
1171
+ { preset: 'top', x: 0, y: 0.5, width: 0.5, height: 0.5 },
1172
+ { preset: 'front', x: 0, y: 0, width: 0.5, height: 0.5 },
1173
+ { preset: 'right', x: 0.5, y: 0, width: 0.5, height: 0.5 },
949
1174
  ];
950
1175
  /** Camera position/rotation for orthographic viewport presets. */
951
1176
  const VIEWPORT_CAMERAS = {
@@ -963,7 +1188,7 @@ const VIEWPORT_CAMERAS = {
963
1188
  function createViewportCamera(preset, aspect = 1, frustumSize = 10) {
964
1189
  const config = VIEWPORT_CAMERAS[preset];
965
1190
  if (config.ortho) {
966
- const camera = new THREE.OrthographicCamera(-frustumSize * aspect / 2, frustumSize * aspect / 2, frustumSize / 2, -frustumSize / 2, 0.01, 10000);
1191
+ const camera = new THREE.OrthographicCamera((-frustumSize * aspect) / 2, (frustumSize * aspect) / 2, frustumSize / 2, -frustumSize / 2, 0.01, 10000);
967
1192
  camera.position.set(...config.position);
968
1193
  camera.up.set(...config.up);
969
1194
  camera.lookAt(0, 0, 0);