@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
@@ -0,0 +1,10 @@
1
+ import { type AnyNode } from '@pascal-app/core';
2
+ /** Where a window mesh keeps the corner side its frame was built fused on (-1, 0 or 1). */
3
+ export declare const FUSED_CORNER_SIDE = "fusedCornerSide";
4
+ /**
5
+ * Whether a window's frame was built for a corner pair that has since been made or broken without
6
+ * the window changing (its partner deleted or moved, a wall moved apart): a build reads the pair
7
+ * once, so the window rebuilds.
8
+ */
9
+ export declare function cornerFrameStale(node: AnyNode, nodes: Readonly<Record<string, AnyNode>>): boolean;
10
+ //# sourceMappingURL=corner-frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"corner-frame.d.ts","sourceRoot":"","sources":["../../../src/systems/window/corner-frame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAA4C,MAAM,kBAAkB,CAAA;AAEzF,2FAA2F;AAC3F,eAAO,MAAM,iBAAiB,oBAAoB,CAAA;AAElD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAMjG"}
@@ -0,0 +1,17 @@
1
+ import { cornerPair, fusesAtCorner, sceneRegistry } from '@pascal-app/core';
2
+ /** Where a window mesh keeps the corner side its frame was built fused on (-1, 0 or 1). */
3
+ export const FUSED_CORNER_SIDE = 'fusedCornerSide';
4
+ /**
5
+ * Whether a window's frame was built for a corner pair that has since been made or broken without
6
+ * the window changing (its partner deleted or moved, a wall moved apart): a build reads the pair
7
+ * once, so the window rebuilds.
8
+ */
9
+ export function cornerFrameStale(node, nodes) {
10
+ if (node.type !== 'window')
11
+ return false;
12
+ const mesh = sceneRegistry.nodes.get(node.id);
13
+ if (!mesh)
14
+ return false;
15
+ const fused = cornerPair(nodes, node)?.post === 'none' && fusesAtCorner(node);
16
+ return Boolean(mesh.userData[FUSED_CORNER_SIDE]) !== fused;
17
+ }
@@ -10,7 +10,7 @@ export declare const DOUBLE_HUNG_BOTTOM_SASH_NAME = "double-hung-bottom-sash";
10
10
  export declare const LOUVERED_WINDOW_SLATS_NAME = "louvered-window-slats";
11
11
  export declare const AWNING_WINDOW_SASH_NAME = "awning-window-sash";
12
12
  export declare const HOPPER_WINDOW_SASH_NAME = "hopper-window-sash";
