@nice2dev/ui-3d 1.0.0 → 1.0.3

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 (204) hide show
  1. package/CHANGELOG.md +115 -1
  2. package/dist/cjs/collaborative/collaborativeScene.js +210 -0
  3. package/dist/cjs/collaborative/collaborativeScene.js.map +1 -0
  4. package/dist/cjs/core/i18n.js +3 -3
  5. package/dist/cjs/core/i18n.js.map +1 -1
  6. package/dist/cjs/dance/DanceBridge.js +162 -0
  7. package/dist/cjs/dance/DanceBridge.js.map +1 -0
  8. package/dist/cjs/dance/DanceScoreEngine.js +210 -0
  9. package/dist/cjs/dance/DanceScoreEngine.js.map +1 -0
  10. package/dist/cjs/dance/PoseDetector.js +199 -0
  11. package/dist/cjs/dance/PoseDetector.js.map +1 -0
  12. package/dist/cjs/index.js +254 -0
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/material/MaterialEditor.module.css.js +6 -0
  15. package/dist/cjs/material/MaterialEditor.module.css.js.map +1 -0
  16. package/dist/cjs/material/NiceMaterialEditor.js +737 -0
  17. package/dist/cjs/material/NiceMaterialEditor.js.map +1 -0
  18. package/dist/cjs/material/materialEditorTypes.js +73 -0
  19. package/dist/cjs/material/materialEditorTypes.js.map +1 -0
  20. package/dist/cjs/material/materialEditorUtils.js +841 -0
  21. package/dist/cjs/material/materialEditorUtils.js.map +1 -0
  22. package/dist/cjs/material/materialNodeDefinitions.js +1285 -0
  23. package/dist/cjs/material/materialNodeDefinitions.js.map +1 -0
  24. package/dist/cjs/model/ModelEditor.js +4 -1
  25. package/dist/cjs/model/ModelEditor.js.map +1 -1
  26. package/dist/cjs/model/ModelEditor.module.css.js +1 -1
  27. package/dist/cjs/model/ModelEditorLeftPanel.js +5 -4
  28. package/dist/cjs/model/ModelEditorLeftPanel.js.map +1 -1
  29. package/dist/cjs/model/ModelEditorMenuBar.js +8 -3
  30. package/dist/cjs/model/ModelEditorMenuBar.js.map +1 -1
  31. package/dist/cjs/model/ModelEditorRightPanel.js +27 -26
  32. package/dist/cjs/model/ModelEditorRightPanel.js.map +1 -1
  33. package/dist/cjs/model/ModelEditorSubComponents.js +20 -16
  34. package/dist/cjs/model/ModelEditorSubComponents.js.map +1 -1
  35. package/dist/cjs/model/ModelEditorTimeline.js +5 -4
  36. package/dist/cjs/model/ModelEditorTimeline.js.map +1 -1
  37. package/dist/cjs/model/ModelEditorToolbar.js +4 -3
  38. package/dist/cjs/model/ModelEditorToolbar.js.map +1 -1
  39. package/dist/cjs/model/ModelEditorViewport.js +2 -2
  40. package/dist/cjs/model/ModelEditorViewport.js.map +1 -1
  41. package/dist/cjs/model/ModelViewer.js +68 -0
  42. package/dist/cjs/model/ModelViewer.js.map +1 -0
  43. package/dist/cjs/model/ModelViewer.module.css.js +6 -0
  44. package/dist/cjs/model/ModelViewer.module.css.js.map +1 -0
  45. package/dist/cjs/model/NiceArmatureEditor.js +255 -0
  46. package/dist/cjs/model/NiceArmatureEditor.js.map +1 -0
  47. package/dist/cjs/model/NiceMorphTargetEditor.js +206 -0
  48. package/dist/cjs/model/NiceMorphTargetEditor.js.map +1 -0
  49. package/dist/cjs/model/NiceOctree.js +339 -0
  50. package/dist/cjs/model/NiceOctree.js.map +1 -0
  51. package/dist/cjs/model/NicePhysicsSimulation.js +283 -0
  52. package/dist/cjs/model/NicePhysicsSimulation.js.map +1 -0
  53. package/dist/cjs/model/NiceProceduralGeometry.js +269 -0
  54. package/dist/cjs/model/NiceProceduralGeometry.js.map +1 -0
  55. package/dist/cjs/model/NiceTerrainEditor.js +343 -0
  56. package/dist/cjs/model/NiceTerrainEditor.js.map +1 -0
  57. package/dist/cjs/model/NiceWeightPainter.js +258 -0
  58. package/dist/cjs/model/NiceWeightPainter.js.map +1 -0
  59. package/dist/cjs/model/NiceXRPreview.js +269 -0
  60. package/dist/cjs/model/NiceXRPreview.js.map +1 -0
  61. package/dist/cjs/model/cadModeUtils.js +130 -0
  62. package/dist/cjs/model/cadModeUtils.js.map +1 -0
  63. package/dist/cjs/model/editorShortcuts.js +187 -0
  64. package/dist/cjs/model/editorShortcuts.js.map +1 -0
  65. package/dist/cjs/model/modelEditorTypes.js +11 -0
  66. package/dist/cjs/model/modelEditorTypes.js.map +1 -1
  67. package/dist/cjs/model/modelEditorUtils.js +1049 -0
  68. package/dist/cjs/model/modelEditorUtils.js.map +1 -0
  69. package/dist/cjs/model/simsModeUtils.js +358 -0
  70. package/dist/cjs/model/simsModeUtils.js.map +1 -0
  71. package/dist/cjs/model/useModelEditor.js +319 -115
  72. package/dist/cjs/model/useModelEditor.js.map +1 -1
  73. package/dist/cjs/model/useModelViewer.js +634 -0
  74. package/dist/cjs/model/useModelViewer.js.map +1 -0
  75. package/dist/cjs/nice2dev-ui-3d.css +1 -1
  76. package/dist/cjs/particle/NiceParticleEditor.js +526 -0
  77. package/dist/cjs/particle/NiceParticleEditor.js.map +1 -0
  78. package/dist/cjs/particle/ParticleEditor.module.css.js +6 -0
  79. package/dist/cjs/particle/ParticleEditor.module.css.js.map +1 -0
  80. package/dist/cjs/particle/particleEditorTypes.js +92 -0
  81. package/dist/cjs/particle/particleEditorTypes.js.map +1 -0
  82. package/dist/cjs/particle/particleEditorUtils.js +1084 -0
  83. package/dist/cjs/particle/particleEditorUtils.js.map +1 -0
  84. package/dist/cjs/rendering/NiceCascadedShadows.js +266 -0
  85. package/dist/cjs/rendering/NiceCascadedShadows.js.map +1 -0
  86. package/dist/cjs/rendering/NiceRenderExport.js +341 -0
  87. package/dist/cjs/rendering/NiceRenderExport.js.map +1 -0
  88. package/dist/cjs/rendering/NiceSSAO.js +359 -0
  89. package/dist/cjs/rendering/NiceSSAO.js.map +1 -0
  90. package/dist/cjs/rendering/NiceSSR.js +277 -0
  91. package/dist/cjs/rendering/NiceSSR.js.map +1 -0
  92. package/dist/cjs/rendering/NiceWebGPURenderer.js +215 -0
  93. package/dist/cjs/rendering/NiceWebGPURenderer.js.map +1 -0
  94. package/dist/cjs/ui/dist/index.js +50089 -0
  95. package/dist/cjs/ui/dist/index.js.map +1 -0
  96. package/dist/cjs/uv/NiceUVEditor.js +520 -0
  97. package/dist/cjs/uv/NiceUVEditor.js.map +1 -0
  98. package/dist/cjs/uv/UVEditor.module.css.js +6 -0
  99. package/dist/cjs/uv/UVEditor.module.css.js.map +1 -0
  100. package/dist/cjs/uv/uvEditorTypes.js +98 -0
  101. package/dist/cjs/uv/uvEditorTypes.js.map +1 -0
  102. package/dist/cjs/uv/uvEditorUtils.js +670 -0
  103. package/dist/cjs/uv/uvEditorUtils.js.map +1 -0
  104. package/dist/esm/collaborative/collaborativeScene.js +206 -0
  105. package/dist/esm/collaborative/collaborativeScene.js.map +1 -0
  106. package/dist/esm/dance/DanceBridge.js +158 -0
  107. package/dist/esm/dance/DanceBridge.js.map +1 -0
  108. package/dist/esm/dance/DanceScoreEngine.js +207 -0
  109. package/dist/esm/dance/DanceScoreEngine.js.map +1 -0
  110. package/dist/esm/dance/PoseDetector.js +195 -0
  111. package/dist/esm/dance/PoseDetector.js.map +1 -0
  112. package/dist/esm/index.js +35 -1
  113. package/dist/esm/index.js.map +1 -1
  114. package/dist/esm/material/MaterialEditor.module.css.js +4 -0
  115. package/dist/esm/material/MaterialEditor.module.css.js.map +1 -0
  116. package/dist/esm/material/NiceMaterialEditor.js +734 -0
  117. package/dist/esm/material/NiceMaterialEditor.js.map +1 -0
  118. package/dist/esm/material/materialEditorTypes.js +62 -0
  119. package/dist/esm/material/materialEditorTypes.js.map +1 -0
  120. package/dist/esm/material/materialEditorUtils.js +811 -0
  121. package/dist/esm/material/materialEditorUtils.js.map +1 -0
  122. package/dist/esm/material/materialNodeDefinitions.js +1280 -0
  123. package/dist/esm/material/materialNodeDefinitions.js.map +1 -0
  124. package/dist/esm/model/ModelEditor.js +4 -2
  125. package/dist/esm/model/ModelEditor.js.map +1 -1
  126. package/dist/esm/model/ModelEditor.module.css.js +1 -1
  127. package/dist/esm/model/ModelEditorLeftPanel.js +5 -4
  128. package/dist/esm/model/ModelEditorLeftPanel.js.map +1 -1
  129. package/dist/esm/model/ModelEditorMenuBar.js +8 -3
  130. package/dist/esm/model/ModelEditorMenuBar.js.map +1 -1
  131. package/dist/esm/model/ModelEditorRightPanel.js +27 -26
  132. package/dist/esm/model/ModelEditorRightPanel.js.map +1 -1
  133. package/dist/esm/model/ModelEditorSubComponents.js +17 -13
  134. package/dist/esm/model/ModelEditorSubComponents.js.map +1 -1
  135. package/dist/esm/model/ModelEditorTimeline.js +5 -4
  136. package/dist/esm/model/ModelEditorTimeline.js.map +1 -1
  137. package/dist/esm/model/ModelEditorToolbar.js +4 -3
  138. package/dist/esm/model/ModelEditorToolbar.js.map +1 -1
  139. package/dist/esm/model/ModelEditorViewport.js +2 -2
  140. package/dist/esm/model/ModelEditorViewport.js.map +1 -1
  141. package/dist/esm/model/ModelViewer.js +65 -0
  142. package/dist/esm/model/ModelViewer.js.map +1 -0
  143. package/dist/esm/model/ModelViewer.module.css.js +4 -0
  144. package/dist/esm/model/ModelViewer.module.css.js.map +1 -0
  145. package/dist/esm/model/NiceArmatureEditor.js +233 -0
  146. package/dist/esm/model/NiceArmatureEditor.js.map +1 -0
  147. package/dist/esm/model/NiceMorphTargetEditor.js +184 -0
  148. package/dist/esm/model/NiceMorphTargetEditor.js.map +1 -0
  149. package/dist/esm/model/NiceOctree.js +317 -0
  150. package/dist/esm/model/NiceOctree.js.map +1 -0
  151. package/dist/esm/model/NicePhysicsSimulation.js +261 -0
  152. package/dist/esm/model/NicePhysicsSimulation.js.map +1 -0
  153. package/dist/esm/model/NiceProceduralGeometry.js +242 -0
  154. package/dist/esm/model/NiceProceduralGeometry.js.map +1 -0
  155. package/dist/esm/model/NiceTerrainEditor.js +321 -0
  156. package/dist/esm/model/NiceTerrainEditor.js.map +1 -0
  157. package/dist/esm/model/NiceWeightPainter.js +236 -0
  158. package/dist/esm/model/NiceWeightPainter.js.map +1 -0
  159. package/dist/esm/model/NiceXRPreview.js +247 -0
  160. package/dist/esm/model/NiceXRPreview.js.map +1 -0
  161. package/dist/esm/model/cadModeUtils.js +103 -0
  162. package/dist/esm/model/cadModeUtils.js.map +1 -0
  163. package/dist/esm/model/editorShortcuts.js +185 -0
  164. package/dist/esm/model/editorShortcuts.js.map +1 -0
  165. package/dist/esm/model/modelEditorTypes.js +11 -0
  166. package/dist/esm/model/modelEditorTypes.js.map +1 -1
  167. package/dist/esm/model/modelEditorUtils.js +997 -0
  168. package/dist/esm/model/modelEditorUtils.js.map +1 -0
  169. package/dist/esm/model/simsModeUtils.js +325 -0
  170. package/dist/esm/model/simsModeUtils.js.map +1 -0
  171. package/dist/esm/model/useModelEditor.js +204 -0
  172. package/dist/esm/model/useModelEditor.js.map +1 -1
  173. package/dist/esm/model/useModelViewer.js +613 -0
  174. package/dist/esm/model/useModelViewer.js.map +1 -0
  175. package/dist/esm/nice2dev-ui-3d.css +1 -1
  176. package/dist/esm/particle/NiceParticleEditor.js +523 -0
  177. package/dist/esm/particle/NiceParticleEditor.js.map +1 -0
  178. package/dist/esm/particle/ParticleEditor.module.css.js +4 -0
  179. package/dist/esm/particle/ParticleEditor.module.css.js.map +1 -0
  180. package/dist/esm/particle/particleEditorTypes.js +84 -0
  181. package/dist/esm/particle/particleEditorTypes.js.map +1 -0
  182. package/dist/esm/particle/particleEditorUtils.js +1054 -0
  183. package/dist/esm/particle/particleEditorUtils.js.map +1 -0
  184. package/dist/esm/rendering/NiceCascadedShadows.js +244 -0
  185. package/dist/esm/rendering/NiceCascadedShadows.js.map +1 -0
  186. package/dist/esm/rendering/NiceRenderExport.js +319 -0
  187. package/dist/esm/rendering/NiceRenderExport.js.map +1 -0
  188. package/dist/esm/rendering/NiceSSAO.js +337 -0
  189. package/dist/esm/rendering/NiceSSAO.js.map +1 -0
  190. package/dist/esm/rendering/NiceSSR.js +255 -0
  191. package/dist/esm/rendering/NiceSSR.js.map +1 -0
  192. package/dist/esm/rendering/NiceWebGPURenderer.js +193 -0
  193. package/dist/esm/rendering/NiceWebGPURenderer.js.map +1 -0
  194. package/dist/esm/ui/dist/index.js +49686 -0
  195. package/dist/esm/ui/dist/index.js.map +1 -0
  196. package/dist/esm/uv/NiceUVEditor.js +518 -0
  197. package/dist/esm/uv/NiceUVEditor.js.map +1 -0
  198. package/dist/esm/uv/UVEditor.module.css.js +4 -0
  199. package/dist/esm/uv/UVEditor.module.css.js.map +1 -0
  200. package/dist/esm/uv/uvEditorTypes.js +88 -0
  201. package/dist/esm/uv/uvEditorTypes.js.map +1 -0
  202. package/dist/esm/uv/uvEditorUtils.js +621 -0
  203. package/dist/esm/uv/uvEditorUtils.js.map +1 -0
  204. package/package.json +3 -4
