@nice2dev/ui-3d 1.3.15 → 1.3.17

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 (108) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/collaborative/collaborativeScene.js +160 -206
  3. package/dist/cjs/core/editorAsk.js +51 -41
  4. package/dist/cjs/core/i18n.js +13 -14
  5. package/dist/cjs/core/threeCompat.js +29 -42
  6. package/dist/cjs/dance/DanceBridge.js +148 -177
  7. package/dist/cjs/dance/DanceScoreEngine.js +209 -221
  8. package/dist/cjs/dance/PoseDetector.js +302 -275
  9. package/dist/cjs/index.js +110 -113
  10. package/dist/cjs/material/MaterialEditor.module.css.js +242 -5
  11. package/dist/cjs/material/NiceMaterialEditor.js +1213 -757
  12. package/dist/cjs/material/materialEditorTypes.js +35 -49
  13. package/dist/cjs/material/materialEditorUtils.js +684 -747
  14. package/dist/cjs/material/materialNodeDefinitions.js +1411 -1459
  15. package/dist/cjs/model/ModelEditor.js +90 -40
  16. package/dist/cjs/model/ModelEditor.module.css.js +257 -5
  17. package/dist/cjs/model/ModelEditorLeftPanel.js +126 -47
  18. package/dist/cjs/model/ModelEditorMenuBar.js +359 -32
  19. package/dist/cjs/model/ModelEditorRightPanel.js +647 -131
  20. package/dist/cjs/model/ModelEditorSubComponents.js +277 -112
  21. package/dist/cjs/model/ModelEditorTimeline.js +232 -34
  22. package/dist/cjs/model/ModelEditorToolbar.js +170 -14
  23. package/dist/cjs/model/ModelEditorViewport.js +231 -39
  24. package/dist/cjs/model/ModelViewer.js +388 -63
  25. package/dist/cjs/model/ModelViewer.module.css.js +77 -5
  26. package/dist/cjs/model/NiceArmatureEditor.js +473 -274
  27. package/dist/cjs/model/NiceMorphTargetEditor.js +333 -227
  28. package/dist/cjs/model/NiceOctree.js +372 -320
  29. package/dist/cjs/model/NicePhysicsSimulation.js +528 -298
  30. package/dist/cjs/model/NiceProceduralGeometry.js +673 -300
  31. package/dist/cjs/model/NiceTerrainEditor.js +595 -385
  32. package/dist/cjs/model/NiceWeightPainter.js +384 -261
  33. package/dist/cjs/model/NiceXRPreview.js +385 -308
  34. package/dist/cjs/model/cadModeUtils.js +103 -106
  35. package/dist/cjs/model/editorShortcuts.js +171 -194
  36. package/dist/cjs/model/modelEditorTypes.js +100 -118
  37. package/dist/cjs/model/modelEditorUtils.js +1061 -1146
  38. package/dist/cjs/model/simsModeUtils.js +1023 -1053
  39. package/dist/cjs/model/useModelEditor.js +1977 -1933
  40. package/dist/cjs/model/useModelViewer.js +633 -665
  41. package/dist/cjs/nice2dev-ui-3d.css +1 -1
  42. package/dist/cjs/particle/NiceParticleEditor.js +1186 -571
  43. package/dist/cjs/particle/ParticleEditor.module.css.js +122 -5
  44. package/dist/cjs/particle/particleEditorTypes.js +62 -70
  45. package/dist/cjs/particle/particleEditorUtils.js +998 -1013
  46. package/dist/cjs/rendering/NiceCascadedShadows.js +381 -259
  47. package/dist/cjs/rendering/NiceRenderExport.js +424 -337
  48. package/dist/cjs/rendering/NiceSSAO.js +454 -337
  49. package/dist/cjs/rendering/NiceSSR.js +387 -261
  50. package/dist/cjs/rendering/NiceWebGPURenderer.js +359 -230
  51. package/dist/cjs/uv/NiceUVEditor.js +787 -550
  52. package/dist/cjs/uv/UVEditor.module.css.js +53 -5
  53. package/dist/cjs/uv/uvEditorTypes.js +59 -76
  54. package/dist/cjs/uv/uvEditorUtils.js +501 -575
  55. package/dist/esm/collaborative/collaborativeScene.js +163 -205
  56. package/dist/esm/core/editorAsk.js +53 -40
  57. package/dist/esm/core/i18n.js +15 -13
  58. package/dist/esm/core/threeCompat.js +18 -26
  59. package/dist/esm/dance/DanceBridge.js +151 -176
  60. package/dist/esm/dance/DanceScoreEngine.js +211 -220
  61. package/dist/esm/dance/PoseDetector.js +304 -274
  62. package/dist/esm/index.js +258 -36
  63. package/dist/esm/material/MaterialEditor.module.css.js +242 -3
  64. package/dist/esm/material/NiceMaterialEditor.js +1215 -756
  65. package/dist/esm/material/materialEditorTypes.js +45 -48
  66. package/dist/esm/material/materialEditorUtils.js +713 -746
  67. package/dist/esm/material/materialNodeDefinitions.js +1415 -1458
  68. package/dist/esm/model/ModelEditor.js +91 -39
  69. package/dist/esm/model/ModelEditor.module.css.js +257 -3
  70. package/dist/esm/model/ModelEditorLeftPanel.js +126 -45
  71. package/dist/esm/model/ModelEditorMenuBar.js +359 -30
  72. package/dist/esm/model/ModelEditorRightPanel.js +634 -114
  73. package/dist/esm/model/ModelEditorSubComponents.js +265 -94
  74. package/dist/esm/model/ModelEditorTimeline.js +232 -32
  75. package/dist/esm/model/ModelEditorToolbar.js +170 -12
  76. package/dist/esm/model/ModelEditorViewport.js +231 -37
  77. package/dist/esm/model/ModelViewer.js +389 -62
  78. package/dist/esm/model/ModelViewer.module.css.js +77 -3
  79. package/dist/esm/model/NiceArmatureEditor.js +462 -258
  80. package/dist/esm/model/NiceMorphTargetEditor.js +322 -211
  81. package/dist/esm/model/NiceOctree.js +361 -304
  82. package/dist/esm/model/NicePhysicsSimulation.js +517 -282
  83. package/dist/esm/model/NiceProceduralGeometry.js +667 -284
  84. package/dist/esm/model/NiceTerrainEditor.js +584 -369
  85. package/dist/esm/model/NiceWeightPainter.js +373 -245
  86. package/dist/esm/model/NiceXRPreview.js +374 -292
  87. package/dist/esm/model/cadModeUtils.js +95 -88
  88. package/dist/esm/model/editorShortcuts.js +172 -193
  89. package/dist/esm/model/modelEditorTypes.js +90 -100
  90. package/dist/esm/model/modelEditorUtils.js +1056 -1128
  91. package/dist/esm/model/simsModeUtils.js +1021 -1035
  92. package/dist/esm/model/useModelEditor.js +1946 -1920
  93. package/dist/esm/model/useModelViewer.js +619 -647
  94. package/dist/esm/nice2dev-ui-3d.css +1 -1
  95. package/dist/esm/particle/NiceParticleEditor.js +1188 -570
  96. package/dist/esm/particle/ParticleEditor.module.css.js +122 -3
  97. package/dist/esm/particle/particleEditorTypes.js +69 -69
  98. package/dist/esm/particle/particleEditorUtils.js +1027 -1012
  99. package/dist/esm/rendering/NiceCascadedShadows.js +370 -243
  100. package/dist/esm/rendering/NiceRenderExport.js +413 -321
  101. package/dist/esm/rendering/NiceSSAO.js +450 -328
  102. package/dist/esm/rendering/NiceSSR.js +383 -252
  103. package/dist/esm/rendering/NiceWebGPURenderer.js +326 -214
  104. package/dist/esm/uv/NiceUVEditor.js +788 -549
  105. package/dist/esm/uv/UVEditor.module.css.js +53 -3
  106. package/dist/esm/uv/uvEditorTypes.js +68 -75
  107. package/dist/esm/uv/uvEditorUtils.js +515 -557
  108. package/package.json +7 -12
