@pascal-app/viewer 0.8.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. package/dist/components/error-boundary.d.ts +6 -0
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/error-boundary.js +6 -0
  4. package/dist/components/renderers/node-renderer.d.ts +6 -2
  5. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  6. package/dist/components/renderers/node-renderer.js +41 -22
  7. package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
  8. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
  9. package/dist/components/renderers/parametric-node-renderer.js +37 -0
  10. package/dist/components/renderers/scene-renderer.d.ts +1 -1
  11. package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
  12. package/dist/components/viewer/bvh-ecctrl.d.ts +77 -0
  13. package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -0
  14. package/dist/components/viewer/bvh-ecctrl.js +552 -0
  15. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  16. package/dist/components/viewer/frame-limiter.js +39 -4
  17. package/dist/components/viewer/glb-interactive.d.ts +41 -0
  18. package/dist/components/viewer/glb-interactive.d.ts.map +1 -0
  19. package/dist/components/viewer/glb-interactive.js +394 -0
  20. package/dist/components/viewer/glb-reference-nodes.d.ts +29 -0
  21. package/dist/components/viewer/glb-reference-nodes.d.ts.map +1 -0
  22. package/dist/components/viewer/glb-reference-nodes.js +80 -0
  23. package/dist/components/viewer/glb-replace-instances.d.ts +18 -0
  24. package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -0
  25. package/dist/components/viewer/glb-replace-instances.js +61 -0
  26. package/dist/components/viewer/glb-scene.d.ts +54 -0
  27. package/dist/components/viewer/glb-scene.d.ts.map +1 -0
  28. package/dist/components/viewer/glb-scene.js +920 -0
  29. package/dist/components/viewer/glb-walkthrough-controller.d.ts +12 -0
  30. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -0
  31. package/dist/components/viewer/glb-walkthrough-controller.js +357 -0
  32. package/dist/components/viewer/index.d.ts +56 -3
  33. package/dist/components/viewer/index.d.ts.map +1 -1
  34. package/dist/components/viewer/index.js +280 -65
  35. package/dist/components/viewer/lights.d.ts +1 -1
  36. package/dist/components/viewer/lights.d.ts.map +1 -1
  37. package/dist/components/viewer/lights.js +201 -43
  38. package/dist/components/viewer/perf-monitor.d.ts +1 -1
  39. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  40. package/dist/components/viewer/perf-monitor.js +115 -12
  41. package/dist/components/viewer/post-processing.d.ts +7 -1
  42. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  43. package/dist/components/viewer/post-processing.js +395 -63
  44. package/dist/components/viewer/registered-systems.d.ts +12 -0
  45. package/dist/components/viewer/registered-systems.d.ts.map +1 -0
  46. package/dist/components/viewer/registered-systems.js +49 -0
  47. package/dist/components/viewer/scene-bvh.d.ts +18 -0
  48. package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
  49. package/dist/components/viewer/scene-bvh.js +97 -0
  50. package/dist/components/viewer/scene-bvh.test.d.ts +2 -0
  51. package/dist/components/viewer/scene-bvh.test.d.ts.map +1 -0
  52. package/dist/components/viewer/scene-bvh.test.js +13 -0
  53. package/dist/components/viewer/scene-environment.d.ts +2 -0
  54. package/dist/components/viewer/scene-environment.d.ts.map +1 -0
  55. package/dist/components/viewer/scene-environment.js +79 -0
  56. package/dist/components/viewer/selection-manager.d.ts +1 -1
  57. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  58. package/dist/components/viewer/selection-manager.js +37 -14
  59. package/dist/components/viewer/viewer-camera.d.ts +1 -1
  60. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  61. package/dist/components/viewer/walkthrough-controls.d.ts +2 -1
  62. package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
  63. package/dist/components/viewer/walkthrough-controls.js +20 -0
  64. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  65. package/dist/hooks/use-gltf-ktx2.js +3 -22
  66. package/dist/hooks/use-node-events.d.ts +6 -74
  67. package/dist/hooks/use-node-events.d.ts.map +1 -1
  68. package/dist/hooks/use-node-events.js +28 -8
  69. package/dist/index.d.ts +48 -5
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +82 -4
  72. package/dist/lib/backdrop.d.ts +33 -0
  73. package/dist/lib/backdrop.d.ts.map +1 -0
  74. package/dist/lib/backdrop.js +92 -0
  75. package/dist/lib/box-uv.d.ts +13 -0
  76. package/dist/lib/box-uv.d.ts.map +1 -0
  77. package/dist/lib/box-uv.js +32 -0
  78. package/dist/lib/csg-utils.d.ts +10 -0
  79. package/dist/lib/csg-utils.d.ts.map +1 -0
  80. package/dist/lib/csg-utils.js +93 -0
  81. package/dist/lib/csg-utils.test.d.ts +2 -0
  82. package/dist/lib/csg-utils.test.d.ts.map +1 -0
  83. package/dist/lib/csg-utils.test.js +45 -0
  84. package/dist/lib/drawable-geometry.d.ts +21 -0
  85. package/dist/lib/drawable-geometry.d.ts.map +1 -0
  86. package/dist/lib/drawable-geometry.js +22 -0
  87. package/dist/lib/edge-style.d.ts +4 -0
  88. package/dist/lib/edge-style.d.ts.map +1 -0
  89. package/dist/lib/edge-style.js +31 -0
  90. package/dist/lib/gpu-perf.d.ts +4 -0
  91. package/dist/lib/gpu-perf.d.ts.map +1 -0
  92. package/dist/lib/gpu-perf.js +23 -0
  93. package/dist/lib/ink-edges.d.ts +9 -0
  94. package/dist/lib/ink-edges.d.ts.map +1 -0
  95. package/dist/lib/ink-edges.js +49 -0
  96. package/dist/lib/isolation.d.ts +39 -0
  97. package/dist/lib/isolation.d.ts.map +1 -0
  98. package/dist/lib/isolation.js +103 -0
  99. package/dist/lib/ktx2-loader.d.ts +43 -0
  100. package/dist/lib/ktx2-loader.d.ts.map +1 -0
  101. package/dist/lib/ktx2-loader.js +136 -0
  102. package/dist/lib/layers.d.ts +28 -0
  103. package/dist/lib/layers.d.ts.map +1 -1
  104. package/dist/lib/layers.js +28 -0
  105. package/dist/lib/materials.d.ts +38 -15
  106. package/dist/lib/materials.d.ts.map +1 -1
  107. package/dist/lib/materials.js +364 -72
  108. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  109. package/dist/lib/merged-outline-node.js +7 -0
  110. package/dist/lib/polygon-union.d.ts +4 -0
  111. package/dist/lib/polygon-union.d.ts.map +1 -0
  112. package/dist/lib/polygon-union.js +289 -0
  113. package/dist/lib/polygon-union.test.d.ts +2 -0
  114. package/dist/lib/polygon-union.test.d.ts.map +1 -0
  115. package/dist/lib/polygon-union.test.js +105 -0
  116. package/dist/lib/scene-themes.d.ts +30 -0
  117. package/dist/lib/scene-themes.d.ts.map +1 -0
  118. package/dist/lib/scene-themes.js +202 -0
  119. package/dist/lib/shadow-only.d.ts +4 -0
  120. package/dist/lib/shadow-only.d.ts.map +1 -0
  121. package/dist/lib/shadow-only.js +36 -0
  122. package/dist/lib/tsl-compat.d.ts +12 -0
  123. package/dist/lib/tsl-compat.d.ts.map +1 -0
  124. package/dist/lib/tsl-compat.js +13 -0
  125. package/dist/store/use-viewer.d.ts +92 -12
  126. package/dist/store/use-viewer.d.ts.map +1 -1
  127. package/dist/store/use-viewer.js +201 -6
  128. package/dist/store/use-viewer.test.d.ts +2 -0
  129. package/dist/store/use-viewer.test.d.ts.map +1 -0
  130. package/dist/store/use-viewer.test.js +42 -0
  131. package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
  132. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  133. package/dist/systems/ceiling/ceiling-system.js +66 -13
  134. package/dist/systems/door/door-animation.test.d.ts +2 -0
  135. package/dist/systems/door/door-animation.test.d.ts.map +1 -0
  136. package/dist/systems/door/door-animation.test.js +51 -0
  137. package/dist/systems/door/door-floor-alignment.test.d.ts +2 -0
  138. package/dist/systems/door/door-floor-alignment.test.d.ts.map +1 -0
  139. package/dist/systems/door/door-floor-alignment.test.js +50 -0
  140. package/dist/systems/door/door-system.d.ts +24 -0
  141. package/dist/systems/door/door-system.d.ts.map +1 -1
  142. package/dist/systems/door/door-system.js +864 -180
  143. package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
  144. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
  145. package/dist/systems/elevator/elevator-interaction-system.js +62 -0
  146. package/dist/systems/fence/fence-system.d.ts +20 -0
  147. package/dist/systems/fence/fence-system.d.ts.map +1 -1
  148. package/dist/systems/fence/fence-system.js +229 -57
  149. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
  150. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
  151. package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
  152. package/dist/systems/geometry/geometry-system.d.ts +41 -0
  153. package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
  154. package/dist/systems/geometry/geometry-system.js +348 -0
  155. package/dist/systems/geometry/geometry-system.test.d.ts +2 -0
  156. package/dist/systems/geometry/geometry-system.test.d.ts.map +1 -0
  157. package/dist/systems/geometry/geometry-system.test.js +30 -0
  158. package/dist/systems/interactive/control-widget.d.ts +10 -0
  159. package/dist/systems/interactive/control-widget.d.ts.map +1 -0
  160. package/dist/systems/interactive/control-widget.js +35 -0
  161. package/dist/systems/interactive/interactive-system.d.ts +1 -1
  162. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  163. package/dist/systems/interactive/interactive-system.js +55 -56
  164. package/dist/systems/item/item-system.d.ts +7 -0
  165. package/dist/systems/item/item-system.d.ts.map +1 -1
  166. package/dist/systems/item/item-system.js +28 -17
  167. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  168. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  169. package/dist/systems/item-light/item-light-system.js +18 -5
  170. package/dist/systems/level/level-system.d.ts.map +1 -1
  171. package/dist/systems/level/level-system.js +28 -4
  172. package/dist/systems/level/level-utils.d.ts +0 -3
  173. package/dist/systems/level/level-utils.d.ts.map +1 -1
  174. package/dist/systems/level/level-utils.js +2 -41
  175. package/dist/systems/roof/roof-materials.d.ts +3 -2
  176. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  177. package/dist/systems/roof/roof-materials.js +59 -14
  178. package/dist/systems/roof/roof-system.d.ts +46 -6
  179. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  180. package/dist/systems/roof/roof-system.js +1365 -170
  181. package/dist/systems/slab/slab-system.d.ts +5 -3
  182. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  183. package/dist/systems/slab/slab-system.js +204 -68
  184. package/dist/systems/slab/slab-system.test.d.ts +2 -0
  185. package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
  186. package/dist/systems/slab/slab-system.test.js +63 -0
  187. package/dist/systems/stair/stair-materials.d.ts +4 -3
  188. package/dist/systems/stair/stair-materials.d.ts.map +1 -1
  189. package/dist/systems/stair/stair-materials.js +30 -15
  190. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  191. package/dist/systems/stair/stair-system.js +55 -63
  192. package/dist/systems/surface-hole-geometry.d.ts +3 -0
  193. package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
  194. package/dist/systems/surface-hole-geometry.js +4 -0
  195. package/dist/systems/wall/opening-cutout-geometry.d.ts +31 -0
  196. package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -0
  197. package/dist/systems/wall/opening-cutout-geometry.js +167 -0
  198. package/dist/systems/wall/opening-cutout-geometry.test.d.ts +2 -0
  199. package/dist/systems/wall/opening-cutout-geometry.test.d.ts.map +1 -0
  200. package/dist/systems/wall/opening-cutout-geometry.test.js +172 -0
  201. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  202. package/dist/systems/wall/wall-cutout.js +62 -17
  203. package/dist/systems/wall/wall-materials.d.ts +12 -7
  204. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  205. package/dist/systems/wall/wall-materials.js +279 -46
  206. package/dist/systems/wall/wall-opening-cutout.test.d.ts +2 -0
  207. package/dist/systems/wall/wall-opening-cutout.test.d.ts.map +1 -0
  208. package/dist/systems/wall/wall-opening-cutout.test.js +59 -0
  209. package/dist/systems/wall/wall-support-extension.test.d.ts +2 -0
  210. package/dist/systems/wall/wall-support-extension.test.d.ts.map +1 -0
  211. package/dist/systems/wall/wall-support-extension.test.js +48 -0
  212. package/dist/systems/wall/wall-system.d.ts +2 -2
  213. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  214. package/dist/systems/wall/wall-system.js +493 -193
  215. package/dist/systems/window/window-animation-system.d.ts +12 -0
  216. package/dist/systems/window/window-animation-system.d.ts.map +1 -1
  217. package/dist/systems/window/window-animation-system.js +16 -5
  218. package/dist/systems/window/window-system.d.ts +7 -0
  219. package/dist/systems/window/window-system.d.ts.map +1 -1
  220. package/dist/systems/window/window-system.js +308 -18
  221. package/package.json +5 -6
  222. package/dist/components/renderers/building/building-renderer.d.ts +0 -5
  223. package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
  224. package/dist/components/renderers/building/building-renderer.js +0 -11
  225. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
  226. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
  227. package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
  228. package/dist/components/renderers/column/column-renderer.d.ts +0 -5
  229. package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
  230. package/dist/components/renderers/column/column-renderer.js +0 -597
  231. package/dist/components/renderers/door/door-renderer.d.ts +0 -5
  232. package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
  233. package/dist/components/renderers/door/door-renderer.js +0 -16
  234. package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
  235. package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
  236. package/dist/components/renderers/fence/fence-renderer.js +0 -15
  237. package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
  238. package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
  239. package/dist/components/renderers/guide/guide-renderer.js +0 -38
  240. package/dist/components/renderers/item/item-renderer.d.ts +0 -5
  241. package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
  242. package/dist/components/renderers/item/item-renderer.js +0 -172
  243. package/dist/components/renderers/level/level-renderer.d.ts +0 -5
  244. package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
  245. package/dist/components/renderers/level/level-renderer.js +0 -11
  246. package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
  247. package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
  248. package/dist/components/renderers/roof/roof-materials.js +0 -16
  249. package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
  250. package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
  251. package/dist/components/renderers/roof/roof-renderer.js +0 -32
  252. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
  253. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
  254. package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
  255. package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
  256. package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
  257. package/dist/components/renderers/scan/scan-renderer.js +0 -54
  258. package/dist/components/renderers/site/site-renderer.d.ts +0 -5
  259. package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
  260. package/dist/components/renderers/site/site-renderer.js +0 -106
  261. package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
  262. package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
  263. package/dist/components/renderers/slab/slab-renderer.js +0 -58
  264. package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
  265. package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
  266. package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
  267. package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
  268. package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
  269. package/dist/components/renderers/stair/stair-renderer.js +0 -603
  270. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
  271. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
  272. package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -51
  273. package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
  274. package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
  275. package/dist/components/renderers/wall/wall-renderer.js +0 -16
  276. package/dist/components/renderers/window/window-renderer.d.ts +0 -5
  277. package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
  278. package/dist/components/renderers/window/window-renderer.js +0 -21
  279. package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
  280. package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
  281. package/dist/components/renderers/zone/zone-renderer.js +0 -181
  282. package/dist/components/viewer/ground-occluder.d.ts +0 -2
  283. package/dist/components/viewer/ground-occluder.d.ts.map +0 -1
  284. package/dist/components/viewer/ground-occluder.js +0 -79
