@pascal-app/core 0.8.0 → 0.9.1

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 (272) hide show
  1. package/dist/events/bus.d.ts +39 -13
  2. package/dist/events/bus.d.ts.map +1 -1
  3. package/dist/hooks/scene-registry/scene-registry.d.ts +6 -22
  4. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  5. package/dist/hooks/scene-registry/scene-registry.js +33 -26
  6. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts +14 -0
  7. package/dist/hooks/spatial-grid/floor-placed-elevation.d.ts.map +1 -0
  8. package/dist/hooks/spatial-grid/floor-placed-elevation.js +51 -0
  9. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts +2 -0
  10. package/dist/hooks/spatial-grid/floor-placed-elevation.test.d.ts.map +1 -0
  11. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +273 -0
  12. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +13 -2
  13. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  14. package/dist/hooks/spatial-grid/spatial-grid-manager.js +95 -9
  15. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts +28 -1
  16. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  17. package/dist/hooks/spatial-grid/spatial-grid-sync.js +91 -18
  18. package/dist/index.d.ts +22 -4
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +19 -2
  21. package/dist/lib/polygon-relations.d.ts +10 -0
  22. package/dist/lib/polygon-relations.d.ts.map +1 -0
  23. package/dist/lib/polygon-relations.js +73 -0
  24. package/dist/lib/space-detection-pause.test.d.ts +2 -0
  25. package/dist/lib/space-detection-pause.test.d.ts.map +1 -0
  26. package/dist/lib/space-detection-pause.test.js +42 -0
  27. package/dist/lib/space-detection.d.ts +30 -1
  28. package/dist/lib/space-detection.d.ts.map +1 -1
  29. package/dist/lib/space-detection.js +215 -36
  30. package/dist/lib/space-detection.test.d.ts +2 -0
  31. package/dist/lib/space-detection.test.d.ts.map +1 -0
  32. package/dist/lib/space-detection.test.js +77 -0
  33. package/dist/material-library.d.ts +1 -1
  34. package/dist/material-library.d.ts.map +1 -1
  35. package/dist/material-library.js +1940 -143
  36. package/dist/registry/__bench__/relations-resolver.bench.d.ts +15 -0
  37. package/dist/registry/__bench__/relations-resolver.bench.d.ts.map +1 -0
  38. package/dist/registry/__bench__/relations-resolver.bench.js +170 -0
  39. package/dist/registry/handles.d.ts +321 -0
  40. package/dist/registry/handles.d.ts.map +1 -0
  41. package/dist/registry/handles.js +24 -0
  42. package/dist/registry/index.d.ts +7 -0
  43. package/dist/registry/index.d.ts.map +1 -0
  44. package/dist/registry/index.js +4 -0
  45. package/dist/registry/registry.d.ts +93 -0
  46. package/dist/registry/registry.d.ts.map +1 -0
  47. package/dist/registry/registry.js +201 -0
  48. package/dist/registry/registry.test.d.ts +2 -0
  49. package/dist/registry/registry.test.d.ts.map +1 -0
  50. package/dist/registry/registry.test.js +183 -0
  51. package/dist/registry/relations-resolver.d.ts +42 -0
  52. package/dist/registry/relations-resolver.d.ts.map +1 -0
  53. package/dist/registry/relations-resolver.js +77 -0
  54. package/dist/registry/relations-resolver.test.d.ts +2 -0
  55. package/dist/registry/relations-resolver.test.d.ts.map +1 -0
  56. package/dist/registry/relations-resolver.test.js +183 -0
  57. package/dist/registry/scene-api.d.ts +44 -0
  58. package/dist/registry/scene-api.d.ts.map +1 -0
  59. package/dist/registry/scene-api.js +111 -0
  60. package/dist/registry/scene-api.test.d.ts +2 -0
  61. package/dist/registry/scene-api.test.d.ts.map +1 -0
  62. package/dist/registry/scene-api.test.js +183 -0
  63. package/dist/registry/subtree.d.ts +67 -0
  64. package/dist/registry/subtree.d.ts.map +1 -0
  65. package/dist/registry/subtree.js +119 -0
  66. package/dist/registry/subtree.test.d.ts +2 -0
  67. package/dist/registry/subtree.test.d.ts.map +1 -0
  68. package/dist/registry/subtree.test.js +133 -0
  69. package/dist/registry/types.d.ts +1420 -0
  70. package/dist/registry/types.d.ts.map +1 -0
  71. package/dist/registry/types.js +1 -0
  72. package/dist/schema/index.d.ts +17 -2
  73. package/dist/schema/index.d.ts.map +1 -1
  74. package/dist/schema/index.js +17 -3
  75. package/dist/schema/material.d.ts +10 -0
  76. package/dist/schema/material.d.ts.map +1 -1
  77. package/dist/schema/material.js +10 -0
  78. package/dist/schema/nodes/box-vent.d.ts +74 -0
  79. package/dist/schema/nodes/box-vent.d.ts.map +1 -0
  80. package/dist/schema/nodes/box-vent.js +63 -0
  81. package/dist/schema/nodes/building.d.ts +1 -1
  82. package/dist/schema/nodes/building.d.ts.map +1 -1
  83. package/dist/schema/nodes/building.js +3 -2
  84. package/dist/schema/nodes/ceiling.d.ts +2 -0
  85. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  86. package/dist/schema/nodes/ceiling.js +1 -1
  87. package/dist/schema/nodes/chimney.d.ts +158 -0
  88. package/dist/schema/nodes/chimney.d.ts.map +1 -0
  89. package/dist/schema/nodes/chimney.js +67 -0
  90. package/dist/schema/nodes/column.d.ts +597 -5
  91. package/dist/schema/nodes/column.d.ts.map +1 -1
  92. package/dist/schema/nodes/column.js +586 -6
  93. package/dist/schema/nodes/cupola.d.ts +66 -0
  94. package/dist/schema/nodes/cupola.d.ts.map +1 -0
  95. package/dist/schema/nodes/cupola.js +28 -0
  96. package/dist/schema/nodes/dormer.d.ts +228 -0
  97. package/dist/schema/nodes/dormer.d.ts.map +1 -0
  98. package/dist/schema/nodes/dormer.js +119 -0
  99. package/dist/schema/nodes/downspout.d.ts +75 -0
  100. package/dist/schema/nodes/downspout.d.ts.map +1 -0
  101. package/dist/schema/nodes/downspout.js +63 -0
  102. package/dist/schema/nodes/elevator.d.ts +105 -0
  103. package/dist/schema/nodes/elevator.d.ts.map +1 -0
  104. package/dist/schema/nodes/elevator.js +54 -0
  105. package/dist/schema/nodes/eyebrow-vent.d.ts +68 -0
  106. package/dist/schema/nodes/eyebrow-vent.d.ts.map +1 -0
  107. package/dist/schema/nodes/eyebrow-vent.js +40 -0
  108. package/dist/schema/nodes/fence.d.ts +1 -0
  109. package/dist/schema/nodes/fence.d.ts.map +1 -1
  110. package/dist/schema/nodes/fence.js +2 -0
  111. package/dist/schema/nodes/gutter.d.ts +84 -0
  112. package/dist/schema/nodes/gutter.d.ts.map +1 -0
  113. package/dist/schema/nodes/gutter.js +76 -0
  114. package/dist/schema/nodes/item.d.ts +4 -0
  115. package/dist/schema/nodes/item.d.ts.map +1 -1
  116. package/dist/schema/nodes/item.js +9 -0
  117. package/dist/schema/nodes/level.d.ts +1 -1
  118. package/dist/schema/nodes/level.d.ts.map +1 -1
  119. package/dist/schema/nodes/level.js +2 -0
  120. package/dist/schema/nodes/ridge-vent.d.ts +67 -0
  121. package/dist/schema/nodes/ridge-vent.d.ts.map +1 -0
  122. package/dist/schema/nodes/ridge-vent.js +30 -0
  123. package/dist/schema/nodes/roof-segment.d.ts +195 -1
  124. package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
  125. package/dist/schema/nodes/roof-segment.js +254 -3
  126. package/dist/schema/nodes/roof.d.ts.map +1 -1
  127. package/dist/schema/nodes/roof.js +4 -16
  128. package/dist/schema/nodes/shelf.d.ts +105 -0
  129. package/dist/schema/nodes/shelf.d.ts.map +1 -0
  130. package/dist/schema/nodes/shelf.js +84 -0
  131. package/dist/schema/nodes/site.d.ts +1 -122
  132. package/dist/schema/nodes/site.d.ts.map +1 -1
  133. package/dist/schema/nodes/site.js +2 -6
  134. package/dist/schema/nodes/skylight.d.ts +279 -0
  135. package/dist/schema/nodes/skylight.d.ts.map +1 -0
  136. package/dist/schema/nodes/skylight.js +152 -0
  137. package/dist/schema/nodes/slab.d.ts +2 -0
  138. package/dist/schema/nodes/slab.d.ts.map +1 -1
  139. package/dist/schema/nodes/slab.js +1 -1
  140. package/dist/schema/nodes/solar-panel.d.ts +117 -0
  141. package/dist/schema/nodes/solar-panel.d.ts.map +1 -0
  142. package/dist/schema/nodes/solar-panel.js +47 -0
  143. package/dist/schema/nodes/surface-hole-metadata.d.ts +2 -0
  144. package/dist/schema/nodes/surface-hole-metadata.d.ts.map +1 -1
  145. package/dist/schema/nodes/surface-hole-metadata.js +3 -1
  146. package/dist/schema/nodes/turbine-vent.d.ts +68 -0
  147. package/dist/schema/nodes/turbine-vent.d.ts.map +1 -0
  148. package/dist/schema/nodes/turbine-vent.js +41 -0
  149. package/dist/schema/types.d.ts +1325 -125
  150. package/dist/schema/types.d.ts.map +1 -1
  151. package/dist/schema/types.js +26 -0
  152. package/dist/services/alignment-anchors.d.ts +100 -0
  153. package/dist/services/alignment-anchors.d.ts.map +1 -0
  154. package/dist/services/alignment-anchors.js +283 -0
  155. package/dist/services/alignment-anchors.test.d.ts +2 -0
  156. package/dist/services/alignment-anchors.test.d.ts.map +1 -0
  157. package/dist/services/alignment-anchors.test.js +383 -0
  158. package/dist/services/alignment.d.ts +126 -0
  159. package/dist/services/alignment.d.ts.map +1 -0
  160. package/dist/services/alignment.js +169 -0
  161. package/dist/services/alignment.test.d.ts +2 -0
  162. package/dist/services/alignment.test.d.ts.map +1 -0
  163. package/dist/services/alignment.test.js +92 -0
  164. package/dist/services/drag-session.d.ts +47 -0
  165. package/dist/services/drag-session.d.ts.map +1 -0
  166. package/dist/services/drag-session.js +100 -0
  167. package/dist/services/drag-session.test.d.ts +2 -0
  168. package/dist/services/drag-session.test.d.ts.map +1 -0
  169. package/dist/services/drag-session.test.js +219 -0
  170. package/dist/services/hosting.d.ts +80 -0
  171. package/dist/services/hosting.d.ts.map +1 -0
  172. package/dist/services/hosting.js +122 -0
  173. package/dist/services/hosting.test.d.ts +2 -0
  174. package/dist/services/hosting.test.d.ts.map +1 -0
  175. package/dist/services/hosting.test.js +199 -0
  176. package/dist/services/index.d.ts +7 -0
  177. package/dist/services/index.d.ts.map +1 -0
  178. package/dist/services/index.js +6 -0
  179. package/dist/services/movement.d.ts +48 -0
  180. package/dist/services/movement.d.ts.map +1 -0
  181. package/dist/services/movement.js +65 -0
  182. package/dist/services/movement.test.d.ts +2 -0
  183. package/dist/services/movement.test.d.ts.map +1 -0
  184. package/dist/services/movement.test.js +133 -0
  185. package/dist/services/single-undo-dance.test.d.ts +2 -0
  186. package/dist/services/single-undo-dance.test.d.ts.map +1 -0
  187. package/dist/services/single-undo-dance.test.js +210 -0
  188. package/dist/services/snap.d.ts +72 -0
  189. package/dist/services/snap.d.ts.map +1 -0
  190. package/dist/services/snap.js +120 -0
  191. package/dist/services/snap.test.d.ts +2 -0
  192. package/dist/services/snap.test.d.ts.map +1 -0
  193. package/dist/services/snap.test.js +95 -0
  194. package/dist/solar-panel-presets.d.ts +17 -0
  195. package/dist/solar-panel-presets.d.ts.map +1 -0
  196. package/dist/solar-panel-presets.js +43 -0
  197. package/dist/store/actions/node-actions.d.ts +14 -2
  198. package/dist/store/actions/node-actions.d.ts.map +1 -1
  199. package/dist/store/actions/node-actions.js +492 -15
  200. package/dist/store/actions/node-mutation-sanitize.test.d.ts +2 -0
  201. package/dist/store/actions/node-mutation-sanitize.test.d.ts.map +1 -0
  202. package/dist/store/actions/node-mutation-sanitize.test.js +147 -0
  203. package/dist/store/actions/reparent.test.d.ts +2 -0
  204. package/dist/store/actions/reparent.test.d.ts.map +1 -0
  205. package/dist/store/actions/reparent.test.js +212 -0
  206. package/dist/store/use-interactive.d.ts +39 -0
  207. package/dist/store/use-interactive.d.ts.map +1 -1
  208. package/dist/store/use-interactive.js +75 -0
  209. package/dist/store/use-live-node-overrides.d.ts +22 -0
  210. package/dist/store/use-live-node-overrides.d.ts.map +1 -0
  211. package/dist/store/use-live-node-overrides.js +59 -0
  212. package/dist/store/use-scene-elevator-migration.test.d.ts +2 -0
  213. package/dist/store/use-scene-elevator-migration.test.d.ts.map +1 -0
  214. package/dist/store/use-scene-elevator-migration.test.js +109 -0
  215. package/dist/store/use-scene.d.ts +11 -0
  216. package/dist/store/use-scene.d.ts.map +1 -1
  217. package/dist/store/use-scene.js +196 -6
  218. package/dist/systems/elevator/elevator-dispatch.d.ts +12 -0
  219. package/dist/systems/elevator/elevator-dispatch.d.ts.map +1 -0
  220. package/dist/systems/elevator/elevator-dispatch.js +57 -0
  221. package/dist/systems/elevator/elevator-geometry.d.ts +15 -0
  222. package/dist/systems/elevator/elevator-geometry.d.ts.map +1 -0
  223. package/dist/systems/elevator/elevator-geometry.js +50 -0
  224. package/dist/systems/elevator/elevator-opening-sync.d.ts +6 -0
  225. package/dist/systems/elevator/elevator-opening-sync.d.ts.map +1 -0
  226. package/dist/systems/elevator/elevator-opening-sync.js +218 -0
  227. package/dist/systems/elevator/elevator-opening-sync.test.d.ts +2 -0
  228. package/dist/systems/elevator/elevator-opening-sync.test.d.ts.map +1 -0
  229. package/dist/systems/elevator/elevator-opening-sync.test.js +146 -0
  230. package/dist/systems/elevator/elevator-opening-system.d.ts +2 -0
  231. package/dist/systems/elevator/elevator-opening-system.d.ts.map +1 -0
  232. package/dist/systems/elevator/elevator-opening-system.js +48 -0
  233. package/dist/systems/elevator/elevator-runtime-system.d.ts +2 -0
  234. package/dist/systems/elevator/elevator-runtime-system.d.ts.map +1 -0
  235. package/dist/systems/elevator/elevator-runtime-system.js +8 -0
  236. package/dist/systems/elevator/elevator-runtime.d.ts +18 -0
  237. package/dist/systems/elevator/elevator-runtime.d.ts.map +1 -0
  238. package/dist/systems/elevator/elevator-runtime.js +225 -0
  239. package/dist/systems/elevator/elevator-runtime.test.d.ts +2 -0
  240. package/dist/systems/elevator/elevator-runtime.test.d.ts.map +1 -0
  241. package/dist/systems/elevator/elevator-runtime.test.js +64 -0
  242. package/dist/systems/elevator/elevator-service.d.ts +19 -0
  243. package/dist/systems/elevator/elevator-service.d.ts.map +1 -0
  244. package/dist/systems/elevator/elevator-service.js +111 -0
  245. package/dist/systems/stair/stair-footprint.d.ts +44 -0
  246. package/dist/systems/stair/stair-footprint.d.ts.map +1 -0
  247. package/dist/systems/stair/stair-footprint.js +168 -0
  248. package/dist/systems/stair/stair-opening-preview.d.ts +1777 -0
  249. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -0
  250. package/dist/systems/stair/stair-opening-preview.js +93 -0
  251. package/dist/systems/stair/stair-opening-preview.test.d.ts +2 -0
  252. package/dist/systems/stair/stair-opening-preview.test.d.ts.map +1 -0
  253. package/dist/systems/stair/stair-opening-preview.test.js +80 -0
  254. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  255. package/dist/systems/stair/stair-opening-sync.js +172 -147
  256. package/dist/systems/stair/stair-opening-sync.test.js +385 -2
  257. package/dist/systems/stair/stair-opening-system.d.ts +2 -0
  258. package/dist/systems/stair/stair-opening-system.d.ts.map +1 -0
  259. package/dist/systems/stair/stair-opening-system.js +104 -0
  260. package/dist/systems/wall/wall-move.d.ts +48 -0
  261. package/dist/systems/wall/wall-move.d.ts.map +1 -0
  262. package/dist/systems/wall/wall-move.js +193 -0
  263. package/dist/utils/clone-scene-graph.d.ts +6 -4
  264. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  265. package/dist/utils/clone-scene-graph.js +6 -4
  266. package/dist/utils/clone-scene-graph.test.d.ts +2 -0
  267. package/dist/utils/clone-scene-graph.test.d.ts.map +1 -0
  268. package/dist/utils/clone-scene-graph.test.js +59 -0
  269. package/dist/validation/validate-build-json.d.ts +43 -0
  270. package/dist/validation/validate-build-json.d.ts.map +1 -0
  271. package/dist/validation/validate-build-json.js +227 -0
  272. package/package.json +9 -2
