@pascal-app/viewer 0.8.0 → 0.9.2

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 (284) hide show
  1. package/dist/components/error-boundary.d.ts +6 -0
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/error-boundary.js +6 -0
  4. package/dist/components/renderers/node-renderer.d.ts +6 -2
  5. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  6. package/dist/components/renderers/node-renderer.js +41 -22
  7. package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
  8. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
  9. package/dist/components/renderers/parametric-node-renderer.js +37 -0
  10. package/dist/components/renderers/scene-renderer.d.ts +1 -1
  11. package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
  12. package/dist/components/viewer/bvh-ecctrl.d.ts +77 -0
  13. package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -0
  14. package/dist/components/viewer/bvh-ecctrl.js +552 -0
  15. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  16. package/dist/components/viewer/frame-limiter.js +39 -4
  17. package/dist/components/viewer/glb-interactive.d.ts +41 -0
  18. package/dist/components/viewer/glb-interactive.d.ts.map +1 -0
  19. package/dist/components/viewer/glb-interactive.js +394 -0
  20. package/dist/components/viewer/glb-reference-nodes.d.ts +29 -0
  21. package/dist/components/viewer/glb-reference-nodes.d.ts.map +1 -0
  22. package/dist/components/viewer/glb-reference-nodes.js +80 -0
  23. package/dist/components/viewer/glb-replace-instances.d.ts +18 -0
  24. package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -0
  25. package/dist/components/viewer/glb-replace-instances.js +61 -0
  26. package/dist/components/viewer/glb-scene.d.ts +54 -0
  27. package/dist/components/viewer/glb-scene.d.ts.map +1 -0
  28. package/dist/components/viewer/glb-scene.js +920 -0
  29. package/dist/components/viewer/glb-walkthrough-controller.d.ts +12 -0
  30. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -0
  31. package/dist/components/viewer/glb-walkthrough-controller.js +357 -0
  32. package/dist/components/viewer/index.d.ts +56 -3
  33. package/dist/components/viewer/index.d.ts.map +1 -1
  34. package/dist/components/viewer/index.js +280 -65
  35. package/dist/components/viewer/lights.d.ts +1 -1
  36. package/dist/components/viewer/lights.d.ts.map +1 -1
  37. package/dist/components/viewer/lights.js +201 -43
  38. package/dist/components/viewer/perf-monitor.d.ts +1 -1
  39. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  40. package/dist/components/viewer/perf-monitor.js +115 -12
  41. package/dist/components/viewer/post-processing.d.ts +7 -1
  42. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  43. package/dist/components/viewer/post-processing.js +395 -63
  44. package/dist/components/viewer/registered-systems.d.ts +12 -0
  45. package/dist/components/viewer/registered-systems.d.ts.map +1 -0
  46. package/dist/components/viewer/registered-systems.js +49 -0
  47. package/dist/components/viewer/scene-bvh.d.ts +18 -0
  48. package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
  49. package/dist/components/viewer/scene-bvh.js +97 -0
  50. package/dist/components/viewer/scene-bvh.test.d.ts +2 -0
  51. package/dist/components/viewer/scene-bvh.test.d.ts.map +1 -0
  52. package/dist/components/viewer/scene-bvh.test.js +13 -0
  53. package/dist/components/viewer/scene-environment.d.ts +2 -0
  54. package/dist/components/viewer/scene-environment.d.ts.map +1 -0
  55. package/dist/components/viewer/scene-environment.js +79 -0
  56. package/dist/components/viewer/selection-manager.d.ts +1 -1
  57. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  58. package/dist/components/viewer/selection-manager.js +37 -14
  59. package/dist/components/viewer/viewer-camera.d.ts +1 -1
  60. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  61. package/dist/components/viewer/walkthrough-controls.d.ts +2 -1
  62. package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
  63. package/dist/components/viewer/walkthrough-controls.js +20 -0
  64. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  65. package/dist/hooks/use-gltf-ktx2.js +3 -22
  66. package/dist/hooks/use-node-events.d.ts +6 -74
  67. package/dist/hooks/use-node-events.d.ts.map +1 -1
  68. package/dist/hooks/use-node-events.js +28 -8
  69. package/dist/index.d.ts +48 -5
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +82 -4
  72. package/dist/lib/backdrop.d.ts +33 -0
  73. package/dist/lib/backdrop.d.ts.map +1 -0
  74. package/dist/lib/backdrop.js +92 -0
  75. package/dist/lib/box-uv.d.ts +13 -0
  76. package/dist/lib/box-uv.d.ts.map +1 -0
  77. package/dist/lib/box-uv.js +32 -0
  78. package/dist/lib/csg-utils.d.ts +10 -0
  79. package/dist/lib/csg-utils.d.ts.map +1 -0
  80. package/dist/lib/csg-utils.js +93 -0
  81. package/dist/lib/csg-utils.test.d.ts +2 -0
  82. package/dist/lib/csg-utils.test.d.ts.map +1 -0
  83. package/dist/lib/csg-utils.test.js +45 -0
  84. package/dist/lib/drawable-geometry.d.ts +21 -0
  85. package/dist/lib/drawable-geometry.d.ts.map +1 -0
  86. package/dist/lib/drawable-geometry.js +22 -0
  87. package/dist/lib/edge-style.d.ts +4 -0
  88. package/dist/lib/edge-style.d.ts.map +1 -0
  89. package/dist/lib/edge-style.js +31 -0
  90. package/dist/lib/gpu-perf.d.ts +4 -0
  91. package/dist/lib/gpu-perf.d.ts.map +1 -0
  92. package/dist/lib/gpu-perf.js +23 -0
  93. package/dist/lib/ink-edges.d.ts +9 -0
  94. package/dist/lib/ink-edges.d.ts.map +1 -0
  95. package/dist/lib/ink-edges.js +49 -0
  96. package/dist/lib/isolation.d.ts +39 -0
  97. package/dist/lib/isolation.d.ts.map +1 -0
  98. package/dist/lib/isolation.js +103 -0
  99. package/dist/lib/ktx2-loader.d.ts +43 -0
  100. package/dist/lib/ktx2-loader.d.ts.map +1 -0
  101. package/dist/lib/ktx2-loader.js +136 -0
  102. package/dist/lib/layers.d.ts +28 -0
  103. package/dist/lib/layers.d.ts.map +1 -1
  104. package/dist/lib/layers.js +28 -0
  105. package/dist/lib/materials.d.ts +38 -15
  106. package/dist/lib/materials.d.ts.map +1 -1
  107. package/dist/lib/materials.js +364 -72
  108. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  109. package/dist/lib/merged-outline-node.js +7 -0
  110. package/dist/lib/polygon-union.d.ts +4 -0
  111. package/dist/lib/polygon-union.d.ts.map +1 -0
  112. package/dist/lib/polygon-union.js +289 -0
  113. package/dist/lib/polygon-union.test.d.ts +2 -0
  114. package/dist/lib/polygon-union.test.d.ts.map +1 -0
  115. package/dist/lib/polygon-union.test.js +105 -0
  116. package/dist/lib/scene-themes.d.ts +30 -0
  117. package/dist/lib/scene-themes.d.ts.map +1 -0
  118. package/dist/lib/scene-themes.js +202 -0
  119. package/dist/lib/shadow-only.d.ts +4 -0
  120. package/dist/lib/shadow-only.d.ts.map +1 -0
  121. package/dist/lib/shadow-only.js +36 -0
  122. package/dist/lib/tsl-compat.d.ts +12 -0
  123. package/dist/lib/tsl-compat.d.ts.map +1 -0
  124. package/dist/lib/tsl-compat.js +13 -0
  125. package/dist/store/use-viewer.d.ts +92 -12
  126. package/dist/store/use-viewer.d.ts.map +1 -1
  127. package/dist/store/use-viewer.js +201 -6
  128. package/dist/store/use-viewer.test.d.ts +2 -0
  129. package/dist/store/use-viewer.test.d.ts.map +1 -0
  130. package/dist/store/use-viewer.test.js +42 -0
  131. package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
  132. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  133. package/dist/systems/ceiling/ceiling-system.js +66 -13
  134. package/dist/systems/door/door-animation.test.d.ts +2 -0
  135. package/dist/systems/door/door-animation.test.d.ts.map +1 -0
  136. package/dist/systems/door/door-animation.test.js +51 -0
  137. package/dist/systems/door/door-floor-alignment.test.d.ts +2 -0
  138. package/dist/systems/door/door-floor-alignment.test.d.ts.map +1 -0
  139. package/dist/systems/door/door-floor-alignment.test.js +50 -0
  140. package/dist/systems/door/door-system.d.ts +24 -0
  141. package/dist/systems/door/door-system.d.ts.map +1 -1
  142. package/dist/systems/door/door-system.js +864 -180
  143. package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
  144. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
  145. package/dist/systems/elevator/elevator-interaction-system.js +62 -0
  146. package/dist/systems/fence/fence-system.d.ts +20 -0
  147. package/dist/systems/fence/fence-system.d.ts.map +1 -1
  148. package/dist/systems/fence/fence-system.js +229 -57
  149. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
  150. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
  151. package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
  152. package/dist/systems/geometry/geometry-system.d.ts +41 -0
  153. package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
  154. package/dist/systems/geometry/geometry-system.js +348 -0
  155. package/dist/systems/geometry/geometry-system.test.d.ts +2 -0
  156. package/dist/systems/geometry/geometry-system.test.d.ts.map +1 -0
  157. package/dist/systems/geometry/geometry-system.test.js +30 -0
  158. package/dist/systems/interactive/control-widget.d.ts +10 -0
  159. package/dist/systems/interactive/control-widget.d.ts.map +1 -0
  160. package/dist/systems/interactive/control-widget.js +35 -0
  161. package/dist/systems/interactive/interactive-system.d.ts +1 -1
  162. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  163. package/dist/systems/interactive/interactive-system.js +55 -56
  164. package/dist/systems/item/item-system.d.ts +7 -0
  165. package/dist/systems/item/item-system.d.ts.map +1 -1
  166. package/dist/systems/item/item-system.js +28 -17
  167. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  168. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  169. package/dist/systems/item-light/item-light-system.js +18 -5
  170. package/dist/systems/level/level-system.d.ts.map +1 -1
  171. package/dist/systems/level/level-system.js +28 -4
  172. package/dist/systems/level/level-utils.d.ts +0 -3
  173. package/dist/systems/level/level-utils.d.ts.map +1 -1
  174. package/dist/systems/level/level-utils.js +2 -41
  175. package/dist/systems/roof/roof-materials.d.ts +3 -2
  176. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  177. package/dist/systems/roof/roof-materials.js +59 -14
  178. package/dist/systems/roof/roof-system.d.ts +46 -6
  179. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  180. package/dist/systems/roof/roof-system.js +1365 -170
  181. package/dist/systems/slab/slab-system.d.ts +5 -3
  182. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  183. package/dist/systems/slab/slab-system.js +204 -68
  184. package/dist/systems/slab/slab-system.test.d.ts +2 -0
  185. package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
  186. package/dist/systems/slab/slab-system.test.js +63 -0
  187. package/dist/systems/stair/stair-materials.d.ts +4 -3
  188. package/dist/systems/stair/stair-materials.d.ts.map +1 -1
  189. package/dist/systems/stair/stair-materials.js +30 -15
  190. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  191. package/dist/systems/stair/stair-system.js +55 -63
  192. package/dist/systems/surface-hole-geometry.d.ts +3 -0
  193. package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
  194. package/dist/systems/surface-hole-geometry.js +4 -0
  195. package/dist/systems/wall/opening-cutout-geometry.d.ts +31 -0
  196. package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -0
  197. package/dist/systems/wall/opening-cutout-geometry.js +167 -0
  198. package/dist/systems/wall/opening-cutout-geometry.test.d.ts +2 -0
  199. package/dist/systems/wall/opening-cutout-geometry.test.d.ts.map +1 -0
  200. package/dist/systems/wall/opening-cutout-geometry.test.js +172 -0
  201. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  202. package/dist/systems/wall/wall-cutout.js +62 -17
  203. package/dist/systems/wall/wall-materials.d.ts +12 -7
  204. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  205. package/dist/systems/wall/wall-materials.js +279 -46
  206. package/dist/systems/wall/wall-opening-cutout.test.d.ts +2 -0
  207. package/dist/systems/wall/wall-opening-cutout.test.d.ts.map +1 -0
  208. package/dist/systems/wall/wall-opening-cutout.test.js +59 -0
  209. package/dist/systems/wall/wall-support-extension.test.d.ts +2 -0
  210. package/dist/systems/wall/wall-support-extension.test.d.ts.map +1 -0
  211. package/dist/systems/wall/wall-support-extension.test.js +48 -0
  212. package/dist/systems/wall/wall-system.d.ts +2 -2
  213. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  214. package/dist/systems/wall/wall-system.js +493 -193
  215. package/dist/systems/window/window-animation-system.d.ts +12 -0
  216. package/dist/systems/window/window-animation-system.d.ts.map +1 -1
  217. package/dist/systems/window/window-animation-system.js +16 -5
  218. package/dist/systems/window/window-system.d.ts +7 -0
  219. package/dist/systems/window/window-system.d.ts.map +1 -1
  220. package/dist/systems/window/window-system.js +308 -18
  221. package/package.json +5 -6
  222. package/dist/components/renderers/building/building-renderer.d.ts +0 -5
  223. package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
  224. package/dist/components/renderers/building/building-renderer.js +0 -11
  225. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
  226. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
  227. package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
  228. package/dist/components/renderers/column/column-renderer.d.ts +0 -5
  229. package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
  230. package/dist/components/renderers/column/column-renderer.js +0 -597
  231. package/dist/components/renderers/door/door-renderer.d.ts +0 -5
  232. package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
  233. package/dist/components/renderers/door/door-renderer.js +0 -16
  234. package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
  235. package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
  236. package/dist/components/renderers/fence/fence-renderer.js +0 -15
  237. package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
  238. package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
  239. package/dist/components/renderers/guide/guide-renderer.js +0 -38
  240. package/dist/components/renderers/item/item-renderer.d.ts +0 -5
  241. package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
  242. package/dist/components/renderers/item/item-renderer.js +0 -172
  243. package/dist/components/renderers/level/level-renderer.d.ts +0 -5
  244. package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
  245. package/dist/components/renderers/level/level-renderer.js +0 -11
  246. package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
  247. package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
  248. package/dist/components/renderers/roof/roof-materials.js +0 -16
  249. package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
  250. package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
  251. package/dist/components/renderers/roof/roof-renderer.js +0 -32
  252. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
  253. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
  254. package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
  255. package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
  256. package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
  257. package/dist/components/renderers/scan/scan-renderer.js +0 -54
  258. package/dist/components/renderers/site/site-renderer.d.ts +0 -5
  259. package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
  260. package/dist/components/renderers/site/site-renderer.js +0 -106
  261. package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
  262. package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
  263. package/dist/components/renderers/slab/slab-renderer.js +0 -58
  264. package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
  265. package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
  266. package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
  267. package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
  268. package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
  269. package/dist/components/renderers/stair/stair-renderer.js +0 -603
  270. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
  271. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
  272. package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -51
  273. package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
  274. package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
  275. package/dist/components/renderers/wall/wall-renderer.js +0 -16
  276. package/dist/components/renderers/window/window-renderer.d.ts +0 -5
  277. package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
  278. package/dist/components/renderers/window/window-renderer.js +0 -21
  279. package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
  280. package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
  281. package/dist/components/renderers/zone/zone-renderer.js +0 -181
  282. package/dist/components/viewer/ground-occluder.d.ts +0 -2
  283. package/dist/components/viewer/ground-occluder.d.ts.map +0 -1
  284. package/dist/components/viewer/ground-occluder.js +0 -79
