@pascal-app/viewer 1.0.4 → 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 (183) 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 +4 -1
  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.map +1 -1
  9. package/dist/components/viewer/glb-interactive.js +1 -1
  10. package/dist/components/viewer/glb-reference-nodes.js +1 -1
  11. package/dist/components/viewer/glb-replace-instances.js +1 -1
  12. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  13. package/dist/components/viewer/glb-scene.js +2 -3
  14. package/dist/components/viewer/glb-walkthrough-controller.js +1 -1
  15. package/dist/components/viewer/index.d.ts +10 -1
  16. package/dist/components/viewer/index.d.ts.map +1 -1
  17. package/dist/components/viewer/index.js +28 -12
  18. package/dist/components/viewer/lights.d.ts.map +1 -1
  19. package/dist/components/viewer/lights.js +107 -21
  20. package/dist/components/viewer/perf-panel.d.ts.map +1 -1
  21. package/dist/components/viewer/perf-panel.js +1 -1
  22. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  23. package/dist/components/viewer/post-processing.js +96 -33
  24. package/dist/components/viewer/registered-systems.js +1 -1
  25. package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
  26. package/dist/components/viewer/scene-bvh.js +1 -1
  27. package/dist/components/viewer/scene-environment.d.ts.map +1 -1
  28. package/dist/components/viewer/scene-environment.js +31 -11
  29. package/dist/components/viewer/selection-manager.js +15 -7
  30. package/dist/components/viewer/underground-ambiance.d.ts +9 -0
  31. package/dist/components/viewer/underground-ambiance.d.ts.map +1 -0
  32. package/dist/components/viewer/underground-ambiance.js +105 -0
  33. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  34. package/dist/components/viewer/viewer-camera.js +1 -1
  35. package/dist/components/viewer/viewer-presentations.d.ts.map +1 -1
  36. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  37. package/dist/hooks/use-gltf-ktx2.js +2 -0
  38. package/dist/hooks/use-node-events.d.ts.map +1 -1
  39. package/dist/index.d.ts +16 -7
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +16 -7
  42. package/dist/lib/backdrop.d.ts +17 -0
  43. package/dist/lib/backdrop.d.ts.map +1 -1
  44. package/dist/lib/backdrop.js +18 -1
  45. package/dist/lib/construction-dust.d.ts +57 -0
  46. package/dist/lib/construction-dust.d.ts.map +1 -0
  47. package/dist/lib/construction-dust.js +187 -0
  48. package/dist/lib/csg-utils.js +1 -1
  49. package/dist/lib/foundation-materials.d.ts +26 -0
  50. package/dist/lib/foundation-materials.d.ts.map +1 -0
  51. package/dist/lib/foundation-materials.js +65 -0
  52. package/dist/lib/gltf-unlit.d.ts +10 -0
  53. package/dist/lib/gltf-unlit.d.ts.map +1 -0
  54. package/dist/lib/gltf-unlit.js +23 -0
  55. package/dist/lib/ground-window.d.ts +33 -0
  56. package/dist/lib/ground-window.d.ts.map +1 -0
  57. package/dist/lib/ground-window.js +114 -0
  58. package/dist/lib/hero-pose.d.ts +1 -1
  59. package/dist/lib/hero-pose.d.ts.map +1 -1
  60. package/dist/lib/ink-edges.d.ts +2 -1
  61. package/dist/lib/ink-edges.d.ts.map +1 -1
  62. package/dist/lib/ktx2-loader.d.ts +7 -0
  63. package/dist/lib/ktx2-loader.d.ts.map +1 -1
  64. package/dist/lib/ktx2-loader.js +35 -0
  65. package/dist/lib/layer-pass.d.ts.map +1 -1
  66. package/dist/lib/layers.d.ts +11 -7
  67. package/dist/lib/layers.d.ts.map +1 -1
  68. package/dist/lib/layers.js +16 -6
  69. package/dist/lib/materials.d.ts +5 -0
  70. package/dist/lib/materials.d.ts.map +1 -1
  71. package/dist/lib/materials.js +34 -4
  72. package/dist/lib/merged-outline-node.d.ts +5 -0
  73. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  74. package/dist/lib/merged-outline-node.js +80 -2
  75. package/dist/lib/pointer-events.d.ts +20 -4
  76. package/dist/lib/pointer-events.d.ts.map +1 -1
  77. package/dist/lib/pointer-events.js +78 -14
  78. package/dist/lib/post-processing-resources.d.ts.map +1 -1
  79. package/dist/lib/reveal-candidates.d.ts +80 -0
  80. package/dist/lib/reveal-candidates.d.ts.map +1 -0
  81. package/dist/lib/reveal-candidates.js +524 -0
  82. package/dist/lib/reveal-ghost.d.ts +9 -0
  83. package/dist/lib/reveal-ghost.d.ts.map +1 -0
  84. package/dist/lib/reveal-ghost.js +62 -0
  85. package/dist/lib/reveal-pending.d.ts +12 -0
  86. package/dist/lib/reveal-pending.d.ts.map +1 -0
  87. package/dist/lib/reveal-pending.js +12 -0
  88. package/dist/lib/reveal-pose.d.ts +70 -0
  89. package/dist/lib/reveal-pose.d.ts.map +1 -0
  90. package/dist/lib/reveal-pose.js +248 -0
  91. package/dist/lib/reveal-schedule.d.ts +208 -0
  92. package/dist/lib/reveal-schedule.d.ts.map +1 -0
  93. package/dist/lib/reveal-schedule.js +410 -0
  94. package/dist/lib/reveal-texture-scale.d.ts +5 -0
  95. package/dist/lib/reveal-texture-scale.d.ts.map +1 -0
  96. package/dist/lib/reveal-texture-scale.js +113 -0
  97. package/dist/lib/reveal-windows.d.ts +23 -0
  98. package/dist/lib/reveal-windows.d.ts.map +1 -0
  99. package/dist/lib/reveal-windows.js +31 -0
  100. package/dist/lib/reveal-wipe.d.ts +71 -0
  101. package/dist/lib/reveal-wipe.d.ts.map +1 -0
  102. package/dist/lib/reveal-wipe.js +105 -0
  103. package/dist/lib/scene-pass-mrt.d.ts +12 -0
  104. package/dist/lib/scene-pass-mrt.d.ts.map +1 -0
  105. package/dist/lib/scene-pass-mrt.js +23 -0
  106. package/dist/lib/scene-themes.d.ts +30 -8
  107. package/dist/lib/scene-themes.d.ts.map +1 -1
  108. package/dist/lib/scene-themes.js +36 -0
  109. package/dist/lib/scene-visibility.d.ts +11 -1
  110. package/dist/lib/scene-visibility.d.ts.map +1 -1
  111. package/dist/lib/scene-visibility.js +38 -0
  112. package/dist/lib/site-surfaces.d.ts +32 -0
  113. package/dist/lib/site-surfaces.d.ts.map +1 -0
  114. package/dist/lib/site-surfaces.js +70 -0
  115. package/dist/lib/snapshot-pipeline.d.ts +2 -1
  116. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  117. package/dist/lib/snapshot-pipeline.js +35 -15
  118. package/dist/lib/tsl-compat.d.ts +2 -2
  119. package/dist/lib/tsl-compat.d.ts.map +1 -1
  120. package/dist/lib/underground-ambiance.d.ts +168 -0
  121. package/dist/lib/underground-ambiance.d.ts.map +1 -0
  122. package/dist/lib/underground-ambiance.js +334 -0
  123. package/dist/store/use-viewer.d.ts +184 -27
  124. package/dist/store/use-viewer.d.ts.map +1 -1
  125. package/dist/store/use-viewer.js +155 -19
  126. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  127. package/dist/systems/ceiling/ceiling-system.js +2 -0
  128. package/dist/systems/construction-reveal/construction-dust.d.ts +16 -0
  129. package/dist/systems/construction-reveal/construction-dust.d.ts.map +1 -0
  130. package/dist/systems/construction-reveal/construction-dust.js +92 -0
  131. package/dist/systems/construction-reveal/construction-reveal.d.ts +276 -0
  132. package/dist/systems/construction-reveal/construction-reveal.d.ts.map +1 -0
  133. package/dist/systems/construction-reveal/construction-reveal.js +2383 -0
  134. package/dist/systems/construction-reveal/dust-material.d.ts +10 -0
  135. package/dist/systems/construction-reveal/dust-material.d.ts.map +1 -0
  136. package/dist/systems/construction-reveal/dust-material.js +17 -0
  137. package/dist/systems/construction-reveal/ghost-dress.d.ts +10 -0
  138. package/dist/systems/construction-reveal/ghost-dress.d.ts.map +1 -0
  139. package/dist/systems/construction-reveal/ghost-dress.js +24 -0
  140. package/dist/systems/elevator/elevator-interaction-system.d.ts +7 -0
  141. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
  142. package/dist/systems/elevator/elevator-interaction-system.js +18 -1
  143. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  144. package/dist/systems/interactive/control-widget.d.ts.map +1 -1
  145. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  146. package/dist/systems/interactive/interactive-system.js +16 -6
  147. package/dist/systems/interactive/scripted-clips.js +1 -1
  148. package/dist/systems/item-light/item-light-system.js +1 -1
  149. package/dist/systems/level/level-hold.d.ts +4 -0
  150. package/dist/systems/level/level-hold.d.ts.map +1 -0
  151. package/dist/systems/level/level-hold.js +14 -0
  152. package/dist/systems/level/level-system.d.ts.map +1 -1
  153. package/dist/systems/level/level-system.js +16 -1
  154. package/dist/systems/level/level-utils.d.ts +13 -3
  155. package/dist/systems/level/level-utils.d.ts.map +1 -1
  156. package/dist/systems/level/level-utils.js +56 -8
  157. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts +103 -0
  158. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts.map +1 -0
  159. package/dist/systems/roof/__fixtures__/roof-plane-cases.js +317 -0
  160. package/dist/systems/roof/roof-materials.d.ts +4 -2
  161. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  162. package/dist/systems/roof/roof-materials.js +18 -24
  163. package/dist/systems/roof/roof-system.d.ts +8 -1
  164. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  165. package/dist/systems/roof/roof-system.js +195 -45
  166. package/dist/systems/wall/wall-cutout-cache.d.ts +32 -3
  167. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  168. package/dist/systems/wall/wall-cutout-cache.js +95 -2
  169. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  170. package/dist/systems/wall/wall-material-variant.d.ts.map +1 -1
  171. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  172. package/dist/systems/wall/wall-materials.js +39 -4
  173. package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -1
  174. package/dist/systems/wall/wall-system.d.ts +5 -0
  175. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  176. package/dist/systems/wall/wall-system.js +42 -19
  177. package/dist/systems/window/corner-frame.d.ts +10 -0
  178. package/dist/systems/window/corner-frame.d.ts.map +1 -0
  179. package/dist/systems/window/corner-frame.js +17 -0
  180. package/dist/systems/window/window-system.d.ts +1 -1
  181. package/dist/systems/window/window-system.d.ts.map +1 -1
  182. package/dist/systems/window/window-system.js +31 -6
  183. package/package.json +8 -8
