@pascal-app/viewer 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/dist/capture/capture-runtime.js +1 -1
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/renderers/node-renderer.d.ts +1 -1
  4. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  5. package/dist/components/renderers/node-renderer.js +12 -4
  6. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -1
  7. package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -1
  8. package/dist/components/viewer/frame-limiter.d.ts +1 -0
  9. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  10. package/dist/components/viewer/frame-limiter.js +22 -3
  11. package/dist/components/viewer/glb-interactive.d.ts +25 -1
  12. package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
  13. package/dist/components/viewer/glb-interactive.js +253 -86
  14. package/dist/components/viewer/glb-mechanisms.d.ts +10 -0
  15. package/dist/components/viewer/glb-mechanisms.d.ts.map +1 -0
  16. package/dist/components/viewer/glb-mechanisms.js +19 -0
  17. package/dist/components/viewer/glb-reference-nodes.js +1 -1
  18. package/dist/components/viewer/glb-replace-instances.js +1 -1
  19. package/dist/components/viewer/glb-scene.d.ts +45 -27
  20. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  21. package/dist/components/viewer/glb-scene.js +421 -508
  22. package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
  23. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  24. package/dist/components/viewer/glb-walkthrough-controller.js +13 -2
  25. package/dist/components/viewer/index.d.ts +24 -1
  26. package/dist/components/viewer/index.d.ts.map +1 -1
  27. package/dist/components/viewer/index.js +111 -37
  28. package/dist/components/viewer/lights.d.ts.map +1 -1
  29. package/dist/components/viewer/lights.js +113 -22
  30. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  31. package/dist/components/viewer/perf-monitor.js +6 -0
  32. package/dist/components/viewer/perf-panel.d.ts.map +1 -1
  33. package/dist/components/viewer/perf-panel.js +1 -1
  34. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  35. package/dist/components/viewer/post-processing.js +104 -36
  36. package/dist/components/viewer/registered-systems.d.ts.map +1 -1
  37. package/dist/components/viewer/registered-systems.js +17 -3
  38. package/dist/components/viewer/render-error.d.ts +18 -0
  39. package/dist/components/viewer/render-error.d.ts.map +1 -0
  40. package/dist/components/viewer/render-error.js +29 -0
  41. package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
  42. package/dist/components/viewer/scene-bvh.js +1 -1
  43. package/dist/components/viewer/scene-environment.d.ts.map +1 -1
  44. package/dist/components/viewer/scene-environment.js +31 -11
  45. package/dist/components/viewer/scene-ground-replacement.d.ts +6 -0
  46. package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -1
  47. package/dist/components/viewer/scene-ground-replacement.js +15 -0
  48. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  49. package/dist/components/viewer/selection-manager.js +25 -7
  50. package/dist/components/viewer/underground-ambiance.d.ts +9 -0
  51. package/dist/components/viewer/underground-ambiance.d.ts.map +1 -0
  52. package/dist/components/viewer/underground-ambiance.js +105 -0
  53. package/dist/components/viewer/viewer-camera.d.ts +4 -0
  54. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  55. package/dist/components/viewer/viewer-camera.js +12 -2
  56. package/dist/components/viewer/viewer-presentations.d.ts.map +1 -1
  57. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  58. package/dist/hooks/use-gltf-ktx2.js +2 -0
  59. package/dist/hooks/use-node-events.d.ts.map +1 -1
  60. package/dist/index.d.ts +36 -16
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +34 -19
  63. package/dist/lib/asset-url.d.ts.map +1 -1
  64. package/dist/lib/asset-url.js +7 -1
  65. package/dist/lib/backdrop.d.ts +17 -0
  66. package/dist/lib/backdrop.d.ts.map +1 -1
  67. package/dist/lib/backdrop.js +18 -1
  68. package/dist/lib/construction-dust.d.ts +57 -0
  69. package/dist/lib/construction-dust.d.ts.map +1 -0
  70. package/dist/lib/construction-dust.js +187 -0
  71. package/dist/lib/csg-utils.js +1 -1
  72. package/dist/lib/display-state.d.ts +25 -0
  73. package/dist/lib/display-state.d.ts.map +1 -0
  74. package/dist/lib/display-state.js +1 -0
  75. package/dist/lib/foundation-materials.d.ts +26 -0
  76. package/dist/lib/foundation-materials.d.ts.map +1 -0
  77. package/dist/lib/foundation-materials.js +65 -0
  78. package/dist/lib/gltf-unlit.d.ts +10 -0
  79. package/dist/lib/gltf-unlit.d.ts.map +1 -0
  80. package/dist/lib/gltf-unlit.js +23 -0
  81. package/dist/lib/ground-window.d.ts +33 -0
  82. package/dist/lib/ground-window.d.ts.map +1 -0
  83. package/dist/lib/ground-window.js +114 -0
  84. package/dist/lib/hero-pose.d.ts +13 -3
  85. package/dist/lib/hero-pose.d.ts.map +1 -1
  86. package/dist/lib/hero-pose.js +72 -5
  87. package/dist/lib/ink-edges.d.ts +6 -3
  88. package/dist/lib/ink-edges.d.ts.map +1 -1
  89. package/dist/lib/ink-edges.js +17 -7
  90. package/dist/lib/key-light-override.d.ts +11 -0
  91. package/dist/lib/key-light-override.d.ts.map +1 -0
  92. package/dist/lib/key-light-override.js +14 -0
  93. package/dist/lib/ktx2-loader.d.ts +7 -0
  94. package/dist/lib/ktx2-loader.d.ts.map +1 -1
  95. package/dist/lib/ktx2-loader.js +35 -0
  96. package/dist/lib/layer-pass.d.ts.map +1 -1
  97. package/dist/lib/layers.d.ts +11 -7
  98. package/dist/lib/layers.d.ts.map +1 -1
  99. package/dist/lib/layers.js +16 -6
  100. package/dist/lib/live-frame-hold.d.ts +14 -0
  101. package/dist/lib/live-frame-hold.d.ts.map +1 -0
  102. package/dist/lib/live-frame-hold.js +28 -0
  103. package/dist/lib/materials.d.ts +19 -7
  104. package/dist/lib/materials.d.ts.map +1 -1
  105. package/dist/lib/materials.js +65 -9
  106. package/dist/lib/merged-outline-node.d.ts +5 -0
  107. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  108. package/dist/lib/merged-outline-node.js +80 -2
  109. package/dist/lib/node-top-surface-height.d.ts +6 -0
  110. package/dist/lib/node-top-surface-height.d.ts.map +1 -0
  111. package/dist/lib/node-top-surface-height.js +74 -0
  112. package/dist/lib/perf-probe-state.d.ts +39 -0
  113. package/dist/lib/perf-probe-state.d.ts.map +1 -0
  114. package/dist/lib/perf-probe-state.js +17 -0
  115. package/dist/lib/pointer-events.d.ts +26 -2
  116. package/dist/lib/pointer-events.d.ts.map +1 -1
  117. package/dist/lib/pointer-events.js +99 -12
  118. package/dist/lib/post-processing-resources.d.ts.map +1 -1
  119. package/dist/lib/procedural-emission.d.ts +7 -0
  120. package/dist/lib/procedural-emission.d.ts.map +1 -0
  121. package/dist/lib/procedural-emission.js +54 -0
  122. package/dist/lib/reveal-candidates.d.ts +80 -0
  123. package/dist/lib/reveal-candidates.d.ts.map +1 -0
  124. package/dist/lib/reveal-candidates.js +524 -0
  125. package/dist/lib/reveal-ghost.d.ts +9 -0
  126. package/dist/lib/reveal-ghost.d.ts.map +1 -0
  127. package/dist/lib/reveal-ghost.js +62 -0
  128. package/dist/lib/reveal-pending.d.ts +12 -0
  129. package/dist/lib/reveal-pending.d.ts.map +1 -0
  130. package/dist/lib/reveal-pending.js +12 -0
  131. package/dist/lib/reveal-pose.d.ts +70 -0
  132. package/dist/lib/reveal-pose.d.ts.map +1 -0
  133. package/dist/lib/reveal-pose.js +248 -0
  134. package/dist/lib/reveal-schedule.d.ts +208 -0
  135. package/dist/lib/reveal-schedule.d.ts.map +1 -0
  136. package/dist/lib/reveal-schedule.js +410 -0
  137. package/dist/lib/reveal-texture-scale.d.ts +5 -0
  138. package/dist/lib/reveal-texture-scale.d.ts.map +1 -0
  139. package/dist/lib/reveal-texture-scale.js +113 -0
  140. package/dist/lib/reveal-windows.d.ts +23 -0
  141. package/dist/lib/reveal-windows.d.ts.map +1 -0
  142. package/dist/lib/reveal-windows.js +31 -0
  143. package/dist/lib/reveal-wipe.d.ts +71 -0
  144. package/dist/lib/reveal-wipe.d.ts.map +1 -0
  145. package/dist/lib/reveal-wipe.js +105 -0
  146. package/dist/lib/scene-pass-mrt.d.ts +12 -0
  147. package/dist/lib/scene-pass-mrt.d.ts.map +1 -0
  148. package/dist/lib/scene-pass-mrt.js +23 -0
  149. package/dist/lib/scene-support-height.d.ts +4 -0
  150. package/dist/lib/scene-support-height.d.ts.map +1 -0
  151. package/dist/lib/scene-support-height.js +34 -0
  152. package/dist/lib/scene-themes.d.ts +30 -8
  153. package/dist/lib/scene-themes.d.ts.map +1 -1
  154. package/dist/lib/scene-themes.js +36 -0
  155. package/dist/lib/scene-visibility.d.ts +17 -1
  156. package/dist/lib/scene-visibility.d.ts.map +1 -1
  157. package/dist/lib/scene-visibility.js +53 -3
  158. package/dist/lib/scripted-opening.d.ts +11 -0
  159. package/dist/lib/scripted-opening.d.ts.map +1 -0
  160. package/dist/lib/scripted-opening.js +20 -0
  161. package/dist/lib/site-surfaces.d.ts +32 -0
  162. package/dist/lib/site-surfaces.d.ts.map +1 -0
  163. package/dist/lib/site-surfaces.js +70 -0
  164. package/dist/lib/snapshot-pipeline.d.ts +34 -3
  165. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  166. package/dist/lib/snapshot-pipeline.js +247 -117
  167. package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
  168. package/dist/lib/terrain-perimeter-fill.js +22 -14
  169. package/dist/lib/tsl-compat.d.ts +2 -2
  170. package/dist/lib/tsl-compat.d.ts.map +1 -1
  171. package/dist/lib/underground-ambiance.d.ts +168 -0
  172. package/dist/lib/underground-ambiance.d.ts.map +1 -0
  173. package/dist/lib/underground-ambiance.js +334 -0
  174. package/dist/lib/zone-geometry.d.ts +11 -0
  175. package/dist/lib/zone-geometry.d.ts.map +1 -0
  176. package/dist/lib/zone-geometry.js +46 -0
  177. package/dist/store/use-item-light-pool.d.ts +14 -10
  178. package/dist/store/use-item-light-pool.d.ts.map +1 -1
  179. package/dist/store/use-item-light-pool.js +52 -26
  180. package/dist/store/use-viewer.d.ts +194 -26
  181. package/dist/store/use-viewer.d.ts.map +1 -1
  182. package/dist/store/use-viewer.js +164 -21
  183. package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
  184. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  185. package/dist/systems/ceiling/ceiling-system.js +88 -6
  186. package/dist/systems/construction-reveal/construction-dust.d.ts +16 -0
  187. package/dist/systems/construction-reveal/construction-dust.d.ts.map +1 -0
  188. package/dist/systems/construction-reveal/construction-dust.js +92 -0
  189. package/dist/systems/construction-reveal/construction-reveal.d.ts +276 -0
  190. package/dist/systems/construction-reveal/construction-reveal.d.ts.map +1 -0
  191. package/dist/systems/construction-reveal/construction-reveal.js +2383 -0
  192. package/dist/systems/construction-reveal/dust-material.d.ts +10 -0
  193. package/dist/systems/construction-reveal/dust-material.d.ts.map +1 -0
  194. package/dist/systems/construction-reveal/dust-material.js +17 -0
  195. package/dist/systems/construction-reveal/ghost-dress.d.ts +10 -0
  196. package/dist/systems/construction-reveal/ghost-dress.d.ts.map +1 -0
  197. package/dist/systems/construction-reveal/ghost-dress.js +24 -0
  198. package/dist/systems/door/door-system.d.ts.map +1 -1
  199. package/dist/systems/door/door-system.js +55 -17
  200. package/dist/systems/elevator/elevator-interaction-system.d.ts +7 -0
  201. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
  202. package/dist/systems/elevator/elevator-interaction-system.js +18 -1
  203. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
  204. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  205. package/dist/systems/floor-elevation/floor-elevation-system.js +33 -5
  206. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  207. package/dist/systems/geometry/geometry-system.js +71 -11
  208. package/dist/systems/interactive/control-widget.d.ts.map +1 -1
  209. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  210. package/dist/systems/interactive/interactive-system.js +52 -17
  211. package/dist/systems/interactive/procedural-controls.d.ts +13 -0
  212. package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
  213. package/dist/systems/interactive/procedural-controls.js +17 -0
  214. package/dist/systems/interactive/scripted-clips.d.ts +19 -0
  215. package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
  216. package/dist/systems/interactive/scripted-clips.js +134 -0
  217. package/dist/systems/item/item-system.d.ts.map +1 -1
  218. package/dist/systems/item/item-system.js +9 -3
  219. package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
  220. package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
  221. package/dist/systems/item/object-cut-invalidation.js +68 -0
  222. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  223. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  224. package/dist/systems/item-light/item-light-system.js +48 -53
  225. package/dist/systems/level/level-hold.d.ts +4 -0
  226. package/dist/systems/level/level-hold.d.ts.map +1 -0
  227. package/dist/systems/level/level-hold.js +14 -0
  228. package/dist/systems/level/level-system.d.ts.map +1 -1
  229. package/dist/systems/level/level-system.js +19 -6
  230. package/dist/systems/level/level-utils.d.ts +19 -6
  231. package/dist/systems/level/level-utils.d.ts.map +1 -1
  232. package/dist/systems/level/level-utils.js +68 -9
  233. package/dist/systems/level/shadow-only-levels.d.ts +9 -0
  234. package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
  235. package/dist/systems/level/shadow-only-levels.js +7 -0
  236. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts +103 -0
  237. package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts.map +1 -0
  238. package/dist/systems/roof/__fixtures__/roof-plane-cases.js +317 -0
  239. package/dist/systems/roof/roof-materials.d.ts +16 -2
  240. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  241. package/dist/systems/roof/roof-materials.js +64 -31
  242. package/dist/systems/roof/roof-system.d.ts +21 -4
  243. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  244. package/dist/systems/roof/roof-system.js +374 -49
  245. package/dist/systems/slab/slab-system.d.ts +1 -1
  246. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  247. package/dist/systems/slab/slab-system.js +49 -19
  248. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  249. package/dist/systems/stair/stair-system.js +133 -354
  250. package/dist/systems/wall/wall-cutout-cache.d.ts +39 -3
  251. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  252. package/dist/systems/wall/wall-cutout-cache.js +141 -19
  253. package/dist/systems/wall/wall-cutout.d.ts +6 -0
  254. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  255. package/dist/systems/wall/wall-cutout.js +42 -28
  256. package/dist/systems/wall/wall-finish-data.d.ts +22 -0
  257. package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
  258. package/dist/systems/wall/wall-finish-data.js +17 -0
  259. package/dist/systems/wall/wall-material-variant.d.ts.map +1 -1
  260. package/dist/systems/wall/wall-materials.d.ts +9 -4
  261. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  262. package/dist/systems/wall/wall-materials.js +135 -153
  263. package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -1
  264. package/dist/systems/wall/wall-system.d.ts +33 -2
  265. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  266. package/dist/systems/wall/wall-system.js +621 -159
  267. package/dist/systems/window/corner-frame.d.ts +10 -0
  268. package/dist/systems/window/corner-frame.d.ts.map +1 -0
  269. package/dist/systems/window/corner-frame.js +17 -0
  270. package/dist/systems/window/window-system.d.ts +1 -1
  271. package/dist/systems/window/window-system.d.ts.map +1 -1
  272. package/dist/systems/window/window-system.js +71 -27
  273. package/package.json +10 -10
  274. package/dist/systems/fence/fence-system.d.ts +0 -22
  275. package/dist/systems/fence/fence-system.d.ts.map +0 -1
  276. package/dist/systems/fence/fence-system.js +0 -357