@@ -1,4 +1,4 @@
1
- import { type WallNode } from '../schema';
1
+ import { type CeilingNode as CeilingNodeType, type SlabNode as SlabNodeType, type WallNode } from '../schema';
2
2
  type Point2D = {
3
3
  x: number;
4
4
  y: number;
@@ -15,12 +15,41 @@ type WallSideUpdate = {
15
15
  frontSide: 'interior' | 'exterior' | 'unknown';
16
16
  backSide: 'interior' | 'exterior' | 'unknown';
17
17
  };
18
+ export type AutoSlabSyncPlan = {
19
+ create: SlabNodeType[];
20
+ update: Array<{
21
+ id: SlabNodeType['id'];
22
+ data: Partial<SlabNodeType>;
23
+ }>;
24
+ delete: Array<SlabNodeType['id']>;
25
+ };
26
+ export type AutoCeilingSyncPlan = {
27
+ create: CeilingNodeType[];
28
+ update: Array<{
29
+ id: CeilingNodeType['id'];
30
+ data: Partial<CeilingNodeType>;
31
+ }>;
32
+ delete: Array<CeilingNodeType['id']>;
33
+ };
34
+ export type AutoCeilingPlanningContext = {
35
+ walls?: WallNode[];
36
+ slabs?: SlabNodeType[];
37
+ };
18
38
  export declare function resolveWallSurfaceSides(wall: Pick<WallNode, 'start' | 'end' | 'thickness' | 'frontSide' | 'backSide'>, roomPolygons: Point2D[][]): Pick<WallSideUpdate, 'frontSide' | 'backSide'>;
39
+ export declare function planAutoSlabsForLevel(roomPolygons: Point2D[][], existingSlabs: SlabNodeType[]): AutoSlabSyncPlan;
40
+ export declare function projectAutoSlabsForPlan(existingSlabs: SlabNodeType[], plan: AutoSlabSyncPlan): SlabNodeType[];
41
+ export declare function planAutoCeilingsForLevel(roomPolygons: Point2D[][], existingCeilings: CeilingNodeType[], context?: AutoCeilingPlanningContext): AutoCeilingSyncPlan;
19
42
  export declare function detectSpacesForLevel(levelId: string, walls: WallNode[]): {
20
43
  roomPolygons: Point2D[][];
21
44
  spaces: Space[];
22
45
  wallUpdates: WallSideUpdate[];
23
46
  };
47
+ /** Pause the wall-driven auto slab/ceiling sync. Refcounted — pair with `resumeSpaceDetection`. */
48
+ export declare function pauseSpaceDetection(): void;
49
+ /** Resume the wall-driven auto slab/ceiling sync. No-op if not currently paused. */
50
+ export declare function resumeSpaceDetection(): void;
51
+ /** True iff the wall-driven auto slab/ceiling sync is currently paused. */
52
+ export declare function isSpaceDetectionPaused(): boolean;
24
53
  export declare function initSpaceDetectionSync(sceneStore: any, editorStore: any): () => void;
25
54
  export declare function wallTouchesOthers(wall: WallNode, otherWalls: WallNode[]): boolean;
26
55
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"space-detection.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,QAAQ,EACd,MAAM,WAAW,CAAA;AAalB,KAAK,OAAO,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvC,MAAM,MAAM,KAAK,GAAG;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAChC,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAA;IAC9C,QAAQ,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAA;CAC9C,CAAA;AAkWD,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,OAAO,GAAG,KAAK,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAC,EAC9E,YAAY,EAAE,OAAO,EAAE,EAAE,GACxB,IAAI,CAAC,cAAc,EAAE,WAAW,GAAG,UAAU,CAAC,CA4ChD;AAyWD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE;;;;EAEtE;AAsED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,GAAG,MAAM,IAAI,CAuDpF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,OAAO,CAiBjF"}
1
+ {"version":3,"file":"space-detection.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection.ts"],"names":[],"mappings":"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"}
@@ -4,9 +4,11 @@ import { getClampedWallCurveOffset, getWallCurveFrameAt, isCurvedWall, } from '.
4
4
  import { simplifyClosedPolygon } from './polygon-geometry';
