@pascal-app/core 0.9.1 → 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 (321) hide show
  1. package/dist/events/bus.d.ts +30 -2
  2. package/dist/events/bus.d.ts.map +1 -1
  3. package/dist/hooks/scene-registry/item-clip-registry.d.ts +18 -0
  4. package/dist/hooks/scene-registry/item-clip-registry.d.ts.map +1 -0
  5. package/dist/hooks/scene-registry/item-clip-registry.js +10 -0
  6. package/dist/hooks/scene-registry/scene-registry.d.ts +8 -1
  7. package/dist/hooks/scene-registry/scene-registry.d.ts.map +1 -1
  8. package/dist/hooks/scene-registry/scene-registry.js +27 -1
  9. package/dist/hooks/scene-registry/scene-registry.test.d.ts +2 -0
  10. package/dist/hooks/scene-registry/scene-registry.test.d.ts.map +1 -0
  11. package/dist/hooks/scene-registry/scene-registry.test.js +26 -0
  12. package/dist/hooks/spatial-grid/floor-placed-elevation.test.js +93 -0
  13. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts +69 -12
  14. package/dist/hooks/spatial-grid/spatial-grid-manager.d.ts.map +1 -1
  15. package/dist/hooks/spatial-grid/spatial-grid-manager.js +476 -195
  16. package/dist/hooks/spatial-grid/spatial-grid-sync.d.ts.map +1 -1
  17. package/dist/hooks/spatial-grid/spatial-grid-sync.js +19 -1
  18. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts +2 -0
  19. package/dist/hooks/spatial-grid/wall-slab-overlap.test.d.ts.map +1 -0
  20. package/dist/hooks/spatial-grid/wall-slab-overlap.test.js +363 -0
  21. package/dist/index.d.ts +16 -6
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +15 -5
  24. package/dist/lib/level-name.d.ts +4 -0
  25. package/dist/lib/level-name.d.ts.map +1 -0
  26. package/dist/lib/level-name.js +10 -0
  27. package/dist/lib/measurement-geometry.d.ts +19 -0
  28. package/dist/lib/measurement-geometry.d.ts.map +1 -0
  29. package/dist/lib/measurement-geometry.js +232 -0
  30. package/dist/lib/measurement-geometry.test.d.ts +2 -0
  31. package/dist/lib/measurement-geometry.test.d.ts.map +1 -0
  32. package/dist/lib/measurement-geometry.test.js +102 -0
  33. package/dist/lib/polygon-geometry.d.ts +0 -1
  34. package/dist/lib/polygon-geometry.d.ts.map +1 -1
  35. package/dist/lib/polygon-geometry.js +0 -18
  36. package/dist/lib/selection-proxy.d.ts +4 -0
  37. package/dist/lib/selection-proxy.d.ts.map +1 -0
  38. package/dist/lib/selection-proxy.js +12 -0
  39. package/dist/lib/slab-polygon.d.ts +31 -2
  40. package/dist/lib/slab-polygon.d.ts.map +1 -1
  41. package/dist/lib/slab-polygon.js +525 -31
  42. package/dist/lib/slab-polygon.test.d.ts +2 -0
  43. package/dist/lib/slab-polygon.test.d.ts.map +1 -0
  44. package/dist/lib/slab-polygon.test.js +635 -0
  45. package/dist/lib/slots.d.ts +14 -0
  46. package/dist/lib/slots.d.ts.map +1 -0
  47. package/dist/lib/slots.js +26 -0
  48. package/dist/lib/space-detection.d.ts +25 -2
  49. package/dist/lib/space-detection.d.ts.map +1 -1
  50. package/dist/lib/space-detection.js +414 -118
  51. package/dist/lib/space-detection.test.js +301 -2
  52. package/dist/lib/wall-distance.d.ts +60 -0
  53. package/dist/lib/wall-distance.d.ts.map +1 -0
  54. package/dist/lib/wall-distance.js +85 -0
  55. package/dist/lib/zone-quantities.d.ts +21 -0
  56. package/dist/lib/zone-quantities.d.ts.map +1 -0
  57. package/dist/lib/zone-quantities.js +183 -0
  58. package/dist/lib/zone-quantities.test.d.ts +2 -0
  59. package/dist/lib/zone-quantities.test.d.ts.map +1 -0
  60. package/dist/lib/zone-quantities.test.js +82 -0
  61. package/dist/material-library.d.ts +20 -1
  62. package/dist/material-library.d.ts.map +1 -1
  63. package/dist/material-library.js +2181 -441
  64. package/dist/registry/handles.d.ts +75 -2
  65. package/dist/registry/handles.d.ts.map +1 -1
  66. package/dist/registry/index.d.ts +3 -3
  67. package/dist/registry/index.d.ts.map +1 -1
  68. package/dist/registry/index.js +1 -1
  69. package/dist/registry/registry.d.ts +46 -3
  70. package/dist/registry/registry.d.ts.map +1 -1
  71. package/dist/registry/registry.js +82 -1
  72. package/dist/registry/registry.test.js +14 -1
  73. package/dist/registry/subtree.d.ts.map +1 -1
  74. package/dist/registry/subtree.js +4 -0
  75. package/dist/registry/subtree.test.js +30 -0
  76. package/dist/registry/types.d.ts +717 -38
  77. package/dist/registry/types.d.ts.map +1 -1
  78. package/dist/schema/index.d.ts +21 -5
  79. package/dist/schema/index.d.ts.map +1 -1
  80. package/dist/schema/index.js +17 -3
  81. package/dist/schema/material.d.ts +3 -2
  82. package/dist/schema/material.d.ts.map +1 -1
  83. package/dist/schema/material.js +7 -5
  84. package/dist/schema/material.test.d.ts +2 -0
  85. package/dist/schema/material.test.d.ts.map +1 -0
  86. package/dist/schema/material.test.js +43 -0
  87. package/dist/schema/nodes/box-vent.d.ts +2 -2
  88. package/dist/schema/nodes/cabinet.d.ts +534 -0
  89. package/dist/schema/nodes/cabinet.d.ts.map +1 -0
  90. package/dist/schema/nodes/cabinet.js +137 -0
  91. package/dist/schema/nodes/ceiling.d.ts +3 -2
  92. package/dist/schema/nodes/ceiling.d.ts.map +1 -1
  93. package/dist/schema/nodes/ceiling.js +4 -0
  94. package/dist/schema/nodes/chimney.d.ts +4 -4
  95. package/dist/schema/nodes/column.d.ts +3 -2
  96. package/dist/schema/nodes/column.d.ts.map +1 -1
  97. package/dist/schema/nodes/column.js +3 -0
  98. package/dist/schema/nodes/cupola.d.ts +2 -2
  99. package/dist/schema/nodes/door.d.ts +13 -5
  100. package/dist/schema/nodes/door.d.ts.map +1 -1
  101. package/dist/schema/nodes/door.js +12 -0
  102. package/dist/schema/nodes/dormer.d.ts +8 -8
  103. package/dist/schema/nodes/dormer.js +1 -1
  104. package/dist/schema/nodes/downspout.d.ts +2 -2
  105. package/dist/schema/nodes/duct-fitting.d.ts +91 -0
  106. package/dist/schema/nodes/duct-fitting.d.ts.map +1 -0
  107. package/dist/schema/nodes/duct-fitting.js +92 -0
  108. package/dist/schema/nodes/duct-segment.d.ts +61 -0
  109. package/dist/schema/nodes/duct-segment.d.ts.map +1 -0
  110. package/dist/schema/nodes/duct-segment.js +73 -0
  111. package/dist/schema/nodes/duct-terminal.d.ts +57 -0
  112. package/dist/schema/nodes/duct-terminal.d.ts.map +1 -0
  113. package/dist/schema/nodes/duct-terminal.js +51 -0
  114. package/dist/schema/nodes/elevator.d.ts +3 -2
  115. package/dist/schema/nodes/elevator.d.ts.map +1 -1
  116. package/dist/schema/nodes/elevator.js +3 -0
  117. package/dist/schema/nodes/eyebrow-vent.d.ts +2 -2
  118. package/dist/schema/nodes/fence.d.ts +18 -2
  119. package/dist/schema/nodes/fence.d.ts.map +1 -1
  120. package/dist/schema/nodes/fence.js +25 -2
  121. package/dist/schema/nodes/gutter.d.ts +2 -2
  122. package/dist/schema/nodes/hvac-equipment.d.ts +63 -0
  123. package/dist/schema/nodes/hvac-equipment.d.ts.map +1 -0
  124. package/dist/schema/nodes/hvac-equipment.js +55 -0
  125. package/dist/schema/nodes/item.d.ts +8 -0
  126. package/dist/schema/nodes/item.d.ts.map +1 -1
  127. package/dist/schema/nodes/item.js +11 -0
  128. package/dist/schema/nodes/level.d.ts +1 -1
  129. package/dist/schema/nodes/level.d.ts.map +1 -1
  130. package/dist/schema/nodes/level.js +2 -0
  131. package/dist/schema/nodes/lineset.d.ts +42 -0
  132. package/dist/schema/nodes/lineset.d.ts.map +1 -0
  133. package/dist/schema/nodes/lineset.js +38 -0
  134. package/dist/schema/nodes/liquid-line.d.ts +34 -0
  135. package/dist/schema/nodes/liquid-line.d.ts.map +1 -0
  136. package/dist/schema/nodes/liquid-line.js +24 -0
  137. package/dist/schema/nodes/measurement.d.ts +306 -0
  138. package/dist/schema/nodes/measurement.d.ts.map +1 -0
  139. package/dist/schema/nodes/measurement.js +87 -0
  140. package/dist/schema/nodes/measurement.test.d.ts +2 -0
  141. package/dist/schema/nodes/measurement.test.d.ts.map +1 -0
  142. package/dist/schema/nodes/measurement.test.js +139 -0
  143. package/dist/schema/nodes/pipe-fitting.d.ts +56 -0
  144. package/dist/schema/nodes/pipe-fitting.d.ts.map +1 -0
  145. package/dist/schema/nodes/pipe-fitting.js +45 -0
  146. package/dist/schema/nodes/pipe-segment.d.ts +48 -0
  147. package/dist/schema/nodes/pipe-segment.d.ts.map +1 -0
  148. package/dist/schema/nodes/pipe-segment.js +36 -0
  149. package/dist/schema/nodes/pipe-trap.d.ts +43 -0
  150. package/dist/schema/nodes/pipe-trap.d.ts.map +1 -0
  151. package/dist/schema/nodes/pipe-trap.js +36 -0
  152. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts +2 -0
  153. package/dist/schema/nodes/ridge-vent-defaults.test.d.ts.map +1 -0
  154. package/dist/schema/nodes/ridge-vent-defaults.test.js +205 -0
  155. package/dist/schema/nodes/ridge-vent.d.ts +18 -3
  156. package/dist/schema/nodes/ridge-vent.d.ts.map +1 -1
  157. package/dist/schema/nodes/ridge-vent.js +221 -3
  158. package/dist/schema/nodes/roof-segment-shape.d.ts +90 -0
  159. package/dist/schema/nodes/roof-segment-shape.d.ts.map +1 -0
  160. package/dist/schema/nodes/roof-segment-shape.js +318 -0
  161. package/dist/schema/nodes/roof-segment-shape.test.d.ts +2 -0
  162. package/dist/schema/nodes/roof-segment-shape.test.d.ts.map +1 -0
  163. package/dist/schema/nodes/roof-segment-shape.test.js +175 -0
  164. package/dist/schema/nodes/roof-segment-surface.test.d.ts +2 -0
  165. package/dist/schema/nodes/roof-segment-surface.test.d.ts.map +1 -0
  166. package/dist/schema/nodes/roof-segment-surface.test.js +48 -0
  167. package/dist/schema/nodes/roof-segment-trim.test.d.ts +2 -0
  168. package/dist/schema/nodes/roof-segment-trim.test.d.ts.map +1 -0
  169. package/dist/schema/nodes/roof-segment-trim.test.js +107 -0
  170. package/dist/schema/nodes/roof-segment-walls.d.ts +101 -0
  171. package/dist/schema/nodes/roof-segment-walls.d.ts.map +1 -0
  172. package/dist/schema/nodes/roof-segment-walls.js +330 -0
  173. package/dist/schema/nodes/roof-segment-walls.test.d.ts +2 -0
  174. package/dist/schema/nodes/roof-segment-walls.test.d.ts.map +1 -0
  175. package/dist/schema/nodes/roof-segment-walls.test.js +92 -0
  176. package/dist/schema/nodes/roof-segment.d.ts +84 -8
  177. package/dist/schema/nodes/roof-segment.d.ts.map +1 -1
  178. package/dist/schema/nodes/roof-segment.js +290 -5
  179. package/dist/schema/nodes/roof.d.ts +8 -8
  180. package/dist/schema/nodes/shelf.d.ts +3 -2
  181. package/dist/schema/nodes/shelf.d.ts.map +1 -1
  182. package/dist/schema/nodes/shelf.js +6 -0
  183. package/dist/schema/nodes/skylight.d.ts +10 -10
  184. package/dist/schema/nodes/slab.d.ts +3 -2
  185. package/dist/schema/nodes/slab.d.ts.map +1 -1
  186. package/dist/schema/nodes/slab.js +4 -0
  187. package/dist/schema/nodes/solar-panel.d.ts +4 -4
  188. package/dist/schema/nodes/stair-segment.d.ts +4 -4
  189. package/dist/schema/nodes/stair.d.ts +13 -12
  190. package/dist/schema/nodes/stair.d.ts.map +1 -1
  191. package/dist/schema/nodes/stair.js +3 -0
  192. package/dist/schema/nodes/turbine-vent.d.ts +2 -2
  193. package/dist/schema/nodes/wall.d.ts +245 -6
  194. package/dist/schema/nodes/wall.d.ts.map +1 -1
  195. package/dist/schema/nodes/wall.js +257 -0
  196. package/dist/schema/nodes/wall.test.d.ts +2 -0
  197. package/dist/schema/nodes/wall.test.d.ts.map +1 -0
  198. package/dist/schema/nodes/wall.test.js +208 -0
  199. package/dist/schema/nodes/window.d.ts +11 -3
  200. package/dist/schema/nodes/window.d.ts.map +1 -1
  201. package/dist/schema/nodes/window.js +12 -0
  202. package/dist/schema/nodes/zone.d.ts +2 -0
  203. package/dist/schema/nodes/zone.d.ts.map +1 -1
  204. package/dist/schema/nodes/zone.js +6 -0
  205. package/dist/schema/scene-material.d.ts +41 -0
  206. package/dist/schema/scene-material.d.ts.map +1 -0
  207. package/dist/schema/scene-material.js +9 -0
  208. package/dist/schema/types.d.ts +1089 -94
  209. package/dist/schema/types.d.ts.map +1 -1
  210. package/dist/schema/types.js +23 -0
  211. package/dist/services/alignment-anchors.d.ts.map +1 -1
  212. package/dist/services/alignment-anchors.js +30 -1
  213. package/dist/services/alignment.d.ts +5 -0
  214. package/dist/services/alignment.d.ts.map +1 -1
  215. package/dist/services/alignment.js +2 -0
  216. package/dist/services/hosting.d.ts +8 -0
  217. package/dist/services/hosting.d.ts.map +1 -1
  218. package/dist/services/hosting.js +13 -4
  219. package/dist/services/hosting.test.js +32 -1
  220. package/dist/services/index.d.ts +8 -2
  221. package/dist/services/index.d.ts.map +1 -1
  222. package/dist/services/index.js +8 -2
  223. package/dist/services/level-height.d.ts +28 -0
  224. package/dist/services/level-height.d.ts.map +1 -0
  225. package/dist/services/level-height.js +62 -0
  226. package/dist/services/opening-guides.d.ts +141 -0
  227. package/dist/services/opening-guides.d.ts.map +1 -0
  228. package/dist/services/opening-guides.js +267 -0
  229. package/dist/services/opening-guides.test.d.ts +2 -0
  230. package/dist/services/opening-guides.test.d.ts.map +1 -0
  231. package/dist/services/opening-guides.test.js +208 -0
  232. package/dist/services/port-connectivity.d.ts +115 -0
  233. package/dist/services/port-connectivity.d.ts.map +1 -0
  234. package/dist/services/port-connectivity.js +300 -0
  235. package/dist/services/port-connectivity.test.d.ts +2 -0
  236. package/dist/services/port-connectivity.test.d.ts.map +1 -0
  237. package/dist/services/port-connectivity.test.js +297 -0
  238. package/dist/services/riser-diagram.d.ts +40 -0
  239. package/dist/services/riser-diagram.d.ts.map +1 -0
  240. package/dist/services/riser-diagram.js +110 -0
  241. package/dist/services/riser-diagram.test.d.ts +2 -0
  242. package/dist/services/riser-diagram.test.d.ts.map +1 -0
  243. package/dist/services/riser-diagram.test.js +66 -0
  244. package/dist/services/snap.d.ts +9 -1
  245. package/dist/services/snap.d.ts.map +1 -1
  246. package/dist/services/snap.js +21 -1
  247. package/dist/services/snap.test.js +43 -1
  248. package/dist/services/system-graph.d.ts +30 -0
  249. package/dist/services/system-graph.d.ts.map +1 -0
  250. package/dist/services/system-graph.js +164 -0
  251. package/dist/services/system-graph.test.d.ts +2 -0
  252. package/dist/services/system-graph.test.d.ts.map +1 -0
  253. package/dist/services/system-graph.test.js +138 -0
  254. package/dist/services/validate-dwv.d.ts +32 -0
  255. package/dist/services/validate-dwv.d.ts.map +1 -0
  256. package/dist/services/validate-dwv.js +118 -0
  257. package/dist/services/validate-dwv.test.d.ts +2 -0
  258. package/dist/services/validate-dwv.test.d.ts.map +1 -0
  259. package/dist/services/validate-dwv.test.js +109 -0
  260. package/dist/store/actions/node-actions.d.ts.map +1 -1
  261. package/dist/store/actions/node-actions.js +94 -1
  262. package/dist/store/actions/ridge-vent-update.test.d.ts +2 -0
  263. package/dist/store/actions/ridge-vent-update.test.d.ts.map +1 -0
  264. package/dist/store/actions/ridge-vent-update.test.js +264 -0
  265. package/dist/store/history-control.d.ts +11 -0
  266. package/dist/store/history-control.d.ts.map +1 -1
  267. package/dist/store/history-control.js +33 -0
  268. package/dist/store/use-scene-dirty-tracking.test.d.ts +2 -0
  269. package/dist/store/use-scene-dirty-tracking.test.d.ts.map +1 -0
  270. package/dist/store/use-scene-dirty-tracking.test.js +68 -0
  271. package/dist/store/use-scene-measurement-roundtrip.test.d.ts +2 -0
  272. package/dist/store/use-scene-measurement-roundtrip.test.d.ts.map +1 -0
  273. package/dist/store/use-scene-measurement-roundtrip.test.js +97 -0
  274. package/dist/store/use-scene-plugins.test.d.ts +2 -0
  275. package/dist/store/use-scene-plugins.test.d.ts.map +1 -0
  276. package/dist/store/use-scene-plugins.test.js +51 -0
  277. package/dist/store/use-scene-wall-slot-migration.test.d.ts +2 -0
  278. package/dist/store/use-scene-wall-slot-migration.test.d.ts.map +1 -0
  279. package/dist/store/use-scene-wall-slot-migration.test.js +210 -0
  280. package/dist/store/use-scene-window-migration.test.d.ts +2 -0
  281. package/dist/store/use-scene-window-migration.test.d.ts.map +1 -0
  282. package/dist/store/use-scene-window-migration.test.js +85 -0
  283. package/dist/store/use-scene.d.ts +17 -2
  284. package/dist/store/use-scene.d.ts.map +1 -1
  285. package/dist/store/use-scene.js +358 -51
  286. package/dist/systems/elevator/elevator-service.js +1 -1
  287. package/dist/systems/fence/fence-centerline.d.ts +12 -0
  288. package/dist/systems/fence/fence-centerline.d.ts.map +1 -0
  289. package/dist/systems/fence/fence-centerline.js +37 -0
  290. package/dist/systems/fence/fence-spline.d.ts +39 -0
  291. package/dist/systems/fence/fence-spline.d.ts.map +1 -0
  292. package/dist/systems/fence/fence-spline.js +196 -0
  293. package/dist/systems/fence/fence-spline.test.d.ts +2 -0
  294. package/dist/systems/fence/fence-spline.test.d.ts.map +1 -0
  295. package/dist/systems/fence/fence-spline.test.js +136 -0
  296. package/dist/systems/stair/stair-footprint.d.ts.map +1 -1
  297. package/dist/systems/stair/stair-footprint.js +21 -11
  298. package/dist/systems/stair/stair-opening-preview.d.ts +1139 -469
  299. package/dist/systems/stair/stair-opening-preview.d.ts.map +1 -1
  300. package/dist/systems/stair/stair-opening-sync.d.ts.map +1 -1
  301. package/dist/systems/stair/stair-opening-sync.js +1 -18
  302. package/dist/systems/stair/stair-opening-sync.test.js +39 -0
  303. package/dist/systems/wall/wall-mitering.d.ts.map +1 -1
  304. package/dist/systems/wall/wall-mitering.js +38 -2
  305. package/dist/systems/wall/wall-mitering.test.d.ts +2 -0
  306. package/dist/systems/wall/wall-mitering.test.d.ts.map +1 -0
  307. package/dist/systems/wall/wall-mitering.test.js +63 -0
  308. package/dist/utils/clone-scene-graph.d.ts +1 -0
  309. package/dist/utils/clone-scene-graph.d.ts.map +1 -1
  310. package/dist/utils/clone-scene-graph.js +23 -3
  311. package/dist/utils/clone-scene-graph.test.js +3 -0
  312. package/dist/utils/heal-scene-graph.d.ts +18 -0
  313. package/dist/utils/heal-scene-graph.d.ts.map +1 -0
  314. package/dist/utils/heal-scene-graph.js +90 -0
  315. package/dist/utils/heal-scene-graph.test.d.ts +2 -0
  316. package/dist/utils/heal-scene-graph.test.d.ts.map +1 -0
  317. package/dist/utils/heal-scene-graph.test.js +88 -0
  318. package/dist/validation/validate-build-json.d.ts +1 -0
  319. package/dist/validation/validate-build-json.d.ts.map +1 -1
  320. package/dist/validation/validate-build-json.js +31 -2
  321. package/package.json +3 -3