@@ -1,314 +1,697 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { NiceIcon } from '@nice2dev/ui';
3
- import { useState, useCallback } from 'react';
4
- import * as THREE from 'three';
5
- import { useNiceTranslation } from '../core/i18n.js';
6
-
7
- /* ── Generators ── */
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { NiceIcon } from "@nice2dev/ui";
3
+ import { useState, useCallback } from "react";
4
+ import * as THREE from "three";
5
+ import { useNiceTranslation } from "../core/i18n.js";
8
6
  function seededRng(seed) {
9
- let s = seed;
10
- return () => {
11
- s = (s * 16807) % 2147483647;
12
- return (s - 1) / 2147483646;
13
- };
7
+ let s = seed;
8
+ return () => {
9
+ s = s * 16807 % 2147483647;
10
+ return (s - 1) / 2147483646;
11
+ };
14
12
  }
15
13
  function generateTree(params) {
16
- const group = new THREE.Group();
17
- group.name = 'ProceduralTree';
18
- const rng = seededRng(params.seed);
19
- const trunkMat = new THREE.MeshStandardMaterial({ color: 0x8b4513 });
20
- const leafMat = new THREE.MeshStandardMaterial({ color: 0x228b22 });
21
- function addBranch(parent, position, direction, length, radius, depth) {
22
- const geo = new THREE.CylinderGeometry(radius * params.branchTaper, radius, length, 8);
23
- geo.translate(0, length / 2, 0);
24
- const mesh = new THREE.Mesh(geo, trunkMat);
25
- mesh.position.copy(position);
26
- // Orient along direction
27
- const up = new THREE.Vector3(0, 1, 0);
28
- const quat = new THREE.Quaternion().setFromUnitVectors(up, direction.clone().normalize());
29
- mesh.quaternion.copy(quat);
30
- parent.add(mesh);
31
- if (depth <= 0) {
32
- // Add leaf cluster
33
- const leafGeo = new THREE.IcosahedronGeometry(length * 0.6, 1);
34
- const leaf = new THREE.Mesh(leafGeo, leafMat);
35
- const tip = position.clone().addScaledVector(direction.clone().normalize(), length);
36
- leaf.position.copy(tip);
37
- parent.add(leaf);
38
- return;
39
- }
40
- const tip = position.clone().addScaledVector(direction.clone().normalize(), length);
41
- const angleRad = (params.branchAngle * Math.PI) / 180;
42
- for (let i = 0; i < params.branches; i++) {
43
- const phi = (i / params.branches) * Math.PI * 2 + rng() * 0.5;
44
- const theta = angleRad + (rng() - 0.5) * 0.3;
45
- const newDir = new THREE.Vector3(Math.sin(theta) * Math.cos(phi), Math.cos(theta), Math.sin(theta) * Math.sin(phi));
46
- // Blend with parent direction
47
- newDir.add(direction.clone().normalize().multiplyScalar(0.5)).normalize();
48
- addBranch(parent, tip, newDir, length * (0.6 + rng() * 0.2), radius * params.branchTaper, depth - 1);
49
- }
14
+ const group = new THREE.Group();
15
+ group.name = "ProceduralTree";
16
+ const rng = seededRng(params.seed);
17
+ const trunkMat = new THREE.MeshStandardMaterial({ color: 9127187 });
18
+ const leafMat = new THREE.MeshStandardMaterial({ color: 2263842 });
19
+ function addBranch(parent, position, direction, length, radius, depth) {
20
+ const geo = new THREE.CylinderGeometry(radius * params.branchTaper, radius, length, 8);
21
+ geo.translate(0, length / 2, 0);
22
+ const mesh = new THREE.Mesh(geo, trunkMat);
23
+ mesh.position.copy(position);
24
+ const up = new THREE.Vector3(0, 1, 0);
25
+ const quat = new THREE.Quaternion().setFromUnitVectors(up, direction.clone().normalize());
26
+ mesh.quaternion.copy(quat);
27
+ parent.add(mesh);
28
+ if (depth <= 0) {
29
+ const leafGeo = new THREE.IcosahedronGeometry(length * 0.6, 1);
30
+ const leaf = new THREE.Mesh(leafGeo, leafMat);
31
+ const tip2 = position.clone().addScaledVector(direction.clone().normalize(), length);
32
+ leaf.position.copy(tip2);
33
+ parent.add(leaf);
34
+ return;
50
35
  }
51
- addBranch(group, new THREE.Vector3(), new THREE.Vector3(0, 1, 0), params.trunkHeight, params.trunkRadius, params.depth);
52
- return group;
36
+ const tip = position.clone().addScaledVector(direction.clone().normalize(), length);
37
+ const angleRad = params.branchAngle * Math.PI / 180;
38
+ for (let i = 0; i < params.branches; i++) {
39
+ const phi = i / params.branches * Math.PI * 2 + rng() * 0.5;
40
+ const theta = angleRad + (rng() - 0.5) * 0.3;
41
+ const newDir = new THREE.Vector3(
42
+ Math.sin(theta) * Math.cos(phi),
43
+ Math.cos(theta),
44
+ Math.sin(theta) * Math.sin(phi)
45
+ );
46
+ newDir.add(direction.clone().normalize().multiplyScalar(0.5)).normalize();
47
+ addBranch(
48
+ parent,
49
+ tip,
50
+ newDir,
51
+ length * (0.6 + rng() * 0.2),
52
+ radius * params.branchTaper,
53
+ depth - 1
54
+ );
55
+ }
56
+ }
57
+ addBranch(
58
+ group,
59
+ new THREE.Vector3(),
60
+ new THREE.Vector3(0, 1, 0),
61
+ params.trunkHeight,
62
+ params.trunkRadius,
63
+ params.depth
64
+ );
65
+ return group;
53
66
  }
54
67
  function generateRock(params) {
55
- const geo = new THREE.IcosahedronGeometry(params.radius, params.detail);
56
- const posAttr = geo.attributes.position;
57
- const rng = seededRng(params.seed);
58
- for (let i = 0; i < posAttr.count; i++) {
59
- const vertex = new THREE.Vector3().fromBufferAttribute(posAttr, i);
60
- const normal = vertex.clone().normalize();
61
- // Noise displacement
62
- const noise = (rng() - 0.5) * 2 * params.noiseAmount;
63
- const freq = params.noiseScale;
64
- const displacement = Math.sin(vertex.x * freq) * Math.cos(vertex.y * freq) * Math.sin(vertex.z * freq);
65
- const totalDisp = (noise + displacement * params.noiseAmount) * params.radius;
66
- vertex.addScaledVector(normal, totalDisp);
67
- posAttr.setXYZ(i, vertex.x, vertex.y, vertex.z);
68
- }
69
- geo.computeVertexNormals();
70
- const mesh = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({ color: 0x808080, roughness: 0.9 }));
71
- mesh.name = 'ProceduralRock';
72
- mesh.castShadow = true;
73
- return mesh;
68
+ const geo = new THREE.IcosahedronGeometry(params.radius, params.detail);
69
+ const posAttr = geo.attributes.position;
70
+ const rng = seededRng(params.seed);
71
+ for (let i = 0; i < posAttr.count; i++) {
72
+ const vertex = new THREE.Vector3().fromBufferAttribute(posAttr, i);
73
+ const normal = vertex.clone().normalize();
74
+ const noise = (rng() - 0.5) * 2 * params.noiseAmount;
75
+ const freq = params.noiseScale;
76
+ const displacement = Math.sin(vertex.x * freq) * Math.cos(vertex.y * freq) * Math.sin(vertex.z * freq);
77
+ const totalDisp = (noise + displacement * params.noiseAmount) * params.radius;
78
+ vertex.addScaledVector(normal, totalDisp);
79
+ posAttr.setXYZ(i, vertex.x, vertex.y, vertex.z);
80
+ }
81
+ geo.computeVertexNormals();
82
+ const mesh = new THREE.Mesh(
83
+ geo,
84
+ new THREE.MeshStandardMaterial({ color: 8421504, roughness: 0.9 })
85
+ );
86
+ mesh.name = "ProceduralRock";
87
+ mesh.castShadow = true;
88
+ return mesh;
74
89
  }