5
5
  const DEFAULT_AUTO_SLAB_ELEVATION = 0.05;
6
6
  const DEFAULT_AUTO_CEILING_HEIGHT = 2.5;
7
+ const CEILING_HEIGHT_EPSILON = 1e-6;
7
8
  const ROOM_CURVE_TOLERANCE = 0.04;
8
9
  const MAX_CURVE_SUBDIVISION_DEPTH = 6;
9
10
  const AUTO_SLAB_POLYGON_SIMPLIFY_TOLERANCE = 0.08;
11
+ const WALL_ROOM_BOUNDARY_TOLERANCE = 0.08;
10
12
  function pointFromTuple(point) {
11
13
  return { x: point[0], y: point[1] };
12
14
  }
@@ -114,6 +116,80 @@ function bboxOverlapArea(a, b) {
114
116
  const iy = Math.max(0, Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY));
115
117
  return ix * iy;
116
118
  }
119
+ function pointDistanceToPolygonBoundary(point, polygon) {
120
+ let minDistance = Number.POSITIVE_INFINITY;
121
+ for (let index = 0; index < polygon.length; index += 1) {
122
+ const start = polygon[index];
123
+ const end = polygon[(index + 1) % polygon.length];
124
+ if (!(start && end))
125
+ continue;
126
+ minDistance = Math.min(minDistance, distanceToSegment(pointToTuple(point), pointToTuple(start), pointToTuple(end)));
127
+ }
128
+ return minDistance;
129
+ }
130
+ function wallBoundsRoom(wall, roomPolygon) {
131
+ const sampled = sampleWallPointsForRoomDetection(wall);
132
+ if (sampled.length === 0)
133
+ return false;
134
+ const candidates = sampled.length === 2
135
+ ? [
136
+ sampled[0],
137
+ {
138
+ x: (sampled[0].x + sampled[1].x) / 2,
139
+ y: (sampled[0].y + sampled[1].y) / 2,
140
+ },
141
+ sampled[1],
142
+ ]
143
+ : sampled;
144
+ const matchingPoints = candidates.filter((point) => pointDistanceToPolygonBoundary(point, roomPolygon) <= WALL_ROOM_BOUNDARY_TOLERANCE);
145
+ return matchingPoints.length >= 2;
146
+ }
147
+ function pointIsOnSlab(point, slab) {
148
+ if (slab.polygon.length < 3)
149
+ return false;
150
+ const slabPolygon = slab.polygon.map(pointFromTuple);
151
+ if (!pointInPolygon(point, slabPolygon))
152
+ return false;
153
+ for (const hole of slab.holes ?? []) {
154
+ if (hole.length >= 3 && pointInPolygon(point, hole.map(pointFromTuple))) {
155
+ return false;
156
+ }
157
+ }
158
+ return true;
159
+ }
160
+ function slabSupportsRoom(roomPolygon, slab) {
161
+ if (slab.polygon.length < 3)
162
+ return false;
163
+ if (polygonSignature(slab.polygon.map(pointFromTuple)) === polygonSignature(roomPolygon)) {
164
+ return true;
165
+ }
166
+ return pointIsOnSlab(polygonCentroid(roomPolygon), slab);
167
+ }
168
+ function resolveRoomSlabElevation(roomPolygon, slabs = []) {
169
+ let maxElevation = 0;
170
+ for (const slab of slabs) {
171
+ if (!slabSupportsRoom(roomPolygon, slab))
172
+ continue;
173
+ maxElevation = Math.max(maxElevation, slab.elevation ?? DEFAULT_AUTO_SLAB_ELEVATION);
174
+ }
175
+ return maxElevation;
176
+ }
177
+ function resolveRoomWallHeight(roomPolygon, walls = []) {
178
+ let maxHeight = 0;
179
+ for (const wall of walls) {
180
+ if (!wallBoundsRoom(wall, roomPolygon))
181
+ continue;
182
+ const height = wall.height ?? DEFAULT_AUTO_CEILING_HEIGHT;
183
+ if (Number.isFinite(height)) {
184
+ maxHeight = Math.max(maxHeight, height);
185
+ }
186
+ }
187
+ return maxHeight > 0 ? maxHeight : DEFAULT_AUTO_CEILING_HEIGHT;
188
+ }
189
+ function resolveAutoCeilingHeight(roomPolygon, context = {}) {
190
+ return (resolveRoomSlabElevation(roomPolygon, context.slabs) +
191
+ resolveRoomWallHeight(roomPolygon, context.walls));
192
+ }
117
193
  function getWallDirection(wall) {
118
194
  const dx = wall.end[0] - wall.start[0];
119
195
  const dy = wall.end[1] - wall.start[1];
@@ -340,12 +416,48 @@ function wallGeometrySignature(wall) {
340
416
  wall.end[0].toFixed(4),
341
417
  wall.end[1].toFixed(4),
342
418
  (wall.thickness ?? 0.2).toFixed(4),
419
+ (wall.height ?? DEFAULT_AUTO_CEILING_HEIGHT).toFixed(4),
343
420
  getClampedWallCurveOffset(wall).toFixed(4),
344
421
  ].join('|');
345
422
  }