@@ -0,0 +1,289 @@
1
+ const EPSILON = 1e-7;
2
+ const KEY_SCALE = 1e6;
3
+ function pointsEqual(a, b, tolerance = EPSILON) {
4
+ return Math.hypot(a[0] - b[0], a[1] - b[1]) <= tolerance;
5
+ }
6
+ function pointKey(point) {
7
+ return `${Math.round(point[0] * KEY_SCALE)}:${Math.round(point[1] * KEY_SCALE)}`;
8
+ }
9
+ function interpolate(a, b, t) {
10
+ return [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t];
11
+ }
12
+ function cross(ax, ay, bx, by) {
13
+ return ax * by - ay * bx;
14
+ }
15
+ function polygonArea(points) {
16
+ let area = 0;
17
+ for (let i = 0; i < points.length; i++) {
18
+ const current = points[i];
19
+ const next = points[(i + 1) % points.length];
20
+ area += current[0] * next[1] - next[0] * current[1];
21
+ }
22
+ return area / 2;
23
+ }
24
+ function pointOnSegment(point, start, end) {
25
+ const dx = end[0] - start[0];
26
+ const dz = end[1] - start[1];
27
+ const crossValue = cross(point[0] - start[0], point[1] - start[1], dx, dz);
28
+ if (Math.abs(crossValue) > EPSILON)
29
+ return false;
30
+ const dot = (point[0] - start[0]) * (point[0] - end[0]) + (point[1] - start[1]) * (point[1] - end[1]);
31
+ return dot <= EPSILON;
32
+ }
33
+ function pointInPolygonOrOnBoundary(point, polygon) {
34
+ if (polygon.some((start, index) => pointOnSegment(point, start, polygon[(index + 1) % polygon.length]))) {
35
+ return true;
36
+ }
37
+ return pointInPolygon(point, polygon);
38
+ }
39
+ function pointInPolygon(point, polygon) {
40
+ let inside = false;
41
+ for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
42
+ const pi = polygon[i];
43
+ const pj = polygon[j];
44
+ if (pointOnSegment(point, pj, pi))
45
+ return false;
46
+ const intersects = pi[1] > point[1] !== pj[1] > point[1] &&
47
+ point[0] < ((pj[0] - pi[0]) * (point[1] - pi[1])) / (pj[1] - pi[1]) + pi[0];
48
+ if (intersects)
49
+ inside = !inside;
50
+ }
51
+ return inside;
52
+ }
53
+ function normalizeRing(ring) {
54
+ const normalized = [];
55
+ for (const [x, z] of ring) {
56
+ if (!Number.isFinite(x) || !Number.isFinite(z))
57
+ continue;
58
+ const point = [x, z];
59
+ const previous = normalized[normalized.length - 1];
60
+ if (!previous || !pointsEqual(previous, point)) {
61
+ normalized.push(point);
62
+ }
63
+ }
64
+ const first = normalized[0];
65
+ const last = normalized[normalized.length - 1];
66
+ if (first && last && pointsEqual(first, last)) {
67
+ normalized.pop();
68
+ }
69
+ if (normalized.length < 3 || Math.abs(polygonArea(normalized)) <= EPSILON)
70
+ return [];
71
+ return polygonArea(normalized) < 0 ? [...normalized].reverse() : normalized;
72
+ }
73
+ function addSplit(edge, t) {
74
+ if (t < -EPSILON || t > 1 + EPSILON)
75
+ return;
76
+ const clamped = Math.max(0, Math.min(1, t));
77
+ if (edge.splits.some((split) => Math.abs(split - clamped) <= EPSILON))
78
+ return;
79
+ edge.splits.push(clamped);
80
+ }
81
+ function parameterOnEdge(point, edge) {
82
+ const dx = edge.end[0] - edge.start[0];
83
+ const dz = edge.end[1] - edge.start[1];
84
+ const lengthSquared = dx * dx + dz * dz;
85
+ if (lengthSquared <= EPSILON)
86
+ return 0;
87
+ return ((point[0] - edge.start[0]) * dx + (point[1] - edge.start[1]) * dz) / lengthSquared;
88
+ }
89
+ function addIntersectionSplits(left, right) {
90
+ const rx = left.end[0] - left.start[0];
91
+ const rz = left.end[1] - left.start[1];
92
+ const sx = right.end[0] - right.start[0];
93
+ const sz = right.end[1] - right.start[1];
94
+ const qpx = right.start[0] - left.start[0];
95
+ const qpz = right.start[1] - left.start[1];
96
+ const denominator = cross(rx, rz, sx, sz);
97
+ const numerator = cross(qpx, qpz, rx, rz);
98
+ if (Math.abs(denominator) <= EPSILON) {
99
+ if (Math.abs(numerator) > EPSILON)
100
+ return;
101
+ for (const point of [left.start, left.end, right.start, right.end]) {
102
+ if (pointOnSegment(point, left.start, left.end) &&
103
+ pointOnSegment(point, right.start, right.end)) {
104
+ addSplit(left, parameterOnEdge(point, left));
105
+ addSplit(right, parameterOnEdge(point, right));
106
+ }
107
+ }
108
+ return;
109
+ }
110
+ const t = cross(qpx, qpz, sx, sz) / denominator;
111
+ const u = cross(qpx, qpz, rx, rz) / denominator;
112
+ if (t < -EPSILON || t > 1 + EPSILON || u < -EPSILON || u > 1 + EPSILON)
113
+ return;
114
+ addSplit(left, t);
115
+ addSplit(right, u);
116
+ }
117
+ function buildEdges(polygons) {
118
+ const edges = [];
119
+ polygons.forEach((polygon, polygonIndex) => {
120
+ for (let i = 0; i < polygon.length; i++) {
121
+ edges.push({
122
+ start: polygon[i],
123
+ end: polygon[(i + 1) % polygon.length],
124
+ polygonIndex,
125
+ splits: [0, 1],
126
+ });
127
+ }
128
+ });
129
+ for (let i = 0; i < edges.length; i++) {
130
+ for (let j = i + 1; j < edges.length; j++) {
131
+ const left = edges[i];
132
+ const right = edges[j];
133
+ if (left.polygonIndex === right.polygonIndex)
134
+ continue;
135
+ addIntersectionSplits(left, right);
136
+ }
137
+ }
138
+ return edges;
139
+ }
140
+ function buildBoundarySegments(edges, polygons) {
141
+ const segments = [];
142
+ for (const edge of edges) {
143
+ const splits = [...edge.splits].sort((a, b) => a - b);
144
+ for (let i = 0; i < splits.length - 1; i++) {
145
+ const startT = splits[i];
146
+ const endT = splits[i + 1];
147
+ if (endT - startT <= EPSILON)
148
+ continue;
149
+ const start = interpolate(edge.start, edge.end, startT);
150
+ const end = interpolate(edge.start, edge.end, endT);
151
+ const mid = interpolate(edge.start, edge.end, (startT + endT) / 2);
152
+ const insideAnother = polygons.some((polygon, index) => index !== edge.polygonIndex && pointInPolygon(mid, polygon));
153
+ if (!insideAnother) {
154
+ segments.push({ start, end, used: false });
155
+ }
156
+ }
157
+ }
158
+ return removeDuplicateInteriorSegments(segments);
159
+ }
160
+ function segmentKey(segment) {
161
+ const start = pointKey(segment.start);
162
+ const end = pointKey(segment.end);
163
+ return start < end ? `${start}|${end}` : `${end}|${start}`;
164
+ }
165
+ function removeDuplicateInteriorSegments(segments) {
166
+ const groups = new Map();
167
+ for (const segment of segments) {
168
+ const key = segmentKey(segment);
169
+ const group = groups.get(key);
170
+ if (group) {
171
+ group.push(segment);
172
+ }
173
+ else {
174
+ groups.set(key, [segment]);
175
+ }
176
+ }
177
+ const result = [];
178
+ for (const group of groups.values()) {
179
+ if (group.length === 1) {
180
+ result.push(group[0]);
181
+ continue;
182
+ }
183
+ const firstStart = pointKey(group[0].start);
184
+ const firstEnd = pointKey(group[0].end);
185
+ const hasOppositeDirection = group.some((segment) => pointKey(segment.start) === firstEnd && pointKey(segment.end) === firstStart);
186
+ if (!hasOppositeDirection) {
187
+ result.push(group[0]);
188
+ }
189
+ }
190
+ return result;
191
+ }
192
+ function assembleRings(segments) {
193
+ const byStart = new Map();
194
+ for (const segment of segments) {
195
+ const key = pointKey(segment.start);
196
+ const group = byStart.get(key);
197
+ if (group) {
198
+ group.push(segment);
199
+ }
200
+ else {
201
+ byStart.set(key, [segment]);
202
+ }
203
+ }
204
+ const rings = [];
205
+ for (const firstSegment of segments) {
206
+ if (firstSegment.used)
207
+ continue;
208
+ firstSegment.used = true;
209
+ const ring = [firstSegment.start, firstSegment.end];
210
+ const startKey = pointKey(firstSegment.start);
211
+ let currentKey = pointKey(firstSegment.end);
212
+ while (currentKey !== startKey) {
213
+ const next = byStart.get(currentKey)?.find((segment) => !segment.used);
214
+ if (!next)
215
+ break;
216
+ next.used = true;
217
+ ring.push(next.end);
218
+ currentKey = pointKey(next.end);
219
+ }
220
+ if (currentKey !== startKey)
221
+ continue;
222
+ const last = ring[ring.length - 1];
223
+ if (last && pointsEqual(ring[0], last)) {
224
+ ring.pop();
225
+ }
226
+ const normalized = normalizeRing(ring);
227
+ if (normalized.length >= 3) {
228
+ rings.push(normalized);
229
+ }
230
+ }
231
+ return rings;
232
+ }
233
+ export function unionPolygons(polygons) {
234
+ const validPolygons = polygons.map(normalizeRing).filter((polygon) => polygon.length >= 3);
235
+ if (validPolygons.length <= 1)
236
+ return validPolygons;
237
+ const edges = buildEdges(validPolygons);
238
+ const segments = buildBoundarySegments(edges, validPolygons);
239
+ const rings = assembleRings(segments);
240
+ return rings.length > 0 ? rings : validPolygons;
241
+ }
242
+ function buildDifferenceBoundarySegments(edges, polygons) {
243
+ const subject = polygons[0];
244
+ const cutters = polygons.slice(1);
245
+ if (!subject)
246
+ return [];
247
+ const segments = [];
248
+ for (const edge of edges) {
249
+ const splits = [...edge.splits].sort((a, b) => a - b);
250
+ for (let i = 0; i < splits.length - 1; i++) {
251
+ const startT = splits[i];
252
+ const endT = splits[i + 1];
253
+ if (endT - startT <= EPSILON)
254
+ continue;
255
+ const start = interpolate(edge.start, edge.end, startT);
256
+ const end = interpolate(edge.start, edge.end, endT);
257
+ const mid = interpolate(edge.start, edge.end, (startT + endT) / 2);
258
+ if (edge.polygonIndex === 0) {
259
+ const insideCutter = cutters.some((cutter) => pointInPolygonOrOnBoundary(mid, cutter));
260
+ if (!insideCutter) {
261
+ segments.push({ start, end, used: false });
262
+ }
263
+ continue;
264
+ }
265
+ const insideSubject = pointInPolygon(mid, subject);
266
+ const insideAnotherCutter = cutters.some((cutter, cutterIndex) => cutterIndex !== edge.polygonIndex - 1 && pointInPolygonOrOnBoundary(mid, cutter));
267
+ if (insideSubject && !insideAnotherCutter) {
268
+ segments.push({ start: end, end: start, used: false });
269
+ }
270
+ }
271
+ }
272
+ return removeDuplicateInteriorSegments(segments);
273
+ }
274
+ export function subtractPolygonsFromPolygon(subject, cutters) {
275
+ const validSubject = normalizeRing(subject);
276
+ if (validSubject.length < 3)
277
+ return [];
278
+ const validCutters = cutters.map(normalizeRing).filter((polygon) => polygon.length >= 3);
279
+ if (validCutters.length === 0)
280
+ return [validSubject];
281
+ const polygons = [validSubject, ...validCutters];
282
+ const edges = buildEdges(polygons);
283
+ const segments = buildDifferenceBoundarySegments(edges, polygons);
284
+ const rings = assembleRings(segments);
285
+ if (rings.length > 0)
286
+ return rings;
287
+ const fullyCovered = validSubject.every((point) => validCutters.some((cutter) => pointInPolygonOrOnBoundary(point, cutter)));
288
+ return fullyCovered ? [] : [validSubject];
289
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=polygon-union.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polygon-union.test.d.ts","sourceRoot":"","sources":["../../src/lib/polygon-union.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,105 @@
1
+ // @ts-expect-error — bun:test is provided by the Bun runtime; viewer does not
2
+ // depend on @types/bun so the import type is unresolved at compile time.
3
+ import { describe, expect, test } from 'bun:test';
4
+ import { subtractPolygonsFromPolygon, unionPolygons } from './polygon-union';
5
+ function polygonArea(points) {
6
+ let area = 0;
7
+ for (let i = 0; i < points.length; i++) {
8
+ const current = points[i];
9
+ const next = points[(i + 1) % points.length];
10
+ area += current[0] * next[1] - next[0] * current[1];
11
+ }
12
+ return Math.abs(area / 2);
13
+ }
14
+ describe('unionPolygons', () => {
15
+ test('collapses a contained polygon into the containing polygon', () => {
16
+ const small = [
17
+ [0, 0],
18
+ [1, 0],
19
+ [1, 1],
20
+ [0, 1],
21
+ ];
22
+ const large = [
23
+ [-1, -1],
24
+ [2, -1],
25
+ [2, 2],
26
+ [-1, 2],
27
+ ];
28
+ const result = unionPolygons([small, large]);
29
+ expect(result).toHaveLength(1);
30
+ expect(polygonArea(result[0])).toBeCloseTo(9);
31
+ });
32
+ test('combines overlapping rectangles into one boundary', () => {
33
+ const left = [
34
+ [0, 0],
35
+ [2, 0],
36
+ [2, 2],
37
+ [0, 2],
38
+ ];
39
+ const right = [
40
+ [1, 1],
41
+ [3, 1],
42
+ [3, 3],
43
+ [1, 3],
44
+ ];
45
+ const result = unionPolygons([left, right]);
46
+ expect(result).toHaveLength(1);
47
+ expect(result[0]).toHaveLength(8);
48
+ expect(polygonArea(result[0])).toBeCloseTo(7);
49
+ });
50
+ test('keeps disjoint polygons as separate boundaries', () => {
51
+ const left = [
52
+ [0, 0],
53
+ [1, 0],
54
+ [1, 1],
55
+ [0, 1],
56
+ ];
57
+ const right = [
58
+ [2, 0],
59
+ [3, 0],
60
+ [3, 1],
61
+ [2, 1],
62
+ ];
63
+ const result = unionPolygons([left, right]);
64
+ expect(result).toHaveLength(2);
65
+ expect(result.map(polygonArea)).toEqual([1, 1]);
66
+ });
67
+ });
68
+ describe('subtractPolygonsFromPolygon', () => {
69
+ test('turns a boundary-overlapping cutter into an indentation', () => {
70
+ const slab = [
71
+ [0, 0],
72
+ [4, 0],
73
+ [4, 3],
74
+ [0, 3],
75
+ ];
76
+ const cutout = [
77
+ [1, -0.5],
78
+ [3, -0.5],
79
+ [3, 1],
80
+ [1, 1],
81
+ ];
82
+ const result = subtractPolygonsFromPolygon(slab, [cutout]);
83
+ expect(result).toHaveLength(1);
84
+ expect(result[0]).toContainEqual([1, 1]);
85
+ expect(result[0]).toContainEqual([3, 1]);
86
+ expect(polygonArea(result[0])).toBeCloseTo(10);
87
+ });
88
+ test('returns separate contours when a cutter splits the subject', () => {
89
+ const slab = [
90
+ [0, 0],
91
+ [4, 0],
92
+ [4, 3],
93
+ [0, 3],
94
+ ];
95
+ const cutout = [
96
+ [1.5, -1],
97
+ [2.5, -1],
98
+ [2.5, 4],
99
+ [1.5, 4],
100
+ ];
101
+ const result = subtractPolygonsFromPolygon(slab, [cutout]);
102
+ expect(result).toHaveLength(2);
103
+ expect(result.map(polygonArea).sort((a, b) => a - b)).toEqual([4.5, 4.5]);
104
+ });
105
+ });
@@ -0,0 +1,30 @@
1
+ import type { SurfaceRole } from '@pascal-app/core';
2
+ export type SceneTheme = {
3
+ id: string;
4
+ name: string;
5
+ appearance: 'light' | 'dark';
6
+ background: string;
7
+ backgroundSky?: string;
8
+ ground: string;
9
+ ambient: {
10
+ color: string;
11
+ intensity: number;
12
+ };
13
+ hemi?: {
14
+ sky: string;
15
+ ground: string;
16
+ intensity: number;
17
+ };
18
+ lights: Array<{
19
+ position: [number, number, number];
20
+ color: string;
21
+ intensity: number;
22
+ castShadow?: boolean;
23
+ }>;
24
+ toneMappingExposure: number;
25
+ clayTints?: Partial<Record<SurfaceRole, string>>;
26
+ };
27
+ export declare const SCENE_THEMES: SceneTheme[];
28
+ export declare const SCENE_THEME_IDS: string[];
29
+ export declare function getSceneTheme(id: string): SceneTheme;
30
+ //# sourceMappingURL=scene-themes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene-themes.d.ts","sourceRoot":"","sources":["../../src/lib/scene-themes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IAIZ,UAAU,EAAE,OAAO,GAAG,MAAM,CAAA;IAC5B,UAAU,EAAE,MAAM,CAAA;IAIlB,aAAa,CAAC,EAAE,MAAM,CAAA;IAItB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,IAAI,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACzD,MAAM,EAAE,KAAK,CAAC;QACZ,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;QAClC,KAAK,EAAE,MAAM,CAAA;QACb,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,OAAO,CAAA;KACrB,CAAC,CAAA;IACF,mBAAmB,EAAE,MAAM,CAAA;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAA;CACjD,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,UAAU,EAoMpC,CAAA;AAED,eAAO,MAAM,eAAe,UAAwC,CAAA;AAIpE,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,CAEpD"}
@@ -0,0 +1,202 @@
1
+ export const SCENE_THEMES = [
2
+ {
3
+ id: 'studio',
4
+ name: 'Studio',
5
+ appearance: 'light',
6
+ background: '#fbfbfa',
7
+ backgroundSky: '#b6cfe7',
8
+ ground: '#e9e7e2',
9
+ ambient: { color: '#ffffff', intensity: 0.15 },
10
+ hemi: { sky: '#ffffff', ground: '#aaa49a', intensity: 0.45 },
11
+ lights: [
12
+ { position: [10, 10, 10], color: '#ffffff', intensity: 4, castShadow: true },
13
+ { position: [-10, 10, -10], color: '#ffffff', intensity: 0.6 },
14
+ ],
15
+ toneMappingExposure: 0.9,
16
+ clayTints: {
17
+ wall: '#e9e5db',
18
+ floor: '#d8d2c4',
19
+ ceiling: '#f1ede4',
20
+ roof: '#c4bba6',
21
+ glazing: '#cdd8df',
22
+ },
23
+ },
24
+ {
25
+ id: 'paper',
26
+ name: 'Paper',
27
+ appearance: 'light',
28
+ background: '#ede9df',
29
+ backgroundSky: '#c0d2e4',
30
+ ground: '#e7e1d3',
31
+ ambient: { color: '#fff9eb', intensity: 0.55 },
32
+ hemi: { sky: '#fff5d9', ground: '#c2b89c', intensity: 0.35 },
33
+ lights: [
34
+ { position: [16, 22, 12], color: '#fff1c8', intensity: 2.6, castShadow: true },
35
+ { position: [-14, 10, -6], color: '#dde5ff', intensity: 0.35 },
36
+ ],
37
+ toneMappingExposure: 1,
38
+ clayTints: {
39
+ wall: '#efe9da',
40
+ floor: '#ddd4bf',
41
+ ceiling: '#f5efe0',
42
+ roof: '#b9b09a',
43
+ glazing: '#cdd5d8',
44
+ },
45
+ },
46
+ {
47
+ id: 'sunset',
48
+ name: 'Sunset',
49
+ appearance: 'light',
50
+ background: '#f6e8d4',
51
+ backgroundSky: '#b5bede',
52
+ ground: '#ecd9bf',
53
+ ambient: { color: '#ffd9a8', intensity: 0.45 },
54
+ hemi: { sky: '#ffd9a8', ground: '#5b4634', intensity: 0.4 },
55
+ lights: [
56
+ { position: [22, 8, 8], color: '#ffb070', intensity: 3.4, castShadow: true },
57
+ { position: [-14, 16, -10], color: '#a4b8ff', intensity: 0.4 },
58
+ ],
59
+ toneMappingExposure: 1,
60
+ clayTints: {
61
+ wall: '#f3e3cf',
62
+ floor: '#e2cdab',
63
+ ceiling: '#f6e7d2',
64
+ roof: '#a6764f',
65
+ glazing: '#e7c9a8',
66
+ },
67
+ },
68
+ {
69
+ id: 'overcast',
70
+ name: 'Overcast',
71
+ appearance: 'light',
72
+ background: '#e6e7e6',
73
+ backgroundSky: '#c3ccd6',
74
+ ground: '#dadcd9',
75
+ ambient: { color: '#eef0ef', intensity: 1.1 },
76
+ hemi: { sky: '#f4f5f3', ground: '#bcbfbb', intensity: 0.9 },
77
+ lights: [{ position: [12, 28, 10], color: '#f4f5f3', intensity: 0.8, castShadow: true }],
78
+ toneMappingExposure: 0.9,
79
+ clayTints: {
80
+ wall: '#dedfdc',
81
+ floor: '#cdcec9',
82
+ ceiling: '#e8e9e6',
83
+ roof: '#a3a49e',
84
+ glazing: '#c6cdd0',
85
+ },
86
+ },
87
+ {
88
+ id: 'blueprint',
89
+ name: 'Blueprint',
90
+ appearance: 'light',
91
+ background: '#dde6ef',
92
+ backgroundSky: '#a5c4e2',
93
+ ground: '#c9d6e6',
94
+ ambient: { color: '#cfdcec', intensity: 0.7 },
95
+ hemi: { sky: '#dfeaf6', ground: '#5b6b80', intensity: 0.55 },
96
+ lights: [
97
+ { position: [16, 24, 12], color: '#e6efff', intensity: 1.8, castShadow: true },
98
+ { position: [-12, 10, -8], color: '#9fb6d8', intensity: 0.4 },
99
+ ],
100
+ toneMappingExposure: 0.9,
101
+ clayTints: {
102
+ wall: '#9fb6d2',
103
+ floor: '#8ba2c2',
104
+ ceiling: '#aec0d8',
105
+ roof: '#5f789b',
106
+ glazing: '#b6d7ea',
107
+ },
108
+ },
109
+ {
110
+ id: 'mediterranean',
111
+ name: 'Mediterranean',
112
+ appearance: 'light',
113
+ background: '#bdd6e8',
114
+ backgroundSky: '#8ab4d6',
115
+ ground: '#ddd2bb',
116
+ ambient: { color: '#d6e6f3', intensity: 0.5 },
117
+ hemi: { sky: '#a8c8e2', ground: '#d8c9a4', intensity: 0.6 },
118
+ lights: [
119
+ { position: [18, 20, 12], color: '#fff4d4', intensity: 3.6, castShadow: true },
120
+ { position: [-12, 8, -8], color: '#8fb3d8', intensity: 0.7 },
121
+ ],
122
+ toneMappingExposure: 0.9,
123
+ clayTints: {
124
+ wall: '#f6f1e6',
125
+ floor: '#e0d6c2',
126
+ ceiling: '#f3ede0',
127
+ roof: '#3e6585',
128
+ glazing: '#bcd3e2',
129
+ },
130
+ },
131
+ {
132
+ id: 'twilight',
133
+ name: 'Twilight',
134
+ appearance: 'dark',
135
+ background: '#3a3550',
136
+ backgroundSky: '#272338',
137
+ ground: '#67618a',
138
+ ambient: { color: '#a89cc8', intensity: 0.55 },
139
+ hemi: { sky: '#d8a8c0', ground: '#3a3450', intensity: 0.7 },
140
+ lights: [
141
+ { position: [-14, 22, -10], color: '#a4b6e8', intensity: 1.4, castShadow: true },
142
+ { position: [14, 6, 8], color: '#ffb070', intensity: 0.9 },
143
+ ],
144
+ toneMappingExposure: 0.9,
145
+ clayTints: {
146
+ wall: '#c5b9cf',
147
+ floor: '#ad9fbb',
148
+ ceiling: '#d2c6dc',
149
+ roof: '#5b4f74',
150
+ glazing: '#c3b6d4',
151
+ },
152
+ },
153
+ {
154
+ id: 'night',
155
+ name: 'Night',
156
+ appearance: 'dark',
157
+ background: '#1f2433',
158
+ backgroundSky: '#12161f',
159
+ ground: '#4a5470',
160
+ ambient: { color: '#a0b0ff', intensity: 0.25 },
161
+ hemi: { sky: '#3a4666', ground: '#232a3d', intensity: 0.55 },
162
+ lights: [
163
+ { position: [10, 10, 10], color: '#e0e5ff', intensity: 1.2, castShadow: true },
164
+ { position: [-10, 10, -10], color: '#8090ff', intensity: 0.3 },
165
+ ],
166
+ toneMappingExposure: 0.9,
167
+ clayTints: {
168
+ wall: '#aab3c6',
169
+ floor: '#98a1b5',
170
+ ceiling: '#b7bfd0',
171
+ roof: '#5b6680',
172
+ glazing: '#aebbd0',
173
+ },
174
+ },
175
+ {
176
+ id: 'verdant',
177
+ name: 'Verdant',
178
+ appearance: 'light',
179
+ background: '#d6e4d2',
180
+ backgroundSky: '#aecde0',
181
+ ground: '#c7d6b4',
182
+ ambient: { color: '#e3efdd', intensity: 0.5 },
183
+ hemi: { sky: '#cfe6cf', ground: '#8ea06f', intensity: 0.65 },
184
+ lights: [
185
+ { position: [16, 22, 12], color: '#fff6d8', intensity: 3, castShadow: true },
186
+ { position: [-12, 10, -8], color: '#bfe0c2', intensity: 0.5 },
187
+ ],
188
+ toneMappingExposure: 0.9,
189
+ clayTints: {
190
+ wall: '#eef0e6',
191
+ floor: '#d8ddc6',
192
+ ceiling: '#f1f3ea',
193
+ roof: '#6f8a5a',
194
+ glazing: '#c4dcd0',
195
+ },
196
+ },
197
+ ];
198
+ export const SCENE_THEME_IDS = SCENE_THEMES.map((theme) => theme.id);
199
+ const SCENE_THEME_BY_ID = new Map(SCENE_THEMES.map((theme) => [theme.id, theme]));
200
+ export function getSceneTheme(id) {
201
+ return SCENE_THEME_BY_ID.get(id) ?? SCENE_THEMES[0];
202
+ }
@@ -0,0 +1,4 @@
1
+ import type { Object3D } from 'three';
2
+ export declare function applyShadowOnly(root: Object3D): void;
3
+ export declare function clearShadowOnly(root: Object3D): void;
4
+ //# sourceMappingURL=shadow-only.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shadow-only.d.ts","sourceRoot":"","sources":["../../src/lib/shadow-only.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAuBrC,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CASpD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,CAOpD"}
@@ -0,0 +1,36 @@
1
+ import { SCENE_LAYER, SHADOW_ONLY_LAYER } from './layers';
2
+ /**
3
+ * Shadow-caster-only hiding: removes an object (and its descendants) from the
4
+ * color passes while keeping it in the shadow map, so a hidden roof or level
5
+ * still shadows the interior and sun enters through windows correctly.
6
+ *
7
+ * Uses layer masks instead of `visible = false` for two reasons: `visible`
8
+ * cascades (and, critically, prunes the object from the shadow pass too),
9
+ * while layers are tested per-object against the rendering camera — the main
10
+ * camera never enables {@link SHADOW_ONLY_LAYER}, but every shadow-casting
11
+ * light's shadow camera does (see `lights.tsx`).
12
+ *
13
+ * The original `layers.mask` is stashed under a private Symbol so
14
+ * {@link clearShadowOnly} restores the exact prior state. Both calls are
15
+ * idempotent and cheap to reapply.
16
+ */
17
+ const ORIGINAL_LAYERS = Symbol('pascal:shadow-only:original-layers');
18
+ export function applyShadowOnly(root) {
19
+ root.traverse((obj) => {
20
+ const carrier = obj;
21
+ if (carrier[ORIGINAL_LAYERS] === undefined) {
22
+ carrier[ORIGINAL_LAYERS] = obj.layers.mask;
23
+ }
24
+ obj.layers.disable(SCENE_LAYER);
25
+ obj.layers.enable(SHADOW_ONLY_LAYER);
26
+ });
27
+ }
28
+ export function clearShadowOnly(root) {
29
+ root.traverse((obj) => {
30
+ const carrier = obj;
31
+ if (carrier[ORIGINAL_LAYERS] === undefined)
32
+ return;
33
+ obj.layers.mask = carrier[ORIGINAL_LAYERS];
34
+ delete carrier[ORIGINAL_LAYERS];
35
+ });
36
+ }