@pascal-app/viewer 1.0.3 → 1.0.4

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 (164) hide show
  1. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  2. package/dist/components/renderers/node-renderer.js +8 -3
  3. package/dist/components/viewer/frame-limiter.d.ts +1 -0
  4. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  5. package/dist/components/viewer/frame-limiter.js +22 -3
  6. package/dist/components/viewer/glb-interactive.d.ts +25 -1
  7. package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
  8. package/dist/components/viewer/glb-interactive.js +252 -85
  9. package/dist/components/viewer/glb-mechanisms.d.ts +10 -0
  10. package/dist/components/viewer/glb-mechanisms.d.ts.map +1 -0
  11. package/dist/components/viewer/glb-mechanisms.js +19 -0
  12. package/dist/components/viewer/glb-scene.d.ts +45 -27
  13. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  14. package/dist/components/viewer/glb-scene.js +420 -506
  15. package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
  16. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  17. package/dist/components/viewer/glb-walkthrough-controller.js +12 -1
  18. package/dist/components/viewer/index.d.ts +14 -0
  19. package/dist/components/viewer/index.d.ts.map +1 -1
  20. package/dist/components/viewer/index.js +84 -26
  21. package/dist/components/viewer/lights.d.ts.map +1 -1
  22. package/dist/components/viewer/lights.js +6 -1
  23. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  24. package/dist/components/viewer/perf-monitor.js +6 -0
  25. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  26. package/dist/components/viewer/post-processing.js +8 -3
  27. package/dist/components/viewer/registered-systems.d.ts.map +1 -1
  28. package/dist/components/viewer/registered-systems.js +16 -2
  29. package/dist/components/viewer/render-error.d.ts +18 -0
  30. package/dist/components/viewer/render-error.d.ts.map +1 -0
  31. package/dist/components/viewer/render-error.js +29 -0
  32. package/dist/components/viewer/scene-ground-replacement.d.ts +6 -0
  33. package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -1
  34. package/dist/components/viewer/scene-ground-replacement.js +15 -0
  35. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  36. package/dist/components/viewer/selection-manager.js +10 -0
  37. package/dist/components/viewer/viewer-camera.d.ts +4 -0
  38. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  39. package/dist/components/viewer/viewer-camera.js +11 -1
  40. package/dist/index.d.ts +22 -11
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +20 -14
  43. package/dist/lib/asset-url.d.ts.map +1 -1
  44. package/dist/lib/asset-url.js +7 -1
  45. package/dist/lib/display-state.d.ts +25 -0
  46. package/dist/lib/display-state.d.ts.map +1 -0
  47. package/dist/lib/display-state.js +1 -0
  48. package/dist/lib/hero-pose.d.ts +12 -2
  49. package/dist/lib/hero-pose.d.ts.map +1 -1
  50. package/dist/lib/hero-pose.js +72 -5
  51. package/dist/lib/ink-edges.d.ts +4 -2
  52. package/dist/lib/ink-edges.d.ts.map +1 -1
  53. package/dist/lib/ink-edges.js +17 -7
  54. package/dist/lib/key-light-override.d.ts +11 -0
  55. package/dist/lib/key-light-override.d.ts.map +1 -0
  56. package/dist/lib/key-light-override.js +14 -0
  57. package/dist/lib/live-frame-hold.d.ts +14 -0
  58. package/dist/lib/live-frame-hold.d.ts.map +1 -0
  59. package/dist/lib/live-frame-hold.js +28 -0
  60. package/dist/lib/materials.d.ts +14 -7
  61. package/dist/lib/materials.d.ts.map +1 -1
  62. package/dist/lib/materials.js +31 -5
  63. package/dist/lib/node-top-surface-height.d.ts +6 -0
  64. package/dist/lib/node-top-surface-height.d.ts.map +1 -0
  65. package/dist/lib/node-top-surface-height.js +74 -0
  66. package/dist/lib/perf-probe-state.d.ts +39 -0
  67. package/dist/lib/perf-probe-state.d.ts.map +1 -0
  68. package/dist/lib/perf-probe-state.js +17 -0
  69. package/dist/lib/pointer-events.d.ts +8 -0
  70. package/dist/lib/pointer-events.d.ts.map +1 -1
  71. package/dist/lib/pointer-events.js +27 -4
  72. package/dist/lib/procedural-emission.d.ts +7 -0
  73. package/dist/lib/procedural-emission.d.ts.map +1 -0
  74. package/dist/lib/procedural-emission.js +54 -0
  75. package/dist/lib/scene-support-height.d.ts +4 -0
  76. package/dist/lib/scene-support-height.d.ts.map +1 -0
  77. package/dist/lib/scene-support-height.js +34 -0
  78. package/dist/lib/scene-visibility.d.ts +6 -0
  79. package/dist/lib/scene-visibility.d.ts.map +1 -1
  80. package/dist/lib/scene-visibility.js +15 -3
  81. package/dist/lib/scripted-opening.d.ts +11 -0
  82. package/dist/lib/scripted-opening.d.ts.map +1 -0
  83. package/dist/lib/scripted-opening.js +20 -0
  84. package/dist/lib/snapshot-pipeline.d.ts +32 -2
  85. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  86. package/dist/lib/snapshot-pipeline.js +215 -105
  87. package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
  88. package/dist/lib/terrain-perimeter-fill.js +22 -14
  89. package/dist/lib/zone-geometry.d.ts +11 -0
  90. package/dist/lib/zone-geometry.d.ts.map +1 -0
  91. package/dist/lib/zone-geometry.js +46 -0
  92. package/dist/store/use-item-light-pool.d.ts +14 -10
  93. package/dist/store/use-item-light-pool.d.ts.map +1 -1
  94. package/dist/store/use-item-light-pool.js +52 -26
  95. package/dist/store/use-viewer.d.ts +11 -0
  96. package/dist/store/use-viewer.d.ts.map +1 -1
  97. package/dist/store/use-viewer.js +13 -6
  98. package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
  99. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  100. package/dist/systems/ceiling/ceiling-system.js +86 -6
  101. package/dist/systems/door/door-system.d.ts.map +1 -1
  102. package/dist/systems/door/door-system.js +55 -17
  103. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
  104. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  105. package/dist/systems/floor-elevation/floor-elevation-system.js +33 -5
  106. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  107. package/dist/systems/geometry/geometry-system.js +71 -11
  108. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  109. package/dist/systems/interactive/interactive-system.js +40 -15
  110. package/dist/systems/interactive/procedural-controls.d.ts +13 -0
  111. package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
  112. package/dist/systems/interactive/procedural-controls.js +17 -0
  113. package/dist/systems/interactive/scripted-clips.d.ts +19 -0
  114. package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
  115. package/dist/systems/interactive/scripted-clips.js +134 -0
  116. package/dist/systems/item/item-system.d.ts.map +1 -1
  117. package/dist/systems/item/item-system.js +9 -3
  118. package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
  119. package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
  120. package/dist/systems/item/object-cut-invalidation.js +68 -0
  121. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  122. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  123. package/dist/systems/item-light/item-light-system.js +47 -52
  124. package/dist/systems/level/level-system.d.ts.map +1 -1
  125. package/dist/systems/level/level-system.js +3 -5
  126. package/dist/systems/level/level-utils.d.ts +7 -4
  127. package/dist/systems/level/level-utils.d.ts.map +1 -1
  128. package/dist/systems/level/level-utils.js +17 -6
  129. package/dist/systems/level/shadow-only-levels.d.ts +9 -0
  130. package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
  131. package/dist/systems/level/shadow-only-levels.js +7 -0
  132. package/dist/systems/roof/roof-materials.d.ts +14 -2
  133. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  134. package/dist/systems/roof/roof-materials.js +54 -15
  135. package/dist/systems/roof/roof-system.d.ts +13 -3
  136. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  137. package/dist/systems/roof/roof-system.js +188 -13
  138. package/dist/systems/slab/slab-system.d.ts +1 -1
  139. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  140. package/dist/systems/slab/slab-system.js +49 -19
  141. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  142. package/dist/systems/stair/stair-system.js +133 -354
  143. package/dist/systems/wall/wall-cutout-cache.d.ts +7 -0
  144. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  145. package/dist/systems/wall/wall-cutout-cache.js +46 -17
  146. package/dist/systems/wall/wall-cutout.d.ts +6 -0
  147. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  148. package/dist/systems/wall/wall-cutout.js +42 -28
  149. package/dist/systems/wall/wall-finish-data.d.ts +22 -0
  150. package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
  151. package/dist/systems/wall/wall-finish-data.js +17 -0
  152. package/dist/systems/wall/wall-materials.d.ts +9 -4
  153. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  154. package/dist/systems/wall/wall-materials.js +99 -152
  155. package/dist/systems/wall/wall-system.d.ts +28 -2
  156. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  157. package/dist/systems/wall/wall-system.js +582 -143
  158. package/dist/systems/window/window-system.d.ts +1 -1
  159. package/dist/systems/window/window-system.d.ts.map +1 -1
  160. package/dist/systems/window/window-system.js +41 -22
  161. package/package.json +5 -5
  162. package/dist/systems/fence/fence-system.d.ts +0 -22
  163. package/dist/systems/fence/fence-system.d.ts.map +0 -1
  164. package/dist/systems/fence/fence-system.js +0 -357
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { findLevelAncestorId, sceneRegistry, useInteractive, useScene } from '@pascal-app/core';
3
- import { useFrame } from '@react-three/fiber';
2
+ import { findLevelAncestorId, sceneRegistry, useScene } from '@pascal-app/core';
3
+ import { useFrame, useThree } from '@react-three/fiber';
4
4
  import { useRef } from 'react';