346
423
  function levelWallSnapshot(walls) {
347
424
  return walls.map(wallGeometrySignature).sort().join('||');
348
425
  }
426
+ function slabGeometrySignature(slab) {
427
+ const polygon = slab.polygon
428
+ .map((point) => `${point[0].toFixed(4)},${point[1].toFixed(4)}`)
429
+ .join(';');
430
+ const holes = (slab.holes ?? [])
431
+ .map((hole) => hole.map((point) => `${point[0].toFixed(4)},${point[1].toFixed(4)}`).join(';'))
432
+ .join('/');
433
+ return [slab.id, (slab.elevation ?? DEFAULT_AUTO_SLAB_ELEVATION).toFixed(4), polygon, holes].join('|');
434
+ }
435
+ function levelSlabSnapshot(slabs) {
436
+ return slabs.map(slabGeometrySignature).sort().join('||');
437
+ }
438
+ function levelStructureSnapshots(nodes) {
439
+ const byLevel = new Map();
440
+ const getEntry = (levelId) => {
441
+ const entry = byLevel.get(levelId) ?? { walls: [], slabs: [] };
442
+ byLevel.set(levelId, entry);
443
+ return entry;
444
+ };
445
+ for (const node of Object.values(nodes)) {
446
+ if (!(node && typeof node === 'object' && 'parentId' in node && node.parentId))
447
+ continue;
448
+ if (node.type === 'wall') {
449
+ getEntry(node.parentId).walls.push(node);
450
+ }
451
+ else if (node.type === 'slab') {
452
+ getEntry(node.parentId).slabs.push(SlabNode.parse(node));
453
+ }
454
+ }
455
+ const snapshots = new Map();
456
+ for (const [levelId, entry] of byLevel.entries()) {
457
+ snapshots.set(levelId, `${levelWallSnapshot(entry.walls)}##${levelSlabSnapshot(entry.slabs)}`);
458
+ }
459
+ return snapshots;
460
+ }
349
461
  function buildSpace(levelId, polygon) {
350
462
  const signature = polygonSignature(polygon);
351
463
  return {
@@ -356,7 +468,7 @@ function buildSpace(levelId, polygon) {
356
468
  isExterior: false,
357
469
  };
358
470
  }
359
- function syncAutoSlabsForLevel(levelId, roomPolygons, existingSlabs, sceneStore) {
471
+ export function planAutoSlabsForLevel(roomPolygons, existingSlabs) {
360
472
  const manualSlabs = existingSlabs.filter((slab) => !slab.autoFromWalls);
361
473
  const manualSignatures = new Set(manualSlabs.map((slab) => polygonSignature(slab.polygon.map(pointFromTuple))));
362
474
  const detected = roomPolygons
@@ -459,17 +571,42 @@ function syncAutoSlabsForLevel(levelId, roomPolygons, existingSlabs, sceneStore)
459
571
  autoFromWalls: true,
460
572
  }));