@@ -0,0 +1,232 @@
1
+ const GEOMETRY_EPSILON = 1e-9;
2
+ export const MEASUREMENT_PLANAR_TOLERANCE = 0.01;
3
+ const subtract = (a, b) => [
4
+ a[0] - b[0],
5
+ a[1] - b[1],
6
+ a[2] - b[2],
7
+ ];
8
+ const cross = (a, b) => [
9
+ a[1] * b[2] - a[2] * b[1],
10
+ a[2] * b[0] - a[0] * b[2],
11
+ a[0] * b[1] - a[1] * b[0],
12
+ ];
13
+ const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
14
+ const magnitude = (point) => Math.hypot(point[0], point[1], point[2]);
15
+ export function measurementDistance(start, end) {
16
+ return magnitude(subtract(end, start));
17
+ }
18
+ export function measurementAnchorFallback(anchor) {
19
+ return Array.isArray(anchor) ? anchor : anchor.fallback;
20
+ }
21
+ export function measurementAngle(start, vertex, end) {
22
+ const a = subtract(start, vertex);
23
+ const b = subtract(end, vertex);
24
+ const denominator = magnitude(a) * magnitude(b);
25
+ if (!Number.isFinite(denominator) || denominator <= GEOMETRY_EPSILON)
26
+ return 0;
27
+ return Math.acos(Math.max(-1, Math.min(1, dot(a, b) / denominator)));
28
+ }
29
+ export function measurementPerimeter(points) {
30
+ if (points.length < 3)
31
+ return 0;
32
+ let length = 0;
33
+ for (let index = 0; index < points.length; index++) {
34
+ length += measurementDistance(points[index], points[(index + 1) % points.length]);
35
+ }
36
+ return length;
37
+ }
38
+ export function measurementFeatureLength(feature) {
39
+ const geometry = feature.geometry;
40
+ if (geometry.kind === 'point')
41
+ return null;
42
+ const points = geometry.kind === 'segment' ? [geometry.start, geometry.end] : geometry.points;
43
+ const closed = geometry.kind === 'polygon' || (geometry.kind === 'path' && geometry.closed === true);
44
+ const segmentCount = closed ? points.length : points.length - 1;
45
+ if (segmentCount <= 0)
46
+ return null;
47
+ let length = 0;
48
+ for (let index = 0; index < segmentCount; index++) {
49
+ const start = points[index];
50
+ const end = points[(index + 1) % points.length];
51
+ length += Math.hypot(end[0] - start[0], end[1] - start[1], end[2] - start[2]);
52
+ }
53
+ return length;
54
+ }
55
+ function closestPointOnMeasurementSegment(point, start, end) {
56
+ const dx = end[0] - start[0];
57
+ const dy = end[1] - start[1];
58
+ const dz = end[2] - start[2];
59
+ const lengthSquared = dx * dx + dy * dy + dz * dz;
60
+ const t = lengthSquared <= 1e-12
61
+ ? 0
62
+ : Math.max(0, Math.min(1, ((point[0] - start[0]) * dx + (point[1] - start[1]) * dy + (point[2] - start[2]) * dz) /
63
+ lengthSquared));
64
+ const resolved = [start[0] + dx * t, start[1] + dy * t, start[2] + dz * t];
65
+ return {
66
+ point: resolved,
67
+ t,
68
+ distance: Math.hypot(point[0] - resolved[0], point[1] - resolved[1], point[2] - resolved[2]),
69
+ };
70
+ }
71
+ export function closestMeasurementFeatureBinding(features, point, maxDistance) {
72
+ let best = null;
73
+ for (const feature of features) {
74
+ const geometry = feature.geometry;
75
+ if (geometry.kind === 'point') {
76
+ const distance = measurementDistance(point, geometry.point);
77
+ const candidate = {
78
+ featureId: feature.id,
79
+ point: geometry.point,
80
+ parameters: { t: 0 },
81
+ distance,
82
+ priority: feature.priority ?? 0,
83
+ };
84
+ if (distance <= maxDistance &&
85
+ (!best ||
86
+ distance < best.distance ||
87
+ (distance === best.distance && candidate.priority > best.priority))) {
88
+ best = candidate;
89
+ }
90
+ continue;
91
+ }
92
+ const points = geometry.kind === 'segment' ? [geometry.start, geometry.end] : geometry.points;
93
+ const closed = geometry.kind === 'polygon' || (geometry.kind === 'path' && geometry.closed === true);
94
+ const count = closed ? points.length : points.length - 1;
95
+ const lengths = [];
96
+ let total = 0;
97
+ for (let index = 0; index < count; index++) {
98
+ const start = points[index];
99
+ const end = points[(index + 1) % points.length];
100
+ const length = measurementDistance(start, end);
101
+ lengths.push(length);
102
+ total += length;
103
+ }
104
+ let before = 0;
105
+ for (let index = 0; index < count; index++) {
106
+ const start = points[index];
107
+ const end = points[(index + 1) % points.length];
108
+ const segment = closestPointOnMeasurementSegment(point, start, end);
109
+ const t = total <= GEOMETRY_EPSILON ? 0 : (before + segment.t * lengths[index]) / total;
110
+ const candidate = {
111
+ featureId: feature.id,
112
+ point: segment.point,
113
+ parameters: { t },
114
+ distance: segment.distance,
115
+ priority: feature.priority ?? 0,
116
+ };
117
+ if (candidate.distance <= maxDistance &&
118
+ (!best ||
119
+ candidate.distance < best.distance ||
120
+ (candidate.distance === best.distance && candidate.priority > best.priority))) {
121
+ best = candidate;
122
+ }
123
+ before += lengths[index];
124
+ }
125
+ }
126
+ if (!best)
127
+ return null;
128
+ const { priority: _priority, ...binding } = best;
129
+ return binding;
130
+ }
131
+ export function remapMeasurementReferences(measurement, idMap) {
132
+ const remap = (anchor) => {
133
+ if (Array.isArray(anchor))
134
+ return anchor;
135
+ const nodeId = idMap.get(anchor.reference.nodeId);
136
+ return nodeId ? { ...anchor, reference: { ...anchor.reference, nodeId } } : anchor;
137
+ };
138
+ switch (measurement.kind) {
139
+ case 'distance':
140
+ return {
141
+ ...measurement,
142
+ points: [remap(measurement.points[0]), remap(measurement.points[1])],
143
+ };
144
+ case 'angle':
145
+ return {
146
+ ...measurement,
147
+ points: [
148
+ remap(measurement.points[0]),
149
+ remap(measurement.points[1]),
150
+ remap(measurement.points[2]),
151
+ ],
152
+ };
153
+ case 'area':
154
+ case 'perimeter':
155
+ return { ...measurement, base: measurement.base.map(remap) };
156
+ case 'volume':
157
+ return { ...measurement, base: measurement.base.map(remap) };
158
+ }
159
+ }
160
+ export function measurementReferenceNodeIds(measurement) {
161
+ const anchors = measurement.kind === 'distance' || measurement.kind === 'angle'
162
+ ? measurement.points
163
+ : measurement.base;
164
+ const ids = new Set();
165
+ for (const anchor of anchors) {
166
+ if (!Array.isArray(anchor))
167
+ ids.add(anchor.reference.nodeId);
168
+ }
169
+ return [...ids];
170
+ }
171
+ export function measurementAreaVector(points) {
172
+ if (points.length < 3)
173
+ return [0, 0, 0];
174
+ let x = 0;
175
+ let y = 0;
176
+ let z = 0;
177
+ for (let index = 0; index < points.length; index++) {
178
+ const current = points[index];
179
+ const next = points[(index + 1) % points.length];
180
+ x += (current[1] - next[1]) * (current[2] + next[2]);
181
+ y += (current[2] - next[2]) * (current[0] + next[0]);
182
+ z += (current[0] - next[0]) * (current[1] + next[1]);
183
+ }
184
+ return [x / 2, y / 2, z / 2];
185
+ }
186
+ export function measurementArea(points) {
187
+ return magnitude(measurementAreaVector(points));
188
+ }
189
+ export function measurementNormal(points) {
190
+ const areaVector = measurementAreaVector(points);
191
+ const length = magnitude(areaVector);
192
+ if (!Number.isFinite(length) || length <= GEOMETRY_EPSILON)
193
+ return null;
194
+ return [areaVector[0] / length, areaVector[1] / length, areaVector[2] / length];
195
+ }
196
+ export function areMeasurementPointsCoplanar(points, tolerance = 1e-6) {
197
+ if (points.length < 3 || !Number.isFinite(tolerance))
198
+ return false;
199
+ const normal = measurementNormal(points);
200
+ if (!normal)
201
+ return false;
202
+ const origin = points[0];
203
+ const absoluteTolerance = Math.abs(tolerance);
204
+ return points.every((point) => Math.abs(dot(subtract(point, origin), normal)) <= absoluteTolerance);
205
+ }
206
+ export function measurementCentroid(points) {
207
+ if (points.length < 3)
208
+ return null;
209
+ const normal = measurementNormal(points);
210
+ if (!normal)
211
+ return null;
212
+ const origin = points[0];
213
+ let totalWeight = 0;
214
+ let x = 0;
215
+ let y = 0;
216
+ let z = 0;
217
+ for (let index = 1; index < points.length - 1; index++) {
218
+ const current = points[index];
219
+ const next = points[index + 1];
220
+ const weight = dot(cross(subtract(current, origin), subtract(next, origin)), normal);
221
+ totalWeight += weight;
222
+ x += ((origin[0] + current[0] + next[0]) / 3) * weight;
223
+ y += ((origin[1] + current[1] + next[1]) / 3) * weight;
224
+ z += ((origin[2] + current[2] + next[2]) / 3) * weight;
225
+ }
226
+ if (!Number.isFinite(totalWeight) || Math.abs(totalWeight) <= GEOMETRY_EPSILON)
227
+ return null;
228
+ return [x / totalWeight, y / totalWeight, z / totalWeight];
229
+ }
230
+ export function measurementPrismVolume(base, extrusion) {
231
+ return Math.abs(dot(measurementAreaVector(base), extrusion));
232
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=measurement-geometry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measurement-geometry.test.d.ts","sourceRoot":"","sources":["../../src/lib/measurement-geometry.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,102 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { areMeasurementPointsCoplanar, closestMeasurementFeatureBinding, measurementAngle, measurementArea, measurementAreaVector, measurementCentroid, measurementDistance, measurementNormal, measurementPerimeter, measurementPrismVolume, } from './measurement-geometry';
3
+ const expectPointCloseTo = (actual, expected) => {
4
+ expect(actual).not.toBeNull();
5
+ for (let index = 0; index < 3; index++) {
6
+ expect(actual[index]).toBeCloseTo(expected[index]);
7
+ }
8
+ };
9
+ describe('measurement geometry', () => {
10
+ test('measures full 3D distance', () => {
11
+ expect(measurementDistance([1, 2, 3], [4, 6, 15])).toBe(13);
12
+ });
13
+ test('measures the smaller 3D angle and a closed perimeter', () => {
14
+ expect(measurementAngle([1, 0, 0], [0, 0, 0], [0, 1, 0])).toBeCloseTo(Math.PI / 2);
15
+ expect(measurementAngle([0, 0, 0], [0, 0, 0], [1, 0, 0])).toBe(0);
16
+ expect(measurementPerimeter([
17
+ [0, 0, 0],
18
+ [3, 0, 0],
19
+ [3, 0, 4],
20
+ ])).toBe(12);
21
+ });
22
+ test('matches the closest semantic feature with a normalized path position', () => {
23
+ const features = [
24
+ {
25
+ id: 'boundary',
26
+ label: 'Boundary',
27
+ snapKind: 'edge',
28
+ geometry: {
29
+ kind: 'polygon',
30
+ points: [
31
+ [0, 0, 0],
32
+ [2, 0, 0],
33
+ [2, 0, 2],
34
+ [0, 0, 2],
35
+ ],
36
+ },
37
+ },
38
+ ];
39
+ const match = closestMeasurementFeatureBinding(features, [1.9, 0, 1], 0.2);
40
+ expect(match?.featureId).toBe('boundary');
41
+ expect(match?.point).toEqual([2, 0, 1]);
42
+ expect(match?.parameters?.t).toBeCloseTo(0.375);
43
+ expect(match?.distance).toBeCloseTo(0.1);
44
+ });
45
+ test('computes a Newell area vector, area, and winding-aware normal', () => {
46
+ const base = [
47
+ [0, 0, 0],
48
+ [2, 0, 0],
49
+ [2, 1, 1],
50
+ [0, 1, 1],
51
+ ];
52
+ expectPointCloseTo(measurementAreaVector(base), [0, -2, 2]);
53
+ expect(measurementArea(base)).toBeCloseTo(2 * Math.sqrt(2));
54
+ expectPointCloseTo(measurementNormal(base), [0, -Math.SQRT1_2, Math.SQRT1_2]);
55
+ expectPointCloseTo(measurementNormal([...base].reverse()), [0, Math.SQRT1_2, -Math.SQRT1_2]);
56
+ });
57
+ test('checks coplanarity against the Newell normal', () => {
58
+ const base = [
59
+ [0, 0, 0],
60
+ [2, 0, 0],
61
+ [2, 1, 1],
62
+ [0, 1, 1],
63
+ ];
64
+ expect(areMeasurementPointsCoplanar(base)).toBe(true);
65
+ expect(areMeasurementPointsCoplanar([...base, [1, 0.5, 0.51]])).toBe(false);
66
+ expect(areMeasurementPointsCoplanar([
67
+ [0, 0, 0],
68
+ [1, 0, 0],
69
+ [2, 0, 0],
70
+ ])).toBe(false);
71
+ });
72
+ test('computes the area-weighted centroid of a planar 3D polygon', () => {
73
+ expectPointCloseTo(measurementCentroid([
74
+ [0, 0, 0],
75
+ [2, 0, 0],
76
+ [2, 1, 1],
77
+ [0, 1, 1],
78
+ ]), [1, 0.5, 0.5]);
79
+ });
80
+ test('computes area and centroid for a concave polygon', () => {
81
+ const base = [
82
+ [0, 0, 0],
83
+ [2, 0, 0],
84
+ [2, 0, 1],
85
+ [1, 0, 1],
86
+ [1, 0, 2],
87
+ [0, 0, 2],
88
+ ];
89
+ expect(measurementArea(base)).toBeCloseTo(3);
90
+ expectPointCloseTo(measurementCentroid(base), [5 / 6, 0, 5 / 6]);
91
+ });
92
+ test('computes prism volume from normal extrusion and ignores tangential extrusion', () => {
93
+ const base = [
94
+ [0, 0, 0],
95
+ [3, 0, 0],
96
+ [3, 2, 0],
97
+ [0, 2, 0],
98
+ ];
99
+ expect(measurementPrismVolume(base, [5, 7, 4])).toBeCloseTo(24);
100
+ expect(measurementPrismVolume([...base].reverse(), [5, 7, 4])).toBeCloseTo(24);
101
+ });
102
+ });
@@ -1,3 +1,2 @@
1
- export declare function insetPolygonFromCentroid(polygon: Array<[number, number]>, inset: number): Array<[number, number]>;
2
1
  export declare function simplifyClosedPolygon(polygon: Array<[number, number]>, tolerance: number): Array<[number, number]>;
3
2
  //# sourceMappingURL=polygon-geometry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"polygon-geometry.d.ts","sourceRoot":"","sources":["../../src/lib/polygon-geometry.ts"],"names":[],"mappings":"AAAA,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAChC,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoBzB;AA0ED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAChC,SAAS,EAAE,MAAM,GAChB,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiCzB"}
1
+ {"version":3,"file":"polygon-geometry.d.ts","sourceRoot":"","sources":["../../src/lib/polygon-geometry.ts"],"names":[],"mappings":"AAwEA,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAChC,SAAS,EAAE,MAAM,GAChB,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiCzB"}
@@ -1,21 +1,3 @@
1
- export function insetPolygonFromCentroid(polygon, inset) {
2
- if (inset <= 0) {
3
- return polygon.map(([x, z]) => [x, z]);
4
- }
5
- const centroid = polygon.reduce((acc, [x, z]) => ({ x: acc.x + x, z: acc.z + z }), { x: 0, z: 0 });
6
- centroid.x /= Math.max(polygon.length, 1);
7
- centroid.z /= Math.max(polygon.length, 1);
8
- return polygon.map(([x, z]) => {
9
- const dx = x - centroid.x;
10
- const dz = z - centroid.z;
11
- const length = Math.hypot(dx, dz);
12
- if (length <= inset + 1e-6) {
13
- return [x, z];
14
- }
15
- const scale = (length - inset) / length;
16
- return [centroid.x + dx * scale, centroid.z + dz * scale];
17
- });
18
- }
19
1
  function pointLineDistance(point, start, end) {
20
2
  const dx = end[0] - start[0];
21
3
  const dz = end[1] - start[1];
@@ -0,0 +1,4 @@
1
+ import type { AnyNode, AnyNodeId } from '../schema/types';
2
+ export declare function selectionProxyIdFromMetadata(metadata: unknown): AnyNodeId | null;
3
+ export declare function resolveSelectionProxyId(node: AnyNode, nodes: Readonly<Record<string, AnyNode | undefined>>): AnyNodeId;
4
+ //# sourceMappingURL=selection-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection-proxy.d.ts","sourceRoot":"","sources":["../../src/lib/selection-proxy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAEzD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAIhF;AAED,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC,CAAC,GACnD,SAAS,CAIX"}
@@ -0,0 +1,12 @@
1
+ export function selectionProxyIdFromMetadata(metadata) {
2
+ if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata))
3
+ return null;
4
+ const value = metadata.nodeSelectionProxyId;
5
+ return typeof value === 'string' ? value : null;
6
+ }
7
+ export function resolveSelectionProxyId(node, nodes) {
8
+ const proxyId = selectionProxyIdFromMetadata(node.metadata);
9
+ if (!proxyId || !nodes[proxyId])
10
+ return node.id;
11
+ return proxyId;
12
+ }
@@ -1,3 +1,32 @@
1
- import type { SlabNode } from '../schema';
2
- export declare function getRenderableSlabPolygon(slabNode: SlabNode): Array<[number, number]>;
1
+ import type { GeometryContext } from '../registry/types';
2
+ import type { SlabNode, WallNode } from '../schema';
3
+ export type SlabPolygonContext = {
4
+ /** Walls on the slab's level. */
5
+ walls: WallNode[];
6
+ /** Other slabs on the same level — the slab itself must be excluded. */
7
+ siblingSlabs: SlabNode[];
8
+ };
9
+ /**
10
+ * Derive a {@link SlabPolygonContext} from a registry `GeometryContext`:
11
+ * sibling slabs come from `ctx.siblings` (same kind, same parent, self
12
+ * excluded) and walls from the parent level's children.
13
+ */
14
+ export declare function slabPolygonContextFromGeometry(ctx: GeometryContext | undefined): SlabPolygonContext;
15
+ export declare function getRenderableSlabPolygon(slabNode: SlabNode, context: SlabPolygonContext): Array<[number, number]>;
16
+ export type SlabEdgeWallBandSnap = {
17
+ wallId: WallNode['id'];
18
+ /** The candidate edge translated perpendicular onto the wall centerline. */
19
+ edge: [[number, number], [number, number]];
20
+ };
21
+ /**
22
+ * Reshape-snap counterpart of the render band rule: when the edge
23
+ * `a → b` lies inside a wall's footprint band, return the edge
24
+ * translated onto that wall's CENTERLINE — the canonical stored
25
+ * position (matching what auto slabs store); the render rule then
26
+ * places it at the face. `maxLateral` optionally tightens the stick
27
+ * distance (non-magnetic modes keep only a connect-radius stick).
28
+ */
29
+ export declare function snapSlabEdgeToWallBand(a: [number, number], b: [number, number], walls: readonly WallNode[], options?: {
30
+ maxLateral?: number;
31
+ }): SlabEdgeWallBandSnap | null;
3
32
  //# sourceMappingURL=slab-polygon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"slab-polygon.d.ts","sourceRoot":"","sources":["../../src/lib/slab-polygon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAQzC,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAOpF"}
1
+ {"version":3,"file":"slab-polygon.d.ts","sourceRoot":"","sources":["../../src/lib/slab-polygon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,EAAa,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AA8E9D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,iCAAiC;IACjC,KAAK,EAAE,QAAQ,EAAE,CAAA;IACjB,wEAAwE;IACxE,YAAY,EAAE,QAAQ,EAAE,CAAA;CACzB,CAAA;AAuBD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,eAAe,GAAG,SAAS,GAC/B,kBAAkB,CAmBpB;AAED,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,kBAAkB,GAC1B,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAgBzB;AAuID,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IACtB,4EAA4E;IAC5E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAC3C,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EACnB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EACnB,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAChC,oBAAoB,GAAG,IAAI,CA4B7B"}