@@ -1,4 +1,4 @@
1
- import { getConicalRoofCoverage, getDutchEndSlopeFaces, getDutchRoofShapeMetrics, getEffectiveNode, getRoofModuleFaces, getRoofPlanBounds, getRoofSegmentSurfaceY, getRoofShapeInsets, getRoofShapeRatios, getSegmentSlopeFrame, hasSegmentMaterialOverride, isBandedShedSegment, nodeRegistry, normalizeRoofSegmentTrim, pointInPolygon2D, ROOF_SHAPE_DEFAULTS, roofOverlapEntryOwns, roofPlanBoundsOverlap, sceneRegistry, unionPolygons, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
1
+ import { getBarrelRoofFaces, getBarrelRoofSolidPieces, getConicalRoofCoverage, getDutchEndSlopeFaces, getDutchRoofShapeMetrics, getEffectiveNode, getGlazedRoofFaces, getGlazedRoofPieceFaces, getGlazedRoofPieces, getRoofModuleFaces, getRoofPlanBounds, getRoofSegmentDeckReach, getRoofSegmentSurfaceY, getRoofShapeInsets, getRoofShapeRatios, getSegmentSlopeFrame, hasSegmentMaterialOverride, isBandedShedSegment, isGlazedRoofSegment, isIndependentRoofSegment, nodeRegistry, normalizeRoofSegmentTrim, pointInPolygon2D, ROOF_SHAPE_DEFAULTS, roofOverlapEntryOwns, roofPlanBoundsOverlap, sceneRegistry, unionPolygons, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
2
2
  import { useFrame } from '@react-three/fiber';
3
3
  import * as THREE from 'three';
4
4
  import { mergeGeometries, mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
@@ -6,6 +6,7 @@ import { ADDITION, Brush, Evaluator, SUBTRACTION } from 'three-bvh-csg';
6
6
  import { computeBoundsTree } from 'three-mesh-bvh';
7
7
  import { applyWorldScaleBoxUVs } from '../../lib/box-uv';
8
8
  import { ensureRenderableGeometryAttributes, subtractCsgBrush } from '../../lib/csg-utils';
9
+ import { isRevealAssembling } from '../construction-reveal/construction-reveal';
9
10
  function csgGeometry(brush) {
10
11
  return brush.geometry;
11
12
  }
@@ -20,7 +21,7 @@ csgEvaluator.attributes = ['position', 'normal', 'uv', 'uv2'];
20
21
  function computeGeometryBoundsTree(geometry) {
21
22
  ;
22
23
  geometry.computeBoundsTree = computeBoundsTree;
23
- geometry.computeBoundsTree({ maxLeafSize: 10 });
24
+ geometry.computeBoundsTree({ targetLeafSize: 10 });
24
25
  }
25
26
  function prepareBrushForCSG(brush) {
26
27
  ensureRenderableGeometryAttributes(brush.geometry);
@@ -108,10 +109,31 @@ function createDegenerateRoofPlaceholder() {
108
109
  }
109
110
  // Pending merged-roof updates carried across frames (for throttling)
110
111
  const pendingRoofUpdates = new Set();
112
+ const pendingIndependentSegments = new Set();
111
113
  const previousRoofPlanBounds = new Map();
112
114
  const warnedMergedRoofNaNIds = new Set();
113
115
  const MAX_ROOFS_PER_FRAME = 1;
114
116
  const MAX_SEGMENTS_PER_FRAME = 3;
117
+ /** What each roof's shell was last merged from: its node and its segments, as the store held them. */
118
+ const mergedFrom = new Map();
119
+ function rememberMergedInputs(roof, nodes) {
120
+ const inputs = new Map();
121
+ for (const id of [roof.id, ...(roof.children ?? [])]) {
122
+ const node = nodes[id];
123
+ if (node)
124
+ inputs.set(id, node);
125
+ }
126
+ mergedFrom.set(roof.id, inputs);
127
+ }
128
+ /** Whether nothing a roof's shell is made of has changed since it was merged. */
129
+ function mergedFromStillHolds(roofId, nodes) {
130
+ const inputs = mergedFrom.get(roofId);
131
+ const roof = nodes[roofId];
132
+ if (!inputs || roof?.type !== 'roof')
133
+ return false;
134
+ const ids = [roofId, ...(roof.children ?? [])];
135
+ return ids.length === inputs.size && ids.every((id) => inputs.get(id) === nodes[id]);
136
+ }
115
137
  /**
116
138
  * Roofs whose merged shell is still to build: until it is, the roof shows its segments apart (or
117
139
  * an old shell), and a capture would show that. Only roofs on screen count, a first merge (a
@@ -119,7 +141,7 @@ const MAX_SEGMENTS_PER_FRAME = 3;
119
141
  * capture.
120
142
  */
121
143
  export function getPendingRoofMergeCount() {
122
- return countPendingRoofShells(pendingRoofUpdates, (id) => sceneRegistry.nodes.get(id));
144
+ return countPendingRoofShells([...pendingRoofUpdates, ...pendingIndependentSegments], (id) => sceneRegistry.nodes.get(id));
123
145
  }
124
146
  export function countPendingRoofShells(pending, mounted) {
125
147
  let count = 0;
@@ -128,7 +150,19 @@ export function countPendingRoofShells(pending, mounted) {
128
150
  count++;
129
151
  return count;
130
152
  }
131
- function queueSiblingRoofUpdates(roofId, nodes) {
153
+ function queueSiblingRoofUpdates(roofId, nodes, changedSegmentId) {
154
+ const queueIndependent = (roof) => {
155
+ for (const id of roof.children) {
156
+ const segment = nodes[id];
157
+ if (segment?.type === 'roof-segment' &&
158
+ segment.id !== changedSegmentId &&
159
+ isIndependentRoofSegment(getEffectiveNode(segment)))
160
+ pendingIndependentSegments.add(segment.id);
161
+ }
162
+ };
163
+ const currentRoof = nodes[roofId];
164
+ if (currentRoof?.type === 'roof')
165
+ queueIndependent(currentRoof);
132
166
  pendingRoofUpdates.add(roofId);
133
167
  const roof = nodes[roofId]?.type === 'roof' ? getEffectiveNode(nodes[roofId]) : undefined;
134
168
  if (roof?.type !== 'roof' || !roof.parentId)
@@ -186,13 +220,11 @@ function queueSiblingRoofUpdates(roofId, nodes) {
186
220
  if ((currentBounds && roofPlanBoundsOverlap(currentBounds, siblingBounds)) ||
187
221
  (oldBounds && roofPlanBoundsOverlap(oldBounds, siblingBounds))) {
188
222
  pendingRoofUpdates.add(sibling.id);
223
+ queueIndependent(sibling);
189
224
  }
190
225
  }
191
226
  }
192
- // ============================================================================
193
- // ROOF SYSTEM
194
- // ============================================================================
195
- export const RoofSystem = () => {
227
+ export const RoofSystem = ({ geometryAdapter } = {}) => {
196
228
  const dirtyNodes = useScene((state) => state.dirtyNodes);
197
229
  const clearDirty = useScene((state) => state.clearDirty);
198
230
  const rootNodeIds = useScene((state) => state.rootNodeIds);
@@ -205,23 +237,30 @@ export const RoofSystem = () => {
205
237
  // Clear stale pending updates when the scene is unloaded
206
238
  if (rootNodeIds.length === 0) {
207
239
  pendingRoofUpdates.clear();
240
+ pendingIndependentSegments.clear();
208
241
  previousRoofPlanBounds.clear();
209
242
  warnedMergedRoofNaNIds.clear();
243
+ mergedFrom.clear();
210
244
  for (const cached of mergedRoofSegmentGeometryCache.values()) {
211
245
  disposeCachedMergedRoofSegmentGeometrySet(cached);
212
246
  }
213
247
  mergedRoofSegmentGeometryCache.clear();
214
248
  return;
215
249
  }
216
- if (dirtyNodes.size === 0 && pendingRoofUpdates.size === 0)
250
+ if (dirtyNodes.size === 0 &&
251
+ pendingRoofUpdates.size === 0 &&
252
+ pendingIndependentSegments.size === 0)
217
253
  return;
218
254
  const nodes = useScene.getState().nodes;
219
255
  // --- Pass 1: Process dirty roof-segments (throttled) ---
220
256
  let segmentsProcessed = 0;
221
- dirtyNodes.forEach((id) => {
257
+ new Set([...dirtyNodes, ...pendingIndependentSegments]).forEach((id) => {
258
+ const independentCascade = pendingIndependentSegments.has(id) && !dirtyNodes.has(id);
222
259
  const node = nodes[id];
223
- if (!node)
260
+ if (!node) {
261
+ pendingIndependentSegments.delete(id);
224
262
  return;
263
+ }
225
264
  // Cutting roof accessories cascade their dirty mark to the host
226
265
  // segment's parent roof so the merged shell re-CSGs with the new
227
266
  // cut. Non-cutting accessories (vents, panels, gutters, etc.) sit on
@@ -236,6 +275,8 @@ export const RoofSystem = () => {
236
275
  const seg = segId ? nodes[segId] : undefined;
237
276
  if (roofAccessory.buildCut && seg?.parentId) {
238
277
  pendingRoofUpdates.add(seg.parentId);
278
+ if (isIndependentRoofSegment(seg))
279
+ useScene.getState().markDirty(seg.id);
239
280
  }
240
281
  clearDirty(id);
241
282
  return;
@@ -261,7 +302,7 @@ export const RoofSystem = () => {
261
302
  const revealOnly = mesh.parent?.name === 'segments-wrapper' &&
262
303
  mesh.parent?.parent?.getObjectByName('merged-roof')?.visible === true;
263
304
  if (isVisible && !revealOnly && segmentsProcessed < MAX_SEGMENTS_PER_FRAME) {
264
- updateRoofSegmentGeometry(effectiveSegment, mesh, nodes);
305
+ updateRoofSegmentGeometry(effectiveSegment, mesh, nodes, geometryAdapter);
265
306
  segmentsProcessed++;
266
307
  }
267
308
  else if (isVisible && !revealOnly) {
@@ -284,9 +325,12 @@ export const RoofSystem = () => {
284
325
  else {
285
326
  clearDirty(id);
286
327
  }
287
- // Queue the parent roof for a merged geometry update
288
- if (effectiveSegment.parentId) {
289
- queueSiblingRoofUpdates(effectiveSegment.parentId, nodes);
328
+ pendingIndependentSegments.delete(id);
329
+ const parentId = effectiveSegment.parentId;
330
+ if (parentId &&
331
+ !independentCascade &&
332
+ !(isRevealAssembling(parentId) && mergedFromStillHolds(parentId, nodes))) {
333
+ queueSiblingRoofUpdates(parentId, nodes, id);
290
334
  }
291
335
  }
292
336
  else if (node.type === 'roof') {
@@ -302,6 +346,7 @@ export const RoofSystem = () => {
302
346
  const node = nodes[id];
303
347
  if (node?.type !== 'roof') {
304
348
  pendingRoofUpdates.delete(id);
349
+ mergedFrom.delete(id);
305
350
  continue;
306
351
  }
307
352
  const group = sceneRegistry.nodes.get(id);
@@ -310,11 +355,11 @@ export const RoofSystem = () => {
310
355
  const mergedMesh = group.getObjectByName('merged-roof');
311
356
  if (!mergedMesh)
312
357
  continue;
313
- if (mergedMesh.visible !== false) {
314
- // Only rebuild when visible — RoofEditSystem re-triggers via markDirty on edit mode exit
315
- updateMergedRoofGeometry(node, group, nodes);
316
- roofsProcessed++;
317
- }
358
+ // Built even while hidden: a roof assembling from its segments (construction
359
+ // reveal) hides its shell, and must show it whole the moment it is captured.
360
+ rememberMergedInputs(node, nodes);
361
+ updateMergedRoofGeometry(node, group, nodes);
362
+ roofsProcessed++;
318
363
  pendingRoofUpdates.delete(id);
319
364
  }
320
365
  }, 5); // Priority 5: run after all other systems have settled
@@ -323,13 +368,14 @@ export const RoofSystem = () => {
323
368
  // ============================================================================
324
369
  // GEOMETRY GENERATION
325
370
  // ============================================================================
326
- function updateRoofSegmentGeometry(node, mesh, nodes) {
371
+ function updateRoofSegmentGeometry(node, mesh, nodes, adapter) {
327
372
  const newGeo = generateRoofSegmentGeometry(node, nodes);
328
373
  mesh.geometry.dispose();
329
374
  mesh.geometry = newGeo;
330
375
  computeGeometryBoundsTree(newGeo);
331
376
  mesh.position.set(node.position[0], node.position[1], node.position[2]);
332
377
  mesh.rotation.y = node.rotation;
378
+ adapter?.syncGeometry(node, mesh);
333
379
  }
334
380
  /**
335
381
  * Subtract every hosted accessory cut (`capabilities.roofAccessory.
@@ -477,7 +523,7 @@ function updateMergedRoofGeometry(roofNode, group, nodes) {
477
523
  const scn = nodes[id];
478
524
  return scn ? getEffectiveNode(scn) : undefined;
479
525
  })
480
- .filter((n) => n !== undefined && !hasSegmentMaterialOverride(n));
526
+ .filter((n) => n !== undefined && !hasSegmentMaterialOverride(n) && !isIndependentRoofSegment(n));
481
527
  if (children.length === 0) {
482
528
  mergedMesh.geometry.dispose();
483
529
  // Not BoxGeometry: its 6 groups against the merged mesh's 4-material array
@@ -1015,7 +1061,12 @@ function buildSegmentTrimCutBrushes(segment) {
1015
1061
  return [];
1016
1062
  }
1017
1063
  const { activeRh } = getSegmentSlopeFrame(segment);
1018
- const extra = segment.wallThickness + segment.overhang + segment.deckThickness + segment.shingleThickness + 2;
1064
+ const extra = segment.wallThickness +
1065
+ segment.overhang +
1066
+ (segment.gableOverhang ?? 0) +
1067
+ segment.deckThickness +
1068
+ segment.shingleThickness +
1069
+ 2;
1019
1070
  const minX = -segment.width / 2 - extra;
1020
1071
  const maxX = segment.width / 2 + extra;
1021
1072
  const minZ = -segment.depth / 2 - extra;
@@ -1109,6 +1160,9 @@ export function clipGeometryBySegmentTrim(geometry, segment) {
1109
1160
  prepareBrushForCSG(brush);
1110
1161
  const next = csgEvaluator.evaluate(brush, cut, SUBTRACTION);
1111
1162
  const trimmed = csgGeometry(next).clone();
1163
+ const materialIndices = new Map(dummyMats.map((material, index) => [material, index]));
1164
+ for (const group of trimmed.groups)
1165
+ group.materialIndex = mapRoofGroupMaterialIndex(group.materialIndex, csgMaterials(next), materialIndices);
1112
1166
  currentGeometry.dispose();
1113
1167
  next.geometry.dispose();
1114
1168
  ensureRenderableGeometryAttributes(trimmed);
@@ -1145,13 +1199,16 @@ const FASCIA_MATERIAL_INDEX = 2;
1145
1199
  * trims, cuts and sibling roofs clip them like the deck. Gable, hip, shed
1146
1200
  * and flat; null for the other forms.
1147
1201
  */
1148
- function buildFasciaBoards(node, deckExt, eaveTopY, tanTheta) {
1202
+ function buildFasciaBoards(node, reach, eaveTopY, tanTheta) {
1149
1203
  const { roofType, width, depth } = node;
1150
- const { thickness: bt, height: bh } = FASCIA_BOARD;
1204
+ const bt = node.fasciaDepth ?? FASCIA_BOARD.thickness;
1205
+ const bh = node.fasciaHeight ?? FASCIA_BOARD.height;
1206
+ // A flat roof's boards sized on purpose reach its top, over the membrane's edge: a coping band.
1207
+ const flatTopY = node.fasciaHeight !== undefined ? eaveTopY + node.shingleThickness : eaveTopY;
1151
1208
  const out = bt / 2 - FASCIA_EMBED;
1152
- const halfD = depth / 2 + deckExt;
1209
+ const halfD = depth / 2 + reach.z;
1153
1210
  const zE = halfD + out;
1154
- const xE = width / 2 + deckExt + out;
1211
+ const xE = width / 2 + reach.x + out;
1155
1212
  // the eave boards run through the corners to the rake / side boards'
1156
1213
  // outer faces (a lapped return, no notch at the corner)
1157
1214
  const xL = xE + bt / 2;
@@ -1201,11 +1258,12 @@ function buildFasciaBoards(node, deckExt, eaveTopY, tanTheta) {
1201
1258
  }
1202
1259
  }
1203
1260
  else if (roofType === 'hip' || roofType === 'flat') {
1261
+ const y = roofType === 'flat' ? flatTopY : eaveTopY;
1204
1262
  for (const sz of [-1, 1])
1205
- board([-xL, eaveTopY, sz * zE], [xL, eaveTopY, sz * zE], [0, 1]);
1263
+ board([-xL, y, sz * zE], [xL, y, sz * zE], [0, 1]);
1206
1264
  // the side boards butt the eave boards' backs
1207
1265
  for (const sx of [-1, 1])
1208
- board([sx * xE, eaveTopY, -zE + bt / 2], [sx * xE, eaveTopY, zE - bt / 2], [1, 0]);
1266
+ board([sx * xE, y, -zE + bt / 2], [sx * xE, y, zE - bt / 2], [1, 0]);
1209
1267
  }
1210
1268
  else if (roofType === 'shed') {
1211
1269
  // the low eave at +z, the slope rising to −z
@@ -1240,7 +1298,7 @@ function wearsFascia(node, parentRoof) {
1240
1298
  * renderer asks; a dormer, a chimney or a sibling's clip takes the bare shell.
1241
1299
  */
1242
1300
  export function getRoofSegmentBrushes(node, options = {}) {
1243
- const { roofType, width, depth, wallHeight, wallThickness, deckThickness, overhang, shingleThickness, } = node;
1301
+ const { roofType, width, depth, wallHeight, wallThickness, deckThickness, shingleThickness } = node;
1244
1302
  const conicalCoverage = getConicalRoofCoverage(node);
1245
1303
  const { activeRh, tanTheta, cosTheta, sinTheta } = getSegmentSlopeFrame(node);
1246
1304
  const shapeRatios = getRoofShapeRatios({
@@ -1257,8 +1315,9 @@ export function getRoofSegmentBrushes(node, options = {}) {
1257
1315
  // same ratio). A hardcoded 0.25 desyncs the gablet from the parameter.
1258
1316
  const baseI = Math.min(width, depth) * node.dutchHipWidthRatio;
1259
1317
  const plateSeated = wallHeight <= 0;
1260
- const getVol = (wExt, vOffset, baseY, matIndex, isVoid, materialRule, onPlate = false) => {
1261
- const wV = Math.max(0.01, width + 2 * wExt);
1318
+ // `xExt`: how far the volume reaches along x, where a gable's rakes are; the eaves' `wExt` else.
1319
+ const getVol = (wExt, vOffset, baseY, matIndex, isVoid, materialRule, onPlate = false, xExt = wExt) => {
1320
+ const wV = Math.max(0.01, width + 2 * xExt);
1262
1321
  const dV = Math.max(0.01, depth + 2 * wExt);
1263
1322
  const autoDrop = wExt * tanTheta;
1264
1323
  // Floor every prism at 5 cm so CSG never sees a degenerate volume — by
@@ -1301,25 +1360,25 @@ export function getRoofSegmentBrushes(node, options = {}) {
1301
1360
  };
1302
1361
  const wallGeo = getVol(wallThickness / 2, 0, 0, 0, false, undefined, true);
1303
1362
  const innerGeo = getVol(-wallThickness / 2, 0, -5, 2, false, undefined, true);
1304
- const horizontalOverhang = overhang * cosTheta;
1305
- const deckExt = wallThickness / 2 + horizontalOverhang;
1363
+ const reach = getRoofSegmentDeckReach(node);
1364
+ const deckExt = reach.z;
1306
1365
  // A shed's deck and shingle edges take the wall/trim slot, unless it wears
1307
1366
  // fascia boards: then they are fascia like a gable's, in the deck slot.
1308
1367
  const shedEdgesInTrimSlot = roofType === 'shed' && !options.fascia;
1309
1368
  const shedRoofSideMaterialRule = shedEdgesInTrimSlot
1310
1369
  ? (normal) => (normal.y > SHINGLE_SURFACE_EPSILON ? 3 : ROOF_EDGE_MATERIAL_INDEX)
1311
1370
  : undefined;
1312
- const deckTopGeo = getVol(deckExt, verticalRt, 0, 1, false, shedRoofSideMaterialRule);
1313
- const deckBotGeo = getVol(deckExt, 0, -5, 0, true);
1371
+ const deckTopGeo = getVol(deckExt, verticalRt, 0, 1, false, shedRoofSideMaterialRule, false, reach.x);
1372
+ const deckBotGeo = getVol(deckExt, 0, -5, 0, true, undefined, false, reach.x);
1314
1373
  // the deck's top edge at its outer plane, as `getVol` puts it
1315
1374
  const deckEaveDrop = deckExt * tanTheta;
1316
1375
  const deckEaveTopY = plateSeated && deckEaveDrop > 0
1317
1376
  ? wallHeight - deckEaveDrop + verticalRt
1318
1377
  : Math.max(0.05, wallHeight - deckEaveDrop + verticalRt);
1319
- const fasciaGeo = options.fascia ? buildFasciaBoards(node, deckExt, deckEaveTopY, tanTheta) : null;
1378
+ const fasciaGeo = options.fascia ? buildFasciaBoards(node, reach, deckEaveTopY, tanTheta) : null;
1320
1379
  const stSin = shingleThickness * sinTheta;
1321
1380
  const stCos = shingleThickness * cosTheta;
1322
- const shinBotW = Math.max(0.01, width + 2 * deckExt);
1381
+ const shinBotW = Math.max(0.01, width + 2 * reach.x);
1323
1382
  const shinBotD = Math.max(0.01, depth + 2 * deckExt);
1324
1383
  const deckDrop = deckExt * tanTheta;
1325
1384
  const shinBotWh = wallHeight - deckDrop + verticalRt;
@@ -1453,7 +1512,7 @@ export function getRoofSegmentBrushes(node, options = {}) {
1453
1512
  const deckTopBrush = toBrush(deckTopGeo);
1454
1513
  const deckBotBrush = toBrush(deckBotGeo);
1455
1514
  if (deckBotBrush) {
1456
- const wV = Math.max(0.01, width + 2 * deckExt);
1515
+ const wV = Math.max(0.01, width + 2 * reach.x);
1457
1516
  const dV = Math.max(0.01, depth + 2 * deckExt);
1458
1517
  deckBotBrush.scale.set(1 + eps / wV, 1, 1 + eps / dV);
1459
1518
  deckBotBrush.updateMatrixWorld();
@@ -1532,13 +1591,90 @@ export function getRoofSegmentBrushes(node, options = {}) {
1532
1591
  rakeBoards?.dispose();
1533
1592
  return null;
1534
1593
  }
1594
+ function buildIndependentRoofSegmentGeometry(node, nodes) {
1595
+ if (!isIndependentRoofSegment(node))
1596
+ return null;
1597
+ const pieces = isGlazedRoofSegment(node)
1598
+ ? getGlazedRoofPieces(node)
1599
+ : getBarrelRoofSolidPieces(node);
1600
+ const build = (pieces) => {
1601
+ const parts = pieces.map((piece) => createGeometryFromFaces(getGlazedRoofPieceFaces(piece).map((face) => face.map(({ x, y, z }) => new THREE.Vector3(x, y, z))), piece.slot));
1602
+ const geometry = (parts.length ? mergeGeometries(parts, true) : null) ?? new THREE.BufferGeometry();
1603
+ geometry.groups.forEach((group, index) => {
1604
+ group.materialIndex = parts[index]?.groups[0]?.materialIndex ?? 0;
1605
+ });
1606
+ for (const part of parts)
1607
+ part.dispose();
1608
+ ensureRenderableGeometryAttributes(geometry);
1609
+ return geometry;
1610
+ };
1611
+ let covering = build(pieces.filter((piece) => piece.face.normal[1] > 0));
1612
+ let walls = build(pieces.filter((piece) => piece.face.normal[1] <= 0));
1613
+ const subtract = (geometry, cutter) => {
1614
+ if (!geometry.getAttribute('position')?.count)
1615
+ return geometry;
1616
+ const brush = new Brush(geometry, dummyMats);
1617
+ prepareBrushForCSG(brush);
1618
+ const result = csgEvaluator.evaluate(brush, cutter, SUBTRACTION);
1619
+ const next = result.geometry.clone();
1620
+ const indices = new Map(dummyMats.map((material, index) => [material, index]));
1621
+ for (const group of next.groups)
1622
+ group.materialIndex = mapRoofGroupMaterialIndex(group.materialIndex, csgMaterials(result), indices);
1623
+ result.geometry.dispose();
1624
+ geometry.dispose();
1625
+ return next;
1626
+ };
1627
+ for (const id of node.children ?? []) {
1628
+ const stored = nodes?.[id];
1629
+ if (!stored)
1630
+ continue;
1631
+ const child = getEffectiveNode(stored);
1632
+ if (child.metadata?.isTransient)
1633
+ continue;
1634
+ const capability = nodeRegistry.get(child.type)?.capabilities.roofAccessory;
1635
+ const cutGeometry = capability?.buildCut?.(child, node);
1636
+ if (!cutGeometry)
1637
+ continue;
1638
+ const cutter = new Brush(cutGeometry, dummyMats[0]);
1639
+ try {
1640
+ prepareBrushForCSG(cutter);
1641
+ if (capability?.cutScope !== 'wall')
1642
+ covering = subtract(covering, cutter);
1643
+ walls = subtract(walls, cutter);
1644
+ }
1645
+ finally {
1646
+ cutGeometry.dispose();
1647
+ }
1648
+ }
1649
+ const parts = [covering, walls]
1650
+ .filter((part) => part.getAttribute('position')?.count)
1651
+ .map((part) => (part.index ? part.toNonIndexed() : part.clone()));
1652
+ let geometry = (parts.length ? mergeGeometries(parts, false) : null) ?? new THREE.BufferGeometry();
1653
+ let offset = 0;
1654
+ for (const part of parts) {
1655
+ for (const group of part.groups)
1656
+ geometry.addGroup(offset + group.start, group.count, group.materialIndex);
1657
+ offset += part.getAttribute('position').count;
1658
+ part.dispose();
1659
+ }
1660
+ covering.dispose();
1661
+ walls.dispose();
1662
+ geometry = clipGeometryBySegmentTrim(geometry, node) ?? geometry;
1663
+ ensureRenderableGeometryAttributes(geometry);
1664
+ return geometry;
1665
+ }
1535
1666
  export function generateRoofSegmentGeometry(node, nodes) {
1536
1667
  const parentNode = node.parentId ? nodes?.[node.parentId] : undefined;
1537
1668
  const parentRoof = parentNode?.type === 'roof' ? parentNode : undefined;
1538
1669
  const parentRoofPosition = parentRoof?.position;
1539
1670
  const parentRoofRotation = parentRoof?.rotation ?? 0;
1540
1671
  const segmentWorldMatrix = composeSegmentWorldMatrix(parentRoofPosition, parentRoofRotation, node.position, node.rotation ?? 0);
1541
- const directSegmentGeometry = withSegmentUvMatrix(segmentWorldMatrix, () => buildCustomShedGeometry(node, nodes) ?? buildDirectConicalSectorGeometry(node));
1672
+ const directSegmentGeometry = withSegmentUvMatrix(segmentWorldMatrix, () => {
1673
+ const independent = buildIndependentRoofSegmentGeometry(node, nodes);
1674
+ if (independent)
1675
+ return independent;
1676
+ return buildCustomShedGeometry(node, nodes) ?? buildDirectConicalSectorGeometry(node);
1677
+ });
1542
1678
  if (directSegmentGeometry) {
1543
1679
  let result = addShedInsetEndPanels(directSegmentGeometry, [node], false);
1544
1680
  if (nodes) {
@@ -1617,7 +1753,7 @@ export function generateRoofSegmentGeometry(node, nodes) {
1617
1753
  ensureRenderableGeometryAttributes(resultGeo);
1618
1754
  return resultGeo;
1619
1755
  }
1620
- function clipDirectRoofGeometryAgainstSiblings(geometry, node, nodes, space) {
1756
+ export function clipDirectRoofGeometryAgainstSiblings(geometry, node, nodes, space) {
1621
1757
  const siblingInterior = buildOccludingRoofInterior(node, nodes, space);
1622
1758
  if (!siblingInterior)
1623
1759
  return geometry;
@@ -3171,7 +3307,21 @@ function pushRoofUv(uvs, point, normal) {
3171
3307
  * to the true roof surface even on multi-slope roofs (gambrel/mansard/dutch).
3172
3308
  */
3173
3309
  export function getRoofOuterSurfaceFrameAtPoint(segment, lx, lz) {
3174
- const { roofType, width, depth, wallHeight, wallThickness, deckThickness, overhang, shingleThickness, } = segment;
3310
+ if (isGlazedRoofSegment(segment) || segment.roofType === 'barrel') {
3311
+ const face = (isGlazedRoofSegment(segment) ? getGlazedRoofFaces(segment) : getBarrelRoofFaces(segment)).find((face) => face.normal[1] > 0 &&
3312
+ lx >= face.origin[0] &&
3313
+ lx <= face.origin[0] + face.width &&
3314
+ (lz - face.origin[2]) / face.v[2] >= -1e-6 &&
3315
+ (lz - face.origin[2]) / face.v[2] <= face.length + 1e-6);
3316
+ if (face) {
3317
+ const v = (lz - face.origin[2]) / face.v[2];
3318
+ return {
3319
+ point: new THREE.Vector3(lx, face.origin[1] + face.v[1] * v, lz),
3320
+ normal: new THREE.Vector3(...face.normal),
3321
+ };
3322
+ }
3323
+ }
3324
+ const { roofType, width, depth, wallHeight, deckThickness, shingleThickness } = segment;
3175
3325
  const { activeRh, tanTheta, cosTheta, sinTheta } = getSegmentSlopeFrame(segment);
3176
3326
  if (roofType === 'flat' || activeRh === 0) {
3177
3327
  return {
@@ -3180,11 +3330,11 @@ export function getRoofOuterSurfaceFrameAtPoint(segment, lx, lz) {
3180
3330
  };
3181
3331
  }
3182
3332
  const verticalRt = deckThickness / cosTheta;
3183
- const horizontalOverhang = overhang * cosTheta;
3184
- const deckExt = wallThickness / 2 + horizontalOverhang;
3333
+ const reach = getRoofSegmentDeckReach(segment);
3334
+ const deckExt = reach.z;
3185
3335
  const stSin = shingleThickness * sinTheta;
3186
3336
  const stCos = shingleThickness * cosTheta;
3187
- const shinBotW = Math.max(0.01, width + 2 * deckExt);
3337
+ const shinBotW = Math.max(0.01, width + 2 * reach.x);
3188
3338
  const shinBotD = Math.max(0.01, depth + 2 * deckExt);
3189
3339
  const deckDrop = deckExt * tanTheta;
3190
3340
  const shinBotWh = wallHeight - deckDrop + verticalRt;
@@ -1,5 +1,5 @@
1
- import { type WallNode } from '@pascal-app/core';
2
- import { type Camera, type Material, Matrix4, type Mesh, Vector3 } from 'three';
1
+ import { useScene, type WallNode } from '@pascal-app/core';
2
+ import { type Camera, type Material, Matrix4, type Mesh, type Object3D, Vector3 } from 'three';
3
3
  import useViewer, { type WallMode } from '../../store/use-viewer';
4
4
  import { type WallMaterialVariant } from './wall-material-variant';
5
5
  import { type WallMaterialsResolver } from './wall-materials';
@@ -26,7 +26,22 @@ type CachedWall = {
26
26
  export declare const WALL_FACING_HYSTERESIS = 0.001;
27
27
  export declare function wallHiddenFromFacing(node: Pick<WallNode, 'frontSide' | 'backSide'>, mode: WallMode, negativeFacing: boolean): boolean;
28
28
  export declare function wallFacingNegative(dot: number, previous: boolean | undefined): boolean;
29
- export type WallCutoutViewerState = Pick<ReturnType<typeof useViewer.getState>, 'wallMode' | 'shading' | 'textures' | 'colorPreset' | 'sceneTheme' | 'selection' | 'previewSelectedIds' | 'hoveredId' | 'hoverHighlightMode'>;
29
+ export type WallCutoutViewerState = Pick<ReturnType<typeof useViewer.getState>, 'wallMode' | 'shading' | 'textures' | 'colorPreset' | 'sceneTheme' | 'selection' | 'previewSelectedIds' | 'hoveredId' | 'hoverHighlightMode'> & {
30
+ nodesRevealing?: readonly string[];
31
+ };
32
+ /**
33
+ * The walls the cutaway must leave in view because something on them is playing: a wall that is
34
+ * moving in, going out or being edited, a wall that hosts an opening that is, and the walls under a
35
+ * node that is (a facade part). The person's wall mode is not touched: these are stamped visible
36
+ * for as long as they play, and the next frame after hides them again as the view wants.
37
+ */
38
+ export declare function wallsInPlay(nodes: ReturnType<typeof useScene.getState>['nodes'], revealing: readonly string[]): Set<string>;
39
+ /**
40
+ * The visible variant of a mesh's materials, kept on the object by the system that cuts it away, for
41
+ * whoever must draw a copy of the mesh while the cutaway hides it (a deleted wall's ghost). Not in
42
+ * `userData`: `Object3D.clone` copies that through JSON, which would serialize every material.
43
+ */
44
+ export declare const visibleMaterialsOf: WeakMap<Object3D<import("three").Object3DEventMap>, Material<import("three").MaterialEventMap>[]>;
30
45
  export type WallCutoutViewerStore = {
31
46
  getState: () => WallCutoutViewerState;
32
47
  };
@@ -35,6 +50,8 @@ export declare class WallCutoutCache {
35
50
  private readonly materialResolver;
36
51
  readonly walls: Map<string, CachedWall>;
37
52
  readonly rebuilt: Set<string>;
53
+ /** Walls whose palette waits for a preview to give back their groups (see `mayAssign`). */
54
+ private readonly deferred;
38
55
  private viewer;
39
56
  private nodes;
40
57
  private materials;
@@ -51,6 +68,9 @@ export declare class WallCutoutCache {
51
68
  private highlightKey;
52
69
  private transformed;
53
70
  private overrides;
71
+ /** The reveal's list the walls in play were read from, and the walls it named. */
72
+ private revealing;
73
+ private inPlay;
54
74
  constructor(viewerStore?: WallCutoutViewerStore, materialResolver?: WallMaterialsResolver);
55
75
  subscribeLiveTransforms(): () => void;
56
76
  update(camera: Camera, time: number): void;
@@ -62,6 +82,15 @@ export declare class WallCutoutCache {
62
82
  private refreshAppearance;
63
83
  private refreshNormal;
64
84
  private apply;
85
+ /**
86
+ * A paint preview spread over several walls (a room, all matching) regroups
87
+ * each wall's faces onto its own longer array, but only the hovered wall is
88
+ * known here as a preview's owner. A palette that cannot draw the groups the
89
+ * mesh wears now waits for the preview's restore to put them back: the mesh
90
+ * would otherwise keep groups with no material, and every raycast over it
91
+ * throws on `material.side`.
92
+ */
93
+ private mayAssign;
65
94
  }
66
95
  export {};
67
96
  //# sourceMappingURL=wall-cutout-cache.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wall-cutout-cache.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout-cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wall-cutout-cache.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout-cache.ts"],"names":[],"mappings":"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"}