461
573
  }
462
- if (slabsToDelete.length > 0) {
463
- sceneStore.getState().deleteNodes(slabsToDelete);
574
+ return {
575
+ create: slabsToCreate,
576
+ update: slabsToUpdate,
577
+ delete: slabsToDelete,
578
+ };
579
+ }
580
+ function syncAutoSlabsForLevel(levelId, roomPolygons, existingSlabs, sceneStore) {
581
+ const plan = planAutoSlabsForLevel(roomPolygons, existingSlabs);
582
+ if (plan.delete.length > 0) {
583
+ sceneStore.getState().deleteNodes(plan.delete);
584
+ }
585
+ if (plan.update.length > 0) {
586
+ sceneStore.getState().updateNodes(plan.update);
587
+ }
588
+ if (plan.create.length > 0) {
589
+ sceneStore.getState().createNodes(plan.create.map((node) => ({ node, parentId: levelId })));
464
590
  }
465
- if (slabsToUpdate.length > 0) {
466
- sceneStore.getState().updateNodes(slabsToUpdate);
591
+ return plan;
592
+ }
593
+ export function projectAutoSlabsForPlan(existingSlabs, plan) {
594
+ const slabsById = new Map(existingSlabs.map((slab) => [slab.id, slab]));
595
+ for (const id of plan.delete) {
596
+ slabsById.delete(id);
597
+ }
598
+ for (const update of plan.update) {
599
+ const slab = slabsById.get(update.id);
600
+ if (!slab)
601
+ continue;
602
+ slabsById.set(update.id, SlabNode.parse({ ...slab, ...update.data }));
467
603
  }
468
- if (slabsToCreate.length > 0) {
469
- sceneStore.getState().createNodes(slabsToCreate.map((node) => ({ node, parentId: levelId })));
604
+ for (const slab of plan.create) {
605
+ slabsById.set(slab.id, slab);
470
606
  }
607
+ return [...slabsById.values()];
471
608
  }
472
- function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, sceneStore) {
609
+ export function planAutoCeilingsForLevel(roomPolygons, existingCeilings, context = {}) {
473
610
  const manualCeilings = existingCeilings.filter((ceiling) => !ceiling.autoFromWalls);
474
611
  const manualSignatures = new Set(manualCeilings.map((ceiling) => polygonSignature(ceiling.polygon.map(pointFromTuple))));
475
612
  const detected = roomPolygons
@@ -486,6 +623,7 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
486
623
  centroid: polygonCentroid(room.poly),
487
624
  area: Math.abs(polygonArea(room.poly)),
488
625
  bbox: bboxOf(room.poly),
626
+ ceilingHeight: resolveAutoCeilingHeight(room.poly, context),
489
627
  }))
