@pascal-app/viewer 1.0.3 → 1.0.5

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 (276) hide show
  1. package/dist/capture/capture-runtime.js +1 -1
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/renderers/node-renderer.d.ts +1 -1
  4. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  5. package/dist/components/renderers/node-renderer.js +12 -4
  6. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -1
  7. package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -1
  8. package/dist/components/viewer/frame-limiter.d.ts +1 -0
  9. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  10. package/dist/components/viewer/frame-limiter.js +22 -3
  11. package/dist/components/viewer/glb-interactive.d.ts +25 -1
  12. package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
  13. package/dist/components/viewer/glb-interactive.js +253 -86
  14. package/dist/components/viewer/glb-mechanisms.d.ts +10 -0
  15. package/dist/components/viewer/glb-mechanisms.d.ts.map +1 -0
  16. package/dist/components/viewer/glb-mechanisms.js +19 -0
  17. package/dist/components/viewer/glb-reference-nodes.js +1 -1
  18. package/dist/components/viewer/glb-replace-instances.js +1 -1
  19. package/dist/components/viewer/glb-scene.d.ts +45 -27
  20. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  21. package/dist/components/viewer/glb-scene.js +421 -508
  22. package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
  23. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  24. package/dist/components/viewer/glb-walkthrough-controller.js +13 -2
  25. package/dist/components/viewer/index.d.ts +24 -1
  26. package/dist/components/viewer/index.d.ts.map +1 -1
  27. package/dist/components/viewer/index.js +111 -37
  28. package/dist/components/viewer/lights.d.ts.map +1 -1
  29. package/dist/components/viewer/lights.js +113 -22
  30. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  31. package/dist/components/viewer/perf-monitor.js +6 -0
  32. package/dist/components/viewer/perf-panel.d.ts.map +1 -1
  33. package/dist/components/viewer/perf-panel.js +1 -1
  34. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  35. package/dist/components/viewer/post-processing.js +104 -36
  36. package/dist/components/viewer/registered-systems.d.ts.map +1 -1
  37. package/dist/components/viewer/registered-systems.js +17 -3
  38. package/dist/components/viewer/render-error.d.ts +18 -0
  39. package/dist/components/viewer/render-error.d.ts.map +1 -0
  40. package/dist/components/viewer/render-error.js +29 -0
  41. package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
  42. package/dist/components/viewer/scene-bvh.js +1 -1
  43. package/dist/components/viewer/scene-environment.d.ts.map +1 -1
  44. package/dist/components/viewer/scene-environment.js +31 -11
  45. package/dist/components/viewer/scene-ground-replacement.d.ts +6 -0
  46. package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -1
  47. package/dist/components/viewer/scene-ground-replacement.js +15 -0
  48. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  49. package/dist/components/viewer/selection-manager.js +25 -7
  50. package/dist/components/viewer/underground-ambiance.d.ts +9 -0
  51. package/dist/components/viewer/underground-ambiance.d.ts.map +1 -0
  52. package/dist/components/viewer/underground-ambiance.js +105 -0
  53. package/dist/components/viewer/viewer-camera.d.ts +4 -0
  54. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  55. package/dist/components/viewer/viewer-camera.js +12 -2
  56. package/dist/components/viewer/viewer-presentations.d.ts.map +1 -1
  57. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  58. package/dist/hooks/use-gltf-ktx2.js +2 -0
  59. package/dist/hooks/use-node-events.d.ts.map +1 -1
  60. package/dist/index.d.ts +36 -16
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +34 -19
  63. package/dist/lib/asset-url.d.ts.map +1 -1
  64. package/dist/lib/asset-url.js +7 -1
  65. package/dist/lib/backdrop.d.ts +17 -0
  66. package/dist/lib/backdrop.d.ts.map +1 -1
  67. package/dist/lib/backdrop.js +18 -1
  68. package/dist/lib/construction-dust.d.ts +57 -0
  69. package/dist/lib/construction-dust.d.ts.map +1 -0
  70. package/dist/lib/construction-dust.js +187 -0
  71. package/dist/lib/csg-utils.js +1 -1
  72. package/dist/lib/display-state.d.ts +25 -0
  73. package/dist/lib/display-state.d.ts.map +1 -0
  74. package/dist/lib/display-state.js +1 -0
  75. package/dist/lib/foundation-materials.d.ts +26 -0
  76. package/dist/lib/foundation-materials.d.ts.map +1 -0
  77. package/dist/lib/foundation-materials.js +65 -0
  78. package/dist/lib/gltf-unlit.d.ts +10 -0
  79. package/dist/lib/gltf-unlit.d.ts.map +1 -0
  80. package/dist/lib/gltf-unlit.js +23 -0
  81. package/dist/lib/ground-window.d.ts +33 -0
  82. package/dist/lib/ground-window.d.ts.map +1 -0
  83. package/dist/lib/ground-window.js +114 -0
  84. package/dist/lib/hero-pose.d.ts +13 -3
  85. package/dist/lib/hero-pose.d.ts.map +1 -1
  86. package/dist/lib/hero-pose.js +72 -5
  87. package/dist/lib/ink-edges.d.ts +6 -3
  88. package/dist/lib/ink-edges.d.ts.map +1 -1
  89. package/dist/lib/ink-edges.js +17 -7
  90. package/dist/lib/key-light-override.d.ts +11 -0
  91. package/dist/lib/key-light-override.d.ts.map +1 -0
  92. package/dist/lib/key-light-override.js +14 -0
  93. package/dist/lib/ktx2-loader.d.ts +7 -0
  94. package/dist/lib/ktx2-loader.d.ts.map +1 -1
  95. package/dist/lib/ktx2-loader.js +35 -0
  96. package/dist/lib/layer-pass.d.ts.map +1 -1
  97. package/dist/lib/layers.d.ts +11 -7
  98. package/dist/lib/layers.d.ts.map +1 -1
  99. package/dist/lib/layers.js +16 -6
  100. package/dist/lib/live-frame-hold.d.ts +14 -0
  101. package/dist/lib/live-frame-hold.d.ts.map +1 -0
  102. package/dist/lib/live-frame-hold.js +28 -0
  103. package/dist/lib/materials.d.ts +19 -7
  104. package/dist/lib/materials.d.ts.map +1 -1
  105. package/dist/lib/materials.js +65 -9
  106. package/dist/lib/merged-outline-node.d.ts +5 -0
  107. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  108. package/dist/lib/merged-outline-node.js +80 -2
  109. package/dist/lib/node-top-surface-height.d.ts +6 -0
  110. package/dist/lib/node-top-surface-height.d.ts.map +1 -0
  111. package/dist/lib/node-top-surface-height.js +74 -0
  112. package/dist/lib/perf-probe-state.d.ts +39 -0
  113. package/dist/lib/perf-probe-state.d.ts.map +1 -0
  114. package/dist/lib/perf-probe-state.js +17 -0
  115. package/dist/lib/pointer-events.d.ts +26 -2
  116. package/dist/lib/pointer-events.d.ts.map +1 -1
  117. package/dist/lib/pointer-events.js +99 -12
  118. package/dist/lib/post-processing-resources.d.ts.map +1 -1
  119. package/dist/lib/procedural-emission.d.ts +7 -0
  120. package/dist/lib/procedural-emission.d.ts.map +1 -0
  121. package/dist/lib/procedural-emission.js +54 -0
  122. package/dist/lib/reveal-candidates.d.ts +80 -0
  123. package/dist/lib/reveal-candidates.d.ts.map +1 -0
  124. package/dist/lib/reveal-candidates.js +524 -0
  125. package/dist/lib/reveal-ghost.d.ts +9 -0
  126. package/dist/lib/reveal-ghost.d.ts.map +1 -0
  127. package/dist/lib/reveal-ghost.js +62 -0
  128. package/dist/lib/reveal-pending.d.ts +12 -0
  129. package/dist/lib/reveal-pending.d.ts.map +1 -0
  130. package/dist/lib/reveal-pending.js +12 -0
  131. package/dist/lib/reveal-pose.d.ts +70 -0
  132. package/dist/lib/reveal-pose.d.ts.map +1 -0
  133. package/dist/lib/reveal-pose.js +248 -0
  134. package/dist/lib/reveal-schedule.d.ts +208 -0
  135. package/dist/lib/reveal-schedule.d.ts.map +1 -0
  136. package/dist/lib/reveal-schedule.js +410 -0
  137. package/dist/lib/reveal-texture-scale.d.ts +5 -0
  138. package/dist/lib/reveal-texture-scale.d.ts.map +1 -0
  139. package/dist/lib/reveal-texture-scale.js +113 -0
  140. package/dist/lib/reveal-windows.d.ts +23 -0
  141. package/dist/lib/reveal-windows.d.ts.map +1 -0
  142. package/dist/lib/reveal-windows.js +31 -0
  143. package/dist/lib/reveal-wipe.d.ts +71 -0
  144. package/dist/lib/reveal-wipe.d.ts.map +1 -0
  145. package/dist/lib/reveal-wipe.js +105 -0
  146. package/dist/lib/scene-pass-mrt.d.ts +12 -0
  147. package/dist/lib/scene-pass-mrt.d.ts.map +1 -0
  148. package/dist/lib/scene-pass-mrt.js +23 -0
  149. package/dist/lib/scene-support-height.d.ts +4 -0
  150. package/dist/lib/scene-support-height.d.ts.map +1 -0
  151. package/dist/lib/scene-support-height.js +34 -0
  152. package/dist/lib/scene-themes.d.ts +30 -8
  153. package/dist/lib/scene-themes.d.ts.map +1 -1
  154. package/dist/lib/scene-themes.js +36 -0
  155. package/dist/lib/scene-visibility.d.ts +17 -1
  156. package/dist/lib/scene-visibility.d.ts.map +1 -1
  157. package/dist/lib/scene-visibility.js +53 -3
  158. package/dist/lib/scripted-opening.d.ts +11 -0
  159. package/dist/lib/scripted-opening.d.ts.map +1 -0
  160. package/dist/lib/scripted-opening.js +20 -0
  161. package/dist/lib/site-surfaces.d.ts +32 -0
  162. package/dist/lib/site-surfaces.d.ts.map +1 -0
  163. package/dist/lib/site-surfaces.js +70 -0
  164. package/dist/lib/snapshot-pipeline.d.ts +34 -3
  165. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  166. package/dist/lib/snapshot-pipeline.js +247 -117
  167. package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
  168. package/dist/lib/terrain-perimeter-fill.js +22 -14
  169. package/dist/lib/tsl-compat.d.ts +2 -2
  170. package/dist/lib/tsl-compat.d.ts.map +1 -1
  171. package/dist/lib/underground-ambiance.d.ts +168 -0
  172. package/dist/lib/underground-ambiance.d.ts.map +1 -0
  173. package/dist/lib/underground-ambiance.js +334 -0
  174. package/dist/lib/zone-geometry.d.ts +11 -0
  175. package/dist/lib/zone-geometry.d.ts.map +1 -0
  176. package/dist/lib/zone-geometry.js +46 -0
  177. package/dist/store/use-item-light-pool.d.ts +14 -10
  178. package/dist/store/use-item-light-pool.d.ts.map +1 -1
  179. package/dist/store/use-item-light-pool.js +52 -26
  180. package/dist/store/use-viewer.d.ts +194 -26
  181. package/dist/store/use-viewer.d.ts.map +1 -1
  182. package/dist/store/use-viewer.js +164 -21
  183. package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
  184. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  185. package/dist/systems/ceiling/ceiling-system.js +88 -6
  186. package/dist/systems/construction-reveal/construction-dust.d.ts +16 -0
  187. package/dist/systems/construction-reveal/construction-dust.d.ts.map +1 -0
  188. package/dist/systems/construction-reveal/construction-dust.js +92 -0
  189. package/dist/systems/construction-reveal/construction-reveal.d.ts +276 -0
  190. package/dist/systems/construction-reveal/construction-reveal.d.ts.map +1 -0
  191. package/dist/systems/construction-reveal/construction-reveal.js +2383 -0
  192. package/dist/systems/construction-reveal/dust-material.d.ts +10 -0
  193. package/dist/systems/construction-reveal/dust-material.d.ts.map +1 -0
  194. package/dist/systems/construction-reveal/dust-material.js +17 -0
  195. package/dist/systems/construction-reveal/ghost-dress.d.ts +10 -0
  196. package/dist/systems/construction-reveal/ghost-dress.d.ts.map +1 -0
  197. package/dist/systems/construction-reveal/ghost-dress.js +24 -0
  198. package/dist/systems/door/door-system.d.ts.map +1 -1
  199. package/dist/systems/door/door-system.js +55 -17
  200. package/dist/systems/elevator/elevator-interaction-system.d.ts +7 -0
  201. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
  202. package/dist/systems/elevator/elevator-interaction-system.js +18 -1
  203. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
  204. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  205. package/dist/systems/floor-elevation/floor-elevation-system.js +33 -5
  206. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  207. package/dist/systems/geometry/geometry-system.js +71 -11
  208. package/dist/systems/interactive/control-widget.d.ts.map +1 -1
  209. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  210. package/dist/systems/interactive/interactive-system.js +52 -17
  211. package/dist/systems/interactive/procedural-controls.d.ts +13 -0
  212. package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
  213. package/dist/systems/interactive/procedural-controls.js +17 -0
  214. package/dist/systems/interactive/scripted-clips.d.ts +19 -0
  215. package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
  216. package/dist/systems/interactive/scripted-clips.js +134 -0
  217. package/dist/systems/item/item-system.d.ts.map +1 -1
  218. package/dist/systems/item/item-system.js +9 -3
  219. package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
  220. package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
  221. package/dist/systems/item/object-cut-invalidation.js +68 -0
  222. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  223. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  224. package/dist/systems/item-light/item-light-system.js +48 -53
  225. package/dist/systems/level/level-hold.d.ts +4 -0
  226. package/dist/systems/level/level-hold.d.ts.map +1 -0
  227. package/dist/systems/level/level-hold.js +14 -0
  228. package/dist/systems/level/level-system.d.ts.map +1 -1
  229. package/dist/systems/level/level-system.js +19 -6
  230. package/dist/systems/level/level-utils.d.ts +19 -6
  231. package/dist/systems/level/level-utils.d.ts.map +1 -1
  232. package/dist/systems/level/level-utils.js +68 -9
  233. package/dist/systems/level/shadow-only-levels.d.ts +9 -0
  234. package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
  235. package/dist/systems/level/shadow-only-levels.js +7 -0
  236. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts +103 -0
  237. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts.map +1 -0
  238. package/dist/systems/roof/__fixtures__/roof-plane-cases.js +317 -0
  239. package/dist/systems/roof/roof-materials.d.ts +16 -2
  240. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  241. package/dist/systems/roof/roof-materials.js +64 -31
  242. package/dist/systems/roof/roof-system.d.ts +21 -4
  243. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  244. package/dist/systems/roof/roof-system.js +374 -49
  245. package/dist/systems/slab/slab-system.d.ts +1 -1
  246. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  247. package/dist/systems/slab/slab-system.js +49 -19
  248. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  249. package/dist/systems/stair/stair-system.js +133 -354
  250. package/dist/systems/wall/wall-cutout-cache.d.ts +39 -3
  251. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  252. package/dist/systems/wall/wall-cutout-cache.js +141 -19
  253. package/dist/systems/wall/wall-cutout.d.ts +6 -0
  254. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  255. package/dist/systems/wall/wall-cutout.js +42 -28
  256. package/dist/systems/wall/wall-finish-data.d.ts +22 -0
  257. package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
  258. package/dist/systems/wall/wall-finish-data.js +17 -0
  259. package/dist/systems/wall/wall-material-variant.d.ts.map +1 -1
  260. package/dist/systems/wall/wall-materials.d.ts +9 -4
  261. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  262. package/dist/systems/wall/wall-materials.js +135 -153
  263. package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -1
  264. package/dist/systems/wall/wall-system.d.ts +33 -2
  265. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  266. package/dist/systems/wall/wall-system.js +621 -159
  267. package/dist/systems/window/corner-frame.d.ts +10 -0
  268. package/dist/systems/window/corner-frame.d.ts.map +1 -0
  269. package/dist/systems/window/corner-frame.js +17 -0
  270. package/dist/systems/window/window-system.d.ts +1 -1
  271. package/dist/systems/window/window-system.d.ts.map +1 -1
  272. package/dist/systems/window/window-system.js +71 -27
  273. package/package.json +10 -10
  274. package/dist/systems/fence/fence-system.d.ts +0 -22
  275. package/dist/systems/fence/fence-system.d.ts.map +0 -1
  276. package/dist/systems/fence/fence-system.js +0 -357