@@ -0,0 +1,670 @@
1
+ 'use strict';
2
+
3
+ var THREE = require('three');
4
+
5
+ function _interopNamespaceDefault(e) {
6
+ var n = Object.create(null);
7
+ if (e) {
8
+ Object.keys(e).forEach(function (k) {
9
+ if (k !== 'default') {
10
+ var d = Object.getOwnPropertyDescriptor(e, k);
11
+ Object.defineProperty(n, k, d.get ? d : {
12
+ enumerable: true,
13
+ get: function () { return e[k]; }
14
+ });
15
+ }
16
+ });
17
+ }
18
+ n.default = e;
19
+ return Object.freeze(n);
20
+ }
21
+
22
+ var THREE__namespace = /*#__PURE__*/_interopNamespaceDefault(THREE);
23
+
24
+ /**
25
+ * UV Editor Utilities
26
+ *
27
+ * Core algorithms and utilities for UV mapping operations.
28
+ * @module @nice2dev/ui-3d
29
+ */
30
+ /* ═══════════════════════════════════════════
31
+ ID Generation
32
+ ═══════════════════════════════════════════ */
33
+ let idCounter = 0;
34
+ const generateId = (prefix) => `${prefix}_${Date.now()}_${++idCounter}`;
35
+ /* ═══════════════════════════════════════════
36
+ UV Math Utilities
37
+ ═══════════════════════════════════════════ */
38
+ const uvDistance = (a, b) => Math.sqrt((a.u - b.u) ** 2 + (a.v - b.v) ** 2);
39
+ const uvLerp = (a, b, t) => ({
40
+ u: a.u + (b.u - a.u) * t,
41
+ v: a.v + (b.v - a.v) * t,
42
+ });
43
+ const uvAdd = (a, b) => ({
44
+ u: a.u + b.u,
45
+ v: a.v + b.v,
46
+ });
47
+ const uvSub = (a, b) => ({
48
+ u: a.u - b.u,
49
+ v: a.v - b.v,
50
+ });
51
+ const uvScale = (p, s) => ({
52
+ u: p.u * s,
53
+ v: p.v * s,
54
+ });
55
+ const uvRotate = (p, angle, center = { u: 0.5, v: 0.5 }) => {
56
+ const cos = Math.cos(angle);
57
+ const sin = Math.sin(angle);
58
+ const du = p.u - center.u;
59
+ const dv = p.v - center.v;
60
+ return {
61
+ u: center.u + du * cos - dv * sin,
62
+ v: center.v + du * sin + dv * cos,
63
+ };
64
+ };
65
+ const uvCross = (a, b) => a.u * b.v - a.v * b.u;
66
+ /* ═══════════════════════════════════════════
67
+ Bounds Utilities
68
+ ═══════════════════════════════════════════ */
69
+ const createEmptyBounds = () => ({
70
+ minU: Infinity,
71
+ maxU: -Infinity,
72
+ minV: Infinity,
73
+ maxV: -Infinity,
74
+ });
75
+ const expandBounds = (bounds, point) => ({
76
+ minU: Math.min(bounds.minU, point.u),
77
+ maxU: Math.max(bounds.maxU, point.u),
78
+ minV: Math.min(bounds.minV, point.v),
79
+ maxV: Math.max(bounds.maxV, point.v),
80
+ });
81
+ const mergeBounds = (a, b) => ({
82
+ minU: Math.min(a.minU, b.minU),
83
+ maxU: Math.max(a.maxU, b.maxU),
84
+ minV: Math.min(a.minV, b.minV),
85
+ maxV: Math.max(a.maxV, b.maxV),
86
+ });
87
+ const getBoundsCenter = (bounds) => ({
88
+ u: (bounds.minU + bounds.maxU) / 2,
89
+ v: (bounds.minV + bounds.maxV) / 2,
90
+ });
91
+ const getBoundsSize = (bounds) => ({
92
+ u: bounds.maxU - bounds.minU,
93
+ v: bounds.maxV - bounds.minV,
94
+ });
95
+ const getBoundsArea = (bounds) => (bounds.maxU - bounds.minU) * (bounds.maxV - bounds.minV);
96
+ const boundsContains = (bounds, point) => point.u >= bounds.minU && point.u <= bounds.maxU &&
97
+ point.v >= bounds.minV && point.v <= bounds.maxV;
98
+ const boundsOverlap = (a, b) => a.minU <= b.maxU && a.maxU >= b.minU &&
99
+ a.minV <= b.maxV && a.maxV >= b.minV;
100
+ /* ═══════════════════════════════════════════
101
+ Geometry Extraction
102
+ ═══════════════════════════════════════════ */
103
+ function extractUVMeshData(geometry, channel = 'uv') {
104
+ const uvAttr = geometry.getAttribute(channel);
105
+ const posAttr = geometry.getAttribute('position');
106
+ const indexAttr = geometry.getIndex();
107
+ if (!uvAttr || !posAttr)
108
+ return null;
109
+ const vertices = new Map();
110
+ const edges = new Map();
111
+ const faces = new Map();
112
+ // Extract vertices
113
+ for (let i = 0; i < uvAttr.count; i++) {
114
+ const id = generateId('v');
115
+ vertices.set(id, {
116
+ id,
117
+ position: { u: uvAttr.getX(i), v: uvAttr.getY(i) },
118
+ index: i,
119
+ pinned: false,
120
+ selected: false,
121
+ vertexIndex: i,
122
+ });
123
+ }
124
+ // Extract faces and edges
125
+ const vertexIds = Array.from(vertices.keys());
126
+ const edgeMap = new Map(); // "v1_v2" -> edgeId
127
+ const faceCount = indexAttr ? indexAttr.count / 3 : posAttr.count / 3;
128
+ for (let i = 0; i < faceCount; i++) {
129
+ const faceVertexIds = [];
130
+ const indices = [];
131
+ for (let j = 0; j < 3; j++) {
132
+ const idx = indexAttr ? indexAttr.getX(i * 3 + j) : i * 3 + j;
133
+ indices.push(idx);
134
+ faceVertexIds.push(vertexIds[idx]);
135
+ }
136
+ // Calculate face area in UV space
137
+ const v0 = vertices.get(faceVertexIds[0]).position;
138
+ const v1 = vertices.get(faceVertexIds[1]).position;
139
+ const v2 = vertices.get(faceVertexIds[2]).position;
140
+ const uvArea = Math.abs(uvCross(uvSub(v1, v0), uvSub(v2, v0))) / 2;
141
+ // Calculate face area in 3D space
142
+ const p0 = new THREE__namespace.Vector3().fromBufferAttribute(posAttr, indices[0]);
143
+ const p1 = new THREE__namespace.Vector3().fromBufferAttribute(posAttr, indices[1]);
144
+ const p2 = new THREE__namespace.Vector3().fromBufferAttribute(posAttr, indices[2]);
145
+ const e1 = p1.clone().sub(p0);
146
+ const e2 = p2.clone().sub(p0);
147
+ const area3D = e1.cross(e2).length() / 2;
148
+ const faceId = generateId('f');
149
+ faces.set(faceId, {
150
+ id: faceId,
151
+ vertices: faceVertexIds,
152
+ area: uvArea,
153
+ area3D,
154
+ distortion: area3D > 0 ? uvArea / area3D : 1,
155
+ selected: false,
156
+ faceIndex: i,
157
+ });
158
+ // Create edges
159
+ for (let j = 0; j < 3; j++) {
160
+ const v1Id = faceVertexIds[j];
161
+ const v2Id = faceVertexIds[(j + 1) % 3];
162
+ const edgeKey = [v1Id, v2Id].sort().join('_');
163
+ if (!edgeMap.has(edgeKey)) {
164
+ const edgeId = generateId('e');
165
+ const v1Pos = vertices.get(v1Id).position;
166
+ const v2Pos = vertices.get(v2Id).position;
167
+ edges.set(edgeId, {
168
+ id: edgeId,
169
+ startVertex: v1Id,
170
+ endVertex: v2Id,
171
+ seam: false,
172
+ selected: false,
173
+ length: uvDistance(v1Pos, v2Pos),
174
+ });
175
+ edgeMap.set(edgeKey, edgeId);
176
+ }
177
+ }
178
+ }
179
+ // Compute islands
180
+ const islands = computeIslands(vertices, edges, faces);
181
+ return {
182
+ vertices,
183
+ edges,
184
+ faces,
185
+ islands,
186
+ geometryId: geometry.uuid,
187
+ dirty: false,
188
+ };
189
+ }
190
+ /* ═══════════════════════════════════════════
191
+ Island Detection
192
+ ═══════════════════════════════════════════ */
193
+ function computeIslands(vertices, edges, faces) {
194
+ const islands = new Map();
195
+ const visited = new Set();
196
+ const faceToIsland = new Map();
197
+ // Build adjacency graph (faces connected by non-seam edges)
198
+ const adjacency = new Map();
199
+ for (const face of faces.values()) {
200
+ adjacency.set(face.id, new Set());
201
+ }
202
+ // Find adjacent faces (sharing vertices)
203
+ const vertexToFaces = new Map();
204
+ for (const face of faces.values()) {
205
+ for (const vId of face.vertices) {
206
+ if (!vertexToFaces.has(vId))
207
+ vertexToFaces.set(vId, new Set());
208
+ vertexToFaces.get(vId).add(face.id);
209
+ }
210
+ }
211
+ for (const face of faces.values()) {
212
+ for (const vId of face.vertices) {
213
+ const connectedFaces = vertexToFaces.get(vId);
214
+ for (const otherFaceId of connectedFaces) {
215
+ if (otherFaceId !== face.id) {
216
+ adjacency.get(face.id).add(otherFaceId);
217
+ }
218
+ }
219
+ }
220
+ }
221
+ // Flood fill to find islands
222
+ const floodFill = (startFaceId) => {
223
+ var _a;
224
+ const stack = [startFaceId];
225
+ const islandFaces = [];
226
+ while (stack.length > 0) {
227
+ const faceId = stack.pop();
228
+ if (visited.has(faceId))
229
+ continue;
230
+ visited.add(faceId);
231
+ islandFaces.push(faceId);
232
+ for (const neighborId of (_a = adjacency.get(faceId)) !== null && _a !== void 0 ? _a : []) {
233
+ if (!visited.has(neighborId)) {
234
+ stack.push(neighborId);
235
+ }
236
+ }
237
+ }
238
+ return islandFaces;
239
+ };
240
+ for (const face of faces.values()) {
241
+ if (visited.has(face.id))
242
+ continue;
243
+ const islandFaces = floodFill(face.id);
244
+ const islandId = generateId('i');
245
+ // Gather island vertices and edges
246
+ const islandVertices = new Set();
247
+ const islandEdges = new Set();
248
+ for (const faceId of islandFaces) {
249
+ faceToIsland.set(faceId, islandId);
250
+ const f = faces.get(faceId);
251
+ for (const vId of f.vertices) {
252
+ islandVertices.add(vId);
253
+ }
254
+ }
255
+ for (const edge of edges.values()) {
256
+ if (islandVertices.has(edge.startVertex) && islandVertices.has(edge.endVertex)) {
257
+ islandEdges.add(edge.id);
258
+ }
259
+ }
260
+ // Calculate bounds
261
+ let bounds = createEmptyBounds();
262
+ let totalArea = 0;
263
+ let centerU = 0, centerV = 0;
264
+ for (const vId of islandVertices) {
265
+ const v = vertices.get(vId);
266
+ bounds = expandBounds(bounds, v.position);
267
+ centerU += v.position.u;
268
+ centerV += v.position.v;
269
+ }
270
+ for (const faceId of islandFaces) {
271
+ totalArea += faces.get(faceId).area;
272
+ }
273
+ const vertexCount = islandVertices.size;
274
+ islands.set(islandId, {
275
+ id: islandId,
276
+ faces: islandFaces,
277
+ vertices: Array.from(islandVertices),
278
+ edges: Array.from(islandEdges),
279
+ bounds,
280
+ area: totalArea,
281
+ selected: false,
282
+ center: { u: centerU / vertexCount, v: centerV / vertexCount },
283
+ rotation: 0,
284
+ scale: { u: 1, v: 1 },
285
+ });
286
+ }
287
+ return islands;
288
+ }
289
+ /* ═══════════════════════════════════════════
290
+ Projection Functions
291
+ ═══════════════════════════════════════════ */
292
+ function applyProjection(geometry, settings, selectedFaces) {
293
+ const posAttr = geometry.getAttribute('position');
294
+ let uvAttr = geometry.getAttribute('uv');
295
+ if (!uvAttr) {
296
+ uvAttr = new THREE__namespace.BufferAttribute(new Float32Array(posAttr.count * 2), 2);
297
+ geometry.setAttribute('uv', uvAttr);
298
+ }
299
+ const indices = selectedFaces !== null && selectedFaces !== void 0 ? selectedFaces : Array.from({ length: posAttr.count }, (_, i) => i);
300
+ switch (settings.type) {
301
+ case 'planar':
302
+ applyPlanarProjection(geometry, settings, indices);
303
+ break;
304
+ case 'cylindrical':
305
+ applyCylindricalProjection(geometry, settings, indices);
306
+ break;
307
+ case 'spherical':
308
+ applySphericalProjection(geometry, settings, indices);
309
+ break;
310
+ case 'box':
311
+ applyBoxProjection(geometry, settings, indices);
312
+ break;
313
+ default:
314
+ applyPlanarProjection(geometry, { ...settings, axis: 'z'}, indices);
315
+ }
316
+ uvAttr.needsUpdate = true;
317
+ }
318
+ function applyPlanarProjection(geometry, settings, indices) {
319
+ const posAttr = geometry.getAttribute('position');
320
+ const uvAttr = geometry.getAttribute('uv');
321
+ const { scale, offset, axis } = settings;
322
+ for (const i of indices) {
323
+ const x = posAttr.getX(i);
324
+ const y = posAttr.getY(i);
325
+ const z = posAttr.getZ(i);
326
+ let u, v;
327
+ switch (axis) {
328
+ case 'x':
329
+ u = (z - offset.z) * scale.z;
330
+ v = (y - offset.y) * scale.y;
331
+ break;
332
+ case 'y':
333
+ u = (x - offset.x) * scale.x;
334
+ v = (z - offset.z) * scale.z;
335
+ break;
336
+ case 'z':
337
+ default:
338
+ u = (x - offset.x) * scale.x;
339
+ v = (y - offset.y) * scale.y;
340
+ break;
341
+ }
342
+ uvAttr.setXY(i, u, v);
343
+ }
344
+ }
345
+ function applyCylindricalProjection(geometry, settings, indices) {
346
+ const posAttr = geometry.getAttribute('position');
347
+ const uvAttr = geometry.getAttribute('uv');
348
+ const { scale, offset, axis } = settings;
349
+ for (const i of indices) {
350
+ const x = posAttr.getX(i) - offset.x;
351
+ const y = posAttr.getY(i) - offset.y;
352
+ const z = posAttr.getZ(i) - offset.z;
353
+ let u, v;
354
+ switch (axis) {
355
+ case 'x':
356
+ u = (Math.atan2(z, y) / (2 * Math.PI) + 0.5) * scale.x;
357
+ v = x * scale.y;
358
+ break;
359
+ case 'y':
360
+ u = (Math.atan2(x, z) / (2 * Math.PI) + 0.5) * scale.x;
361
+ v = y * scale.y;
362
+ break;
363
+ case 'z':
364
+ default:
365
+ u = (Math.atan2(y, x) / (2 * Math.PI) + 0.5) * scale.x;
366
+ v = z * scale.y;
367
+ break;
368
+ }
369
+ uvAttr.setXY(i, u, v);
370
+ }
371
+ }
372
+ function applySphericalProjection(geometry, settings, indices) {
373
+ const posAttr = geometry.getAttribute('position');
374
+ const uvAttr = geometry.getAttribute('uv');
375
+ const { scale, offset } = settings;
376
+ for (const i of indices) {
377
+ const x = posAttr.getX(i) - offset.x;
378
+ const y = posAttr.getY(i) - offset.y;
379
+ const z = posAttr.getZ(i) - offset.z;
380
+ const r = Math.sqrt(x * x + y * y + z * z);
381
+ const theta = Math.acos(y / r);
382
+ const phi = Math.atan2(z, x);
383
+ const u = (phi / (2 * Math.PI) + 0.5) * scale.x;
384
+ const v = (theta / Math.PI) * scale.y;
385
+ uvAttr.setXY(i, u, v);
386
+ }
387
+ }
388
+ function applyBoxProjection(geometry, settings, indices) {
389
+ var _a, _b, _c;
390
+ const posAttr = geometry.getAttribute('position');
391
+ const normalAttr = geometry.getAttribute('normal');
392
+ const uvAttr = geometry.getAttribute('uv');
393
+ if (!normalAttr) {
394
+ geometry.computeVertexNormals();
395
+ }
396
+ const { scale, offset } = settings;
397
+ for (const i of indices) {
398
+ const x = posAttr.getX(i) - offset.x;
399
+ const y = posAttr.getY(i) - offset.y;
400
+ const z = posAttr.getZ(i) - offset.z;
401
+ const nx = Math.abs((_a = normalAttr === null || normalAttr === void 0 ? void 0 : normalAttr.getX(i)) !== null && _a !== void 0 ? _a : 0);
402
+ const ny = Math.abs((_b = normalAttr === null || normalAttr === void 0 ? void 0 : normalAttr.getY(i)) !== null && _b !== void 0 ? _b : 1);
403
+ const nz = Math.abs((_c = normalAttr === null || normalAttr === void 0 ? void 0 : normalAttr.getZ(i)) !== null && _c !== void 0 ? _c : 0);
404
+ let u, v;
405
+ if (nx >= ny && nx >= nz) {
406
+ // Project on YZ plane
407
+ u = z * scale.z;
408
+ v = y * scale.y;
409
+ }
410
+ else if (ny >= nx && ny >= nz) {
411
+ // Project on XZ plane
412
+ u = x * scale.x;
413
+ v = z * scale.z;
414
+ }
415
+ else {
416
+ // Project on XY plane
417
+ u = x * scale.x;
418
+ v = y * scale.y;
419
+ }
420
+ uvAttr.setXY(i, u, v);
421
+ }
422
+ }
423
+ /* ═══════════════════════════════════════════
424
+ UV Packing
425
+ ═══════════════════════════════════════════ */
426
+ function packIslands(meshData, options) {
427
+ const transforms = new Map();
428
+ const islands = Array.from(meshData.islands.values());
429
+ if (islands.length === 0) {
430
+ return { transforms, utilization: 0, success: true, iterations: 0 };
431
+ }
432
+ // Sort islands by area (largest first) for better packing
433
+ if (options.areaPriority) {
434
+ islands.sort((a, b) => b.area - a.area);
435
+ }
436
+ // Simple shelf-based packing algorithm
437
+ const padding = options.padding / options.resolution;
438
+ const margin = options.margin;
439
+ let currentX = margin;
440
+ let currentY = margin;
441
+ let rowHeight = 0;
442
+ let maxX = 0;
443
+ let maxY = 0;
444
+ for (const island of islands) {
445
+ const size = getBoundsSize(island.bounds);
446
+ const width = size.u + padding * 2;
447
+ const height = size.v + padding * 2;
448
+ // Check if fits in current row
449
+ if (currentX + width > 1 - margin) {
450
+ // Move to next row
451
+ currentX = margin;
452
+ currentY += rowHeight + padding;
453
+ rowHeight = 0;
454
+ }
455
+ // Calculate translation to move island to current position
456
+ const translation = {
457
+ u: currentX + padding - island.bounds.minU,
458
+ v: currentY + padding - island.bounds.minV,
459
+ };
460
+ transforms.set(island.id, {
461
+ translation,
462
+ rotation: 0,
463
+ scale: 1,
464
+ });
465
+ currentX += width;
466
+ rowHeight = Math.max(rowHeight, height);
467
+ maxX = Math.max(maxX, currentX);
468
+ maxY = Math.max(maxY, currentY + height);
469
+ }
470
+ // Calculate utilization
471
+ const totalIslandArea = islands.reduce((sum, i) => sum + i.area, 0);
472
+ const usedArea = maxX * maxY;
473
+ const utilization = usedArea > 0 ? totalIslandArea / usedArea : 0;
474
+ return {
475
+ transforms,
476
+ utilization,
477
+ success: maxX <= 1 && maxY <= 1,
478
+ iterations: 1,
479
+ };
480
+ }
481
+ function applyIslandTransforms(meshData, transforms) {
482
+ for (const [islandId, transform] of transforms) {
483
+ const island = meshData.islands.get(islandId);
484
+ if (!island)
485
+ continue;
486
+ const center = island.center;
487
+ for (const vertexId of island.vertices) {
488
+ const vertex = meshData.vertices.get(vertexId);
489
+ if (!vertex)
490
+ continue;
491
+ let pos = vertex.position;
492
+ // Apply rotation around center
493
+ if (transform.rotation !== 0) {
494
+ pos = uvRotate(pos, transform.rotation, center);
495
+ }
496
+ // Apply scale from center
497
+ if (transform.scale !== 1) {
498
+ pos = {
499
+ u: center.u + (pos.u - center.u) * transform.scale,
500
+ v: center.v + (pos.v - center.v) * transform.scale,
501
+ };
502
+ }
503
+ // Apply translation
504
+ pos = uvAdd(pos, transform.translation);
505
+ vertex.position = pos;
506
+ }
507
+ // Update island bounds
508
+ let bounds = createEmptyBounds();
509
+ for (const vertexId of island.vertices) {
510
+ bounds = expandBounds(bounds, meshData.vertices.get(vertexId).position);
511
+ }
512
+ island.bounds = bounds;
513
+ island.center = getBoundsCenter(bounds);
514
+ }
515
+ meshData.dirty = true;
516
+ }
517
+ /* ═══════════════════════════════════════════
518
+ UV Relaxation
519
+ ═══════════════════════════════════════════ */
520
+ function relaxUVs(meshData, options, selectedVertices) {
521
+ const verticesToRelax = selectedVertices !== null && selectedVertices !== void 0 ? selectedVertices : new Set(meshData.vertices.keys());
522
+ const affectedVertices = [];
523
+ for (let iter = 0; iter < options.iterations; iter++) {
524
+ for (const vertexId of verticesToRelax) {
525
+ const vertex = meshData.vertices.get(vertexId);
526
+ if (!vertex || (options.preservePinned && vertex.pinned))
527
+ continue;
528
+ // Find connected vertices
529
+ const neighbors = [];
530
+ for (const edge of meshData.edges.values()) {
531
+ if (edge.startVertex === vertexId) {
532
+ neighbors.push(meshData.vertices.get(edge.endVertex).position);
533
+ }
534
+ else if (edge.endVertex === vertexId) {
535
+ neighbors.push(meshData.vertices.get(edge.startVertex).position);
536
+ }
537
+ }
538
+ if (neighbors.length === 0)
539
+ continue;
540
+ // Move towards average of neighbors (Laplacian smoothing)
541
+ const avg = {
542
+ u: neighbors.reduce((s, n) => s + n.u, 0) / neighbors.length,
543
+ v: neighbors.reduce((s, n) => s + n.v, 0) / neighbors.length,
544
+ };
545
+ const factor = 0.5; // Relaxation factor
546
+ vertex.position = uvLerp(vertex.position, avg, factor);
547
+ if (!affectedVertices.includes(vertexId)) {
548
+ affectedVertices.push(vertexId);
549
+ }
550
+ }
551
+ }
552
+ meshData.dirty = true;
553
+ return {
554
+ success: true,
555
+ affectedVertices,
556
+ };
557
+ }
558
+ /* ═══════════════════════════════════════════
559
+ Selection Utilities
560
+ ═══════════════════════════════════════════ */
561
+ function selectVerticesInRect(meshData, rect, additive = false) {
562
+ const selected = [];
563
+ for (const [id, vertex] of meshData.vertices) {
564
+ if (boundsContains(rect, vertex.position)) {
565
+ vertex.selected = true;
566
+ selected.push(id);
567
+ }
568
+ else if (!additive) {
569
+ vertex.selected = false;
570
+ }
571
+ }
572
+ return selected;
573
+ }
574
+ function selectIsland(meshData, islandId, additive = false) {
575
+ if (!additive) {
576
+ for (const v of meshData.vertices.values())
577
+ v.selected = false;
578
+ for (const e of meshData.edges.values())
579
+ e.selected = false;
580
+ for (const f of meshData.faces.values())
581
+ f.selected = false;
582
+ for (const i of meshData.islands.values())
583
+ i.selected = false;
584
+ }
585
+ const island = meshData.islands.get(islandId);
586
+ if (!island)
587
+ return;
588
+ island.selected = true;
589
+ for (const vId of island.vertices) {
590
+ const v = meshData.vertices.get(vId);
591
+ if (v)
592
+ v.selected = true;
593
+ }
594
+ for (const eId of island.edges) {
595
+ const e = meshData.edges.get(eId);
596
+ if (e)
597
+ e.selected = true;
598
+ }
599
+ for (const fId of island.faces) {
600
+ const f = meshData.faces.get(fId);
601
+ if (f)
602
+ f.selected = true;
603
+ }
604
+ }
605
+ function getSelectedVertices(meshData) {
606
+ return Array.from(meshData.vertices.values()).filter(v => v.selected);
607
+ }
608
+ function getSelectedIslands(meshData) {
609
+ return Array.from(meshData.islands.values()).filter(i => i.selected);
610
+ }
611
+ /* ═══════════════════════════════════════════
612
+ Seam Operations
613
+ ═══════════════════════════════════════════ */
614
+ function markSeam(meshData, edgeId, isSeam) {
615
+ const edge = meshData.edges.get(edgeId);
616
+ if (edge) {
617
+ edge.seam = isSeam;
618
+ meshData.dirty = true;
619
+ }
620
+ }
621
+ function clearAllSeams(meshData) {
622
+ for (const edge of meshData.edges.values()) {
623
+ edge.seam = false;
624
+ }
625
+ meshData.dirty = true;
626
+ }
627
+ /* ═══════════════════════════════════════════
628
+ Sync Back to Geometry
629
+ ═══════════════════════════════════════════ */
630
+ function syncToGeometry(meshData, geometry, channel = 'uv') {
631
+ const uvAttr = geometry.getAttribute(channel);
632
+ if (!uvAttr)
633
+ return;
634
+ for (const vertex of meshData.vertices.values()) {
635
+ uvAttr.setXY(vertex.index, vertex.position.u, vertex.position.v);
636
+ }
637
+ uvAttr.needsUpdate = true;
638
+ meshData.dirty = false;
639
+ }
640
+
641
+ exports.applyIslandTransforms = applyIslandTransforms;
642
+ exports.applyProjection = applyProjection;
643
+ exports.boundsContains = boundsContains;
644
+ exports.boundsOverlap = boundsOverlap;
645
+ exports.clearAllSeams = clearAllSeams;
646
+ exports.computeIslands = computeIslands;
647
+ exports.createEmptyBounds = createEmptyBounds;
648
+ exports.expandBounds = expandBounds;
649
+ exports.extractUVMeshData = extractUVMeshData;
650
+ exports.generateId = generateId;
651
+ exports.getBoundsArea = getBoundsArea;
652
+ exports.getBoundsCenter = getBoundsCenter;
653
+ exports.getBoundsSize = getBoundsSize;
654
+ exports.getSelectedIslands = getSelectedIslands;
655
+ exports.getSelectedVertices = getSelectedVertices;
656
+ exports.markSeam = markSeam;
657
+ exports.mergeBounds = mergeBounds;
658
+ exports.packIslands = packIslands;
659
+ exports.relaxUVs = relaxUVs;
660
+ exports.selectIsland = selectIsland;
661
+ exports.selectVerticesInRect = selectVerticesInRect;
662
+ exports.syncToGeometry = syncToGeometry;
663
+ exports.uvAdd = uvAdd;
664
+ exports.uvCross = uvCross;
665
+ exports.uvDistance = uvDistance;
666
+ exports.uvLerp = uvLerp;
667
+ exports.uvRotate = uvRotate;
668
+ exports.uvScale = uvScale;
669
+ exports.uvSub = uvSub;
670
+ //# sourceMappingURL=uvEditorUtils.js.map