490
628
  .filter(({ sig }) => !manualSignatures.has(sig));
491
629
  const existingAuto = existingCeilings.filter((ceiling) => ceiling.autoFromWalls);
@@ -512,7 +650,10 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
512
650
  return;
513
651
  matchedDetectedIdx.add(index);
514
652
  matchedCeilingIds.add(existing.ceiling.id);
515
- updatesById.set(existing.ceiling.id, room.poly.map(pointToTuple));
653
+ updatesById.set(existing.ceiling.id, {
654
+ polygon: room.poly.map(pointToTuple),
655
+ height: room.ceilingHeight,
656
+ });
516
657
  });
517
658
  const remainingDetected = detected
518
659
  .map((room, index) => ({ room, index }))
@@ -541,7 +682,10 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
541
682
  continue;
542
683
  matchedDetectedIdx.add(index);
543
684
  matchedCeilingIds.add(bestMatch.entry.ceiling.id);
544
- updatesById.set(bestMatch.entry.ceiling.id, room.poly.map(pointToTuple));
685
+ updatesById.set(bestMatch.entry.ceiling.id, {
686
+ polygon: room.poly.map(pointToTuple),
687
+ height: room.ceilingHeight,
688
+ });
545
689
  }
546
690
  const ceilingsToDelete = existingAuto
547
691
  .filter((ceiling) => !updatesById.has(ceiling.id))
@@ -549,12 +693,18 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
549
693
  const ceilingsToUpdate = existingAuto
550
694
  .filter((ceiling) => updatesById.has(ceiling.id))
551
695
  .flatMap((ceiling) => {
552
- const polygon = updatesById.get(ceiling.id);
553
- if (!polygon)
696
+ const update = updatesById.get(ceiling.id);
697
+ if (!update)
554
698
  return [];
555
- return sameTuplePolygon(ceiling.polygon, polygon)
556
- ? []
557
- : [{ id: ceiling.id, data: { polygon } }];
699
+ const data = {};
700
+ if (!sameTuplePolygon(ceiling.polygon, update.polygon)) {
701
+ data.polygon = update.polygon;
702
+ }
703
+ if (Math.abs((ceiling.height ?? DEFAULT_AUTO_CEILING_HEIGHT) - update.height) >
704
+ CEILING_HEIGHT_EPSILON) {
705
+ data.height = update.height;
706
+ }
707
+ return Object.keys(data).length === 0 ? [] : [{ id: ceiling.id, data }];
558
708
  });
559
709
  const plannedCeilingsForNaming = [...existingCeilings];
560
710
  const ceilingsToCreate = [];
@@ -570,18 +720,26 @@ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, scene
570
720
  name,
571
721
  polygon: room.poly.map(pointToTuple),
572
722
  holes: [],