@@ -1,9 +1,11 @@
1
1
  // New kind-specific modules belong in nodes; viewer must not depend on nodes.
2
2
  // This extends the existing viewer-owned wall cutout and material implementation.
3
- import { getEffectiveNode, getLibraryMaterialsVersion, getWallFaceBandConfig, getWallPlaneTop, resolveLevelId, resolveWallEffectiveHeight, sceneRegistry, spatialGridManager, useLiveNodeOverrides, useLiveTransforms, useScene, } from '@pascal-app/core';
3
+ import { getEffectiveNode, getLibraryMaterialsVersion, sceneRegistry, useLiveNodeOverrides, useLiveTransforms, useScene, } from '@pascal-app/core';
4
4
  import { Matrix4, Vector3 } from 'three';
5
5
  import { getMaterialTextureVersion } from '../../lib/materials';
6
+ import { hasMaterialsForGroups } from '../../lib/pointer-events';
6
7
  import useViewer, {} from '../../store/use-viewer';
8
+ import { getWallFinishRefs } from './wall-finish-data';
7
9
  import { resolveWallMaterialVariant } from './wall-material-variant';
8
10
  import { getHoverHighlightMaterials, getMaterialsForWall, getSelectionHighlightMaterials, } from './wall-materials';
9
11
  export function sameMaterialArray(a, b) {
@@ -38,6 +40,9 @@ function materialsForVariant(variant, materials) {
38
40
  }
39
41
  }
