@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,73 @@
1
+ import dedent from 'dedent';
2
+ import { z } from 'zod';
3
+ import { BaseNode, nodeType, objectId } from '../base';
4
+ /**
5
+ * Round duct segment — a polyline of 3D points connected by cylindrical
6
+ * duct sections. Forced-air HVAC supply/return runs in US residential.
7
+ *
8
+ * Phase 1 of the HVAC node system: just the geometry primitive. Fittings,
9
+ * terminals, equipment, and typed ports come in later slices.
10
+ *
11
+ * Path coordinates are level-local meters: [x, y, z] tuples. y is height
12
+ * above the level floor. A duct hung at ceiling height through three points
13
+ * is e.g. `[[0, 2.6, 0], [3, 2.6, 0], [3, 2.6, 4]]`.
14
+ *
15
+ * Diameters are nominal US round-duct sizes in inches; the geometry
16
+ * builder converts to meters for the cylinder radius.
17
+ */
18
+ export const DuctSegmentNode = BaseNode.extend({
19
+ id: objectId('duct-segment'),
20
+ type: nodeType('duct-segment'),
21
+ // Polyline path in level-local meters. Minimum two points (start, end).
22
+ path: z.array(z.tuple([z.number(), z.number(), z.number()])).min(2),
23
+ // Cross-section. Round is the branch default; rect is the trunk /
24
+ // plenum profile (real US systems: rect trunk, round branches); oval
25
+ // is the flat-oval profile (two semicircles of the duct height joined
26
+ // by flat sides) used where round won't fit a joist bay.
27
+ shape: z.enum(['round', 'rect', 'oval']).default('round'),
28
+ // Nominal inner diameter in inches (round shape). Common residential
29
+ // sizes 4"–14"; we accept any positive number so the inspector slider
30
+ // stays ergonomic and larger commercial sizes load without a schema bump.
31
+ diameter: z.number().min(2).max(48).default(6),
32
+ // Rect / oval cross-section in inches: width is the horizontal face,
33
+ // height the vertical. Typical residential trunks 12×8 – 24×10. For
34
+ // oval, height is also the end-cap semicircle diameter (width ≥ height).
35
+ width: z.number().min(4).max(60).default(14),
36
+ height: z.number().min(3).max(40).default(8),
37
+ // Cross-section roll (radians) about the run direction. 0 = width
38
+ // horizontal / height vertical (the natural orientation the geometry
39
+ // derives from direction). Non-zero only on a rect riser turned out of
40
+ // the horizontal plane, so its profile stays continuous through the
41
+ // elbow it left instead of snapping to the world-axis fallback.
42
+ roll: z.number().default(0),
43
+ // Construction material. Spiral is round rigid sheet metal with the
44
+ // helical lock seam drawn on the body (round shape only — rect / oval
45
+ // runs render it as plain sheet metal).
46
+ ductMaterial: z.enum(['sheet-metal', 'spiral', 'flex', 'duct-board']).default('flex'),
47
+ // Whether to draw the construction body detail (spiral lock seam /
48
+ // flex wire corrugation) on round runs. Off renders a smooth body —
49
+ // lighter on the eyes and the GPU in dense scenes.
50
+ seamDetail: z.boolean().default(false),
51
+ // Whether the run wears its external insulation wrap (drawn as a
52
+ // translucent shell). Off by default — bare duct.
53
+ insulated: z.boolean().default(false),
54
+ // External insulation R-value (used when insulated). Common flex-duct
55
+ // values are R-4.2, R-6, R-8.
56
+ insulationR: z.number().min(0).max(12).default(0.5),
57
+ // Which side of the air loop this segment belongs to. Drives visual tint
58
+ // and (in later slices) System graph membership.
59
+ system: z.enum(['supply', 'return']).default('supply'),
60
+ slots: z.record(z.string(), z.string()).optional(),
61
+ }).describe(dedent `
62
+ Duct segment - polyline of 3D points connected by duct sections.
63
+ - path: list of [x, y, z] points in level-local meters (min 2)
64
+ - shape: round (branches) | rect (trunks / plenums) | oval (flat-oval, tight joist bays)
65
+ - diameter: nominal inner diameter in inches for round (typ. 4-14 residential)
66
+ - width / height: rect / oval cross-section in inches (typ. 12x8 - 24x10 trunks)
67
+ - roll: cross-section roll in radians (0 = upright; set on risers to stay continuous through their elbow)
68
+ - ductMaterial: sheet-metal | spiral (round rigid, helical seam) | flex | duct-board
69
+ - seamDetail: draw the spiral seam / flex corrugation on round runs (default off)
70
+ - insulated: whether the run wears its external insulation wrap (default off)
71
+ - insulationR: external insulation R-value when insulated (4, 6, 8 typical)
72
+ - system: supply | return (drives visual tint)
73
+ `);
@@ -0,0 +1,57 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Duct terminal — where the air loop meets the room: supply registers,
4
+ * ceiling diffusers, return grilles.
5
+ *
6
+ * Phase 3 of the HVAC node system. Each terminal exposes a single typed
7
+ * port at its collar (behind/above/below the face depending on mount),
8
+ * so duct runs end onto it like any other port.
9
+ *
10
+ * `position` is the center of the visible face in level-local meters —
11
+ * floor registers at y≈0, ceiling diffusers at ceiling height, wall
12
+ * registers at their height on the wall. `rotation` is yaw radians.
13
+ */
14
+ export declare const DuctTerminalNode: z.ZodObject<{
15
+ object: z.ZodDefault<z.ZodLiteral<"node">>;
16
+ name: z.ZodOptional<z.ZodString>;
17
+ parentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
18
+ visible: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
19
+ camera: z.ZodOptional<z.ZodObject<{
20
+ position: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
21
+ target: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
22
+ mode: z.ZodDefault<z.ZodEnum<{
23
+ perspective: "perspective";
24
+ orthographic: "orthographic";
25
+ }>>;
26
+ fov: z.ZodOptional<z.ZodNumber>;
27
+ zoom: z.ZodOptional<z.ZodNumber>;
28
+ }, z.core.$strip>>;
29
+ metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
30
+ id: z.ZodDefault<z.ZodTemplateLiteral<`duct-terminal_${string}`>>;
31
+ type: z.ZodDefault<z.ZodLiteral<"duct-terminal">>;
32
+ position: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
33
+ rotation: z.ZodDefault<z.ZodNumber>;
34
+ terminalType: z.ZodDefault<z.ZodEnum<{
35
+ "supply-register": "supply-register";
36
+ diffuser: "diffuser";
37
+ "return-grille": "return-grille";
38
+ }>>;
39
+ mount: z.ZodDefault<z.ZodEnum<{
40
+ wall: "wall";
41
+ ceiling: "ceiling";
42
+ floor: "floor";
43
+ }>>;
44
+ width: z.ZodDefault<z.ZodNumber>;
45
+ depth: z.ZodDefault<z.ZodNumber>;
46
+ collarShape: z.ZodDefault<z.ZodEnum<{
47
+ round: "round";
48
+ rect: "rect";
49
+ oval: "oval";
50
+ }>>;
51
+ collarDiameter: z.ZodDefault<z.ZodNumber>;
52
+ collarWidth: z.ZodDefault<z.ZodNumber>;
53
+ collarHeight: z.ZodDefault<z.ZodNumber>;
54
+ }, z.core.$strip>;
55
+ export type DuctTerminalNode = z.infer<typeof DuctTerminalNode>;
56
+ export type DuctTerminalNodeId = DuctTerminalNode['id'];
57
+ //# sourceMappingURL=duct-terminal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duct-terminal.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/duct-terminal.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqC5B,CAAA;AACD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAC/D,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA"}
@@ -0,0 +1,51 @@
1
+ import dedent from 'dedent';
2
+ import { z } from 'zod';
3
+ import { BaseNode, nodeType, objectId } from '../base';
4
+ /**
5
+ * Duct terminal — where the air loop meets the room: supply registers,
6
+ * ceiling diffusers, return grilles.
7
+ *
8
+ * Phase 3 of the HVAC node system. Each terminal exposes a single typed
9
+ * port at its collar (behind/above/below the face depending on mount),
10
+ * so duct runs end onto it like any other port.
11
+ *
12
+ * `position` is the center of the visible face in level-local meters —
13
+ * floor registers at y≈0, ceiling diffusers at ceiling height, wall
14
+ * registers at their height on the wall. `rotation` is yaw radians.
15
+ */
16
+ export const DuctTerminalNode = BaseNode.extend({
17
+ id: objectId('duct-terminal'),
18
+ type: nodeType('duct-terminal'),
19
+ // Level-local meters — center of the face.
20
+ position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
21
+ // Yaw in radians.
22
+ rotation: z.number().default(0),
23
+ terminalType: z.enum(['supply-register', 'diffuser', 'return-grille']).default('supply-register'),
24
+ // Which surface the terminal mounts on. Drives face orientation and
25
+ // which way the collar (and its port) points.
26
+ mount: z.enum(['floor', 'ceiling', 'wall']).default('floor'),
27
+ // Face dimensions in meters. Typical floor register ~0.30 × 0.15;
28
+ // ceiling diffusers are square (0.6 × 0.6); return grilles run large.
29
+ width: z.number().min(0.1).max(1.5).default(0.3),
30
+ depth: z.number().min(0.05).max(1.5).default(0.15),
31
+ // Collar cross-section on the duct side. Round is the default; rect and
32
+ // oval (flat-oval) match the duct shapes a run might end with.
33
+ collarShape: z.enum(['round', 'rect', 'oval']).default('round'),
34
+ // Round collar diameter in inches on the duct side.
35
+ collarDiameter: z.number().min(4).max(20).default(6),
36
+ // Rect / oval collar cross-section in inches: width is the horizontal
37
+ // face, height the vertical. For oval, height is also the end-cap
38
+ // semicircle diameter (width ≥ height).
39
+ collarWidth: z.number().min(4).max(20).default(10),
40
+ collarHeight: z.number().min(3).max(20).default(6),
41
+ }).describe(dedent `
42
+ Duct terminal - supply register, ceiling diffuser, or return grille.
43
+ - position: [x, y, z] level-local meters, center of the face
44
+ - rotation: yaw radians
45
+ - terminalType: supply-register | diffuser | return-grille (grille = return side)
46
+ - mount: floor | ceiling | wall - face orientation + collar direction
47
+ - width / depth: face size in meters
48
+ - collarShape: round | rect | oval - duct-side collar cross-section
49
+ - collarDiameter: round collar diameter in inches
50
+ - collarWidth / collarHeight: rect / oval collar cross-section in inches
51
+ `);
@@ -36,7 +36,7 @@ export declare const ElevatorNode: z.ZodObject<{
36
36
  type: z.ZodDefault<z.ZodLiteral<"elevator">>;
37
37
  material: z.ZodOptional<z.ZodObject<{
38
38
  id: z.ZodOptional<z.ZodString>;
39
- preset: z.ZodOptional<z.ZodEnum<{
39
+ preset: z.ZodOptional<z.ZodCatch<z.ZodEnum<{
40
40
  custom: "custom";
41
41
  white: "white";
42
42
  brick: "brick";
@@ -47,7 +47,7 @@ export declare const ElevatorNode: z.ZodObject<{
47
47
  plaster: "plaster";
48
48
  tile: "tile";
49
49
  marble: "marble";
50
- }>>;
50
+ }>>>;
51
51
  properties: z.ZodOptional<z.ZodObject<{
52
52
  color: z.ZodDefault<z.ZodString>;
53
53
  roughness: z.ZodDefault<z.ZodNumber>;
@@ -67,6 +67,7 @@ export declare const ElevatorNode: z.ZodObject<{
67
67
  }, z.core.$strip>>;
68
68
  }, z.core.$strip>>;
69
69
  materialPreset: z.ZodOptional<z.ZodString>;
70
+ slots: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
70
71
  position: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
71
72
  rotation: z.ZodDefault<z.ZodNumber>;
72
73
  width: z.ZodDefault<z.ZodNumber>;
@@ -1 +1 @@
1
- {"version":3,"file":"elevator.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/elevator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,iBAAiB;;;;EAA4D,CAAA;AAC1F,eAAO,MAAM,sBAAsB;;;;EAA4D,CAAA;AAC/F,eAAO,MAAM,kBAAkB;;;EAA6B,CAAA;AAE5D,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAC3E,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AACjE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAEnE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgDxB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA"}
1
+ {"version":3,"file":"elevator.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/elevator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,iBAAiB;;;;EAA4D,CAAA;AAC1F,eAAO,MAAM,sBAAsB;;;;EAA4D,CAAA;AAC/F,eAAO,MAAM,kBAAkB;;;EAA6B,CAAA;AAE5D,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAC3E,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AACjE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AAEnE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmDxB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA"}
@@ -10,6 +10,9 @@ export const ElevatorNode = BaseNode.extend({
10
10
  type: nodeType('elevator'),
11
11
  material: MaterialSchema.optional(),
12
12
  materialPreset: z.string().optional(),
13
+ // Unified paint-slot refs (`scene:`/`library:` MaterialRef per slot id),
14
+ // matching the slot model items/slab/shelf use. Absent = declared default.
15
+ slots: z.record(z.string(), z.string()).optional(),
13
16
  position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
14
17
  // Rotation around the Y axis in radians.
15
18
  rotation: z.number().default(0),
@@ -19,7 +19,7 @@ export declare const EyebrowVentNode: z.ZodObject<{
19
19
  type: z.ZodDefault<z.ZodLiteral<"eyebrow-vent">>;
20
20
  material: z.ZodOptional<z.ZodObject<{
21
21
  id: z.ZodOptional<z.ZodString>;
22
- preset: z.ZodOptional<z.ZodEnum<{
22
+ preset: z.ZodOptional<z.ZodCatch<z.ZodEnum<{
23
23
  custom: "custom";
24
24
  white: "white";
25
25
  brick: "brick";
@@ -30,7 +30,7 @@ export declare const EyebrowVentNode: z.ZodObject<{
30
30
  plaster: "plaster";
31
31
  tile: "tile";
32
32
  marble: "marble";
33
- }>>;
33
+ }>>>;
34
34
  properties: z.ZodOptional<z.ZodObject<{
35
35
  color: z.ZodDefault<z.ZodString>;
36
36
  roughness: z.ZodDefault<z.ZodNumber>;
@@ -3,11 +3,17 @@ export declare const FenceStyle: z.ZodEnum<{
3
3
  slat: "slat";
4
4
  rail: "rail";
5
5
  privacy: "privacy";
6
+ horizontal: "horizontal";
6
7
  }>;
7
8
  export declare const FenceBaseStyle: z.ZodEnum<{
8
9
  floating: "floating";
9
10
  grounded: "grounded";
10
11
  }>;
12
+ export declare const FencePostCap: z.ZodEnum<{
13
+ flat: "flat";
14
+ none: "none";
15
+ pyramid: "pyramid";
16
+ }>;
11
17
  export declare const FenceNode: z.ZodObject<{
12
18
  object: z.ZodDefault<z.ZodLiteral<"node">>;
13
19
  name: z.ZodOptional<z.ZodString>;
@@ -28,7 +34,7 @@ export declare const FenceNode: z.ZodObject<{
28
34
  type: z.ZodDefault<z.ZodLiteral<"fence">>;
29
35
  material: z.ZodOptional<z.ZodObject<{
30
36
  id: z.ZodOptional<z.ZodString>;
31
- preset: z.ZodOptional<z.ZodEnum<{
37
+ preset: z.ZodOptional<z.ZodCatch<z.ZodEnum<{
32
38
  custom: "custom";
33
39
  white: "white";
34
40
  brick: "brick";
@@ -39,7 +45,7 @@ export declare const FenceNode: z.ZodObject<{
39
45
  plaster: "plaster";
40
46
  tile: "tile";
41
47
  marble: "marble";
42
- }>>;
48
+ }>>>;
43
49
  properties: z.ZodOptional<z.ZodObject<{
44
50
  color: z.ZodDefault<z.ZodString>;
45
51
  roughness: z.ZodDefault<z.ZodNumber>;
@@ -59,8 +65,11 @@ export declare const FenceNode: z.ZodObject<{
59
65
  }, z.core.$strip>>;
60
66
  }, z.core.$strip>>;
61
67
  materialPreset: z.ZodOptional<z.ZodString>;
68
+ slots: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
62
69
  start: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
63
70
  end: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
71
+ path: z.ZodOptional<z.ZodArray<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>>;
72
+ tangents: z.ZodOptional<z.ZodArray<z.ZodNullable<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>>>;
64
73
  height: z.ZodDefault<z.ZodNumber>;
65
74
  thickness: z.ZodDefault<z.ZodNumber>;
66
75
  curveOffset: z.ZodOptional<z.ZodNumber>;
@@ -70,6 +79,12 @@ export declare const FenceNode: z.ZodObject<{
70
79
  topRailHeight: z.ZodDefault<z.ZodNumber>;
71
80
  groundClearance: z.ZodDefault<z.ZodNumber>;
72
81
  edgeInset: z.ZodDefault<z.ZodNumber>;
82
+ slatGap: z.ZodDefault<z.ZodNumber>;
83
+ postCap: z.ZodDefault<z.ZodEnum<{
84
+ flat: "flat";
85
+ none: "none";
86
+ pyramid: "pyramid";
87
+ }>>;
73
88
  baseStyle: z.ZodDefault<z.ZodEnum<{
74
89
  floating: "floating";
75
90
  grounded: "grounded";
@@ -80,6 +95,7 @@ export declare const FenceNode: z.ZodObject<{
80
95
  slat: "slat";
81
96
  rail: "rail";
82
97
  privacy: "privacy";
98
+ horizontal: "horizontal";
83
99
  }>>;
84
100
  }, z.core.$strip>;
85
101
  export type FenceNode = z.infer<typeof FenceNode>;
@@ -1 +1 @@
1
- {"version":3,"file":"fence.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/fence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,UAAU;;;;EAAsC,CAAA;AAC7D,eAAO,MAAM,cAAc;;;EAAmC,CAAA;AAE9D,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+BrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA"}
1
+ {"version":3,"file":"fence.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/fence.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAIvB,eAAO,MAAM,UAAU;;;;;EAAoD,CAAA;AAC3E,eAAO,MAAM,cAAc;;;EAAmC,CAAA;AAC9D,eAAO,MAAM,YAAY;;;;EAAsC,CAAA;AAE/D,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAqDrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA"}
@@ -2,15 +2,32 @@ import dedent from 'dedent';
2
2
  import { z } from 'zod';
3
3
  import { BaseNode, nodeType, objectId } from '../base';
4
4
  import { MaterialSchema } from '../material';
5
- export const FenceStyle = z.enum(['slat', 'rail', 'privacy']);
5
+ export const FenceStyle = z.enum(['slat', 'rail', 'privacy', 'horizontal']);
6
6
  export const FenceBaseStyle = z.enum(['floating', 'grounded']);
7
+ export const FencePostCap = z.enum(['none', 'flat', 'pyramid']);
7
8
  export const FenceNode = BaseNode.extend({
8
9
  id: objectId('fence'),
9
10
  type: nodeType('fence'),
10
11
  material: MaterialSchema.optional(),
11
12
  materialPreset: z.string().optional(),
13
+ // Unified paint-slot refs (`scene:`/`library:` MaterialRef per slot id),
14
+ // matching the slot model items/slab/shelf use. Absent = declared default.
15
+ slots: z.record(z.string(), z.string()).optional(),
12
16
  start: z.tuple([z.number(), z.number()]),
13
17
  end: z.tuple([z.number(), z.number()]),
18
+ // Optional spline control points in level coordinate meters. When present
19
+ // (>= 2 points) the fence centerline is a smooth Catmull-Rom curve through
20
+ // these points and start/end/curveOffset no longer define the centerline.
21
+ // start/end are kept in sync with the first/last path point so consumers
22
+ // that read endpoints (handles, bbox, miter references) stay valid. Absent =
23
+ // the straight or single-arc fence defined by start/end (+ curveOffset).
24
+ path: z.array(z.tuple([z.number(), z.number()])).optional(),
25
+ // Optional per-control-point tangent handles, parallel to `path` (same
26
+ // length when present). Each entry is the OUT-handle offset vector [dx, dy]
27
+ // from its path point, in level meters; the IN handle is its mirror so the
28
+ // curve stays smooth through the point. `null` = use the automatic
29
+ // Catmull-Rom tangent for that point. Only meaningful for spline fences.
30
+ tangents: z.array(z.tuple([z.number(), z.number()]).nullable()).optional(),
14
31
  height: z.number().default(1.8),
15
32
  thickness: z.number().default(0.08),
16
33
  curveOffset: z.number().optional(),
@@ -20,6 +37,10 @@ export const FenceNode = BaseNode.extend({
20
37
  topRailHeight: z.number().default(0.04),
21
38
  groundClearance: z.number().default(0),
22
39
  edgeInset: z.number().default(0.015),
40
+ // Reveal between the boards of a `horizontal` fence (0 = flush cladding).
41
+ slatGap: z.number().default(0.01),
42
+ // Topper drawn on each `horizontal`-fence post.
43
+ postCap: FencePostCap.default('pyramid'),
23
44
  baseStyle: FenceBaseStyle.default('grounded'),
24
45
  showInfill: z.boolean().default(true),
25
46
  color: z.string().default('#ffffff'),
@@ -27,8 +48,10 @@ export const FenceNode = BaseNode.extend({
27
48
  }).describe(dedent `
28
49
  Fence node - used to represent a fence segment in the building/site level coordinate system
29
50
  - start/end: fence endpoints in level coordinate system
51
+ - path: optional list of [x, y] points; when set (>= 2) the centerline is a smooth spline through them
52
+ - tangents: optional per-point handle vectors (parallel to path); null entries fall back to the automatic tangent
30
53
  - height/thickness: overall fence dimensions in meters
31
- - curveOffset: midpoint sagitta offset used to bend the fence into an arc
54
+ - curveOffset: midpoint sagitta offset used to bend the fence into an arc (ignored when path is set)
32
55
  - baseHeight/postSpacing/postSize/topRailHeight: exact geometric controls from the plan3D fence model
33
56
  - groundClearance/edgeInset/baseStyle: fence support and inset configuration
34
57
  - showInfill: whether to draw intermediate posts/slats between end posts
@@ -25,7 +25,7 @@ export declare const GutterNode: z.ZodObject<{
25
25
  type: z.ZodDefault<z.ZodLiteral<"gutter">>;
26
26
  material: z.ZodOptional<z.ZodObject<{
27
27
  id: z.ZodOptional<z.ZodString>;
28
- preset: z.ZodOptional<z.ZodEnum<{
28
+ preset: z.ZodOptional<z.ZodCatch<z.ZodEnum<{
29
29
  custom: "custom";
30
30
  white: "white";
31
31
  brick: "brick";
@@ -36,7 +36,7 @@ export declare const GutterNode: z.ZodObject<{
36
36
  plaster: "plaster";
37
37
  tile: "tile";
38
38
  marble: "marble";
39
- }>>;
39
+ }>>>;
40
40
  properties: z.ZodOptional<z.ZodObject<{
41
41
  color: z.ZodDefault<z.ZodString>;
42
42
  roughness: z.ZodDefault<z.ZodNumber>;
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * HVAC equipment — the boxes duct systems start and end at: furnace,
4
+ * air handler, outdoor condenser.
5
+ *
6
+ * Phase 3 of the HVAC node system. Furnaces and air handlers expose
7
+ * typed duct ports (supply plenum on top, return drop on the side) so
8
+ * duct runs and fittings snap onto them. Every unit also exposes a
9
+ * refrigerant service port on its valve face — a condenser, the outdoor
10
+ * half of a split system, carries no duct ports but pipes to the indoor
11
+ * coil through a `lineset` run mating onto that port.
12
+ *
13
+ * Floor-placed: `position` is level-local meters with y at the base,
14
+ * `rotation` is yaw radians (the editor's default R-rotate applies).
15
+ */
16
+ export declare const HvacEquipmentNode: z.ZodObject<{
17
+ object: z.ZodDefault<z.ZodLiteral<"node">>;
18
+ name: z.ZodOptional<z.ZodString>;
19
+ parentId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
20
+ visible: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
21
+ camera: z.ZodOptional<z.ZodObject<{
22
+ position: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
23
+ target: z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>;
24
+ mode: z.ZodDefault<z.ZodEnum<{
25
+ perspective: "perspective";
26
+ orthographic: "orthographic";
27
+ }>>;
28
+ fov: z.ZodOptional<z.ZodNumber>;
29
+ zoom: z.ZodOptional<z.ZodNumber>;
30
+ }, z.core.$strip>>;
31
+ metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
32
+ id: z.ZodDefault<z.ZodTemplateLiteral<`hvac-equipment_${string}`>>;
33
+ type: z.ZodDefault<z.ZodLiteral<"hvac-equipment">>;
34
+ position: z.ZodDefault<z.ZodTuple<[z.ZodNumber, z.ZodNumber, z.ZodNumber], null>>;
35
+ rotation: z.ZodDefault<z.ZodNumber>;
36
+ equipmentType: z.ZodDefault<z.ZodEnum<{
37
+ furnace: "furnace";
38
+ "air-handler": "air-handler";
39
+ condenser: "condenser";
40
+ }>>;
41
+ width: z.ZodDefault<z.ZodNumber>;
42
+ depth: z.ZodDefault<z.ZodNumber>;
43
+ height: z.ZodDefault<z.ZodNumber>;
44
+ supplyShape: z.ZodDefault<z.ZodEnum<{
45
+ round: "round";
46
+ rect: "rect";
47
+ oval: "oval";
48
+ }>>;
49
+ returnShape: z.ZodDefault<z.ZodEnum<{
50
+ round: "round";
51
+ rect: "rect";
52
+ oval: "oval";
53
+ }>>;
54
+ supplyDiameter: z.ZodDefault<z.ZodNumber>;
55
+ returnDiameter: z.ZodDefault<z.ZodNumber>;
56
+ supplyWidth: z.ZodDefault<z.ZodNumber>;
57
+ supplyHeight: z.ZodDefault<z.ZodNumber>;
58
+ returnWidth: z.ZodDefault<z.ZodNumber>;
59
+ returnHeight: z.ZodDefault<z.ZodNumber>;
60
+ }, z.core.$strip>;
61
+ export type HvacEquipmentNode = z.infer<typeof HvacEquipmentNode>;
62
+ export type HvacEquipmentNodeId = HvacEquipmentNode['id'];
63
+ //# sourceMappingURL=hvac-equipment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hvac-equipment.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/hvac-equipment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuC7B,CAAA;AACD,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AACjE,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAA"}
@@ -0,0 +1,55 @@
1
+ import dedent from 'dedent';
2
+ import { z } from 'zod';
3
+ import { BaseNode, nodeType, objectId } from '../base';
4
+ /**
5
+ * HVAC equipment — the boxes duct systems start and end at: furnace,
6
+ * air handler, outdoor condenser.
7
+ *
8
+ * Phase 3 of the HVAC node system. Furnaces and air handlers expose
9
+ * typed duct ports (supply plenum on top, return drop on the side) so
10
+ * duct runs and fittings snap onto them. Every unit also exposes a
11
+ * refrigerant service port on its valve face — a condenser, the outdoor
12
+ * half of a split system, carries no duct ports but pipes to the indoor
13
+ * coil through a `lineset` run mating onto that port.
14
+ *
15
+ * Floor-placed: `position` is level-local meters with y at the base,
16
+ * `rotation` is yaw radians (the editor's default R-rotate applies).
17
+ */
18
+ export const HvacEquipmentNode = BaseNode.extend({
19
+ id: objectId('hvac-equipment'),
20
+ type: nodeType('hvac-equipment'),
21
+ // Level-local meters, y at the unit's base.
22
+ position: z.tuple([z.number(), z.number(), z.number()]).default([0, 0, 0]),
23
+ // Yaw in radians.
24
+ rotation: z.number().default(0),
25
+ equipmentType: z.enum(['furnace', 'air-handler', 'condenser']).default('furnace'),
26
+ // Cabinet dimensions in meters. Defaults match a typical upflow
27
+ // furnace cabinet (~22" × 28" footprint, ~43" tall).
28
+ width: z.number().min(0.3).max(2).default(0.56),
29
+ depth: z.number().min(0.3).max(2).default(0.71),
30
+ height: z.number().min(0.4).max(2.5).default(1.1),
31
+ // Duct collar cross-section on the supply / return connections. Round is
32
+ // the default; rect and oval (flat-oval) match the duct shapes a run
33
+ // might mate with. Condensers carry no duct collars (ignored).
34
+ supplyShape: z.enum(['round', 'rect', 'oval']).default('round'),
35
+ returnShape: z.enum(['round', 'rect', 'oval']).default('round'),
36
+ // Round collar diameters in inches.
37
+ supplyDiameter: z.number().min(6).max(30).default(8),
38
+ returnDiameter: z.number().min(6).max(30).default(8),
39
+ // Rect / oval collar cross-section in inches: width is the horizontal
40
+ // face, height the vertical. For oval, height is also the end-cap
41
+ // semicircle diameter (width ≥ height).
42
+ supplyWidth: z.number().min(6).max(30).default(12),
43
+ supplyHeight: z.number().min(6).max(30).default(8),
44
+ returnWidth: z.number().min(6).max(30).default(14),
45
+ returnHeight: z.number().min(6).max(30).default(8),
46
+ }).describe(dedent `
47
+ HVAC equipment cabinet - furnace, air handler, or outdoor condenser.
48
+ - position: [x, y, z] level-local meters (y = base)
49
+ - rotation: yaw radians
50
+ - equipmentType: furnace | air-handler | condenser
51
+ - width / depth / height: cabinet size in meters
52
+ - supplyShape / returnShape: round | rect | oval duct collar cross-section (ignored by condenser)
53
+ - supplyDiameter / returnDiameter: round collar sizes in inches
54
+ - supplyWidth / supplyHeight / returnWidth / returnHeight: rect / oval collar cross-section in inches
55
+ `);
@@ -241,7 +241,15 @@ export declare const ItemNode: z.ZodObject<{
241
241
  children: z.ZodDefault<z.ZodArray<z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>>>;
242
242
  wallId: z.ZodOptional<z.ZodString>;
243
243
  wallT: z.ZodOptional<z.ZodNumber>;
244
+ roofSegmentId: z.ZodOptional<z.ZodString>;
245
+ roofFace: z.ZodOptional<z.ZodEnum<{
246
+ front: "front";
247
+ back: "back";
248
+ right: "right";
249
+ left: "left";
250
+ }>>;
244
251
  collectionIds: z.ZodOptional<z.ZodArray<z.ZodCustom<`collection_${string}`, `collection_${string}`>>>;
252
+ slots: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
245
253
  asset: z.ZodObject<{
246
254
  id: z.ZodString;
247
255
  category: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/item.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAOvB,QAAA,MAAM,mBAAmB;;;;iBAIvB,CAAA;AAEF,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;iBASvB,CAAA;AAEF,QAAA,MAAM,wBAAwB;;;;;;;;;;iBAO5B,CAAA;AAEF,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAIjB,CAAA;AAIF,QAAA,MAAM,qBAAqB;;;;;;;iBAOzB,CAAA;AAEF,QAAA,MAAM,iBAAiB;;;;;;iBAMrB,CAAA;AAEF,QAAA,MAAM,YAAY;;;;;;;;;;;;;2BAA2E,CAAA;AAI7F,QAAA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGrB,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AACzE,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAA;AACnD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AACnE,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAC3D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AACjD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAE3D,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2Cf,CAAA;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA;AACpD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA;AAE/C,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BnB,CAAA;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAA;AAE/C,eAAO,MAAM,mCAAmC,MAAM,CAAA;AAEtD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAM/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAI5E"}
1
+ {"version":3,"file":"item.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/item.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAOvB,QAAA,MAAM,mBAAmB;;;;iBAIvB,CAAA;AAEF,QAAA,MAAM,mBAAmB;;;;;;;;;;;;;iBASvB,CAAA;AAEF,QAAA,MAAM,wBAAwB;;;;;;;;;;iBAO5B,CAAA;AAEF,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAIjB,CAAA;AAIF,QAAA,MAAM,qBAAqB;;;;;;;iBAOzB,CAAA;AAEF,QAAA,MAAM,iBAAiB;;;;;;iBAMrB,CAAA;AAEF,QAAA,MAAM,YAAY;;;;;;;;;;;;;2BAA2E,CAAA;AAI7F,QAAA,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGrB,CAAA;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAC/D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AACzE,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAA;AACnD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AACnE,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAC3D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AACjD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAE3D,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2Cf,CAAA;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA;AACpD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAA;AAE/C,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAyCnB,CAAA;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAA;AAE/C,eAAO,MAAM,mCAAmC,MAAM,CAAA;AAEtD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAM/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAI5E"}
@@ -108,8 +108,19 @@ export const ItemNode = BaseNode.extend({
108
108
  // Wall attachment properties (only used when asset.attachTo is "wall" or "wall-side")
109
109
  wallId: z.string().optional(),
110
110
  wallT: z.number().optional(), // 0-1 parametric position along wall
111
+ // Alternative wall host: a roof-segment's generated wall face. When
112
+ // set, `position` is FACE-LOCAL — [u along the face, v = bottom edge,
113
+ // z from the wall mid-plane] — exactly the wall-child convention
114
+ // (ItemSystem's wall-side push applies the same way); the renderer
115
+ // mounts the node inside the face frame (`getRoofWallFaceFrame`).
116
+ roofSegmentId: z.string().optional(),
117
+ roofFace: z.enum(['front', 'back', 'right', 'left']).optional(),
111
118
  // Denormalized references to collections this node belongs to
112
119
  collectionIds: z.array(z.custom()).optional(),
120
+ // Per-slot material overrides. Key = slot id (see deriveSlotId), value = a
121
+ // MaterialRef string ('library:<id>' or 'scene:<id>'). Absent = authored /
122
+ // registry default. A dangling ref renders the default (never blocks).
123
+ slots: z.record(z.string(), z.string()).optional(),
113
124
  asset: assetSchema,
114
125
  }).describe(dedent `Item node - used to represent a item in the building
115
126
  - position: position in level coordinate system (or parent coordinate system if attached)
@@ -17,7 +17,7 @@ export declare const LevelNode: z.ZodObject<{
17
17
  metadata: z.ZodDefault<z.ZodOptional<z.ZodJSONSchema>>;
18
18
  id: z.ZodDefault<z.ZodTemplateLiteral<`level_${string}`>>;
19
19
  type: z.ZodDefault<z.ZodLiteral<"level">>;
20
- children: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodDefault<z.ZodTemplateLiteral<`wall_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`fence_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`column_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`zone_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`ceiling_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`roof_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`stair_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`scan_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`guide_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`spawn_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`shelf_${string}`>>]>>>;
20
+ children: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodDefault<z.ZodTemplateLiteral<`wall_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`fence_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`column_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`item_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`zone_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`slab_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`ceiling_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`roof_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`stair_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`scan_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`guide_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`measurement_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`spawn_${string}`>>, z.ZodDefault<z.ZodTemplateLiteral<`shelf_${string}`>>]>>>;
21
21
  level: z.ZodDefault<z.ZodNumber>;
22
22
  }, z.core.$strip>;
23
23
  export type LevelNode = z.infer<typeof LevelNode>;
@@ -1 +1 @@
1
- {"version":3,"file":"level.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/level.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAgBvB,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;iBA8BrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA"}
1
+ {"version":3,"file":"level.d.ts","sourceRoot":"","sources":["../../../src/schema/nodes/level.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAiBvB,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;iBA+BrB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAA"}
@@ -6,6 +6,7 @@ import { ColumnNode } from './column';
6
6
  import { FenceNode } from './fence';
7
7
  import { GuideNode } from './guide';
8
8
  import { ItemNode } from './item';
9
+ import { MeasurementNode } from './measurement';
9
10
  import { RoofNode } from './roof';
10
11
  import { ScanNode } from './scan';
11
12
  import { ShelfNode } from './shelf';
@@ -30,6 +31,7 @@ export const LevelNode = BaseNode.extend({
30
31
  StairNode.shape.id,
31
32
  ScanNode.shape.id,
32
33
  GuideNode.shape.id,
34
+ MeasurementNode.shape.id,
33
35
  SpawnNode.shape.id,
34
36
  ShelfNode.shape.id,
35
37
  ]))