13
- export declare const pendingWindowAnimationRebuilds: Set<`building_${string}` | `level_${string}` | `elevator_${string}` | `unit_${string}` | `block_${string}` | `ceiling_${string}` | `item_${string}` | `procedural-item_${string}` | `column_${string}` | `construction-dimension_${string}` | `wall_${string}` | `roof_${string}` | `slab_${string}` | `fence_${string}` | `structural-grid_${string}` | `imesh_${string}` | `zone_${string}` | `stair_${string}` | `scan_${string}` | `guide_${string}` | `measurement_${string}` | `spawn_${string}` | `shelf_${string}` | `duct-segment_${string}` | `duct-fitting_${string}` | `duct-terminal_${string}` | `hvac-equipment_${string}` | `lineset_${string}` | `liquid-line_${string}` | `pipe-segment_${string}` | `pipe-fitting_${string}` | `pipe-trap_${string}` | `site_${string}` | `leanto_${string}` | `door_${string}` | `window_${string}` | `cabinet_${string}` | `cabinet-module_${string}` | `rseg_${string}` | `sseg_${string}` | `bvent_${string}` | `rvent_${string}` | `tvent_${string}` | `cupola_${string}` | `eyebrow-vent_${string}` | `gutter_${string}` | `chimney_${string}` | `solarpanel_${string}` | `skylight_${string}` | `dormer_${string}` | `downspout_${string}`>;
13
+ export declare const pendingWindowAnimationRebuilds: Set<`block_${string}` | `building_${string}` | `bvent_${string}` | `cabinet-module_${string}` | `cabinet_${string}` | `ceiling_${string}` | `chimney_${string}` | `column_${string}` | `construction-dimension_${string}` | `cupola_${string}` | `door_${string}` | `dormer_${string}` | `downspout_${string}` | `duct-fitting_${string}` | `duct-segment_${string}` | `duct-terminal_${string}` | `elevator_${string}` | `eyebrow-vent_${string}` | `fence-gate_${string}` | `fence-opening_${string}` | `fence_${string}` | `floor-opening_${string}` | `guide_${string}` | `gutter_${string}` | `hvac-equipment_${string}` | `imesh_${string}` | `item_${string}` | `leanto_${string}` | `level_${string}` | `lineset_${string}` | `liquid-line_${string}` | `measurement_${string}` | `pipe-fitting_${string}` | `pipe-segment_${string}` | `pipe-trap_${string}` | `procedural-item_${string}` | `roof_${string}` | `rseg_${string}` | `rvent_${string}` | `scan_${string}` | `separator_${string}` | `shelf_${string}` | `site_${string}` | `skylight_${string}` | `slab_${string}` | `solarpanel_${string}` | `spawn_${string}` | `sseg_${string}` | `stair_${string}` | `structural-grid_${string}` | `tvent_${string}` | `unit_${string}` | `wall_${string}` | `window_${string}` | `zone_${string}`>;
14
14
  export declare const WindowSystem: () => null;