573
- height: DEFAULT_AUTO_CEILING_HEIGHT,
723
+ height: room.ceilingHeight,
574
724
  autoFromWalls: true,
575
725
  }));
576
726
  }
577
- if (ceilingsToDelete.length > 0) {
578
- sceneStore.getState().deleteNodes(ceilingsToDelete);
727
+ return {
728
+ create: ceilingsToCreate,
729
+ update: ceilingsToUpdate,
730
+ delete: ceilingsToDelete,
731
+ };
732
+ }
733
+ function syncAutoCeilingsForLevel(levelId, roomPolygons, existingCeilings, sceneStore, context = {}) {
734
+ const plan = planAutoCeilingsForLevel(roomPolygons, existingCeilings, context);
735
+ if (plan.delete.length > 0) {
736
+ sceneStore.getState().deleteNodes(plan.delete);
579
737
  }
580
- if (ceilingsToUpdate.length > 0) {
581
- sceneStore.getState().updateNodes(ceilingsToUpdate);
738
+ if (plan.update.length > 0) {
739
+ sceneStore.getState().updateNodes(plan.update);
582
740
  }
583
- if (ceilingsToCreate.length > 0) {
584
- sceneStore.getState().createNodes(ceilingsToCreate.map((node) => ({ node, parentId: levelId })));
741
+ if (plan.create.length > 0) {
742
+ sceneStore.getState().createNodes(plan.create.map((node) => ({ node, parentId: levelId })));
585
743
  }
586
744
  }
587
745
  function detectSpacesFromWalls(levelId, walls) {
@@ -626,14 +784,36 @@ function runSpaceDetection(levelIds, sceneStore, editorStore, nodes) {
626
784
  },
627
785
  })));
628
786
  }
629
- syncAutoSlabsForLevel(levelId, roomPolygons, slabs.map((slab) => SlabNode.parse(slab)), sceneStore);
630
- syncAutoCeilingsForLevel(levelId, roomPolygons, ceilings.map((ceiling) => CeilingNode.parse(ceiling)), sceneStore);
787
+ const parsedSlabs = slabs.map((slab) => SlabNode.parse(slab));
788
+ const slabPlan = syncAutoSlabsForLevel(levelId, roomPolygons, parsedSlabs, sceneStore);
789
+ const projectedSlabs = projectAutoSlabsForPlan(parsedSlabs, slabPlan);
790
+ syncAutoCeilingsForLevel(levelId, roomPolygons, ceilings.map((ceiling) => CeilingNode.parse(ceiling)), sceneStore, { walls, slabs: projectedSlabs });
631
791
  for (const space of spaces) {
632
792
  nextSpaces[space.id] = space;
633
793
  }
634
794
  }
635
795
  editorStore.getState().setSpaces(nextSpaces);
636
796
  }