40
42
  }
43
+ function wallPaletteKey(mesh) {
44
+ return getWallFinishRefs(mesh.geometry).join('|');
45
+ }
41
46
  // About 0.06 degrees: retain the previous side at edge-on poses without
42
47
  // introducing a perceptible delay when an orbit crosses a wall's plane.
43
48
  export const WALL_FACING_HYSTERESIS = 0.001;
@@ -57,11 +62,51 @@ export function wallFacingNegative(dot, previous) {
57
62
  return dot < 0;
58
63
  return previous ? dot < WALL_FACING_HYSTERESIS : dot < -WALL_FACING_HYSTERESIS;
59
64
  }
65
+ const NOT_REVEALING = [];
66
+ /**
67
+ * The walls the cutaway must leave in view because something on them is playing: a wall that is
68
+ * moving in, going out or being edited, a wall that hosts an opening that is, and the walls under a
69
+ * node that is (a facade part). The person's wall mode is not touched: these are stamped visible
70
+ * for as long as they play, and the next frame after hides them again as the view wants.
71
+ */
72
+ export function wallsInPlay(nodes, revealing) {
73
+ const walls = new Set();
74
+ if (revealing.length === 0)
75
+ return walls;
76
+ const down = (id, depth) => {
77
+ const node = nodes[id];
78
+ if (!node)
79
+ return;
80
+ if (node.type === 'wall')
81
+ walls.add(id);
82
+ if (depth >= 4)
83
+ return;
84
+ for (const child of node.children ?? [])
85
+ down(child, depth + 1);
86
+ };
87
+ for (const id of revealing) {
88
+ // The wall itself, or an opening in it: the host stands visible while the opening plays.
89
+ for (let node = nodes[id], guard = 0; node && guard < 16; node = node.parentId ? nodes[node.parentId] : undefined, guard += 1)
90
+ if (node.type === 'wall')
91
+ walls.add(node.id);
92
+ // What stands under a node that plays rides with it.
93
+ down(id, 0);
94
+ }
95
+ return walls;
96
+ }
97
+ /**
98
+ * The visible variant of a mesh's materials, kept on the object by the system that cuts it away, for
99
+ * whoever must draw a copy of the mesh while the cutaway hides it (a deleted wall's ghost). Not in
100
+ * `userData`: `Object3D.clone` copies that through JSON, which would serialize every material.
101
+ */
102
+ export const visibleMaterialsOf = new WeakMap();
60
103
  export class WallCutoutCache {
61
104
  viewerStore;
62
105
  materialResolver;
63
106
  walls = new Map();
64
107
  rebuilt = new Set();
108
+ /** Walls whose palette waits for a preview to give back their groups (see `mayAssign`). */
109
+ deferred = new Set();
65
110
  viewer;
66
111
  nodes;
67
112
  materials;
@@ -78,6 +123,9 @@ export class WallCutoutCache {
78
123
  highlightKey = '';
79
124
  transformed = new Set();
80
125
  overrides = useLiveNodeOverrides.getState().overrides;
126
+ /** The reveal's list the walls in play were read from, and the walls it named. */
127
+ revealing = NOT_REVEALING;
128
+ inPlay = new Set();
81
129
  constructor(viewerStore = useViewer, materialResolver = getMaterialsForWall) {
82
130
  this.viewerStore = viewerStore;
83
131
  this.materialResolver = materialResolver;
@@ -131,24 +179,47 @@ export class WallCutoutCache {
131
179
  const previewId = (state) => state && state.hoverHighlightMode !== 'default' && state.hoverHighlightMode !== 'delete'
132
180
  ? state.hoveredId
133
181
  : null;
134
- const releasedPreview = previewId(previous) !== previewId(viewer) ? previewId(previous) : null;
182
+ const releasedPreview = previewId(previous) === previewId(viewer) ? null : previewId(previous);
135
183
  this.viewer = viewer;
184
+ // A wall whose play starts or ends, or a node that moves under one, changes only its own stamp.
185
+ const revealing = viewer.nodesRevealing ?? NOT_REVEALING;
186
+ const playChanged = new Set();
187
+ if (this.revealing !== revealing || (nodesChanged && revealing.length > 0)) {
188
+ const next = wallsInPlay(scene.nodes, revealing);
189
+ for (const id of next)
190
+ if (!this.inPlay.has(id))
191
+ playChanged.add(id);
192
+ for (const id of this.inPlay)
193
+ if (!next.has(id))
194
+ playChanged.add(id);
195
+ this.inPlay = next;
196
+ this.revealing = revealing;
197
+ }
198
+ // After the stamps are current: a deferred wall is applied with the walls in play it stands among.
199
+ for (const id of this.deferred.size > 0 ? Array.from(this.deferred) : []) {
200
+ this.deferred.delete(id);
201
+ const wall = this.walls.get(id);
202
+ if (wall)
203
+ this.apply(wall, viewer.wallMode, false);
204
+ }
136
205
  const invalidated = overridesChanged ||
137
206
  appearanceChanged ||
138
207
  nodesChanged ||
139
208
  registryChanged ||
140
209
  this.rebuilt.size > 0 ||
141
210
  this.transformed.size > 0 ||
142
- releasedPreview !== null;
143
- if (!invalidated && viewer.wallMode !== 'cutaway')
211
+ releasedPreview !== null ||
212
+ playChanged.size > 0;
213
+ const cutaway = viewer.wallMode === 'cutaway';
214
+ if (!invalidated && !cutaway)
144
215
  return;
145
216
  camera.getWorldDirection(this.cameraDirection);
146
217
  this.cameraTarget.copy(this.cameraDirection).add(camera.position);
147
- const cameraChanged = viewer.wallMode === 'cutaway' &&
218
+ const cameraChanged = cutaway &&
148
219
  time - this.lastUpdateTime > 0.1 &&
149
220
  (camera.position.distanceTo(this.lastCameraPosition) > 0.5 ||
150
221
  this.cameraTarget.distanceTo(this.lastCameraTarget) > 0.3);
151
- if (!invalidated && !cameraChanged)
222
+ if (!(invalidated || cameraChanged))
152
223
  return;
153
224
  if (registryChanged || nodesChanged) {
154
225
  for (const [id, wall] of this.walls) {
@@ -194,6 +265,7 @@ export class WallCutoutCache {
194
265
  assignedMaterials: undefined,
195
266
  visibleVariant: { key: 'visible', materials: [] },
196
267
  hiddenVariant: { key: 'invisible', materials: [] },
268
+ paletteKey: '',
197
269
  };
198
270
  this.walls.set(id, wall);
199
271
  }
@@ -203,7 +275,9 @@ export class WallCutoutCache {
203
275
  this.refreshNormal(wall, visited);
204
276
  const overrideChanged = this.overrides.get(id) !== overrides.get(id);
205
277
  wall.node = getEffectiveNode(node);
206
- if (added || appearanceChanged || overrideChanged || (nodesChanged && changed))
278
+ const repaletted = rebuilt && wallPaletteKey(wall.mesh) !== wall.paletteKey;
279
+ const refresh = added || appearanceChanged || overrideChanged || (nodesChanged && changed) || repaletted;
280
+ if (refresh)
207
281
  this.refreshAppearance(wall);
208
282
  if (added ||
209
283
  appearanceChanged ||
@@ -211,8 +285,9 @@ export class WallCutoutCache {
211
285
  changed ||
212
286
  rebuilt ||
213
287
  releasedPreview === id ||
288
+ playChanged.has(id) ||
214
289
  cameraChanged) {
215
- this.apply(wall, viewer.wallMode, added || appearanceChanged || overrideChanged || (nodesChanged && changed));
290
+ this.apply(wall, viewer.wallMode, refresh);
216
291
  }
217
292
  }
218
293
  }
@@ -235,19 +310,35 @@ export class WallCutoutCache {
235
310
  this.lastUpdateTime = time;
236
311
  }
237
312
  }
313
+ /**
314
+ * A rebuilt wall whose finish palette changed must swap materials in the same
315
+ * frame: its new groups already index the new palette.
316
+ */
317
+ handleRebuilt(id) {
318
+ this.rebuilt.add(id);
319
+ const wall = this.walls.get(id);
320
+ const viewer = this.viewer;
321
+ if (!(wall && viewer) || wall.mesh !== sceneRegistry.nodes.get(id))
322
+ return;
323
+ if (wallPaletteKey(wall.mesh) === wall.paletteKey)
324
+ return;
325
+ const node = useScene.getState().nodes[id];
326
+ if (node?.type !== 'wall')
327
+ return;
328
+ wall.node = getEffectiveNode(node);
329
+ this.refreshAppearance(wall);
330
+ this.apply(wall, viewer.wallMode, true);
331
+ }
238
332
  refreshAppearance(wall) {
239
333
  const viewer = this.viewer;
240
334
  const scene = useScene.getState();
241
335
  const node = wall.node;
242
336
  const deleted = viewer.hoverHighlightMode === 'delete' && viewer.hoveredId === node.id;
243
- let selectionHighlighted = !deleted && this.selected.has(node.id);
244
- if (selectionHighlighted) {
245
- const levelId = resolveLevelId(node, scene.nodes);
246
- const support = spatialGridManager.getSlabSupportForWall(levelId, node.start, node.end, node.curveOffset ?? 0, node.thickness, node.supportSlabId);
247
- const height = resolveWallEffectiveHeight(node, getWallPlaneTop(node, levelId, scene.nodes), support.elevation);
248
- selectionHighlighted = !getWallFaceBandConfig(node, height).enabled;
249
- }
250
- const materials = this.materialResolver(node, viewer.shading, viewer.textures, viewer.colorPreset, viewer.sceneTheme, scene.materials);
337
+ // Painted parts stay readable while their wall is selected for editing.
338
+ const selectionHighlighted = !deleted && this.selected.has(node.id) && !(node.faceRegions?.length ?? 0);
339
+ const finishRefs = getWallFinishRefs(wall.mesh.geometry);
340
+ wall.paletteKey = finishRefs.join('|');
341
+ const materials = this.materialResolver(node, viewer.shading, viewer.textures, viewer.colorPreset, viewer.sceneTheme, scene.materials, finishRefs);
251
342
  const variant = (hidden) => {
252
343
  const key = resolveWallMaterialVariant({
253
344
  translucentMode: viewer.wallMode === 'translucent',
@@ -260,6 +351,8 @@ export class WallCutoutCache {
260
351
  };
261
352
  wall.visibleVariant = variant(false);
262
353
  wall.hiddenVariant = variant(true);
354
+ // The plain visible palette (no selection or hover tint): what the wall wears when a capture sees it.
355
+ visibleMaterialsOf.set(wall.mesh, materials.visible);
263
356
  }
264
357
  refreshNormal(wall, visited) {
265
358
  const update = (object) => {
@@ -281,7 +374,10 @@ export class WallCutoutCache {
281
374
  if (mode === 'cutaway') {
282
375
  wall.negativeFacing = wallFacingNegative(wall.normal.dot(this.cameraDirection), wall.negativeFacing);
283
376
  }
284
- const hidden = wallHiddenFromFacing(wall.node, mode, wall.negativeFacing ?? false);
377
+ // A wall in play is seen whatever the cutaway says (walls down is the person's own choice, and
378
+ // stays); `mode` itself is the person's, untouched.
379
+ const hidden = wallHiddenFromFacing(wall.node, mode, wall.negativeFacing ?? false) &&
380
+ !(mode === 'cutaway' && this.inPlay.has(wall.node.id));
285
381
  wall.hidden = hidden;
286
382
  // The wall batch and pointer handlers consume this boolean, including
287
383
  // false on stamp lift; translucent walls must continue receiving events.
@@ -289,11 +385,15 @@ export class WallCutoutCache {
289
385
  if (wall.mesh.userData.wallHidden !== stamp)
290
386
  wall.mesh.userData.wallHidden = stamp;
291
387
  const variant = hidden ? wall.hiddenVariant : wall.visibleVariant;
292
- // Non-highlight hover owns a temporary material until its restore callback runs.
388
+ // Non-highlight hover (a paint preview) owns a temporary material until its
389
+ // restore callback runs — unless the rebuilt geometry now draws a finish that
390
+ // array has no entry for (a click just added one): then the new palette goes
391
+ // on and the preview is dropped, rather than leaving groups with no material.
293
392
  const viewer = this.viewer;
294
393
  if (viewer.hoveredId === wall.node.id &&
295
394
  viewer.hoverHighlightMode !== 'default' &&
296
- viewer.hoverHighlightMode !== 'delete') {
395
+ viewer.hoverHighlightMode !== 'delete' &&
396
+ hasMaterialsForGroups(wall.mesh)) {
297
397
  if (refresh)
298
398
  wall.variantKey = undefined;
299
399
  return;
@@ -301,13 +401,35 @@ export class WallCutoutCache {
301
401
  if (wall.variantKey !== variant.key || refresh) {
302
402
  if (wall.mesh.material !== variant.materials &&
303
403
  !sameMaterialArray(wall.mesh.material, variant.materials)) {
404
+ if (!this.mayAssign(wall, variant.materials)) {
405
+ wall.variantKey = undefined;
406
+ return;
407
+ }
304
408
  wall.mesh.material = variant.materials;
305
409
  }
306
410
  wall.variantKey = variant.key;
307
411
  wall.assignedMaterials = wall.mesh.material;
308
412
  }
309
413
  else if (wall.mesh.material !== wall.assignedMaterials) {
414
+ if (!this.mayAssign(wall, wall.assignedMaterials))
415
+ return;
310
416
  wall.mesh.material = wall.assignedMaterials;
311
417
  }
312
418
  }
419
+ /**
420
+ * A paint preview spread over several walls (a room, all matching) regroups
421
+ * each wall's faces onto its own longer array, but only the hovered wall is
422
+ * known here as a preview's owner. A palette that cannot draw the groups the
423
+ * mesh wears now waits for the preview's restore to put them back: the mesh
424
+ * would otherwise keep groups with no material, and every raycast over it
425
+ * throws on `material.side`.
426
+ */
427
+ mayAssign(wall, materials) {
428
+ if (hasMaterialsForGroups(wall.mesh, materials) || !hasMaterialsForGroups(wall.mesh)) {
429
+ this.deferred.delete(wall.node.id);
430
+ return true;
431
+ }
432
+ this.deferred.add(wall.node.id);
433
+ return false;
434
+ }
313
435
  }
@@ -20,6 +20,12 @@ export declare function runWallCutoutFrame(cache: WallCutoutCache, { camera, clo
20
20
  * from the assigned material variant.
21
21
  */
22
22
  export declare function getWallHideState(wallNode: WallNode, wallMesh: Mesh, wallMode: WallMode, cameraDir: Vector3): boolean;
23
+ /**
24
+ * The walls as a capture draws them, until the returned function puts them back: a delete or a
25
+ * selection highlight taken off and, with `wholeWalls`, every wall whole whatever the wall mode.
26
+ * The palettes come from the renderer's own resolver, so a curtain wall keeps its glass.
27
+ */
28
+ export declare function wallsForCapture(materialResolver: WallMaterialsResolver, viewerStore: WallCutoutViewerStore, wholeWalls: boolean): () => void;
23
29
  export declare const WallCutout: ({ viewerStore, materialResolver, }: {
24
30
  viewerStore?: WallCutoutViewerStore;
25
31
  materialResolver?: WallMaterialsResolver;
@@ -1 +1 @@
1
- {"version":3,"file":"wall-cutout.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAGlG,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACjD,OAAkB,EAAE,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAEL,eAAe,EACf,KAAK,qBAAqB,EAE3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,kBAAkB,CAAA;AAKzB,eAAO,MAAM,0BAA0B,IAAI,CAAA;AAE3C,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,eAAe,EACtB,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,QAGtE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,IAAI,EACd,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,OAAO,GACjB,OAAO,CAKT;AAED,eAAO,MAAM,UAAU,GAAI,oCAGxB;IACD,WAAW,CAAC,EAAE,qBAAqB,CAAA;IACnC,gBAAgB,CAAC,EAAE,qBAAqB,CAAA;CACzC,SA8DA,CAAA"}
1
+ {"version":3,"file":"wall-cutout.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAGlG,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACjD,OAAkB,EAAE,KAAK,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAEL,eAAe,EACf,KAAK,qBAAqB,EAE3B,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,kBAAkB,CAAA;AAKzB,eAAO,MAAM,0BAA0B,IAAI,CAAA;AAE3C,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,eAAe,EACtB,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,QAGtE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,IAAI,EACd,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,OAAO,GACjB,OAAO,CAKT;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,gBAAgB,EAAE,qBAAqB,EACvC,WAAW,EAAE,qBAAqB,EAClC,UAAU,EAAE,OAAO,GAClB,MAAM,IAAI,CAmCZ;AAED,eAAO,MAAM,UAAU,uCAGpB;IACD,WAAW,CAAC,EAAE,qBAAqB,CAAA;IACnC,gBAAgB,CAAC,EAAE,qBAAqB,CAAA;CACzC,SAiCA,CAAA"}
@@ -4,6 +4,7 @@ import { useEffect, useMemo } from 'react';
4
4
  import { Vector3 } from 'three/webgpu';
5
5
  import useViewer, {} from '../../store/use-viewer';
6
6
  import { sameMaterialArray, WallCutoutCache, wallHiddenFromFacing, } from './wall-cutout-cache';
7
+ import { getWallFinishRefs } from './wall-finish-data';
7
8
  import { getMaterialsForWall, getSelectionHighlightMaterials, } from './wall-materials';
8
9
  import { subscribeWallRebuilds } from './wall-rebuild-notifications';
9
10
  const v = new Vector3();
@@ -27,43 +28,56 @@ export function getWallHideState(wallNode, wallMesh, wallMode, cameraDir) {
27
28
  wallMesh.getWorldDirection(v);
28
29
  return wallHiddenFromFacing(wallNode, wallMode, v.dot(cameraDir) < 0);
29
30
  }
31
+ /**
32
+ * The walls as a capture draws them, until the returned function puts them back: a delete or a
33
+ * selection highlight taken off and, with `wholeWalls`, every wall whole whatever the wall mode.
34
+ * The palettes come from the renderer's own resolver, so a curtain wall keeps its glass.
35
+ */
36
+ export function wallsForCapture(materialResolver, viewerStore, wholeWalls) {
37
+ const snapshot = new Map();
38
+ const viewer = viewerStore.getState();
39
+ sceneRegistry.byType.wall.forEach((wallId) => {
40
+ const wallMesh = sceneRegistry.nodes.get(wallId);
41
+ if (!wallMesh)
42
+ return;
43
+ const wallNode = useScene.getState().nodes[wallId];
44
+ if (wallNode?.type !== 'wall')
45
+ return;
46
+ const mats = materialResolver(wallNode, viewer.shading, viewer.textures, viewer.colorPreset, viewer.sceneTheme, useScene.getState().materials, getWallFinishRefs(wallMesh.geometry));
47
+ const current = wallMesh.material;
48
+ snapshot.set(wallMesh, current);
49
+ if (wholeWalls || current === mats.deleteVisible) {
50
+ wallMesh.material = mats.visible;
51
+ }
52
+ else if (current === mats.deleteInvisible) {
53
+ wallMesh.material = mats.invisible;
54
+ }
55
+ else if (current === mats.deleteTranslucent ||
56
+ sameMaterialArray(current, getSelectionHighlightMaterials(mats.translucent))) {
57
+ wallMesh.material = mats.translucent;
58
+ }
59
+ });
60
+ return () => {
61
+ snapshot.forEach((material, mesh) => {
62
+ mesh.material = material;
63
+ });
64
+ };
65
+ }
30
66
  export const WallCutout = ({ viewerStore = useViewer, materialResolver = getMaterialsForWall, }) => {
31
67
  const cache = useMemo(() => new WallCutoutCache(viewerStore, materialResolver), [viewerStore, materialResolver]);
32
- useEffect(() => subscribeWallRebuilds((id) => cache.rebuilt.add(id)), [cache]);
68
+ useEffect(() => subscribeWallRebuilds((id) => cache.handleRebuilt(id)), [cache]);
33
69
  useEffect(() => cache.subscribeLiveTransforms(), [cache]);
34
70
  // Camera changes reach PostProcessing (1) in this frame. WallSystem (4)
35
71
  // notifies the next frame; WallBatchSystem (5) reads this frame's stamps.
36
72
  useFrame((state) => runWallCutoutFrame(cache, state), WALL_CUTOUT_FRAME_PRIORITY);
37
73
  useEffect(() => {
38
- const snapshot = new Map();
39
- const restoreForCapture = () => {
40
- sceneRegistry.byType.wall.forEach((wallId) => {
41
- const wallMesh = sceneRegistry.nodes.get(wallId);
42
- if (!wallMesh)
43
- return;
44
- const wallNode = useScene.getState().nodes[wallId];
45
- if (wallNode?.type !== 'wall')
46
- return;
47
- const mats = materialResolver(wallNode, viewerStore.getState().shading, viewerStore.getState().textures, viewerStore.getState().colorPreset, viewerStore.getState().sceneTheme, useScene.getState().materials);
48
- const current = wallMesh.material;
49
- snapshot.set(wallMesh, current);
50
- if (current === mats.deleteVisible) {
51
- wallMesh.material = mats.visible;
52
- }
53
- else if (current === mats.deleteInvisible) {
54
- wallMesh.material = mats.invisible;
55
- }
56
- else if (current === mats.deleteTranslucent ||
57
- sameMaterialArray(current, getSelectionHighlightMaterials(mats.translucent))) {
58
- wallMesh.material = mats.translucent;
59
- }
60
- });
74
+ let restore = null;
75
+ const restoreForCapture = (event) => {
76
+ restore = wallsForCapture(materialResolver, viewerStore, event?.wholeWalls === true);
61
77
  };
62
78
  const reapplyAfterCapture = () => {
63
- snapshot.forEach((mat, mesh) => {
64
- mesh.material = mat;
65
- });
66
- snapshot.clear();
79
+ restore?.();
80
+ restore = null;
67
81
  };
68
82
  emitter.on('thumbnail:before-capture', restoreForCapture);
69
83
  emitter.on('thumbnail:after-capture', reapplyAfterCapture);
@@ -0,0 +1,22 @@
1
+ import type { BufferGeometry } from 'three';
2
+ /** A run of one face's base, in wall-local x (m) and local y. */
3
+ export type WallFaceBaseRun = {
4
+ start: number;
5
+ end: number;
6
+ y: number;
7
+ };
8
+ /**
9
+ * What the wall's material array and the paint resolver need from a build: the
10
+ * finish refs behind material indices 3.. and each face's base runs (null when
11
+ * both faces stand on the wall's own base).
12
+ */
13
+ export type WallFinishGeometryData = {
14
+ refs: readonly string[];
15
+ faceBase: Record<'a' | 'b', WallFaceBaseRun[]> | null;
16
+ };
17
+ export declare function getWallFinishData(geometry: BufferGeometry | undefined | null): WallFinishGeometryData | undefined;
18
+ /** Finish refs a built wall geometry's material indices 3.. point at. */
19
+ export declare function getWallFinishRefs(geometry: BufferGeometry | undefined | null): readonly string[];
20
+ /** Local y of a face's base at wall-local station `x` (0 when the faces share the wall base). */
21
+ export declare function getWallFaceBaseAt(data: Pick<WallFinishGeometryData, 'faceBase'> | undefined, face: 'a' | 'b', x: number): number;
22
+ //# sourceMappingURL=wall-finish-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wall-finish-data.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-finish-data.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C,iEAAiE;AACjE,MAAM,MAAM,eAAe,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvE;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAC,GAAG,GAAG,GAAG,EAAE,eAAe,EAAE,CAAC,GAAG,IAAI,CAAA;CACtD,CAAA;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,GAC1C,sBAAsB,GAAG,SAAS,CAEpC;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,GAAG,SAAS,MAAM,EAAE,CAEhG;AAED,iGAAiG;AACjG,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,CAAC,sBAAsB,EAAE,UAAU,CAAC,GAAG,SAAS,EAC1D,IAAI,EAAE,GAAG,GAAG,GAAG,EACf,CAAC,EAAE,MAAM,GACR,MAAM,CAKR"}
@@ -0,0 +1,17 @@
1
+ export function getWallFinishData(geometry) {
2
+ return geometry?.userData.wallFinish;
3
+ }
4
+ /** Finish refs a built wall geometry's material indices 3.. point at. */
5
+ export function getWallFinishRefs(geometry) {
6
+ return getWallFinishData(geometry)?.refs ?? [];
7
+ }
8
+ /** Local y of a face's base at wall-local station `x` (0 when the faces share the wall base). */
9
+ export function getWallFaceBaseAt(data, face, x) {
10
+ const runs = data?.faceBase?.[face];
11
+ if (!runs?.length)
12
+ return 0;
13
+ for (const run of runs)
14
+ if (x >= run.start && x < run.end)
15
+ return run.y;
16
+ return x < runs[0].start ? runs[0].y : runs.at(-1).y;
17
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"wall-material-variant.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-material-variant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,WAAW,GACX,aAAa,GACb,gBAAgB,GAChB,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,qBAAqB,GACrB,uBAAuB,GACvB,iBAAiB,CAAA;AAErB,eAAO,MAAM,0BAA0B,GAAI,yFAMxC;IACD,6DAA6D;IAC7D,eAAe,EAAE,OAAO,CAAA;IACxB,2EAA2E;IAC3E,MAAM,EAAE,OAAO,CAAA;IACf,6DAA6D;IAC7D,iBAAiB,EAAE,OAAO,CAAA;IAC1B,uEAAuE;IACvE,oBAAoB,EAAE,OAAO,CAAA;IAC7B,4DAA4D;IAC5D,gBAAgB,EAAE,OAAO,CAAA;CAC1B,KAAG,mBAMH,CAAA"}
1
+ {"version":3,"file":"wall-material-variant.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-material-variant.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,WAAW,GACX,aAAa,GACb,gBAAgB,GAChB,kBAAkB,GAClB,oBAAoB,GACpB,mBAAmB,GACnB,qBAAqB,GACrB,uBAAuB,GACvB,iBAAiB,CAAA;AAErB,eAAO,MAAM,0BAA0B,4FAMpC;IACD,6DAA6D;IAC7D,eAAe,EAAE,OAAO,CAAA;IACxB,2EAA2E;IAC3E,MAAM,EAAE,OAAO,CAAA;IACf,6DAA6D;IAC7D,iBAAiB,EAAE,OAAO,CAAA;IAC1B,uEAAuE;IACvE,oBAAoB,EAAE,OAAO,CAAA;IAC7B,4DAA4D;IAC5D,gBAAgB,EAAE,OAAO,CAAA;CAC1B,KAAG,mBAMH,CAAA"}
@@ -20,7 +20,7 @@ export type WallMaterialOverride = {
20
20
  owned?: Material[];
21
21
  };
22
22
  };
23
- export type WallMaterialsResolver = (wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials) => WallMaterials;
23
+ export type WallMaterialsResolver = (wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials, finishRefs?: readonly string[]) => WallMaterials;
24
24
  /** Lazy light-emissive selection variant of a wall's material array (keeps texture). */
25
25
  export declare function getSelectionHighlightMaterials(materials: WallMaterialArray): WallMaterialArray;
26
26
  /**
@@ -29,8 +29,13 @@ export declare function getSelectionHighlightMaterials(materials: WallMaterialAr
29
29
  * film so the wall the click would select reads under the cursor).
30
30
  */
31
31
  export declare function getHoverHighlightMaterials(materials: WallMaterialArray): WallMaterialArray;
32
- export declare function getWallMaterialHash(wallNode: WallNode, shading: RenderShading, sceneMaterials?: SceneMaterials, overrideHash?: string): string;
33
- export declare function getMaterialsForWall(wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials, override?: WallMaterialOverride): WallMaterials;
34
- export declare function getVisibleWallMaterials(wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials): WallMaterialArray;
32
+ export declare function getWallMaterialHash(wallNode: WallNode, shading: RenderShading, sceneMaterials?: SceneMaterials, finishRefs?: readonly string[], overrideHash?: string): string;
33
+ /**
34
+ * A wall's material palette: 0 caps/edges (themed role), 1 face a, 2 face b,
35
+ * then one entry per `finishRefs` — the region and room finishes the built
36
+ * geometry's groups point at (`getWallFinishRefs(mesh.geometry)`).
37
+ */
38
+ export declare function getMaterialsForWall(wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials, finishRefs?: readonly string[], override?: WallMaterialOverride): WallMaterials;
39
+ export declare function getVisibleWallMaterials(wallNode: WallNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string, sceneMaterials?: SceneMaterials, finishRefs?: readonly string[]): WallMaterialArray;
35
40
  export {};
36
41
  //# sourceMappingURL=wall-materials.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wall-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,eAAe,EAGpB,KAAK,QAAQ,EAId,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAG5C,OAAO,EAEL,KAAK,WAAW,EAMhB,KAAK,aAAa,EAGnB,MAAM,qBAAqB,CAAA;AAE5B,KAAK,cAAc,GAAG,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,SAAS,CAAA;AAexE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,EAAE,CAAA;AAE1C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,iBAAiB,CAAA;IAC1B,SAAS,EAAE,iBAAiB,CAAA;IAC5B,WAAW,EAAE,iBAAiB,CAAA;IAC9B,aAAa,EAAE,iBAAiB,CAAA;IAChC,eAAe,EAAE,iBAAiB,CAAA;IAClC,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAA;CAC1B;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM;QAAE,OAAO,EAAE,iBAAiB,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAA;CACjE,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,aAAa,EACvB,QAAQ,CAAC,EAAE,OAAO,EAClB,WAAW,CAAC,EAAE,WAAW,EACzB,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,KAC5B,aAAa,CAAA;AA+SlB,wFAAwF;AACxF,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,CAS9F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,CAS1F;AA4DD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,aAAa,EACtB,cAAc,CAAC,EAAE,cAAc,EAC/B,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAeR;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,EAC/B,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,aAAa,CAqIf;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,GAC9B,iBAAiB,CAGnB"}
1
+ {"version":3,"file":"wall-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,aAAa,EAClB,KAAK,eAAe,EAGpB,KAAK,QAAQ,EAId,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAS5C,OAAO,EAEL,KAAK,WAAW,EAMhB,KAAK,aAAa,EAInB,MAAM,qBAAqB,CAAA;AAE5B,KAAK,cAAc,GAAG,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,SAAS,CAAA;AAexE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,EAAE,CAAA;AAE1C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,iBAAiB,CAAA;IAC1B,SAAS,EAAE,iBAAiB,CAAA;IAC5B,WAAW,EAAE,iBAAiB,CAAA;IAC9B,aAAa,EAAE,iBAAiB,CAAA;IAChC,eAAe,EAAE,iBAAiB,CAAA;IAClC,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAA;CAC1B;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM;QAAE,OAAO,EAAE,iBAAiB,CAAC;QAAC,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;KAAE,CAAA;CACjE,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,CAClC,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,aAAa,EACvB,QAAQ,CAAC,EAAE,OAAO,EAClB,WAAW,CAAC,EAAE,WAAW,EACzB,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,EAC/B,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,KAC3B,aAAa,CAAA;AA4SlB,wFAAwF;AACxF,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,CAS9F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,CAS1F;AA4DD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,aAAa,EACtB,cAAc,CAAC,EAAE,cAAc,EAC/B,UAAU,GAAE,SAAS,MAAM,EAAO,EAClC,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAQR;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,EAC/B,UAAU,GAAE,SAAS,MAAM,EAAO,EAClC,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,aAAa,CAmFf;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,EAClB,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,cAAc,EAC/B,UAAU,GAAE,SAAS,MAAM,EAAO,GACjC,iBAAiB,CAUnB"}