75
90
  function generateBuilding(params) {
76
- const group = new THREE.Group();
77
- group.name = 'ProceduralBuilding';
78
- const wallMat = new THREE.MeshStandardMaterial({ color: 0xd4c5a9 });
79
- const windowMat = new THREE.MeshStandardMaterial({
80
- color: 0x87ceeb,
81
- metalness: 0.3,
82
- roughness: 0.1,
83
- });
84
- const roofMat = new THREE.MeshStandardMaterial({ color: 0x8b0000 });
85
- const totalHeight = params.floors * params.floorHeight;
86
- // Main body
87
- const bodyGeo = new THREE.BoxGeometry(params.width, totalHeight, params.depth);
88
- bodyGeo.translate(0, totalHeight / 2, 0);
89
- group.add(new THREE.Mesh(bodyGeo, wallMat));
90
- // Windows
91
- for (let floor = 0; floor < params.floors; floor++) {
92
- const y = floor * params.floorHeight + params.floorHeight * 0.5;
93
- for (let w = 0; w < params.windowsPerFloor; w++) {
94
- const x = (w - (params.windowsPerFloor - 1) / 2) * (params.width / (params.windowsPerFloor + 1));
95
- // Front face windows
96
- const winGeo = new THREE.PlaneGeometry(params.windowWidth, params.windowHeight);
97
- const winMesh = new THREE.Mesh(winGeo, windowMat);
98
- winMesh.position.set(x, y, params.depth / 2 + 0.01);
99
- group.add(winMesh);
100
- // Back face windows
101
- const winBack = winMesh.clone();
102
- winBack.position.z = -params.depth / 2 - 0.01;
103
- winBack.rotation.y = Math.PI;
104
- group.add(winBack);
105
- }
91
+ const group = new THREE.Group();
92
+ group.name = "ProceduralBuilding";
93
+ const wallMat = new THREE.MeshStandardMaterial({ color: 13944233 });
94
+ const windowMat = new THREE.MeshStandardMaterial({
95
+ color: 8900331,
96
+ metalness: 0.3,
97
+ roughness: 0.1
98
+ });
99
+ const roofMat = new THREE.MeshStandardMaterial({ color: 9109504 });
100
+ const totalHeight = params.floors * params.floorHeight;
101
+ const bodyGeo = new THREE.BoxGeometry(params.width, totalHeight, params.depth);
102
+ bodyGeo.translate(0, totalHeight / 2, 0);
103
+ group.add(new THREE.Mesh(bodyGeo, wallMat));
104
+ for (let floor = 0; floor < params.floors; floor++) {
105
+ const y = floor * params.floorHeight + params.floorHeight * 0.5;
106
+ for (let w = 0; w < params.windowsPerFloor; w++) {
107
+ const x = (w - (params.windowsPerFloor - 1) / 2) * (params.width / (params.windowsPerFloor + 1));
108
+ const winGeo = new THREE.PlaneGeometry(params.windowWidth, params.windowHeight);
109
+ const winMesh = new THREE.Mesh(winGeo, windowMat);
110
+ winMesh.position.set(x, y, params.depth / 2 + 0.01);
111
+ group.add(winMesh);
112
+ const winBack = winMesh.clone();
113
+ winBack.position.z = -params.depth / 2 - 0.01;
114
+ winBack.rotation.y = Math.PI;
115
+ group.add(winBack);
116
+ }
117
+ }
118
+ switch (params.roofStyle) {
119
+ case "gable": {
120
+ const shape = new THREE.Shape();
121
+ shape.moveTo(-params.width / 2, 0);
122
+ shape.lineTo(0, params.roofHeight);
123
+ shape.lineTo(params.width / 2, 0);
124
+ shape.lineTo(-params.width / 2, 0);
125
+ const extrudeGeo = new THREE.ExtrudeGeometry(shape, {
126
+ depth: params.depth,
127
+ bevelEnabled: false
128
+ });
129
+ const roof = new THREE.Mesh(extrudeGeo, roofMat);
130
+ roof.position.set(0, totalHeight, -params.depth / 2);
131
+ group.add(roof);
132
+ break;
133
+ }
134
+ case "hip": {
135
+ const roofGeo = new THREE.ConeGeometry(
136
+ Math.max(params.width, params.depth) * 0.7,
137
+ params.roofHeight,
138
+ 4
139
+ );
140
+ roofGeo.rotateY(Math.PI / 4);
141
+ const roof = new THREE.Mesh(roofGeo, roofMat);
142
+ roof.position.y = totalHeight + params.roofHeight / 2;
143
+ group.add(roof);
144
+ break;
145
+ }
146
+ default: {
147
+ const flatGeo = new THREE.BoxGeometry(params.width + 0.4, 0.2, params.depth + 0.4);
148
+ const roof = new THREE.Mesh(flatGeo, roofMat);
149
+ roof.position.y = totalHeight + 0.1;
150
+ group.add(roof);
106
151
  }
107
- // Roof
108
- switch (params.roofStyle) {
109
- case 'gable': {
110
- const shape = new THREE.Shape();
111
- shape.moveTo(-params.width / 2, 0);
112
- shape.lineTo(0, params.roofHeight);
113
- shape.lineTo(params.width / 2, 0);
114
- shape.lineTo(-params.width / 2, 0);
115
- const extrudeGeo = new THREE.ExtrudeGeometry(shape, {
116
- depth: params.depth,
117
- bevelEnabled: false,
118
- });
119
- const roof = new THREE.Mesh(extrudeGeo, roofMat);
120
- roof.position.set(0, totalHeight, -params.depth / 2);
121
- group.add(roof);
122
- break;
123
- }
124
- case 'hip': {
125
- const roofGeo = new THREE.ConeGeometry(Math.max(params.width, params.depth) * 0.7, params.roofHeight, 4);
126
- roofGeo.rotateY(Math.PI / 4);
127
- const roof = new THREE.Mesh(roofGeo, roofMat);
128
- roof.position.y = totalHeight + params.roofHeight / 2;
129
- group.add(roof);
130
- break;
131
- }
132
- default: {
133
- // flat roof
134
- const flatGeo = new THREE.BoxGeometry(params.width + 0.4, 0.2, params.depth + 0.4);
135
- const roof = new THREE.Mesh(flatGeo, roofMat);
136
- roof.position.y = totalHeight + 0.1;
137
- group.add(roof);
138
- }
152
+ }
153
+ group.traverse((child) => {
154
+ if (child instanceof THREE.Mesh) {
155
+ child.castShadow = true;
156
+ child.receiveShadow = true;
139
157
  }
140
- group.traverse((child) => {
141
- if (child instanceof THREE.Mesh) {
142
- child.castShadow = true;
143
- child.receiveShadow = true;
144
- }
145
- });
146
- return group;
158
+ });
159
+ return group;
147
160
  }
148
161
  function generatePipe(params) {
149
- const curve = new THREE.CatmullRomCurve3(params.points, params.closed);
150
- const geo = new THREE.TubeGeometry(curve, params.segments, params.radius, params.radialSegments, params.closed);
151
- const mesh = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({ color: 0xaaaaaa, metalness: 0.8, roughness: 0.2 }));
152
- mesh.name = 'ProceduralPipe';
153
- mesh.castShadow = true;
154
- return mesh;
162
+ const curve = new THREE.CatmullRomCurve3(params.points, params.closed);
163
+ const geo = new THREE.TubeGeometry(
164
+ curve,
165
+ params.segments,
166
+ params.radius,
167
+ params.radialSegments,
168
+ params.closed
169
+ );
170
+ const mesh = new THREE.Mesh(
171
+ geo,
172
+ new THREE.MeshStandardMaterial({ color: 11184810, metalness: 0.8, roughness: 0.2 })
173
+ );
174
+ mesh.name = "ProceduralPipe";
175
+ mesh.castShadow = true;
176
+ return mesh;
155
177
  }
156
178
  function generateGear(params) {
157
- const shape = new THREE.Shape();
158
- const angleStep = (Math.PI * 2) / (params.teeth * 2);
159
- for (let i = 0; i < params.teeth * 2; i++) {
160
- const angle = i * angleStep;
161
- const r = i % 2 === 0 ? params.outerRadius : params.outerRadius - params.toothDepth;
162
- const x = Math.cos(angle) * r;
163
- const y = Math.sin(angle) * r;
164
- if (i === 0) {
165
- shape.moveTo(x, y);
166
- }
167
- else {
168
- shape.lineTo(x, y);
169
- }
179
+ const shape = new THREE.Shape();
180
+ const angleStep = Math.PI * 2 / (params.teeth * 2);
181
+ for (let i = 0; i < params.teeth * 2; i++) {
182
+ const angle = i * angleStep;
183
+ const r = i % 2 === 0 ? params.outerRadius : params.outerRadius - params.toothDepth;
184
+ const x = Math.cos(angle) * r;
185
+ const y = Math.sin(angle) * r;
186
+ if (i === 0) {
187
+ shape.moveTo(x, y);
188
+ } else {
189
+ shape.lineTo(x, y);
170
190
  }
171
- shape.closePath();
172
- // Inner hole
173
- const hole = new THREE.Path();
174
- const holeSegments = 32;
175
- for (let i = 0; i <= holeSegments; i++) {
176
- const angle = (i / holeSegments) * Math.PI * 2;
177
- const x = Math.cos(angle) * params.innerRadius;
178
- const y = Math.sin(angle) * params.innerRadius;
179
- if (i === 0) {
180
- hole.moveTo(x, y);
181
- }
182
- else {
183
- hole.lineTo(x, y);
184
- }
191
+ }
192
+ shape.closePath();
193
+ const hole = new THREE.Path();
194
+ const holeSegments = 32;
195
+ for (let i = 0; i <= holeSegments; i++) {
196
+ const angle = i / holeSegments * Math.PI * 2;
197
+ const x = Math.cos(angle) * params.innerRadius;
198
+ const y = Math.sin(angle) * params.innerRadius;
199
+ if (i === 0) {
200
+ hole.moveTo(x, y);
201
+ } else {
202
+ hole.lineTo(x, y);
185
203
  }
186
- shape.holes.push(hole);
187
- const geo = new THREE.ExtrudeGeometry(shape, {
188
- depth: params.thickness,
189
- bevelEnabled: false,
190
- });
191
- geo.rotateX(-Math.PI / 2);
192
- geo.translate(0, params.thickness / 2, 0);
193
- const mesh = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({ color: 0x888888, metalness: 0.7, roughness: 0.3 }));
194
- mesh.name = 'ProceduralGear';
195
- mesh.castShadow = true;
196
- return mesh;
204
+ }
205
+ shape.holes.push(hole);
206
+ const geo = new THREE.ExtrudeGeometry(shape, {
207
+ depth: params.thickness,
208
+ bevelEnabled: false
209
+ });
210
+ geo.rotateX(-Math.PI / 2);
211
+ geo.translate(0, params.thickness / 2, 0);
212
+ const mesh = new THREE.Mesh(
213
+ geo,
214
+ new THREE.MeshStandardMaterial({ color: 8947848, metalness: 0.7, roughness: 0.3 })
215
+ );
216
+ mesh.name = "ProceduralGear";
217
+ mesh.castShadow = true;
218
+ return mesh;
197
219
  }
198
- /* ── Hook ── */
199
220
  const DEFAULT_TREE = {
200
- trunkRadius: 0.3,
201
- trunkHeight: 4,
202
- branches: 3,
203
- depth: 3,
204
- branchAngle: 30,
205
- branchLength: 3,
206
- branchTaper: 0.7,
207
- seed: 42,
221
+ trunkRadius: 0.3,
222
+ trunkHeight: 4,
223
+ branches: 3,
224
+ depth: 3,
225
+ branchAngle: 30,
226
+ branchLength: 3,
227
+ branchTaper: 0.7,
228
+ seed: 42
208
229
  };
209
230
  const DEFAULT_ROCK = {
210
- radius: 1,
211
- detail: 3,
212
- noiseScale: 3,
213
- noiseAmount: 0.2,
214
- seed: 42,
231
+ radius: 1,
232
+ detail: 3,
233
+ noiseScale: 3,
234
+ noiseAmount: 0.2,
235
+ seed: 42
215
236
  };
216
237
  const DEFAULT_BUILDING = {
217
- width: 8,
218
- depth: 6,
219
- floors: 4,
220
- floorHeight: 3,
221
- windowsPerFloor: 3,
222
- windowWidth: 1.2,
223
- windowHeight: 1.5,
224
- roofStyle: 'gable',
225
- roofHeight: 3,
238
+ width: 8,
239
+ depth: 6,
240
+ floors: 4,
241
+ floorHeight: 3,
242
+ windowsPerFloor: 3,
243
+ windowWidth: 1.2,
244
+ windowHeight: 1.5,
245
+ roofStyle: "gable",
246
+ roofHeight: 3
226
247
  };
227
248
  const DEFAULT_PIPE = {
228
- radius: 0.2,
229
- segments: 64,
230
- radialSegments: 8,
231
- closed: false,
232
- points: [
233
- new THREE.Vector3(0, 0, 0),
234
- new THREE.Vector3(2, 2, 0),
235
- new THREE.Vector3(4, 0, 2),
236
- new THREE.Vector3(6, 2, 2),
237
- ],
249
+ radius: 0.2,
250
+ segments: 64,
251
+ radialSegments: 8,
252
+ closed: false,
253
+ points: [
254
+ new THREE.Vector3(0, 0, 0),
255
+ new THREE.Vector3(2, 2, 0),
256
+ new THREE.Vector3(4, 0, 2),
257
+ new THREE.Vector3(6, 2, 2)
258
+ ]
238
259
  };
239
260
  const DEFAULT_GEAR = {
240
- innerRadius: 0.5,
241
- outerRadius: 2,
242
- teeth: 12,
243
- toothDepth: 0.3,
244
- thickness: 0.4,
261
+ innerRadius: 0.5,
262
+ outerRadius: 2,
263
+ teeth: 12,
264
+ toothDepth: 0.3,
265
+ thickness: 0.4
245
266
  };
246
267
  function useProceduralGeometry(scene, onGenerated) {
247
- const [type, setType] = useState('tree');
248
- const [treeParams, setTreeParams] = useState(DEFAULT_TREE);
249
- const [rockParams, setRockParams] = useState(DEFAULT_ROCK);
250
- const [buildingParams, setBuildingParams] = useState(DEFAULT_BUILDING);
251
- const [pipeParams, setPipeParams] = useState(DEFAULT_PIPE);
252
- const [gearParams, setGearParams] = useState(DEFAULT_GEAR);
253
- const generate = useCallback(() => {
254
- if (!scene) {
255
- return;
256
- }
257
- let result;
258
- switch (type) {
259
- case 'tree':
260
- result = generateTree(treeParams);
261
- break;
262
- case 'rock':
263
- result = generateRock(rockParams);
264
- break;
265
- case 'building':
266
- result = generateBuilding(buildingParams);
267
- break;
268
- case 'pipe':
269
- result = generatePipe(pipeParams);
270
- break;
271
- case 'gear':
272
- result = generateGear(gearParams);
273
- break;
274
- }
275
- scene.add(result);
276
- onGenerated === null || onGenerated === void 0 ? void 0 : onGenerated(result);
277
- }, [scene, type, treeParams, rockParams, buildingParams, pipeParams, gearParams, onGenerated]);
278
- return {
279
- type,
280
- setType,
281
- treeParams,
282
- setTreeParams,
283
- rockParams,
284
- setRockParams,
285
- buildingParams,
286
- setBuildingParams,
287
- pipeParams,
288
- setPipeParams,
289
- gearParams,
290
- setGearParams,
291
- generate,
292
- };
268
+ const [type, setType] = useState("tree");
269
+ const [treeParams, setTreeParams] = useState(DEFAULT_TREE);
270
+ const [rockParams, setRockParams] = useState(DEFAULT_ROCK);
271
+ const [buildingParams, setBuildingParams] = useState(DEFAULT_BUILDING);
272
+ const [pipeParams, setPipeParams] = useState(DEFAULT_PIPE);
273
+ const [gearParams, setGearParams] = useState(DEFAULT_GEAR);
274
+ const generate = useCallback(() => {
275
+ if (!scene) {
276
+ return;
277
+ }
278
+ let result;
279
+ switch (type) {
280
+ case "tree":
281
+ result = generateTree(treeParams);
282
+ break;
283
+ case "rock":
284
+ result = generateRock(rockParams);
285
+ break;
286
+ case "building":
287
+ result = generateBuilding(buildingParams);
288
+ break;
289
+ case "pipe":
290
+ result = generatePipe(pipeParams);
291
+ break;
292
+ case "gear":
293
+ result = generateGear(gearParams);
294
+ break;
295
+ }
296
+ scene.add(result);
297
+ onGenerated?.(result);
298
+ }, [scene, type, treeParams, rockParams, buildingParams, pipeParams, gearParams, onGenerated]);
299
+ return {
300
+ type,
301
+ setType,
302
+ treeParams,
303
+ setTreeParams,
304
+ rockParams,
305
+ setRockParams,
306
+ buildingParams,
307
+ setBuildingParams,
308
+ pipeParams,
309
+ setPipeParams,
310
+ gearParams,
311
+ setGearParams,
312
+ generate
313
+ };
293
314
  }
294
- /* ── Component ── */
295
- const NiceProceduralGeometry = ({ scene, onGenerated, className = '', }) => {
296
- const { t } = useNiceTranslation();
297
- const { type, setType, treeParams, setTreeParams, rockParams, setRockParams, buildingParams, setBuildingParams, gearParams, setGearParams, generate, } = useProceduralGeometry(scene, onGenerated);
298
- return (jsxs("div", { className: `nice-procedural-geometry ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsx("strong", { children: t('proceduralGeometry.proceduralGeometry', 'Procedural Geometry') }), jsxs("select", { value: type, onChange: (e) => setType(e.target.value), style: { width: '100%', padding: '4px 8px' }, children: [jsx("option", { value: "tree", children: t('proceduralGeometry.tree', 'Tree') }), jsx("option", { value: "rock", children: t('proceduralGeometry.rock', 'Rock') }), jsx("option", { value: "building", children: t('proceduralGeometry.building', 'Building') }), jsx("option", { value: "pipe", children: t('proceduralGeometry.pipe', 'Pipe') }), jsx("option", { value: "gear", children: t('proceduralGeometry.gear', 'Gear') })] }), type === 'tree' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: [t('proceduralGeometry.height', 'Height: {trunkHeight}').replace('{trunkHeight}', String(treeParams.trunkHeight.toFixed(1))), jsx("input", { type: "range", min: 1, max: 15, step: 0.5, value: treeParams.trunkHeight, onChange: (e) => setTreeParams({ ...treeParams, trunkHeight: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.branches', 'Branches: {branches}').replace('{branches}', String(treeParams.branches)), jsx("input", { type: "range", min: 1, max: 6, value: treeParams.branches, onChange: (e) => setTreeParams({ ...treeParams, branches: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.depth', 'Depth: {depth}').replace('{depth}', String(treeParams.depth)), jsx("input", { type: "range", min: 1, max: 5, value: treeParams.depth, onChange: (e) => setTreeParams({ ...treeParams, depth: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.angle', 'Angle: {branchAngle}°').replace('{branchAngle}', String(treeParams.branchAngle)), jsx("input", { type: "range", min: 10, max: 80, value: treeParams.branchAngle, onChange: (e) => setTreeParams({ ...treeParams, branchAngle: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.seed', 'Seed: {seed}').replace('{seed}', String(treeParams.seed)), jsx("input", { type: "range", min: 1, max: 999, value: treeParams.seed, onChange: (e) => setTreeParams({ ...treeParams, seed: parseInt(e.target.value, 10) }), style: { width: '100%' } })] })] })), type === 'rock' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: [t('proceduralGeometry.radius', 'Radius: {radius}').replace('{radius}', String(rockParams.radius.toFixed(1))), jsx("input", { type: "range", min: 0.2, max: 5, step: 0.1, value: rockParams.radius, onChange: (e) => setRockParams({ ...rockParams, radius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.detail', 'Detail: {detail}').replace('{detail}', String(rockParams.detail)), jsx("input", { type: "range", min: 1, max: 5, value: rockParams.detail, onChange: (e) => setRockParams({ ...rockParams, detail: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.noise', 'Noise: {noiseAmount}').replace('{noiseAmount}', String(rockParams.noiseAmount.toFixed(2))), jsx("input", { type: "range", min: 0, max: 0.5, step: 0.01, value: rockParams.noiseAmount, onChange: (e) => setRockParams({ ...rockParams, noiseAmount: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.seed', 'Seed: {seed}').replace('{seed}', String(rockParams.seed)), jsx("input", { type: "range", min: 1, max: 999, value: rockParams.seed, onChange: (e) => setRockParams({ ...rockParams, seed: parseInt(e.target.value, 10) }), style: { width: '100%' } })] })] })), type === 'building' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: [t('proceduralGeometry.width', 'Width: {width}').replace('{width}', String(buildingParams.width)), jsx("input", { type: "range", min: 4, max: 20, step: 1, value: buildingParams.width, onChange: (e) => setBuildingParams({ ...buildingParams, width: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.floors', 'Floors: {floors}').replace('{floors}', String(buildingParams.floors)), jsx("input", { type: "range", min: 1, max: 20, value: buildingParams.floors, onChange: (e) => setBuildingParams({ ...buildingParams, floors: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.windowsFloor', 'Windows/Floor: {windowsPerFloor}').replace('{windowsPerFloor}', String(buildingParams.windowsPerFloor)), jsx("input", { type: "range", min: 1, max: 10, value: buildingParams.windowsPerFloor, onChange: (e) => setBuildingParams({
299
- ...buildingParams,
300
- windowsPerFloor: parseInt(e.target.value, 10),
301
- }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.roof', 'Roof:'), jsxs("select", { value: buildingParams.roofStyle, onChange: (e) => setBuildingParams({
302
- ...buildingParams,
303
- roofStyle: e.target.value,
304
- }), style: { marginLeft: 8 }, children: [jsx("option", { value: "flat", children: t('proceduralGeometry.flat', 'Flat') }), jsx("option", { value: "gable", children: t('proceduralGeometry.gable', 'Gable') }), jsx("option", { value: "hip", children: t('proceduralGeometry.hip', 'Hip') })] })] })] })), type === 'gear' && (jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxs("label", { children: [t('proceduralGeometry.outerRadius', 'Outer Radius: {outerRadius}').replace('{outerRadius}', String(gearParams.outerRadius.toFixed(1))), jsx("input", { type: "range", min: 0.5, max: 5, step: 0.1, value: gearParams.outerRadius, onChange: (e) => setGearParams({ ...gearParams, outerRadius: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.teeth', 'Teeth: {teeth}').replace('{teeth}', String(gearParams.teeth)), jsx("input", { type: "range", min: 4, max: 30, value: gearParams.teeth, onChange: (e) => setGearParams({ ...gearParams, teeth: parseInt(e.target.value, 10) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.toothDepth', 'Tooth Depth: {toothDepth}').replace('{toothDepth}', String(gearParams.toothDepth.toFixed(2))), jsx("input", { type: "range", min: 0.05, max: 1, step: 0.05, value: gearParams.toothDepth, onChange: (e) => setGearParams({ ...gearParams, toothDepth: parseFloat(e.target.value) }), style: { width: '100%' } })] }), jsxs("label", { children: [t('proceduralGeometry.thickness', 'Thickness: {thickness}').replace('{thickness}', String(gearParams.thickness.toFixed(2))), jsx("input", { type: "range", min: 0.1, max: 2, step: 0.1, value: gearParams.thickness, onChange: (e) => setGearParams({ ...gearParams, thickness: parseFloat(e.target.value) }), style: { width: '100%' } })] })] })), jsxs("button", { type: "button", onClick: generate, style: {
305
- background: 'var(--nice-color-accent, #6366f1)',
306
- color: 'var(--nice-text-on-primary, #fff)',
307
- border: 'none',
308
- borderRadius: 4,
309
- padding: '6px 12px',
310
- cursor: 'pointer',
311
- }, children: [jsx(NiceIcon, { name: "wand", size: 14 }), " ", t('proceduralGeometry.generate', 'Generate')] })] }));
315
+ const NiceProceduralGeometry = ({
316
+ scene,
317
+ onGenerated,
318
+ className = ""
319
+ }) => {
320
+ const { t } = useNiceTranslation();
321
+ const {
322
+ type,
323
+ setType,
324
+ treeParams,
325
+ setTreeParams,
326
+ rockParams,
327
+ setRockParams,
328
+ buildingParams,
329
+ setBuildingParams,
330
+ gearParams,
331
+ setGearParams,
332
+ generate
333
+ } = useProceduralGeometry(scene, onGenerated);
334
+ return /* @__PURE__ */ jsxs(
335
+ "div",
336
+ {
337
+ className: `nice-procedural-geometry ${className}`,
338
+ style: { display: "grid", gap: 8, fontSize: 13 },
339
+ children: [
340
+ /* @__PURE__ */ jsx("strong", { children: t("proceduralGeometry.proceduralGeometry", "Procedural Geometry") }),
341
+ /* @__PURE__ */ jsxs(
342
+ "select",
343
+ {
344
+ value: type,
345
+ onChange: (e) => setType(e.target.value),
346
+ style: { width: "100%", padding: "4px 8px" },
347
+ children: [
348
+ /* @__PURE__ */ jsx("option", { value: "tree", children: t("proceduralGeometry.tree", "Tree") }),
349
+ /* @__PURE__ */ jsx("option", { value: "rock", children: t("proceduralGeometry.rock", "Rock") }),
350
+ /* @__PURE__ */ jsx("option", { value: "building", children: t("proceduralGeometry.building", "Building") }),
351
+ /* @__PURE__ */ jsx("option", { value: "pipe", children: t("proceduralGeometry.pipe", "Pipe") }),
352
+ /* @__PURE__ */ jsx("option", { value: "gear", children: t("proceduralGeometry.gear", "Gear") })
353
+ ]
354
+ }
355
+ ),
356
+ type === "tree" && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 4 }, children: [
357
+ /* @__PURE__ */ jsxs("label", { children: [
358
+ t("proceduralGeometry.height", "Height: {trunkHeight}").replace(
359
+ "{trunkHeight}",
360
+ String(treeParams.trunkHeight.toFixed(1))
361
+ ),
362
+ /* @__PURE__ */ jsx(
363
+ "input",
364
+ {
365
+ type: "range",
366
+ min: 1,
367
+ max: 15,
368
+ step: 0.5,
369
+ value: treeParams.trunkHeight,
370
+ onChange: (e) => setTreeParams({ ...treeParams, trunkHeight: parseFloat(e.target.value) }),
371
+ style: { width: "100%" }
372
+ }
373
+ )
374
+ ] }),
375
+ /* @__PURE__ */ jsxs("label", { children: [
376
+ t("proceduralGeometry.branches", "Branches: {branches}").replace(
377
+ "{branches}",
378
+ String(treeParams.branches)
379
+ ),
380
+ /* @__PURE__ */ jsx(
381
+ "input",
382
+ {
383
+ type: "range",
384
+ min: 1,
385
+ max: 6,
386
+ value: treeParams.branches,
387
+ onChange: (e) => setTreeParams({ ...treeParams, branches: parseInt(e.target.value, 10) }),
388
+ style: { width: "100%" }
389
+ }
390
+ )
391
+ ] }),
392
+ /* @__PURE__ */ jsxs("label", { children: [
393
+ t("proceduralGeometry.depth", "Depth: {depth}").replace(
394
+ "{depth}",
395
+ String(treeParams.depth)
396
+ ),
397
+ /* @__PURE__ */ jsx(
398
+ "input",
399
+ {
400
+ type: "range",
401
+ min: 1,
402
+ max: 5,
403
+ value: treeParams.depth,
404
+ onChange: (e) => setTreeParams({ ...treeParams, depth: parseInt(e.target.value, 10) }),
405
+ style: { width: "100%" }
406
+ }
407
+ )
408
+ ] }),
409
+ /* @__PURE__ */ jsxs("label", { children: [
410
+ t("proceduralGeometry.angle", "Angle: {branchAngle}°").replace(
411
+ "{branchAngle}",
412
+ String(treeParams.branchAngle)
413
+ ),
414
+ /* @__PURE__ */ jsx(
415
+ "input",
416
+ {
417
+ type: "range",
418
+ min: 10,
419
+ max: 80,
420
+ value: treeParams.branchAngle,
421
+ onChange: (e) => setTreeParams({ ...treeParams, branchAngle: parseInt(e.target.value, 10) }),
422
+ style: { width: "100%" }
423
+ }
424
+ )
425
+ ] }),
426
+ /* @__PURE__ */ jsxs("label", { children: [
427
+ t("proceduralGeometry.seed", "Seed: {seed}").replace(
428
+ "{seed}",
429
+ String(treeParams.seed)
430
+ ),
431
+ /* @__PURE__ */ jsx(
432
+ "input",
433
+ {
434
+ type: "range",
435
+ min: 1,
436
+ max: 999,
437
+ value: treeParams.seed,
438
+ onChange: (e) => setTreeParams({ ...treeParams, seed: parseInt(e.target.value, 10) }),
439
+ style: { width: "100%" }
440
+ }
441
+ )
442
+ ] })
443
+ ] }),
444
+ type === "rock" && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 4 }, children: [
445
+ /* @__PURE__ */ jsxs("label", { children: [
446
+ t("proceduralGeometry.radius", "Radius: {radius}").replace(
447
+ "{radius}",
448
+ String(rockParams.radius.toFixed(1))
449
+ ),
450
+ /* @__PURE__ */ jsx(
451
+ "input",
452
+ {
453
+ type: "range",
454
+ min: 0.2,
455
+ max: 5,
456
+ step: 0.1,
457
+ value: rockParams.radius,
458
+ onChange: (e) => setRockParams({ ...rockParams, radius: parseFloat(e.target.value) }),
459
+ style: { width: "100%" }
460
+ }
461
+ )
462
+ ] }),
463
+ /* @__PURE__ */ jsxs("label", { children: [
464
+ t("proceduralGeometry.detail", "Detail: {detail}").replace(
465
+ "{detail}",
466
+ String(rockParams.detail)
467
+ ),
468
+ /* @__PURE__ */ jsx(
469
+ "input",
470
+ {
471
+ type: "range",
472
+ min: 1,
473
+ max: 5,
474
+ value: rockParams.detail,
475
+ onChange: (e) => setRockParams({ ...rockParams, detail: parseInt(e.target.value, 10) }),
476
+ style: { width: "100%" }
477
+ }
478
+ )
479
+ ] }),
480
+ /* @__PURE__ */ jsxs("label", { children: [
481
+ t("proceduralGeometry.noise", "Noise: {noiseAmount}").replace(
482
+ "{noiseAmount}",
483
+ String(rockParams.noiseAmount.toFixed(2))
484
+ ),
485
+ /* @__PURE__ */ jsx(
486
+ "input",
487
+ {
488
+ type: "range",
489
+ min: 0,
490
+ max: 0.5,
491
+ step: 0.01,
492
+ value: rockParams.noiseAmount,
493
+ onChange: (e) => setRockParams({ ...rockParams, noiseAmount: parseFloat(e.target.value) }),
494
+ style: { width: "100%" }
495
+ }
496
+ )
497
+ ] }),
498
+ /* @__PURE__ */ jsxs("label", { children: [
499
+ t("proceduralGeometry.seed", "Seed: {seed}").replace(
500
+ "{seed}",
501
+ String(rockParams.seed)
502
+ ),
503
+ /* @__PURE__ */ jsx(
504
+ "input",
505
+ {
506
+ type: "range",
507
+ min: 1,
508
+ max: 999,
509
+ value: rockParams.seed,
510
+ onChange: (e) => setRockParams({ ...rockParams, seed: parseInt(e.target.value, 10) }),
511
+ style: { width: "100%" }
512
+ }
513
+ )
514
+ ] })
515
+ ] }),
516
+ type === "building" && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 4 }, children: [
517
+ /* @__PURE__ */ jsxs("label", { children: [
518
+ t("proceduralGeometry.width", "Width: {width}").replace(
519
+ "{width}",
520
+ String(buildingParams.width)
521
+ ),
522
+ /* @__PURE__ */ jsx(
523
+ "input",
524
+ {
525
+ type: "range",
526
+ min: 4,
527
+ max: 20,
528
+ step: 1,
529
+ value: buildingParams.width,
530
+ onChange: (e) => setBuildingParams({ ...buildingParams, width: parseInt(e.target.value, 10) }),
531
+ style: { width: "100%" }
532
+ }
533
+ )
534
+ ] }),
535
+ /* @__PURE__ */ jsxs("label", { children: [
536
+ t("proceduralGeometry.floors", "Floors: {floors}").replace(
537
+ "{floors}",
538
+ String(buildingParams.floors)
539
+ ),
540
+ /* @__PURE__ */ jsx(
541
+ "input",
542
+ {
543
+ type: "range",
544
+ min: 1,
545
+ max: 20,
546
+ value: buildingParams.floors,
547
+ onChange: (e) => setBuildingParams({ ...buildingParams, floors: parseInt(e.target.value, 10) }),
548
+ style: { width: "100%" }
549
+ }
550
+ )
551
+ ] }),
552
+ /* @__PURE__ */ jsxs("label", { children: [
553
+ t("proceduralGeometry.windowsFloor", "Windows/Floor: {windowsPerFloor}").replace(
554
+ "{windowsPerFloor}",
555
+ String(buildingParams.windowsPerFloor)
556
+ ),
557
+ /* @__PURE__ */ jsx(
558
+ "input",
559
+ {
560
+ type: "range",
561
+ min: 1,
562
+ max: 10,
563
+ value: buildingParams.windowsPerFloor,
564
+ onChange: (e) => setBuildingParams({
565
+ ...buildingParams,
566
+ windowsPerFloor: parseInt(e.target.value, 10)
567
+ }),
568
+ style: { width: "100%" }
569
+ }
570
+ )
571
+ ] }),
572
+ /* @__PURE__ */ jsxs("label", { children: [
573
+ t("proceduralGeometry.roof", "Roof:"),
574
+ /* @__PURE__ */ jsxs(
575
+ "select",
576
+ {
577
+ value: buildingParams.roofStyle,
578
+ onChange: (e) => setBuildingParams({
579
+ ...buildingParams,
580
+ roofStyle: e.target.value
581
+ }),
582
+ style: { marginLeft: 8 },
583
+ children: [
584
+ /* @__PURE__ */ jsx("option", { value: "flat", children: t("proceduralGeometry.flat", "Flat") }),
585
+ /* @__PURE__ */ jsx("option", { value: "gable", children: t("proceduralGeometry.gable", "Gable") }),
586
+ /* @__PURE__ */ jsx("option", { value: "hip", children: t("proceduralGeometry.hip", "Hip") })
587
+ ]
588
+ }
589
+ )
590
+ ] })
591
+ ] }),
592
+ type === "gear" && /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 4 }, children: [
593
+ /* @__PURE__ */ jsxs("label", { children: [
594
+ t("proceduralGeometry.outerRadius", "Outer Radius: {outerRadius}").replace(
595
+ "{outerRadius}",
596
+ String(gearParams.outerRadius.toFixed(1))
597
+ ),
598
+ /* @__PURE__ */ jsx(
599
+ "input",
600
+ {
601
+ type: "range",
602
+ min: 0.5,
603
+ max: 5,
604
+ step: 0.1,
605
+ value: gearParams.outerRadius,
606
+ onChange: (e) => setGearParams({ ...gearParams, outerRadius: parseFloat(e.target.value) }),
607
+ style: { width: "100%" }
608
+ }
609
+ )
610
+ ] }),
611
+ /* @__PURE__ */ jsxs("label", { children: [
612
+ t("proceduralGeometry.teeth", "Teeth: {teeth}").replace(
613
+ "{teeth}",
614
+ String(gearParams.teeth)
615
+ ),
616
+ /* @__PURE__ */ jsx(
617
+ "input",
618
+ {
619
+ type: "range",
620
+ min: 4,
621
+ max: 30,
622
+ value: gearParams.teeth,
623
+ onChange: (e) => setGearParams({ ...gearParams, teeth: parseInt(e.target.value, 10) }),
624
+ style: { width: "100%" }
625
+ }
626
+ )
627
+ ] }),
628
+ /* @__PURE__ */ jsxs("label", { children: [
629
+ t("proceduralGeometry.toothDepth", "Tooth Depth: {toothDepth}").replace(
630
+ "{toothDepth}",
631
+ String(gearParams.toothDepth.toFixed(2))
632
+ ),
633
+ /* @__PURE__ */ jsx(
634
+ "input",
635
+ {
636
+ type: "range",
637
+ min: 0.05,
638
+ max: 1,
639
+ step: 0.05,
640
+ value: gearParams.toothDepth,
641
+ onChange: (e) => setGearParams({ ...gearParams, toothDepth: parseFloat(e.target.value) }),
642
+ style: { width: "100%" }
643
+ }
644
+ )
645
+ ] }),
646
+ /* @__PURE__ */ jsxs("label", { children: [
647
+ t("proceduralGeometry.thickness", "Thickness: {thickness}").replace(
648
+ "{thickness}",
649
+ String(gearParams.thickness.toFixed(2))
650
+ ),
651
+ /* @__PURE__ */ jsx(
652
+ "input",
653
+ {
654
+ type: "range",
655
+ min: 0.1,
656
+ max: 2,
657
+ step: 0.1,
658
+ value: gearParams.thickness,
659
+ onChange: (e) => setGearParams({ ...gearParams, thickness: parseFloat(e.target.value) }),
660
+ style: { width: "100%" }
661
+ }
662
+ )
663
+ ] })
664
+ ] }),
665
+ /* @__PURE__ */ jsxs(
666
+ "button",
667
+ {
668
+ type: "button",
669
+ onClick: generate,
670
+ style: {
671
+ background: "var(--nice-color-accent, #6366f1)",
672
+ color: "var(--nice-text-on-primary, #fff)",
673
+ border: "none",
674
+ borderRadius: 4,
675
+ padding: "6px 12px",
676
+ cursor: "pointer"
677
+ },
678
+ children: [
679
+ /* @__PURE__ */ jsx(NiceIcon, { name: "wand", size: 14 }),
680
+ " ",
681
+ t("proceduralGeometry.generate", "Generate")
682
+ ]
683
+ }
684
+ )
685
+ ]
686
+ }
687
+ );
688
+ };
689
+ export {
690
+ NiceProceduralGeometry,
691
+ generateBuilding,
692
+ generateGear,
693
+ generatePipe,
694
+ generateRock,
695
+ generateTree,
696
+ useProceduralGeometry
312
697
  };
313
-
314
- export { NiceProceduralGeometry, generateBuilding, generateGear, generatePipe, generateRock, generateTree, useProceduralGeometry };