797
+ // Refcount of outstanding pause requests, matching the pauseSceneHistory
798
+ // pattern. The community editor flips this off while the AI is actively
799
+ // mutating the scene so the wall-driven auto slab/ceiling sync doesn't race
800
+ // `create_room`'s explicit slabs/ceilings (see plan
801
+ // `ai-pause-space-detection`).
802
+ let spaceDetectionPauseDepth = 0;
803
+ /** Pause the wall-driven auto slab/ceiling sync. Refcounted — pair with `resumeSpaceDetection`. */
804
+ export function pauseSpaceDetection() {
805
+ spaceDetectionPauseDepth += 1;
806
+ }
807
+ /** Resume the wall-driven auto slab/ceiling sync. No-op if not currently paused. */
808
+ export function resumeSpaceDetection() {
809
+ if (spaceDetectionPauseDepth === 0)
810
+ return;
811
+ spaceDetectionPauseDepth -= 1;
812
+ }
813
+ /** True iff the wall-driven auto slab/ceiling sync is currently paused. */
814
+ export function isSpaceDetectionPaused() {
815
+ return spaceDetectionPauseDepth > 0;
816
+ }
637
817
  export function initSpaceDetectionSync(sceneStore, editorStore) {
638
818
  const previousSnapshots = new Map();
639
819
  let isProcessing = false;
@@ -643,18 +823,16 @@ export function initSpaceDetectionSync(sceneStore, editorStore) {
643
823
  if (getSceneHistoryPauseDepth() > 0)
644
824
  return;
645
825
  const nodes = state.nodes;
646
- const wallsByLevel = new Map();
647
- for (const node of Object.values(nodes)) {
648
- if (node && node.type === 'wall' && node.parentId) {
649
- const levelId = node.parentId;
650
- const levelWalls = wallsByLevel.get(levelId) ?? [];
651
- levelWalls.push(node);
652
- wallsByLevel.set(levelId, levelWalls);
826
+ const currentSnapshots = levelStructureSnapshots(nodes);
827
+ // Paused: roll the snapshot forward so we don't backfill (and re-duplicate)
828
+ // every paused change once detection resumes. Whatever the AI built while
829
+ // paused becomes the new baseline; only future changes will reconcile.
830
+ if (spaceDetectionPauseDepth > 0) {
831
+ previousSnapshots.clear();
832
+ for (const [levelId, snapshot] of currentSnapshots.entries()) {
833
+ previousSnapshots.set(levelId, snapshot);
653
834
  }
654
- }
655
- const currentSnapshots = new Map();
656
- for (const [levelId, walls] of wallsByLevel.entries()) {
657
- currentSnapshots.set(levelId, levelWallSnapshot(walls));
835
+ return;
658
836
  }
659
837
  const levelsToUpdate = new Set();
660
838
  for (const levelId of new Set([...previousSnapshots.keys(), ...currentSnapshots.keys()])) {
@@ -677,7 +855,8 @@ export function initSpaceDetectionSync(sceneStore, editorStore) {
677
855
  finally {
678
856
  resumeSceneHistory(sceneStore);
679
857
  previousSnapshots.clear();
680
- for (const [levelId, snapshot] of currentSnapshots.entries()) {
858
+ const postRunSnapshots = levelStructureSnapshots(sceneStore.getState().nodes);
859
+ for (const [levelId, snapshot] of postRunSnapshots.entries()) {
681
860
  previousSnapshots.set(levelId, snapshot);
682
861
  }
683
862
  isProcessing = false;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=space-detection.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"space-detection.test.d.ts","sourceRoot":"","sources":["../../src/lib/space-detection.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { CeilingNode, SlabNode, WallNode } from '../schema';
3
+ import { planAutoCeilingsForLevel } from './space-detection';
4
+ const square = [
5
+ [0, 0],
6
+ [4, 0],
7
+ [4, 3],
8
+ [0, 3],
9
+ ];
10
+ function roomPolygon() {
11
+ return square.map(([x, y]) => ({ x, y }));
12
+ }
13
+ function squareWalls(height = 2.5) {
14
+ return [
15
+ WallNode.parse({ start: [0, 0], end: [4, 0], height }),
16
+ WallNode.parse({ start: [4, 0], end: [4, 3], height }),
17
+ WallNode.parse({ start: [4, 3], end: [0, 3], height }),
18
+ WallNode.parse({ start: [0, 3], end: [0, 0], height }),
19
+ ];
20
+ }
21
+ function slab(elevation) {
22
+ return SlabNode.parse({
23
+ polygon: square,
24
+ elevation,
25
+ autoFromWalls: true,
26
+ });
27
+ }
28
+ describe('planAutoCeilingsForLevel', () => {
29
+ test('creates auto ceilings at the top of the room walls', () => {
30
+ const created = planAutoCeilingsForLevel([roomPolygon()], [], {
31
+ walls: squareWalls(),
32
+ slabs: [slab(0.05)],
33
+ }).create[0];
34
+ expect(created?.height).toBeCloseTo(2.55);
35
+ });
36
+ test('updates existing auto ceiling height when the slab elevation changes', () => {
37
+ const ceiling = CeilingNode.parse({
38
+ polygon: square,
39
+ height: 2.55,
40
+ autoFromWalls: true,
41
+ });
42
+ const plan = planAutoCeilingsForLevel([roomPolygon()], [ceiling], {
43
+ walls: squareWalls(),
44
+ slabs: [slab(0.4)],
45
+ });
46
+ expect(plan.update).toHaveLength(1);
47
+ expect(plan.update[0]?.id).toBe(ceiling.id);
48
+ expect(plan.update[0]?.data.polygon).toBeUndefined();
49
+ expect(plan.update[0]?.data.height).toBeCloseTo(2.9);
50
+ });
51
+ test('updates existing auto ceiling height when wall height changes', () => {
52
+ const ceiling = CeilingNode.parse({
53
+ polygon: square,
54
+ height: 2.55,
55
+ autoFromWalls: true,
56
+ });
57
+ const plan = planAutoCeilingsForLevel([roomPolygon()], [ceiling], {
58
+ walls: squareWalls(3),
59
+ slabs: [slab(0.05)],
60
+ });
61
+ expect(plan.update).toHaveLength(1);
62
+ expect(plan.update[0]?.data.height).toBeCloseTo(3.05);
63
+ });
64
+ test('does not replace a manual ceiling with an auto ceiling', () => {
65
+ const manualCeiling = CeilingNode.parse({
66
+ polygon: square,
67
+ height: 2.5,
68
+ autoFromWalls: false,
69
+ });
70
+ const plan = planAutoCeilingsForLevel([roomPolygon()], [manualCeiling], {
71
+ walls: squareWalls(),
72
+ slabs: [slab(0.4)],
73
+ });
74
+ expect(plan.create).toHaveLength(0);
75
+ expect(plan.update).toHaveLength(0);
76
+ });
77
+ });
@@ -8,7 +8,7 @@ export type MaterialCatalogItem = {
8
8
  previewColor?: string;
9
9
  preset: MaterialPresetPayload;
10
10
  };
11
- export declare const MATERIAL_CATEGORIES: readonly ["wood", "wallpaper", "parquet", "granite", "marble", "other"];
11
+ export declare const MATERIAL_CATEGORIES: readonly ["wood", "flooring", "roof", "other"];
12
12
  export type MaterialCategory = (typeof MATERIAL_CATEGORIES)[number];
13
13
  export declare const MATERIAL_CATALOG: MaterialCatalogItem[];
14
14
  export declare function getMaterialsForCategory(category: MaterialCategory): MaterialCatalogItem[];
@@ -1 +1 @@
1
- {"version":3,"file":"material-library.d.ts","sourceRoot":"","sources":["../src/material-library.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,mBAAmB,CAAA;AAE1B,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,qBAAqB,CAAA;CAC9B,CAAA;AA4BD,eAAO,MAAM,mBAAmB,yEAOtB,CAAA;AACV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,eAAO,MAAM,gBAAgB,EAAE,mBAAmB,EAiiBjD,CAAA;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,mBAAmB,EAAE,CAEzF;AAED,wBAAgB,sBAAsB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAGnF;AAED,eAAO,MAAM,2BAA2B,aAAa,CAAA;AAErD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,UAE9C;AAED,wBAAgB,2BAA2B,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,iBAItE;AAED,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,qBAAqB,GAAG,IAAI,CAIhG"}
1
+ {"version":3,"file":"material-library.d.ts","sourceRoot":"","sources":["../src/material-library.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,mBAAmB,CAAA;AAE1B,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,qBAAqB,CAAA;CAC9B,CAAA;AAuCD,eAAO,MAAM,mBAAmB,gDAAiD,CAAA;AACjF,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEnE,eAAO,MAAM,gBAAgB,EAAE,mBAAmB,EAqzEjD,CAAA;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,mBAAmB,EAAE,CAEzF;AAED,wBAAgB,sBAAsB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAGnF;AAED,eAAO,MAAM,2BAA2B,aAAa,CAAA;AAErD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,UAE9C;AAED,wBAAgB,2BAA2B,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,iBAItE;AAED,wBAAgB,sBAAsB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,qBAAqB,GAAG,IAAI,CAIhG"}