@@ -1,6 +1,7 @@
1
- import { sceneRegistry, useScene } from '@pascal-app/core';
1
+ import { getEffectiveNode, nodeRegistry, sceneRegistry, useLiveTransforms, useScene, } from '@pascal-app/core';
2
2
  import { useFrame } from '@react-three/fiber';
3
3
  import * as THREE from 'three';
4
+ import { mergeSurfaceHolePolygons } from '../surface-hole-geometry';
4
5
  function ensureUv2Attribute(geometry) {
5
6
  const uv = geometry.getAttribute('uv');
6
7
  if (!uv)
@@ -20,11 +21,16 @@ export const CeilingSystem = () => {
20
21
  // Process dirty ceilings
21
22
  dirtyNodes.forEach((id) => {
22
23
  const node = nodes[id];
23
- if (!node || node.type !== 'ceiling')
24
+ if (node?.type !== 'ceiling')
24
25
  return;
25
26
  const mesh = sceneRegistry.nodes.get(id);
26
27
  if (mesh) {
27
- updateCeilingGeometry(node, mesh);
28
+ // Merge any live drag override so the polygon / height resize
29
+ // arrow rebuilds the mesh at pointer rate — zustand only learns
30
+ // the final value on commit. Mirrors WallSystem / GeometrySystem.
31
+ const effective = getEffectiveNode(node);
32
+ const itemHoles = collectCeilingHoles(effective, nodes);
33
+ updateCeilingGeometry(effective, mesh, itemHoles);
28
34
  clearDirty(id);
29
35
  }
30
36
  // If mesh not found, keep it dirty for next frame
@@ -32,28 +38,73 @@ export const CeilingSystem = () => {
32
38
  });
33
39
  return null;
34
40
  };
41
+ /**
42
+ * Collects ceiling-hole polygons from child nodes that declare the `ceilingCut`
43
+ * capability. Each child's `buildCeilingHole` returns a rotated-rectangle
44
+ * footprint in ceiling-local [x, z] space (or `null` to opt out), which is
45
+ * merged as an extra hole before triangulation.
46
+ *
47
+ * The viewer never branches on `child.type` — the dispatch goes through
48
+ * `nodeRegistry`, so any future kind (a heat lamp, a skylight panel, …) can
49
+ * participate just by declaring `capabilities.ceilingCut` on its definition.
50
+ */
51
+ function collectCeilingHoles(ceiling, nodes) {
52
+ const holes = [];
53
+ for (const childId of ceiling.children ?? []) {
54
+ const child = nodes[childId];
55
+ if (!child)
56
+ continue;
57
+ const def = nodeRegistry.get(child.type);
58
+ const hole = def?.capabilities?.ceilingCut?.buildCeilingHole(child);
59
+ if (hole)
60
+ holes.push(hole);
61
+ }
62
+ return holes;
63
+ }
35
64
  /**
36
65
  * Updates the geometry for a single ceiling
37
66
  */
38
- function updateCeilingGeometry(node, mesh) {
39
- const newGeo = generateCeilingGeometry(node);
67
+ function updateCeilingGeometry(node, mesh, extraHoles = []) {
68
+ const newGeo = generateCeilingGeometry(node, extraHoles);
40
69
  mesh.geometry.dispose();
41
70
  mesh.geometry = newGeo;
42
71
  const gridMesh = mesh.getObjectByName('ceiling-grid');
43
72
  if (gridMesh) {
44
73
  gridMesh.geometry.dispose();
45
- gridMesh.geometry = newGeo;
74
+ gridMesh.geometry = newGeo.clone();
46
75
  }
47
- // Position at the ceiling height
48
- mesh.position.y = (node.height ?? 2.5) - 0.01; // Slight offset to avoid z-fighting with upper-level slabs
76
+ // Position at the ceiling height and reset X/Z so live-drag mesh
77
+ // offsets (set by move tools during the drag) don't leak into the
78
+ // canonical position after the rebuild. Matches the pattern used by
79
+ // FenceSystem.updateFenceGeometry / GeometrySystem (both fully reset
80
+ // position+rotation after rebuild).
81
+ const liveTransform = useLiveTransforms.getState().get(node.id);
82
+ mesh.position.x = liveTransform?.position[0] ?? 0;
83
+ mesh.position.z = liveTransform?.position[2] ?? 0;
84
+ mesh.position.y = (node.height ?? 2.5) - 0.01 + (liveTransform?.position[1] ?? 0); // Slight offset to avoid z-fighting with upper-level slabs
49
85
  }
50
86
  /**
51
- * Generates flat ceiling geometry from polygon (no extrusion)
87
+ * Generates flat ceiling geometry from polygon (no extrusion).
88
+ *
89
+ * `extraHoles` are transient, derived cutouts (e.g. recessed-fixture
90
+ * footprints) that are cut alongside the node's persisted `holes` but never
91
+ * stored on the node — they are recomputed on every rebuild.
52
92
  */
53
- export function generateCeilingGeometry(ceilingNode) {
93
+ export function generateCeilingGeometry(ceilingNode, extraHoles = []) {
54
94
  const polygon = ceilingNode.polygon;
55
95
  if (polygon.length < 3) {
56
- return new THREE.BufferGeometry();
96
+ // A degenerate ceiling (fewer than 3 points, e.g. mid-edit) still gets a
97
+ // non-empty position buffer — three zero-vertices forming one invisible
98
+ // triangle. An empty attribute (count 0) would leave WebGPU vertex buffer
99
+ // slot 0 unbound when this mesh (and its cloned grid overlay) is drawn,
100
+ // which the validator rejects ("slot 0 … was not set") and which poisons
101
+ // the whole command encoder.
102
+ const degenerate = new THREE.BufferGeometry();
103
+ degenerate.setAttribute('position', new THREE.Float32BufferAttribute(new Float32Array(9), 3));
104
+ degenerate.setAttribute('normal', new THREE.Float32BufferAttribute(new Float32Array(9), 3));
105
+ degenerate.setAttribute('uv', new THREE.Float32BufferAttribute(new Float32Array(6), 2));
106
+ degenerate.setAttribute('uv2', new THREE.Float32BufferAttribute(new Float32Array(6), 2));
107
+ return degenerate;
57
108
  }
58
109
  // Create shape from polygon
59
110
  // Shape is in X-Y plane, we'll rotate to X-Z plane
@@ -66,8 +117,10 @@ export function generateCeilingGeometry(ceilingNode) {
66
117
  shape.lineTo(pt[0], -pt[1]);
67
118
  }
68
119
  shape.closePath();
69
- // Add holes to the shape
70
- const holes = ceilingNode.holes || [];
120
+ // Add holes to the shape: persisted structural openings (stair/elevator/
121
+ // manual, merged to dissolve overlaps) plus transient recessed-fixture
122
+ // cutouts. Both are in the same ceiling-local [x, z] space.
123
+ const holes = [...mergeSurfaceHolePolygons(ceilingNode.holes || []), ...extraHoles];
71
124
  for (const holePolygon of holes) {
72
125
  if (holePolygon.length < 3)
73
126
  continue;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=door-animation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"door-animation.test.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-animation.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ // @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
2
+ // depend on @types/bun so the import type is unresolved at compile time.
3
+ import { describe, expect, test } from 'bun:test';
4
+ import { DoorNode } from '@pascal-app/core';
5
+ import * as THREE from 'three';
6
+ import { buildDoorPreviewMesh, poseDoorMovingParts } from '../../index';
7
+ // The named moving group each operation-door builder emits at the closed pose.
8
+ const MOVING_GROUP = {
9
+ sliding: 'door-sliding-active',
10
+ pocket: 'door-pocket-leaf',
11
+ barn: 'door-barn-leaf',
12
+ 'garage-tiltup': 'door-tiltup-leaf',
13
+ folding: 'door-fold-0',
14
+ 'garage-sectional': 'door-sectional-0',
15
+ 'garage-rollup': 'door-rollup-curtain',
16
+ };
17
+ function worldPosition(object) {
18
+ object.updateMatrixWorld(true);
19
+ return object.getWorldPosition(new THREE.Vector3());
20
+ }
21
+ describe('operation door open kinematics', () => {
22
+ // Each operation door builds its moving parts in a named group at the closed
23
+ // pose; `poseDoorMovingParts` then drives the open motion (the single source
24
+ // of truth shared by the live system and the GLB exporter). This guards the
25
+ // build-once + pose-at-t contract: the group exists, rests closed, and moves.
26
+ for (const doorType of Object.keys(MOVING_GROUP)) {
27
+ test(`${doorType} builds a moving group that opens`, () => {
28
+ const node = DoorNode.parse({ id: `door_${doorType}`, doorType, operationState: 0 });
29
+ const mesh = buildDoorPreviewMesh(node);
30
+ const group = mesh.getObjectByName(MOVING_GROUP[doorType]);
31
+ expect(group).toBeDefined();
32
+ // Closed snapshot.
33
+ poseDoorMovingParts(node, mesh, 0);
34
+ const closedPos = worldPosition(group);
35
+ const closedScaleY = group.scale.y;
36
+ const closedRotX = group.rotation.x;
37
+ const closedRotY = group.rotation.y;
38
+ // Open snapshot — at least one of translation / rotation / scale changes.
39
+ poseDoorMovingParts(node, mesh, 1);
40
+ const openPos = worldPosition(group);
41
+ const moved = closedPos.distanceTo(openPos) > 0.05 ||
42
+ Math.abs(closedScaleY - group.scale.y) > 0.1 ||
43
+ Math.abs(closedRotX - group.rotation.x) > 0.05 ||
44
+ Math.abs(closedRotY - group.rotation.y) > 0.05;
45
+ expect(moved).toBe(true);
46
+ // Posing back to closed is the identity of the rest pose.
47
+ poseDoorMovingParts(node, mesh, 0);
48
+ expect(worldPosition(group).distanceTo(closedPos)).toBeLessThan(1e-6);
49
+ });
50
+ }
51
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=door-floor-alignment.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"door-floor-alignment.test.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-floor-alignment.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ // @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
2
+ // depend on @types/bun so the import type is unresolved at compile time.
3
+ import { describe, expect, test } from 'bun:test';
4
+ import { DoorNode } from '@pascal-app/core';
5
+ import * as THREE from 'three';
6
+ import { buildDoorPreviewMesh } from '../../index';
7
+ const DOOR_TYPES = [
8
+ 'hinged',
9
+ 'double',
10
+ 'french',
11
+ 'folding',
12
+ 'pocket',
13
+ 'barn',
14
+ 'sliding',
15
+ 'garage-sectional',
16
+ 'garage-rollup',
17
+ 'garage-tiltup',
18
+ ];
19
+ function visibleBounds(mesh) {
20
+ mesh.updateMatrixWorld(true);
21
+ const bounds = new THREE.Box3();
22
+ for (const child of mesh.children) {
23
+ if (child.name === 'cutout')
24
+ continue;
25
+ bounds.expandByObject(child, true);
26
+ }
27
+ return bounds;
28
+ }
29
+ describe('door floor alignment', () => {
30
+ for (const doorType of DOOR_TYPES) {
31
+ test(`${doorType} does not extend below the opening floor`, () => {
32
+ const node = DoorNode.parse({
33
+ id: `door_floor-alignment-${doorType}`,
34
+ doorType,
35
+ operationState: 0,
36
+ threshold: true,
37
+ });
38
+ const mesh = buildDoorPreviewMesh(node);
39
+ const bounds = visibleBounds(mesh);
40
+ expect(bounds.min.y).toBeGreaterThanOrEqual(-node.height / 2 - 1e-6);
41
+ });
42
+ }
43
+ test('keeps the wall cutout bottom locked to the opening floor', () => {
44
+ const node = DoorNode.parse({ id: 'door_floor-alignment-cutout' });
45
+ const mesh = buildDoorPreviewMesh(node);
46
+ const cutout = mesh.getObjectByName('cutout');
47
+ cutout.geometry.computeBoundingBox();
48
+ expect(cutout.geometry.boundingBox?.min.y).toBeCloseTo(-node.height / 2, 6);
49
+ });
50
+ });
@@ -1,2 +1,26 @@
1
+ import { type DoorNode } from '@pascal-app/core';
2
+ import * as THREE from 'three';
1
3
  export declare const DoorSystem: () => null;
4
+ /**
5
+ * Pose an operation door's moving parts (sliding/pocket/barn leaf, tilt-up and
6
+ * sectional panels, folding chain, roll-up curtain) at `value` (0 = closed,
7
+ * 1 = open) by transforming the named groups its builder emitted at the closed
8
+ * pose. Returns true when the door type has a pose path and its groups exist.
9
+ *
10
+ * Mirrors `poseWindowMovingParts`: the live door system calls it after a
11
+ * (re)build so `operationState` is reflected, and the GLB exporter calls it on a
12
+ * clone to sample keyframes. Swing doors are not handled here — their leaf group
13
+ * carries a `pascalSwingLeaf` marker the exporter reads directly.
14
+ *
15
+ * Roll-up is the one type whose live geometry changes (slats vanish onto a
16
+ * drum), which a glTF clip can't express; the baked approximation scales the
17
+ * curtain up into the lintel. The live roll-up animation keeps its full-detail
18
+ * rebuild and is intentionally NOT routed through this scale.
19
+ */
20
+ export declare function poseDoorMovingParts(node: DoorNode, mesh: THREE.Object3D | undefined, value: number): boolean;
21
+ /**
22
+ * Build a fresh door mesh for preview/ghost rendering.
23
+ * Returns a mesh with an invisible hitbox root and visible children (frame, panels, hardware).
24
+ */
25
+ export declare function buildDoorPreviewMesh(node: DoorNode): THREE.Mesh;
2
26
  //# sourceMappingURL=door-system.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAiBA,eAAO,MAAM,UAAU,YA2BtB,CAAA"}
1
+ {"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAYd,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA0D9B,eAAO,MAAM,UAAU,YA6GtB,CAAA;AAupCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,KAAK,CAAC,QAAQ,GAAG,SAAS,EAChC,KAAK,EAAE,MAAM,GACZ,OAAO,CA6GT;AAyuCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC,IAAI,CAI/D"}