5
5
  import { MathUtils, Vector3 } from 'three';
6
+ import { SCENE_LAYER } from '../../lib/layers';
6
7
  import { useItemLightPool } from '../../store/use-item-light-pool';
7
8
  import useViewer from '../../store/use-viewer';
8
9
  const POOL_SIZE = 12;
@@ -19,22 +20,30 @@ const _dir = new Vector3();
19
20
  const _camPos = new Vector3();
20
21
  const _camFwd = new Vector3();
21
22
  const _itemPos = new Vector3();
22
- function scoreRegistration(reg, nodes, selectedLevelId, levelMode, interactiveState) {
23
- // Skip lights that are toggled off — they contribute no illumination
24
- if (reg.toggleIndex >= 0) {
25
- const values = interactiveState.items[reg.nodeId]?.controlValues;
26
- const isOn = Boolean(values?.[reg.toggleIndex]);
27
- if (!isOn)
23
+ function isRendered(nodeId) {
24
+ let object = sceneRegistry.nodes.get(nodeId);
25
+ if (!object?.layers.isEnabled(SCENE_LAYER))
26
+ return false;
27
+ while (object) {
28
+ if (!object.visible)
29
+ return false;
30
+ object = object.parent;
31
+ }
32
+ return true;
33
+ }
34
+ function scoreRegistration(reg, nodes, selectedLevelId, levelMode, hideAbove) {
35
+ if (!reg.isEligible() || !isRendered(reg.nodeId) || !reg.getWorldPosition(_itemPos))
36
+ return Number.POSITIVE_INFINITY;
37
+ const { nodeId } = reg;
38
+ let current = nodes[nodeId];
39
+ while (current) {
40
+ if (current.type !== 'site' && current.visible === false)
28
41
  return Number.POSITIVE_INFINITY;
42
+ current = current.parentId ? nodes[current.parentId] : undefined;
29
43
  }
30
- const { nodeId, effect } = reg;
31
- const obj = sceneRegistry.nodes.get(nodeId);
32
- if (!obj)
44
+ const itemLevelId = findLevelAncestorId(nodeId, nodes);
45
+ if (selectedLevelId && itemLevelId !== selectedLevelId && levelMode === 'solo')
33
46
  return Number.POSITIVE_INFINITY;
34
- obj.getWorldPosition(_itemPos);
35
- _itemPos.x += effect.offset[0];
36
- _itemPos.y += effect.offset[1];
37
- _itemPos.z += effect.offset[2];
38
47
  _dir.copy(_itemPos).sub(_camPos).normalize();
39
48
  const dot = _camFwd.dot(_dir); // 1 = ahead, -1 = behind
40
49
  // Angular component (0 = dead ahead, 2 = directly behind)
@@ -42,12 +51,15 @@ function scoreRegistration(reg, nodes, selectedLevelId, levelMode, interactiveSt
42
51
  // Normalised distance component (assumes scenes < 200 units)
43
52
  const dist = _camPos.distanceTo(_itemPos) / 200;
44
53
  // ── Level factor ──────────────────────────────────────────────────────────
45
- const itemLevelId = findLevelAncestorId(nodeId, nodes);
46
54
  let levelPenalty = 0;
47
55
  if (selectedLevelId) {
48
56
  if (itemLevelId !== selectedLevelId) {
49
- // In solo mode items on other levels are invisible — deprioritize strongly
50
- levelPenalty = levelMode === 'solo' ? 100 : 0.8;
57
+ // Items on hidden levels (every other one in solo, those above the
58
+ // selected one while editing) are invisible — deprioritize strongly
59
+ const itemIndex = nodes[itemLevelId]?.level ?? 0;
60
+ const selectedIndex = nodes[selectedLevelId]?.level ?? 0;
61
+ const hidden = levelMode === 'solo' || (hideAbove && itemIndex > selectedIndex);
62
+ levelPenalty = hidden ? 100 : 0.8;
51
63
  }
52
64
  }
53
65
  else if (itemLevelId) {
@@ -60,6 +72,8 @@ function scoreRegistration(reg, nodes, selectedLevelId, levelMode, interactiveSt
60
72
  return angular * 0.7 + dist * 0.3 + levelPenalty;
61
73
  }
62
74
  export function ItemLightSystem() {
75
+ const scene = useThree((state) => state.scene);
76
+ const bakedOwner = useItemLightPool((state) => state.bakedCanvases.has(scene));
63
77
  const lightRefs = useRef(Array.from({ length: POOL_SIZE }, () => null));
64
78
  const slots = useRef(Array.from({ length: POOL_SIZE }, () => ({ key: null, pendingKey: null, isFadingOut: false })));
65
79
  const reassignTimer = useRef(0);
@@ -67,9 +81,10 @@ export function ItemLightSystem() {
67
81
  const prevReassignCamPos = useRef(new Vector3());
68
82
  const prevReassignCamFwd = useRef(new Vector3(0, 0, -1));
69
83
  useFrame(({ camera }, delta) => {
84
+ if (bakedOwner)
85
+ return;
70
86
  const dt = Math.min(delta, 0.1);
71
87
  const { registrations } = useItemLightPool.getState();
72
- const interactiveState = useInteractive.getState();
73
88
  // ── 1. Throttled priority reassignment ──────────────────────────────────
74
89
  camera.getWorldPosition(_camPos);
75
90
  camera.getWorldDirection(_camFwd);
@@ -86,12 +101,13 @@ export function ItemLightSystem() {
86
101
  const viewerState = useViewer.getState();
87
102
  const selectedLevelId = viewerState.selection.levelId;
88
103
  const levelMode = viewerState.levelMode;
104
+ const hideAbove = viewerState.hideLevelsAboveSelection;
89
105
  // Score every registration
90
106
  const scored = [];
91
107
  for (const [key, reg] of registrations) {
92
108
  scored.push({
93
109
  key,
94
- score: scoreRegistration(reg, nodes, selectedLevelId, levelMode, interactiveState),
110
+ score: scoreRegistration(reg, nodes, selectedLevelId, levelMode, hideAbove),
95
111
  });
96
112
  }
97
113
  scored.sort((a, b) => a.score - b.score);
@@ -155,8 +171,8 @@ export function ItemLightSystem() {
155
171
  const light = lightRefs.current[freeSlot];
156
172
  const reg = registrations.get(key);
157
173
  if (light && reg) {
158
- light.color.set(reg.effect.color);
159
- light.distance = reg.effect.distance ?? 0;
174
+ light.color.set(reg.color);
175
+ light.distance = reg.distance;
160
176
  }
161
177
  }
162
178
  }
@@ -183,19 +199,17 @@ export function ItemLightSystem() {
183
199
  continue;
184
200
  // Fade-out phase: lerp intensity → 0, then complete the transition
185
201
  if (slot.isFadingOut) {
186
- light.visible = true;
187
202
  light.intensity = MathUtils.lerp(light.intensity, 0, dt * 12);
188
203
  if (light.intensity < 0.01) {
189
204
  light.intensity = 0;
190
- light.visible = false;
191
205
  slot.isFadingOut = false;
192
206
  slot.key = slot.pendingKey;
193
207
  slot.pendingKey = null;
194
208
  if (slot.key) {
195
209
  const reg = registrations.get(slot.key);
196
210
  if (reg) {
197
- light.color.set(reg.effect.color);
198
- light.distance = reg.effect.distance ?? 0;
211
+ light.color.set(reg.color);
212
+ light.distance = reg.distance;
199
213
  }
200
214
  }
201
215
  }
@@ -204,45 +218,26 @@ export function ItemLightSystem() {
204
218
  if (!slot.key) {
205
219
  // Idle slot — keep dark
206
220
  light.intensity = 0;
207
- light.visible = false;
208
221
  continue;
209
222
  }
210
223
  const reg = registrations.get(slot.key);
211
224
  if (!reg) {
212
225
  slot.key = null;
213
226
  light.intensity = 0;
214
- light.visible = false;
215
227
  continue;
216
228
  }
217
- // Snap world position each frame
218
- const obj = sceneRegistry.nodes.get(reg.nodeId);
219
- if (obj) {
220
- obj.getWorldPosition(_itemPos);
221
- const [ox, oy, oz] = reg.effect.offset;
222
- light.position.set(_itemPos.x + ox, _itemPos.y + oy, _itemPos.z + oz);
223
- }
224
- // Compute target intensity
225
- const values = interactiveState.items[reg.nodeId]?.controlValues;
226
- const isOn = reg.toggleIndex >= 0 ? Boolean(values?.[reg.toggleIndex]) : true;
227
- let t = 1;
228
- if (reg.hasSlider) {
229
- const raw = values?.[reg.sliderIndex] ?? reg.sliderMin;
230
- t = (raw - reg.sliderMin) / (reg.sliderMax - reg.sliderMin);
231
- }
232
- const targetIntensity = isOn
233
- ? MathUtils.lerp(reg.effect.intensityRange[0], reg.effect.intensityRange[1], t)
234
- : reg.effect.intensityRange[0];
235
- if (targetIntensity > 0) {
236
- light.visible = true;
237
- }
229
+ if (reg.getWorldPosition(_itemPos))
230
+ light.position.copy(_itemPos);
231
+ const targetIntensity = reg.isEligible() && isRendered(reg.nodeId) ? reg.getIntensity() : 0;
238
232
  light.intensity = MathUtils.lerp(light.intensity, targetIntensity, dt * 12);
239
233
  if (targetIntensity <= 0 && light.intensity < 0.01) {
240
234
  light.intensity = 0;
241
- light.visible = false;
242
235
  }
243
236
  }
244
- });
245
- return (_jsx(_Fragment, { children: Array.from({ length: POOL_SIZE }, (_, i) => (_jsx("pointLight", { castShadow: false, intensity: 0, visible: false, ref: (el) => {
237
+ }, 6);
238
+ if (bakedOwner)
239
+ return null;
240
+ return (_jsx(_Fragment, { children: Array.from({ length: POOL_SIZE }, (_, i) => (_jsx("pointLight", { castShadow: false, intensity: 0, visible: true, ref: (el) => {
246
241
  lightRefs.current[i] = el;
247
242
  } }, i))) }));
248
243
  }
@@ -1 +1 @@
1
- {"version":3,"file":"level-system.d.ts","sourceRoot":"","sources":["../../../src/systems/level/level-system.tsx"],"names":[],"mappings":"AAcA,eAAO,MAAM,WAAW,YA6DvB,CAAA"}
1
+ {"version":3,"file":"level-system.d.ts","sourceRoot":"","sources":["../../../src/systems/level/level-system.tsx"],"names":[],"mappings":"AAQA,eAAO,MAAM,WAAW,YA+DvB,CAAA"}
@@ -4,15 +4,12 @@ import { lerp } from 'three/src/math/MathUtils.js';
4
4
  import { applyShadowOnly, clearShadowOnly } from '../../lib/shadow-only';
5
5
  import useViewer from '../../store/use-viewer';
6
6
  import { EXPLODED_GAP, resolveLevelVisibility } from './level-utils';
7
- // Levels currently in shadow-caster-only mode (solo hides them from the color
8
- // passes but keeps their sun shadows). Tracked so we can restore layer masks
9
- // exactly once on transition; apply re-runs every frame so meshes rebuilt
10
- // while hidden (theme/texture changes) get re-hidden.
11
- const shadowOnlyLevels = new WeakSet();
7
+ import { shadowOnlyLevels } from './shadow-only-levels';
12
8
  export const LevelSystem = () => {
13
9
  useFrame((_, delta) => {
14
10
  const nodes = useScene.getState().nodes;
15
11
  const levelMode = useViewer.getState().levelMode;
12
+ const hideAbove = useViewer.getState().hideLevelsAboveSelection;
16
13
  const selectedLevel = useViewer.getState().selection.levelId;
17
14
  const levelElevations = getLevelElevations(nodes);
18
15
  const entries = [];
@@ -44,6 +41,7 @@ export const LevelSystem = () => {
44
41
  obj.position.y = lerp(obj.position.y, targetY, Math.min(1, delta * 12));
45
42
  const { visible, shadowOnly } = resolveLevelVisibility({
46
43
  levelMode,
44
+ hideAbove,
47
45
  hasSelectedLevel: Boolean(selectedLevel),
48
46
  isSelected: level?.id === selectedLevel,
49
47
  index,
@@ -12,14 +12,17 @@ export declare function getLevelPresentationY(levelId: string, nodes: Record<str
12
12
  * shadow-caster only.
13
13
  *
14
14
  * Two unrelated things hide a level and they do not compose: the author's own
15
- * `visible` flag (the sidebar eye), which hides the floor outright, and solo
16
- * mode, which hides every level but the soloed one — keeping the levels ABOVE
17
- * it in the shadow map so the sun still shadows the soloed floor through them.
15
+ * `visible` flag (the sidebar eye), which hides the floor outright, and
16
+ * presentation hiding — solo mode hides every level but the soloed one, and
17
+ * the editor's level display (`hideAbove`) hides the levels above the
18
+ * selected one. Presentation-hidden levels ABOVE the selected one stay in the
19
+ * shadow map so the sun still shadows the selected floor through them.
18
20
  * A level the author hid never enters that shadow-caster branch: its shadows
19
21
  * on the floor below would be exactly what hiding it was meant to remove.
20
22
  */
21
- export declare function resolveLevelVisibility({ levelMode, hasSelectedLevel, isSelected, index, selectedIndex, nodeVisible, }: {
23
+ export declare function resolveLevelVisibility({ levelMode, hideAbove, hasSelectedLevel, isSelected, index, selectedIndex, nodeVisible, }: {
22
24
  levelMode: 'stacked' | 'exploded' | 'solo' | 'manual';
25
+ hideAbove: boolean;
23
26
  hasSelectedLevel: boolean;
24
27
  isSelected: boolean;
25
28
  index: number;
@@ -1 +1 @@
1
- {"version":3,"file":"level-utils.d.ts","sourceRoot":"","sources":["../../../src/systems/level/level-utils.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,IAAI,CAAA;AAE7B;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GACpD,MAAM,CAKR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,KAAK,EACL,aAAa,EACb,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAA;IACrD,gBAAgB,EAAE,OAAO,CAAA;IACzB,UAAU,EAAE,OAAO,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAM5C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,IAAI,CA2CtD"}
1
+ {"version":3,"file":"level-utils.d.ts","sourceRoot":"","sources":["../../../src/systems/level/level-utils.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY,IAAI,CAAA;AAE7B;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,GACpD,MAAM,CAKR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,UAAU,EACV,KAAK,EACL,aAAa,EACb,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAA;IACrD,SAAS,EAAE,OAAO,CAAA;IAClB,gBAAgB,EAAE,OAAO,CAAA;IACzB,UAAU,EAAE,OAAO,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAQ5C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,IAAI,CA+CtD"}
@@ -1,4 +1,6 @@
1
1
  import { getLevelElevations, sceneRegistry, useScene } from '@pascal-app/core';
2
+ import { applyShadowOnly, clearShadowOnly } from '../../lib/shadow-only';
3
+ import { shadowOnlyLevels } from './shadow-only-levels';
2
4
  export const EXPLODED_GAP = 5;
3
5
  /**
4
6
  * The Y a level settles at under the given presentation mode — its stacked
@@ -18,16 +20,19 @@ export function getLevelPresentationY(levelId, nodes, levelMode) {
18
20
  * shadow-caster only.
19
21
  *
20
22
  * Two unrelated things hide a level and they do not compose: the author's own
21
- * `visible` flag (the sidebar eye), which hides the floor outright, and solo
22
- * mode, which hides every level but the soloed one — keeping the levels ABOVE
23
- * it in the shadow map so the sun still shadows the soloed floor through them.
23
+ * `visible` flag (the sidebar eye), which hides the floor outright, and
24
+ * presentation hiding — solo mode hides every level but the soloed one, and
25
+ * the editor's level display (`hideAbove`) hides the levels above the
26
+ * selected one. Presentation-hidden levels ABOVE the selected one stay in the
27
+ * shadow map so the sun still shadows the selected floor through them.
24
28
  * A level the author hid never enters that shadow-caster branch: its shadows
25
29
  * on the floor below would be exactly what hiding it was meant to remove.
26
30
  */
27
- export function resolveLevelVisibility({ levelMode, hasSelectedLevel, isSelected, index, selectedIndex, nodeVisible, }) {
31
+ export function resolveLevelVisibility({ levelMode, hideAbove, hasSelectedLevel, isSelected, index, selectedIndex, nodeVisible, }) {
28
32
  if (!nodeVisible)
29
33
  return { visible: false, shadowOnly: false };
30
- const hidden = levelMode === 'solo' && hasSelectedLevel && !isSelected;
34
+ const hidden = (levelMode === 'solo' && hasSelectedLevel && !isSelected) ||
35
+ (hideAbove && selectedIndex !== undefined && index > selectedIndex);
31
36
  const shadowOnly = hidden && selectedIndex !== undefined && index > selectedIndex;
32
37
  return { visible: shadowOnly || !hidden, shadowOnly };
33
38
  }
@@ -62,11 +67,15 @@ export function snapLevelsToTruePositions() {
62
67
  const levelElevations = getLevelElevations(nodes);
63
68
  // Snapshot current Y and visibility so we can restore them after the render
64
69
  const snapshot = new Map(entries.map(({ levelId, obj }) => [levelId, { y: obj.position.y, visible: obj.visible }]));
65
- // Snap to true stacked positions and undo presentation hiding
70
+ // Snap to true stacked positions and undo presentation hiding — including
71
+ // the levels kept shadow-caster-only above the selected level.
72
+ const shadowOnly = entries.filter(({ obj }) => shadowOnlyLevels.has(obj));
66
73
  for (const { levelId, obj, nodeVisible } of entries) {
67
74
  obj.position.y = levelElevations.get(levelId)?.baseY ?? 0;
68
75
  obj.visible = nodeVisible;
69
76
  }
77
+ for (const { obj } of shadowOnly)
78
+ clearShadowOnly(obj);
70
79
  return () => {
71
80
  for (const { levelId, obj } of entries) {
72
81
  const saved = snapshot.get(levelId);
@@ -75,5 +84,7 @@ export function snapLevelsToTruePositions() {
75
84
  obj.visible = saved.visible;
76
85
  }
77
86
  }
87
+ for (const { obj } of shadowOnly)
88
+ applyShadowOnly(obj);
78
89
  };
79
90
  }
@@ -0,0 +1,9 @@
1
+ import type { Object3D } from 'three';
2
+ /**
3
+ * Levels currently in shadow-caster-only mode (hidden above the selected level
4
+ * by solo or by the editor's level display). Tracked so layer masks are
5
+ * restored exactly once on transition, and so a full-building capture can
6
+ * show them for its one render.
7
+ */
8
+ export declare const shadowOnlyLevels: WeakSet<Object3D<import("three").Object3DEventMap>>;
9
+ //# sourceMappingURL=shadow-only-levels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow-only-levels.d.ts","sourceRoot":"","sources":["../../../src/systems/level/shadow-only-levels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAErC;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,qDAA0B,CAAA"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Levels currently in shadow-caster-only mode (hidden above the selected level
3
+ * by solo or by the editor's level display). Tracked so layer masks are
4
+ * restored exactly once on transition, and so a full-building capture can
5
+ * show them for its one render.
6
+ */
7
+ export const shadowOnlyLevels = new WeakSet();
@@ -1,6 +1,18 @@
1
- import { type RoofNode } from '@pascal-app/core';
1
+ import { type AnyNode, type RoofNode, type SceneMaterial, type SceneMaterialId } from '@pascal-app/core';
2
2
  import type * as THREE from 'three';
3
3
  import { type ColorPreset, type RenderShading } from '../../lib/materials';
4
+ type SceneMaterials = Record<SceneMaterialId, SceneMaterial> | undefined;
4
5
  export type RoofMaterialArray = [THREE.Material, THREE.Material, THREE.Material, THREE.Material];
5
- export declare function getRoofMaterialArray(node: RoofNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string): RoofMaterialArray | null;
6
+ /**
7
+ * The cladding the walls under a roof declare through their ASSEMBLIES
8
+ * (`wall.assembly.exterior.finish` → catalog ref), so the roof's gable band —
9
+ * the triangle of wall above the plate that the roof kind builds — is skinned
10
+ * like the walls it sits on instead of the bare drywall default. The most
11
+ * common ref among the level's walls wins; null when none declares one.
12
+ */
13
+ export declare function levelWallCladdingRef(nodes: Readonly<Record<string, AnyNode | undefined>>, roof: Pick<RoofNode, 'parentId'>): string | null;
14
+ export declare function getRoofMaterialArray(node: RoofNode, shading?: RenderShading, textures?: boolean, colorPreset?: ColorPreset, sceneTheme?: string,
15
+ /** Catalog ref for the gable/trim band when unpainted — see `levelWallCladdingRef`. */
16
+ wallCladdingRef?: string | null, sceneMaterials?: SceneMaterials): RoofMaterialArray | null;
17
+ export {};
6
18
  //# sourceMappingURL=roof-materials.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"roof-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AACnC,OAAO,EACL,KAAK,WAAW,EAIhB,KAAK,aAAa,EAEnB,MAAM,qBAAqB,CAAA;AAa5B,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;AA6BhG,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM,GAClB,iBAAiB,GAAG,IAAI,CAsE1B"}
1
+ {"version":3,"file":"roof-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAGZ,KAAK,QAAQ,EAEb,KAAK,aAAa,EAClB,KAAK,eAAe,EAErB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AACnC,OAAO,EACL,KAAK,WAAW,EAGhB,KAAK,aAAa,EAGnB,MAAM,qBAAqB,CAAA;AAE5B,KAAK,cAAc,GAAG,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,SAAS,CAAA;AAaxE,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;AAqChG;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC,CAAC,EACpD,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,GAC/B,MAAM,GAAG,IAAI,CAkBf;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE,aAA0B,EACnC,QAAQ,UAAO,EACf,WAAW,GAAE,WAAoB,EACjC,UAAU,CAAC,EAAE,MAAM;AACnB,uFAAuF;AACvF,eAAe,GAAE,MAAM,GAAG,IAAW,EACrC,cAAc,CAAC,EAAE,cAAc,GAC9B,iBAAiB,GAAG,IAAI,CAyE1B"}
@@ -1,5 +1,5 @@
1
- import { getEffectiveRoofSurfaceMaterial, } from '@pascal-app/core';
2
- import { createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, resolveSlotDefaultMaterial, } from '../../lib/materials';
1
+ import { getEffectiveRoofSurfaceMaterial, parseMaterialRef, wallAssemblyFinishRef, } from '@pascal-app/core';
2
+ import { createMaterial, createSurfaceRoleMaterial, resolveMaterialRef, resolveSlotDefaultMaterial, } from '../../lib/materials';
3
3
  // Declared catalog defaults for an unpainted roof, per the 4-slot layout
4
4
  // (0 wall/trim · 1 deck · 2 interior soffit · 3 shingle top). The wall/trim
5
5
  // band mirrors the wall kind's default (WALL_SLOT_DEFAULT = concrete-drywall)
@@ -11,22 +11,59 @@ const ROOF_DEFAULT_REFS = [
11
11
  'library:roof-terracottatiles',
12
12
  ];
13
13
  const roofMaterialArrayCache = new Map();
14
- function getSurfaceMaterialSignature(spec) {
14
+ function getSurfaceMaterialSignature(spec, sceneMaterials) {
15
+ const ref = parseMaterialRef(spec.materialPreset);
15
16
  return JSON.stringify({
16
17
  material: spec.material ?? null,
17
18
  materialPreset: spec.materialPreset ?? null,
19
+ sceneMaterial: ref?.kind === 'scene'
20
+ ? (sceneMaterials?.[ref.id]?.material ?? null)
21
+ : null,
18
22
  });
19
23
  }
20
- function createResolvedMaterial(material, materialPreset, shading) {
21
- if (materialPreset) {
22
- return createMaterialFromPresetRef(materialPreset, shading);
23
- }
24
+ function createResolvedMaterial(material, materialPreset, sceneMaterials, shading) {
25
+ // An unknown or dangling ref resolves to nothing: fall through to the
26
+ // colour, then the slot default, as the segment renderer does.
27
+ const preset = resolveMaterialRef(materialPreset, sceneMaterials, shading);
28
+ if (preset)
29
+ return preset;
24
30
  if (material) {
25
31
  return createMaterial(material, shading);
26
32
  }
27
33
  return null;
28
34
  }
29
- export function getRoofMaterialArray(node, shading = 'rendered', textures = true, colorPreset = 'clay', sceneTheme) {
35
+ /**
36
+ * The cladding the walls under a roof declare through their ASSEMBLIES
37
+ * (`wall.assembly.exterior.finish` → catalog ref), so the roof's gable band —
38
+ * the triangle of wall above the plate that the roof kind builds — is skinned
39
+ * like the walls it sits on instead of the bare drywall default. The most
40
+ * common ref among the level's walls wins; null when none declares one.
41
+ */
42
+ export function levelWallCladdingRef(nodes, roof) {
43
+ const levelId = roof.parentId;
44
+ if (!levelId)
45
+ return null;
46
+ const counts = new Map();
47
+ for (const node of Object.values(nodes)) {
48
+ if (node?.type !== 'wall' || node.parentId !== levelId)
49
+ continue;
50
+ const ref = wallAssemblyFinishRef(node);
51
+ if (ref)
52
+ counts.set(ref, (counts.get(ref) ?? 0) + 1);
53
+ }
54
+ let best = null;
55
+ let bestCount = 0;
56
+ for (const [ref, count] of counts) {
57
+ if (count > bestCount) {
58
+ best = ref;
59
+ bestCount = count;
60
+ }
61
+ }
62
+ return best;
63
+ }
64
+ export function getRoofMaterialArray(node, shading = 'rendered', textures = true, colorPreset = 'clay', sceneTheme,
65
+ /** Catalog ref for the gable/trim band when unpainted — see `levelWallCladdingRef`. */
66
+ wallCladdingRef = null, sceneMaterials) {
30
67
  const top = getEffectiveRoofSurfaceMaterial(node, 'top');
31
68
  const edge = getEffectiveRoofSurfaceMaterial(node, 'edge');
32
69
  const wall = getEffectiveRoofSurfaceMaterial(node, 'wall');
@@ -35,9 +72,10 @@ export function getRoofMaterialArray(node, shading = 'rendered', textures = true
35
72
  textures,
36
73
  colorPreset,
37
74
  sceneTheme,
38
- top: getSurfaceMaterialSignature(top),
39
- edge: getSurfaceMaterialSignature(edge),
40
- wall: getSurfaceMaterialSignature(wall),
75
+ wallCladdingRef,
76
+ top: getSurfaceMaterialSignature(top, sceneMaterials),
77
+ edge: getSurfaceMaterialSignature(edge, sceneMaterials),
78
+ wall: getSurfaceMaterialSignature(wall, sceneMaterials),
41
79
  });
42
80
  const cached = roofMaterialArrayCache.get(cacheKey);
43
81
  if (cached)
@@ -63,14 +101,15 @@ export function getRoofMaterialArray(node, shading = 'rendered', textures = true
63
101
  // shingle, soft-white deck/soffit, wall-coloured trim). Used both when the
64
102
  // roof is unpainted and to fill any individual unpainted slot below.
65
103
  const defaultArray = [
66
- resolveSlotDefaultMaterial(ROOF_DEFAULT_REFS[0], shading),
104
+ resolveSlotDefaultMaterial(wallCladdingRef ?? ROOF_DEFAULT_REFS[0], shading),
67
105
  resolveSlotDefaultMaterial(ROOF_DEFAULT_REFS[1], shading),
68
106
  resolveSlotDefaultMaterial(ROOF_DEFAULT_REFS[2], shading),
69
107
  resolveSlotDefaultMaterial(ROOF_DEFAULT_REFS[3], shading),
70
108
  ];
71
- const topMaterial = createResolvedMaterial(top.material, top.materialPreset, shading);
72
- const edgeMaterial = createResolvedMaterial(edge.material, edge.materialPreset, shading);
73
- const wallMaterial = createResolvedMaterial(wall.material, wall.materialPreset, shading);
109
+ const resolve = (spec) => createResolvedMaterial(spec.material, spec.materialPreset, sceneMaterials, shading);
110
+ const topMaterial = resolve(top);
111
+ const edgeMaterial = resolve(edge);
112
+ const wallMaterial = resolve(wall);
74
113
  if (!(topMaterial || edgeMaterial || wallMaterial)) {
75
114
  roofMaterialArrayCache.set(cacheKey, defaultArray);
76
115
  return defaultArray;
@@ -1,6 +1,14 @@
1
1
  import { type AnyNode, type RoofSegmentNode } from '@pascal-app/core';
2
2
  import * as THREE from 'three';
3
3
  import { Brush } from 'three-bvh-csg';
4
+ /**
5
+ * Roofs whose merged shell is still to build: until it is, the roof shows its segments apart (or
6
+ * an old shell), and a capture would show that. Only roofs on screen count, a first merge (a
7
+ * mounted group with no shell yet) included: one never mounted stays queued and would hold every
8
+ * capture.
9
+ */
10
+ export declare function getPendingRoofMergeCount(): number;
11
+ export declare function countPendingRoofShells(pending: Iterable<string>, mounted: (id: string) => THREE.Object3D | undefined): number;
4
12
  export declare const RoofSystem: () => null;
5
13
  /**
6
14
  * Four dummy materials used as identity placeholders during CSG. Shared
@@ -36,10 +44,12 @@ export declare function mapRoofGroupMaterialIndex(groupMaterialIndex: number | u
36
44
  */
37
45
  export declare function clipGeometryBySegmentTrim(geometry: THREE.BufferGeometry | null, segment: RoofSegmentNode): THREE.BufferGeometry | null;
38
46
  /**
39
- * Generate complete hollow-shell geometry for a roof segment.
40
- * Ports the prototype's CSG approach using three-bvh-csg.
47
+ * `fascia`: build the segment's fascia boards into its deck. Only the roof
48
+ * renderer asks; a dormer, a chimney or a sibling's clip takes the bare shell.
41
49
  */
42
- export declare function getRoofSegmentBrushes(node: RoofSegmentNode): RoofSegmentBrushSet | null;
50
+ export declare function getRoofSegmentBrushes(node: RoofSegmentNode, options?: {
51
+ fascia?: boolean;
52
+ }): RoofSegmentBrushSet | null;
43
53
  export declare function generateRoofSegmentGeometry(node: RoofSegmentNode, nodes?: Record<string, AnyNode>): THREE.BufferGeometry;
44
54
  export declare function remapRoofShellFaces(geometry: THREE.BufferGeometry, node: RoofSegmentNode): void;
45
55
  export type SurfaceFrame = {
@@ -1 +1 @@
1
- {"version":3,"file":"roof-system.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAoBZ,KAAK,eAAe,EAQrB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAY,KAAK,EAA0B,MAAM,eAAe,CAAA;AAkMvE,eAAO,MAAM,UAAU,YAwItB,CAAA;AAgbD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE;IAC7B,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;CAMxB,CAAA;AAID,eAAO,MAAM,wBAAwB,IAAI,CAAA;AAEzC,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,KAAK,CAAA;IACf,QAAQ,EAAE,KAAK,CAAA;IACf,SAAS,EAAE,KAAK,CAAA;IAChB,UAAU,EAAE,KAAK,CAAA;IACjB,UAAU,EAAE,KAAK,CAAC,cAAc,GAAG,IAAI,CAAA;CACxC,CAAA;AA0FD,wBAAgB,yBAAyB,CACvC,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,YAAY,EAAE,KAAK,CAAC,QAAQ,EAAE,EAC9B,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,GACtC,MAAM,CAuBR;AAoWD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,KAAK,CAAC,cAAc,GAAG,IAAI,EACrC,OAAO,EAAE,eAAe,GACvB,KAAK,CAAC,cAAc,GAAG,IAAI,CAyB7B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,eAAe,GAAG,mBAAmB,GAAG,IAAI,CA8TvF;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,eAAe,EACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,KAAK,CAAC,cAAc,CA2GtB;AAuyCD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,eAAe,QA0ExF;AA+qBD,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAA;IACpB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,YAAY,CAqLd"}
1
+ {"version":3,"file":"roof-system.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAoBZ,KAAK,eAAe,EAQrB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAY,KAAK,EAA0B,MAAM,eAAe,CAAA;AAqIvE;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAIjD;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,EACzB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,KAAK,CAAC,QAAQ,GAAG,SAAS,GAClD,MAAM,CAIR;AA+DD,eAAO,MAAM,UAAU,YAwItB,CAAA;AAgbD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,EAAE;IAC7B,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;IACvB,KAAK,CAAC,iBAAiB;CAMxB,CAAA;AAID,eAAO,MAAM,wBAAwB,IAAI,CAAA;AAEzC,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,KAAK,CAAA;IACf,QAAQ,EAAE,KAAK,CAAA;IACf,SAAS,EAAE,KAAK,CAAA;IAChB,UAAU,EAAE,KAAK,CAAA;IACjB,UAAU,EAAE,KAAK,CAAC,cAAc,GAAG,IAAI,CAAA;CACxC,CAAA;AA4FD,wBAAgB,yBAAyB,CACvC,kBAAkB,EAAE,MAAM,GAAG,SAAS,EACtC,YAAY,EAAE,KAAK,CAAC,QAAQ,EAAE,EAC9B,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,GACtC,MAAM,CAuBR;AAuXD;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,KAAK,CAAC,cAAc,GAAG,IAAI,EACrC,OAAO,EAAE,eAAe,GACvB,KAAK,CAAC,cAAc,GAAG,IAAI,CAyB7B;AAoHD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,eAAe,EACrB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACjC,mBAAmB,GAAG,IAAI,CAyV5B;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,eAAe,EACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,KAAK,CAAC,cAAc,CA6GtB;AAuyCD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,eAAe,QA0ExF;AAirBD,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,KAAK,CAAC,OAAO,CAAA;IACpB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM,GACT,YAAY,CAqLd"}