15
15
  /**
16
16
  * Build a fresh window mesh for preview/ghost rendering.
@@ -1 +1 @@
1
- {"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAYL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyB9B,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,8BAA8B,8BAA8B,CAAA;AACzE,eAAO,MAAM,+BAA+B,+BAA+B,CAAA;AAC3E,eAAO,MAAM,gCAAgC,gCAAgC,CAAA;AAC7E,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AACjE,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAS3D,eAAO,MAAM,8BAA8B,uoCAAuB,CAAA;AAElE,eAAO,MAAM,YAAY,YAqHxB,CAAA;AA28GD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,KAAK,CAAC,IAAI,CAInE"}
1
+ {"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAeL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA4B9B,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,8BAA8B,8BAA8B,CAAA;AACzE,eAAO,MAAM,+BAA+B,+BAA+B,CAAA;AAC3E,eAAO,MAAM,gCAAgC,gCAAgC,CAAA;AAC7E,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AACjE,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAS3D,eAAO,MAAM,8BAA8B,gvCAAuB,CAAA;AAElE,eAAO,MAAM,YAAY,YA2HxB,CAAA;AA8+GD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,KAAK,CAAC,IAAI,CAInE"}
@@ -1,12 +1,14 @@
1
- import { DEFAULT_WALL_THICKNESS, getEffectiveNode, getWallThickness, sceneRegistry, useInteractive, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
1
+ import { cornerPair, DEFAULT_WALL_THICKNESS, getEffectiveNode, getOpeningWallPlacement, getWallThickness, sceneRegistry, scriptedSize, useInteractive, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
2
2
  import { useFrame } from '@react-three/fiber';
3
3
  import { useEffect, useRef } from 'react';
4
4
  import * as THREE from 'three';
5
5
  import { applyWorldScaleBoxUVs } from '../../lib/box-uv';
6
- import { createSurfaceRoleMaterial, glassMaterial as defaultGlassMaterial, baseMaterial as getBaseMaterial, resolveMaterialRef, } from '../../lib/materials';
6
+ import { createMaterial, createSurfaceRoleMaterial, glassMaterial as defaultGlassMaterial, baseMaterial as getBaseMaterial, resolveMaterialRef, } from '../../lib/materials';
7
7
  import { timeSpan } from '../../lib/perf-tracks';
8
+ import { settleScriptedOpening } from '../../lib/scripted-opening';
8
9
  import useViewer from '../../store/use-viewer';
9
10
  import { getOpeningCutoutProxyDepth } from '../wall/opening-cutout-geometry';
11
+ import { cornerFrameStale, FUSED_CORNER_SIDE } from './corner-frame';
10
12
  // Invisible material for root mesh — used as selection hitbox only
11
13
  const hitboxMaterial = new THREE.MeshBasicMaterial({ visible: false });
12
14
  let baseMaterial = getBaseMaterial();
@@ -128,11 +130,19 @@ export const WindowSystem = () => {
128
130
  // Merge any live override (width / height / position) so the mesh
129
131
  // rebuild reflects the in-flight drag without zustand churn.
130
132
  const effectiveNode = getEffectiveNode(node);
131
- timeSpan('window', () => updateWindowMesh(effectiveNode, mesh), {
133
+ const built = timeSpan('window', () => updateWindowMesh(effectiveNode, mesh), {
132
134
  properties: [['node', id]],
133
135
  });
136
+ // A scripted opening stays dirty until its artifact has loaded.
137
+ if (!built)
138
+ continue;
134
139
  clearDirty(id);
135
140
  rebuiltWindowsThisFrame += 1;
141
+ // This window's change can make or break its pair: the partner's frame follows.
142
+ const partnerId = node.corner?.partnerId;
143
+ const partner = partnerId ? nodes[partnerId] : undefined;
144
+ if (partner && cornerFrameStale(partner, nodes))
145
+ useScene.getState().markDirty(partner.id);
136
146
  // Rebuild the parent wall so its cutout reflects the updated window geometry
137
147
  // Avoid triggering expensive wall CSG rebuilds while the window is being interactively moved/duplicated.
138
148
  // The editor tools will request a final wall rebuild on commit.
@@ -192,16 +202,19 @@ function windowSlotDefault(slotId) {
192
202
  getBaseMaterial(currentShading));
193
203
  }
194
204
  // Resolve a window's slot to a material: the `node.slots` override (colored mode
195
- // only) → the role/base default. Textures-off ignores overrides — the monochrome
196
- // escape hatch.
205
+ // only) → the window-wide `material` for the frame (glass keeps its own) → the
206
+ // role/base default. Textures-off ignores overrides — the monochrome escape hatch.
197
207
  function resolveWindowSlotMaterial(node, slotId) {
198
208
  const fallback = windowSlotDefault(slotId);
199
209
  if (!currentTextures)
200
210
  return fallback;
201
211
  const ref = node.slots?.[slotId];
202
- if (!ref)
203
- return fallback;
204
- return resolveMaterialRef(ref, currentSceneMaterials, currentShading) ?? fallback;
212
+ const painted = ref ? resolveMaterialRef(ref, currentSceneMaterials, currentShading) : null;
213
+ if (painted)
214
+ return painted;
215
+ if (node.material && slotId === 'frame')
216
+ return createMaterial(node.material, currentShading);
217
+ return fallback;
205
218
  }
206
219
  function addBox(parent, material, w, h, d, x, y, z) {
207
220
  const geometry = new THREE.BoxGeometry(w, h, d);
@@ -1519,15 +1532,39 @@ function addShapedLouveredWindowVisuals(node, mesh) {
1519
1532
  addBox(mesh, baseMaterial, sillW, sillThickness, sillDepth, 0, -height / 2 - sillThickness / 2, sillZ);
1520
1533
  }
1521
1534
  }
1535
+ /**
1536
+ * The side of a corner window fused at the corner, in the window's own frame: 1 its +x side, -1 its
1537
+ * -x side, 0 none. A window facing its wall's back is turned half round, so its +x then points to
1538
+ * the wall's start.
1539
+ */
1540
+ function fusedCornerSide(node, rotationY) {
1541
+ const pair = cornerPair(useScene.getState().nodes, node);
1542
+ if (pair?.post !== 'none')
1543
+ return 0;
1544
+ const flipped = Math.abs(rotationY) > Math.PI / 2;
1545
+ return (pair.end === 'end') !== flipped ? 1 : -1;
1546
+ }
1522
1547
  function updateWindowMesh(node, mesh) {
1523
1548
  currentWindowSlot = undefined;
1549
+ mesh.userData[FUSED_CORNER_SIDE] = 0;
1524
1550
  // Root mesh is an invisible hitbox; all visuals live in child meshes
1525
1551
  mesh.geometry.dispose();
1526
1552
  mesh.geometry = new THREE.BoxGeometry(node.width, node.height, node.frameDepth);
1527
1553
  mesh.material = hitboxMaterial;
1528
1554
  // Sync transform from node (React may lag behind the system by a frame during drag)
1529
- mesh.position.set(node.position[0], node.position[1], node.position[2]);
1530
- mesh.rotation.set(node.rotation[0], node.rotation[1], node.rotation[2]);
1555
+ const parent = node.parentId ? useScene.getState().nodes[node.parentId] : undefined;
1556
+ const placement = parent?.type === 'wall' && !node.roofSegmentId
1557
+ ? getOpeningWallPlacement(getEffectiveNode(parent), node, useScene.getState().nodes)
1558
+ : node;
1559
+ mesh.position.set(...placement.position);
1560
+ mesh.rotation.set(...placement.rotation);
1561
+ // Built from a script: the renderer shows its artifact, not the parametric
1562
+ // frame, and the hit box is what the script built.
1563
+ if (node.source) {
1564
+ mesh.geometry.dispose();
1565
+ mesh.geometry = new THREE.BoxGeometry(...scriptedSize(node.source.manifest));
1566
+ return settleScriptedOpening(mesh);
1567
+ }
1531
1568
  // Dispose and remove all old visual children; preserve 'cutout'
1532
1569
  for (const child of [...mesh.children]) {
1533
1570
  if (child.name === 'cutout')
@@ -1543,64 +1580,68 @@ function updateWindowMesh(node, mesh) {
1543
1580
  const { width, height, frameDepth, frameThickness, columnRatios, rowRatios, columnDividerThickness, rowDividerThickness, sill, sillDepth, sillThickness, openingKind, openingShape, windowType, } = node;
1544
1581
  if (openingKind === 'opening') {
1545
1582
  syncWindowCutout(node, mesh);
1546
- return;
1583
+ return true;
1547
1584
  }
1548
1585
  if (windowType === 'sliding') {
1549
1586
  addSlidingWindowVisuals(node, mesh);
1550
1587
  syncWindowCutout(node, mesh);
1551
- return;
1588
+ return true;
1552
1589
  }
1553
1590
  if (windowType === 'casement') {
1554
1591
  addCasementWindowVisuals(node, mesh);
1555
1592
  syncWindowCutout(node, mesh);
1556
- return;
1593
+ return true;
1557
1594
  }
1558
1595
  if (windowType === 'awning') {
1559
1596
  addAwningWindowVisuals(node, mesh);
1560
1597
  syncWindowCutout(node, mesh);
1561
- return;
1598
+ return true;
1562
1599
  }
1563
1600
  if (windowType === 'hopper') {
1564
1601
  addAwningWindowVisuals(node, mesh);
1565
1602
  syncWindowCutout(node, mesh);
1566
- return;
1603
+ return true;
1567
1604
  }
1568
1605
  if (windowType === 'single-hung') {
1569
1606
  addSingleHungWindowVisuals(node, mesh);
1570
1607
  syncWindowCutout(node, mesh);
1571
- return;
1608
+ return true;
1572
1609
  }
1573
1610
  if (windowType === 'double-hung') {
1574
1611
  addDoubleHungWindowVisuals(node, mesh);
1575
1612
  syncWindowCutout(node, mesh);
1576
- return;
1613
+ return true;
1577
1614
  }
1578
1615
  if (windowType === 'bay') {
1579
1616
  addBayWindowVisuals(node, mesh);
1580
1617
  syncWindowCutout(node, mesh);
1581
- return;
1618
+ return true;
1582
1619
  }
1583
1620
  if (windowType === 'bow') {
1584
1621
  addBowWindowVisuals(node, mesh);
1585
1622
  syncWindowCutout(node, mesh);
1586
- return;
1623
+ return true;
1587
1624
  }
1588
1625
  if (windowType === 'louvered') {
1589
1626
  addLouveredWindowVisuals(node, mesh);
1590
1627
  syncWindowCutout(node, mesh);
1591
- return;
1628
+ return true;
1592
1629
  }
1593
1630
  if (openingShape === 'arch') {
1594
1631
  addArchedWindowVisuals(node, mesh);
1595
1632
  syncWindowCutout(node, mesh);
1596
- return;
1633
+ return true;
1597
1634
  }
1598
1635
  if (openingShape === 'rounded') {
1599
1636
  addRoundedWindowVisuals(node, mesh);
1600
1637
  syncWindowCutout(node, mesh);
1601
- return;
1638
+ return true;
1602
1639
  }
1603
- const innerW = width - 2 * frameThickness;
1640
+ // A corner window fused at the corner: no jamb on that side, the glass runs to the corner.
1641
+ const fused = fusedCornerSide(node, placement.rotation[1]);
1642
+ mesh.userData[FUSED_CORNER_SIDE] = fused;
1643
+ const innerW = width - (fused ? 1 : 2) * frameThickness;
1644
+ const innerX = (fused * frameThickness) / 2;
1604
1645
  const innerH = height - 2 * frameThickness;
1605
1646
  // ── Frame members ──
1606
1647
  // Top / bottom — full width
@@ -1608,8 +1649,10 @@ function updateWindowMesh(node, mesh) {
1608
1649
  addBox(mesh, baseMaterial, width, frameThickness, frameDepth, 0, height / 2 - frameThickness / 2, 0);
1609
1650
  addBox(mesh, baseMaterial, width, frameThickness, frameDepth, 0, -height / 2 + frameThickness / 2, 0);
1610
1651
  // Left / right — inner height to avoid corner overlap
1611
- addBox(mesh, baseMaterial, frameThickness, innerH, frameDepth, -width / 2 + frameThickness / 2, 0, 0);
1612
- addBox(mesh, baseMaterial, frameThickness, innerH, frameDepth, width / 2 - frameThickness / 2, 0, 0);
1652
+ if (fused !== -1)
1653
+ addBox(mesh, baseMaterial, frameThickness, innerH, frameDepth, -width / 2 + frameThickness / 2, 0, 0);
1654
+ if (fused !== 1)
1655
+ addBox(mesh, baseMaterial, frameThickness, innerH, frameDepth, width / 2 - frameThickness / 2, 0, 0);
1613
1656
  // ── Pane grid ──
1614
1657
  const numCols = columnRatios.length;
1615
1658
  const numRows = rowRatios.length;
@@ -1621,7 +1664,7 @@ function updateWindowMesh(node, mesh) {
1621
1664
  const rowHeights = rowRatios.map((r) => (r / rowSum) * usableH);
1622
1665
  // Compute column x-centers starting from left edge of inner area
1623
1666
  const colXCenters = [];
1624
- let cx = -innerW / 2;
1667
+ let cx = innerX - innerW / 2;
1625
1668
  for (let c = 0; c < numCols; c++) {
1626
1669
  colXCenters.push(cx + colWidths[c] / 2);
1627
1670
  cx += colWidths[c];
@@ -1638,7 +1681,7 @@ function updateWindowMesh(node, mesh) {
1638
1681
  cy -= rowDividerThickness;
1639
1682
  }
1640
1683
  // Column dividers — full inner height
1641
- cx = -innerW / 2;
1684
+ cx = innerX - innerW / 2;
1642
1685
  currentWindowSlot = 'frame';
1643
1686
  for (let c = 0; c < numCols - 1; c++) {
1644
1687
  cx += colWidths[c];
@@ -1673,6 +1716,7 @@ function updateWindowMesh(node, mesh) {
1673
1716
  addBox(mesh, baseMaterial, sillW, sillThickness, sillDepth, 0, -height / 2 - sillThickness / 2, sillZ);
1674
1717
  }
1675
1718
  syncWindowCutout(node, mesh);
1719
+ return true;
1676
1720
  }
1677
1721
  function syncWindowCutout(node, mesh) {
1678
1722
  // ── Cutout: invisible raycast hit target for the whole opening ──
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pascal-app/viewer",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "3D viewer component for Pascal building editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -33,7 +33,7 @@
33
33
  "prepublishOnly": "npm run build"
34
34
  },
35
35
  "peerDependencies": {
36
- "@pascal-app/core": "^1.0.3",
36
+ "@pascal-app/core": "^1.0.5",
37
37
  "@react-three/drei": "^10",
38
38
  "@react-three/fiber": "^9",
39
39
  "react": "^18 || ^19",
@@ -42,18 +42,18 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "three-bvh-csg": "^0.0.18",
45
- "three-mesh-bvh": "^0.9.8",
45
+ "three-mesh-bvh": "~0.9.16",
46
46
  "zustand": "^5"
47
47
  },
48
48
  "devDependencies": {
49
- "@pascal-app/core": "^1.0.3",
49
+ "@pascal-app/core": "^1.0.5",
50
50
  "@pascal/typescript-config": "*",
51
- "@react-three/test-renderer": "^9.1.0",
52
- "@types/node": "^22",
53
- "@types/react": "^19.2.2",
54
- "@types/react-dom": "19.2.2",
55
- "@types/three": "^0.184.0",
56
- "typescript": "6.0.3"
51
+ "@react-three/test-renderer": "^9.1.1",
52
+ "@types/node": "^26",
53
+ "@types/react": "19.3.0",
54
+ "@types/react-dom": "19.3.0",
55
+ "@types/three": "^0.186.0",
56
+ "typescript": "7.0.2"
57
57
  },
58
58
  "keywords": [
59
59
  "3d",
@@ -1,22 +0,0 @@
1
- import { type FenceNode } from '@pascal-app/core';
2
- import * as THREE from 'three';
3
- type FencePart = {
4
- geometry?: THREE.BufferGeometry;
5
- position: [number, number, number];
6
- rotationY?: number;
7
- scale: [number, number, number];
8
- shape?: 'box' | 'pyramid';
9
- };
10
- export type FenceSlotId = 'posts' | 'infill' | 'base' | 'rail';
11
- export type FenceSlotParts = Record<FenceSlotId, FencePart[]>;
12
- /**
13
- * Geometry split by paint slot — posts, infill, base, rail — each a separate
14
- * merged BufferGeometry (empty ones included) so the fence renderer can give
15
- * each its own material + `userData.slotId`. Slots match the panel's build
16
- * options 1:1.
17
- */
18
- export declare function generateFenceSlotGeometries(fence: FenceNode): Record<FenceSlotId, THREE.BufferGeometry>;
19
- export declare function generateFenceGeometry(fence: FenceNode): THREE.BufferGeometry<THREE.NormalBufferAttributes, THREE.BufferGeometryEventMap>;
20
- export declare const FenceSystem: () => null;
21
- export {};
22
- //# sourceMappingURL=fence-system.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fence-system.d.ts","sourceRoot":"","sources":["../../../src/systems/fence/fence-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EAKf,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,KAAK,SAAS,GAAG;IACf,QAAQ,CAAC,EAAE,KAAK,CAAC,cAAc,CAAA;IAC/B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAG/B,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;CAC1B,CAAA;AAqPD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;AAE9D,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,EAAE,SAAS,EAAE,CAAC,CAAA;AAsQ7D;;;;;GAKG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,SAAS,GACf,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,cAAc,CAAC,CAQ3C;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,oFAGrD;AAgBD,eAAO,MAAM,WAAW,YAiBvB,CAAA"}