@@ -1,357 +0,0 @@
1
- import { getFenceCenterlineFrameAt, getFenceCenterlineLength, sceneRegistry, useScene, } from '@pascal-app/core';
2
- import { useFrame } from '@react-three/fiber';
3
- import * as THREE from 'three';
4
- import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
5
- const MIN_CURVE_SEGMENT_LENGTH = 0.18;
6
- const HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH = 0.2;
7
- function createFencePartGeometry(part) {
8
- if (part.geometry) {
9
- return part.geometry;
10
- }
11
- const geometry = part.shape === 'pyramid'
12
- ? new THREE.ConeGeometry(0.5, 1, 4, 1, false, Math.PI / 4)
13
- : new THREE.BoxGeometry(1, 1, 1);
14
- geometry.scale(part.scale[0], part.scale[1], part.scale[2]);
15
- if (part.rotationY) {
16
- geometry.rotateY(part.rotationY);
17
- }
18
- geometry.translate(part.position[0], part.position[1], part.position[2]);
19
- applyFenceUVs(geometry);
20
- return geometry;
21
- }
22
- function getFencePointAt(fence, t) {
23
- const frame = getFenceCenterlineFrameAt(fence, t);
24
- return {
25
- point: frame.point,
26
- tangentAngle: Math.atan2(frame.tangent.y, frame.tangent.x),
27
- };
28
- }
29
- function createFenceCurveBlockPart(fence, startT, endT, centerY, height, depth) {
30
- if (endT - startT <= 1e-5)
31
- return null;
32
- const halfHeight = height / 2;
33
- const halfDepth = depth / 2;
34
- const centerlineLength = getFenceCenterlineLength(fence);
35
- const startDistance = startT * centerlineLength;
36
- const endDistance = endT * centerlineLength;
37
- const bottomY = centerY - halfHeight;
38
- const topY = centerY + halfHeight;
39
- const corners = [];
40
- for (const t of [startT, endT]) {
41
- const frame = getFencePointAt(fence, t);
42
- const normalX = -Math.sin(frame.tangentAngle);
43
- const normalZ = Math.cos(frame.tangentAngle);
44
- const outerX = frame.point.x + normalX * halfDepth;
45
- const outerZ = frame.point.y + normalZ * halfDepth;
46
- const innerX = frame.point.x - normalX * halfDepth;
47
- const innerZ = frame.point.y - normalZ * halfDepth;
48
- corners.push([outerX, bottomY, outerZ], [innerX, bottomY, innerZ], [outerX, topY, outerZ], [innerX, topY, innerZ]);
49
- }
50
- const positions = [];
51
- const uvs = [];
52
- const pushVertex = (index, uv) => {
53
- positions.push(...corners[index]);
54
- uvs.push(...uv);
55
- };
56
- const pushQuad = (a, b, c, d, uvA, uvB, uvC, uvD) => {
57
- pushVertex(a, uvA);
58
- pushVertex(b, uvB);
59
- pushVertex(c, uvC);
60
- pushVertex(a, uvA);
61
- pushVertex(c, uvC);
62
- pushVertex(d, uvD);
63
- };
64
- const topOuterV = topY;
65
- const topInnerV = topY + depth;
66
- const innerTopV = topInnerV;
67
- const innerBottomV = topInnerV + height;
68
- const bottomInnerV = bottomY - depth;
69
- pushQuad(0, 4, 6, 2, [startDistance, bottomY], [endDistance, bottomY], [endDistance, topY], [startDistance, topY]);
70
- pushQuad(1, 3, 7, 5, [startDistance, innerBottomV], [startDistance, innerTopV], [endDistance, innerTopV], [endDistance, innerBottomV]);
71
- pushQuad(2, 6, 7, 3, [startDistance, topOuterV], [endDistance, topOuterV], [endDistance, topInnerV], [startDistance, topInnerV]);
72
- pushQuad(0, 1, 5, 4, [startDistance, bottomY], [startDistance, bottomInnerV], [endDistance, bottomInnerV], [endDistance, bottomY]);
73
- pushQuad(0, 2, 3, 1, [0, bottomY], [0, topY], [depth, innerTopV], [depth, innerBottomV]);
74
- pushQuad(4, 5, 7, 6, [0, bottomY], [depth, innerBottomV], [depth, innerTopV], [0, topY]);
75
- const geometry = new THREE.BufferGeometry();
76
- geometry.setAttribute('position', new THREE.Float32BufferAttribute(new Float32Array(positions), 3));
77
- geometry.setAttribute('uv', new THREE.Float32BufferAttribute(new Float32Array(uvs), 2));
78
- geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(new Float32Array(uvs), 2));
79
- geometry.computeVertexNormals();
80
- return {
81
- geometry,
82
- position: [0, 0, 0],
83
- scale: [1, 1, 1],
84
- };
85
- }
86
- function createFenceCurveBlockParts(fence, startT, endT, centerY, height, depth, maxSegmentLength = MIN_CURVE_SEGMENT_LENGTH) {
87
- const length = getFenceCenterlineLength(fence) * Math.max(1e-4, endT - startT);
88
- const segmentCount = Math.max(1, Math.ceil(length / Math.max(1e-4, maxSegmentLength)));
89
- const parts = [];
90
- for (let index = 0; index < segmentCount; index += 1) {
91
- const segmentStartT = startT + (endT - startT) * (index / segmentCount);
92
- const segmentEndT = startT + (endT - startT) * ((index + 1) / segmentCount);
93
- const part = createFenceCurveBlockPart(fence, segmentStartT, segmentEndT, centerY, height, depth);
94
- if (part)
95
- parts.push(part);
96
- }
97
- return parts;
98
- }
99
- function applyFenceUVs(geometry) {
100
- const position = geometry.getAttribute('position');
101
- const normal = geometry.getAttribute('normal');
102
- if (!(position && normal))
103
- return;
104
- // World-scale triplanar UVs: 1 UV unit = 1 metre, sampled from the part's
105
- // local-space (already translated into fence space) coordinates with NO
106
- // per-part origin shift. A shared origin keeps a tiled finish continuous
107
- // across posts, rails, and infill instead of restarting the tile at each
108
- // part's own min corner (the previous behaviour, which broke the 1 m
109
- // contract and made adjacent parts mistile).
110
- const uvs = new Float32Array(position.count * 2);
111
- for (let index = 0; index < position.count; index += 1) {
112
- const px = position.getX(index);
113
- const py = position.getY(index);
114
- const pz = position.getZ(index);
115
- const nx = Math.abs(normal.getX(index));
116
- const ny = Math.abs(normal.getY(index));
117
- const nz = Math.abs(normal.getZ(index));
118
- let u = 0;
119
- let v = 0;
120
- if (ny >= nx && ny >= nz) {
121
- u = px;
122
- v = pz;
123
- }
124
- else if (nx >= nz) {
125
- u = pz;
126
- v = py;
127
- }
128
- else {
129
- u = px;
130
- v = py;
131
- }
132
- uvs[index * 2] = u;
133
- uvs[index * 2 + 1] = v;
134
- }
135
- geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
136
- geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(uvs.slice(), 2));
137
- }
138
- function getStyleDefaults(style) {
139
- if (style === 'privacy') {
140
- return { spacingFactor: 0.42, postFactor: 1.35, baseFactor: 1.2, topFactor: 1.2 };
141
- }
142
- if (style === 'rail') {
143
- return { spacingFactor: 0.68, postFactor: 0.8, baseFactor: 0.85, topFactor: 0.85 };
144
- }
145
- return { spacingFactor: 0.3, postFactor: 0.55, baseFactor: 1, topFactor: 0.75 };
146
- }
147
- /**
148
- * Horizontal-board fence — composite cladding boards stacked between square
149
- * intermediate posts (each capped), instead of the vertical pickets the other
150
- * styles draw. Posts march along the whole span at `postSpacing` (not just the
151
- * two ends), the boards run full-length so they curve with the fence, and a
152
- * thin reveal between boards leaves the groove shadow that reads as cladding.
153
- */
154
- function createHorizontalFenceParts(fence) {
155
- const posts = [];
156
- const infill = [];
157
- const base = [];
158
- const rail = [];
159
- const length = Math.max(getFenceCenterlineLength(fence), 0.01);
160
- const panelDepth = Math.max(fence.thickness, 0.03);
161
- const clearance = Math.max(fence.groundClearance, 0);
162
- const isFloating = fence.baseStyle === 'floating';
163
- const showInfill = fence.showInfill ?? true;
164
- const baseHeight = Math.max(fence.baseHeight, 0.04);
165
- const topRailHeight = Math.max(fence.topRailHeight, 0.01);
166
- const verticalHeight = Math.max(fence.height - baseHeight - topRailHeight, 0.08);
167
- const baseY = isFloating ? clearance : 0;
168
- // Square posts stand proud of the recessed boards on both faces.
169
- const postWidth = Math.max(fence.postSize * 1.4, 0.04);
170
- const postDepth = postWidth;
171
- const boardDepth = Math.min(panelDepth, postDepth - 0.012);
172
- // Stop the horizontal boards / base / rail at the inner faces of the
173
- // end posts. Letting curved spans run all the way to t=0/1 makes them
174
- // overlap the terminal post mesh and creates the broken seam/notch seen
175
- // at curve ends.
176
- const edgeInset = Math.max(fence.edgeInset ?? 0.015, postWidth * 0.5);
177
- const startInsetT = Math.min(0.499, edgeInset / length);
178
- const endInsetT = Math.max(0.501, 1 - edgeInset / length);
179
- // Grounded fences get a kickboard along the bottom; floating ones don't.
180
- if (!isFloating) {
181
- base.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, baseY + baseHeight / 2, baseHeight, postDepth * 0.92, HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH));
182
- }
183
- // Stack full-length boards between the kickboard and the top rail. The board
184
- // height is derived to evenly fill the panel around a ~0.145 m target, with a
185
- // constant reveal between each so the count adapts to any fence height.
186
- if (showInfill) {
187
- const reveal = Math.max(fence.slatGap ?? 0.01, 0);
188
- const infillBottom = baseY + baseHeight;
189
- if (reveal < 0.002) {
190
- // No reveal → one flush panel, so the stacked-board edge seams don't
191
- // read as faint lines where the user asked for a smooth surface.
192
- infill.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, infillBottom + verticalHeight / 2, verticalHeight, boardDepth, HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH));
193
- }
194
- else {
195
- const boardCount = Math.max(1, Math.round(verticalHeight / (0.145 + reveal)));
196
- const slabHeight = Math.max((verticalHeight - reveal * (boardCount - 1)) / boardCount, 0.02);
197
- for (let index = 0; index < boardCount; index += 1) {
198
- const centerY = infillBottom + slabHeight / 2 + index * (slabHeight + reveal);
199
- infill.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, centerY, slabHeight, boardDepth, HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH));
200
- }
201
- }
202
- }
203
- // Top rail caps the boards.
204
- rail.push(...createFenceCurveBlockParts(fence, startInsetT, endInsetT, baseY + baseHeight + verticalHeight + topRailHeight / 2, topRailHeight, Math.max(postDepth * 0.78, 0.02), HORIZONTAL_FENCE_CURVE_SEGMENT_LENGTH));
205
- // Posts at every `postSpacing`, anchored at both ends, each with a flat cap.
206
- const spacing = Math.max(fence.postSpacing, postWidth * 1.4);
207
- const postCount = Math.max(2, Math.floor(length / spacing) + 1);
208
- const postHeight = baseHeight + verticalHeight + topRailHeight + clearance;
209
- const capHeight = Math.max(postWidth * 0.32, 0.03);
210
- const cap = fence.postCap ?? 'pyramid';
211
- for (let index = 0; index < postCount; index += 1) {
212
- const t = postCount === 1 ? 0.5 : index / (postCount - 1);
213
- const frame = getFencePointAt(fence, t);
214
- posts.push({
215
- position: [frame.point.x, postHeight / 2, frame.point.y],
216
- rotationY: -frame.tangentAngle,
217
- scale: [postWidth, postHeight, postDepth],
218
- });
219
- if (cap === 'flat') {
220
- posts.push({
221
- position: [frame.point.x, postHeight + capHeight / 2, frame.point.y],
222
- rotationY: -frame.tangentAngle,
223
- scale: [postWidth * 1.22, capHeight, postDepth * 1.22],
224
- });
225
- }
226
- else if (cap === 'pyramid') {
227
- posts.push({
228
- position: [frame.point.x, postHeight + capHeight * 0.9, frame.point.y],
229
- rotationY: -frame.tangentAngle,
230
- scale: [postWidth * 1.18, capHeight * 1.8, postDepth * 1.18],
231
- shape: 'pyramid',
232
- });
233
- }
234
- }
235
- return { posts, infill, base, rail };
236
- }
237
- function createFenceParts(fence) {
238
- if (fence.style === 'horizontal')
239
- return createHorizontalFenceParts(fence);
240
- const posts = [];
241
- const infill = [];
242
- const base = [];
243
- const rail = [];
244
- const length = Math.max(getFenceCenterlineLength(fence), 0.01);
245
- const panelDepth = Math.max(fence.thickness, 0.03);
246
- const clearance = Math.max(fence.groundClearance, 0);
247
- const styleDefaults = getStyleDefaults(fence.style);
248
- const baseHeight = Math.max(fence.baseHeight * styleDefaults.baseFactor, 0.04);
249
- const topRailHeight = Math.max(fence.topRailHeight * styleDefaults.topFactor, 0.01);
250
- const verticalHeight = Math.max(fence.height - baseHeight - topRailHeight, 0.08);
251
- const postWidth = Math.max(fence.postSize * styleDefaults.postFactor, 0.01);
252
- const spacing = Math.max(fence.postSpacing * styleDefaults.spacingFactor, postWidth * 1.2);
253
- const edgeInset = Math.max(fence.edgeInset ?? 0.015, 0.005);
254
- const isFloating = fence.baseStyle === 'floating';
255
- const showInfill = fence.showInfill ?? true;
256
- const baseY = isFloating ? clearance : 0;
257
- const effectiveBaseHeight = baseHeight;
258
- const startInsetT = Math.min(0.499, edgeInset / length);
259
- const endInsetT = Math.max(0.501, 1 - edgeInset / length);
260
- if (!isFloating) {
261
- base.push(...createFenceCurveBlockParts(fence, 0, 1, baseY + effectiveBaseHeight / 2, effectiveBaseHeight, panelDepth * 1.05));
262
- base.push(...createFenceCurveBlockParts(fence, 0, 1, baseY + effectiveBaseHeight + verticalHeight * 0.15, topRailHeight * 0.8, panelDepth * 0.35));
263
- }
264
- const count = showInfill ? Math.max(2, Math.floor((length - edgeInset * 2) / spacing) + 1) : 2;
265
- const verticalY = baseY + effectiveBaseHeight + verticalHeight / 2;
266
- for (let index = 0; index < count; index += 1) {
267
- const t = count === 1 ? 0.5 : startInsetT + (endInsetT - startInsetT) * (index / (count - 1));
268
- const isEdgePost = index === 0 || index === count - 1;
269
- const fullHeightPost = !showInfill || (isFloating && isEdgePost);
270
- const postHeight = fullHeightPost
271
- ? effectiveBaseHeight + verticalHeight + topRailHeight + clearance
272
- : verticalHeight;
273
- const postY = fullHeightPost ? postHeight / 2 : verticalY;
274
- // End posts are the structural `posts` slot; the intermediate verticals are
275
- // the `infill` slats (only present when showInfill adds them).
276
- const slatHalfT = Math.max(0.0005, postWidth / (2 * length));
277
- const slatStartT = Math.max(0, t - slatHalfT);
278
- const slatEndT = Math.min(1, t + slatHalfT);
279
- const slat = createFenceCurveBlockPart(fence, slatStartT, slatEndT, postY, postHeight, Math.max(panelDepth * 0.35 - 0.001, 0.011));
280
- if (slat) {
281
- ;
282
- (isEdgePost ? posts : infill).push(slat);
283
- }
284
- }
285
- rail.push(...createFenceCurveBlockParts(fence, 0, 1, baseY + effectiveBaseHeight + verticalHeight + topRailHeight / 2, topRailHeight, Math.max(panelDepth * 0.55, 0.018)));
286
- if (isFloating) {
287
- rail.push(...createFenceCurveBlockParts(fence, 0, 1, baseY + effectiveBaseHeight + topRailHeight / 2, topRailHeight, Math.max(panelDepth * 0.55, 0.018)));
288
- }
289
- return { posts, infill, base, rail };
290
- }
291
- function mergeFenceParts(parts) {
292
- // An empty slot group (e.g. infill with showInfill off, or base on a floating
293
- // fence) must not reach mergeGeometries — it throws on an empty array. The
294
- // empty geometry has no position attribute, so the renderer skips its mesh.
295
- if (parts.length === 0)
296
- return new THREE.BufferGeometry();
297
- const geometries = parts.map(createFencePartGeometry);
298
- const merged = mergeGeometries(geometries, false) ?? new THREE.BufferGeometry();
299
- geometries.forEach((geometry) => {
300
- geometry.dispose();
301
- });
302
- const mergedUv = merged.getAttribute('uv');
303
- if (mergedUv) {
304
- merged.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(mergedUv.array), 2));
305
- }
306
- merged.computeVertexNormals();
307
- return merged;
308
- }
309
- /**
310
- * Geometry split by paint slot — posts, infill, base, rail — each a separate
311
- * merged BufferGeometry (empty ones included) so the fence renderer can give
312
- * each its own material + `userData.slotId`. Slots match the panel's build
313
- * options 1:1.
314
- */
315
- export function generateFenceSlotGeometries(fence) {
316
- const parts = createFenceParts(fence);
317
- return {
318
- posts: mergeFenceParts(parts.posts),
319
- infill: mergeFenceParts(parts.infill),
320
- base: mergeFenceParts(parts.base),
321
- rail: mergeFenceParts(parts.rail),
322
- };
323
- }
324
- export function generateFenceGeometry(fence) {
325
- const { posts, infill, base, rail } = createFenceParts(fence);
326
- return mergeFenceParts([...posts, ...infill, ...base, ...rail]);
327
- }
328
- function updateFenceGeometry(fenceId) {
329
- const node = useScene.getState().nodes[fenceId];
330
- if (node?.type !== 'fence')
331
- return;
332
- const mesh = sceneRegistry.nodes.get(fenceId);
333
- if (!mesh)
334
- return;
335
- const newGeometry = generateFenceGeometry(node);
336
- mesh.geometry.dispose();
337
- mesh.geometry = newGeometry;
338
- mesh.position.set(0, 0, 0);
339
- mesh.rotation.set(0, 0, 0);
340
- }
341
- export const FenceSystem = () => {
342
- const dirtyNodes = useScene((state) => state.dirtyNodes);
343
- const clearDirty = useScene((state) => state.clearDirty);
344
- useFrame(() => {
345
- if (dirtyNodes.size === 0)
346
- return;
347
- const nodes = useScene.getState().nodes;
348
- dirtyNodes.forEach((id) => {
349
- const node = nodes[id];
350
- if (node?.type !== 'fence')
351
- return;
352
- updateFenceGeometry(id);
353
- clearDirty(id);
354
- });
355
- }, 4);
356
- return null;
357
- };