@pascal-app/nodes 1.0.1 → 1.0.3

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 (319) hide show
  1. package/dist/block/definition.d.ts.map +1 -1
  2. package/dist/block/definition.js +4 -1
  3. package/dist/block/face-host.d.ts.map +1 -1
  4. package/dist/block/face-host.js +19 -2
  5. package/dist/block/hosted-edit.d.ts +13 -0
  6. package/dist/block/hosted-edit.d.ts.map +1 -0
  7. package/dist/block/hosted-edit.js +205 -0
  8. package/dist/block/last-operation.d.ts +1 -1
  9. package/dist/block/last-operation.d.ts.map +1 -1
  10. package/dist/block/last-operation.js +6 -4
  11. package/dist/block/modal-session.d.ts +2 -1
  12. package/dist/block/modal-session.d.ts.map +1 -1
  13. package/dist/block/modal-session.js +2 -2
  14. package/dist/block/selection.d.ts.map +1 -1
  15. package/dist/block/selection.js +82 -24
  16. package/dist/block/surface.d.ts +4 -0
  17. package/dist/block/surface.d.ts.map +1 -0
  18. package/dist/block/surface.js +54 -0
  19. package/dist/block/use-block-face-operation.d.ts +2 -2
  20. package/dist/block/use-block-face-operation.d.ts.map +1 -1
  21. package/dist/block/use-block-face-operation.js +25 -11
  22. package/dist/box-vent/definition.d.ts.map +1 -1
  23. package/dist/box-vent/definition.js +1 -0
  24. package/dist/building/renderer.d.ts.map +1 -1
  25. package/dist/building/renderer.js +1 -1
  26. package/dist/cabinet/definition.d.ts.map +1 -1
  27. package/dist/cabinet/definition.js +3 -1
  28. package/dist/cabinet/hosted-resize.d.ts +3 -6
  29. package/dist/cabinet/hosted-resize.d.ts.map +1 -1
  30. package/dist/cabinet/hosted-resize.js +9 -153
  31. package/dist/cabinet/placement-snap.d.ts.map +1 -1
  32. package/dist/cabinet/placement-snap.js +4 -2
  33. package/dist/cabinet/surfaces.d.ts +1 -0
  34. package/dist/cabinet/surfaces.d.ts.map +1 -1
  35. package/dist/cabinet/surfaces.js +13 -1
  36. package/dist/cabinet/tool.d.ts.map +1 -1
  37. package/dist/cabinet/tool.js +8 -4
  38. package/dist/ceiling/definition.d.ts.map +1 -1
  39. package/dist/ceiling/definition.js +5 -0
  40. package/dist/ceiling/renderer.d.ts.map +1 -1
  41. package/dist/ceiling/renderer.js +3 -2
  42. package/dist/chimney/definition.d.ts.map +1 -1
  43. package/dist/chimney/definition.js +1 -0
  44. package/dist/column/definition.d.ts.map +1 -1
  45. package/dist/column/definition.js +10 -3
  46. package/dist/column/floorplan-affordances.d.ts.map +1 -1
  47. package/dist/column/floorplan-affordances.js +6 -1
  48. package/dist/column/floorplan.d.ts.map +1 -1
  49. package/dist/column/floorplan.js +23 -11
  50. package/dist/column/hosted-resize.d.ts +116 -0
  51. package/dist/column/hosted-resize.d.ts.map +1 -0
  52. package/dist/column/hosted-resize.js +9 -0
  53. package/dist/column/panel.d.ts.map +1 -1
  54. package/dist/column/panel.js +6 -3
  55. package/dist/column/renderer.d.ts.map +1 -1
  56. package/dist/column/renderer.js +25 -103
  57. package/dist/column/shape.d.ts +22 -0
  58. package/dist/column/shape.d.ts.map +1 -0
  59. package/dist/column/shape.js +120 -0
  60. package/dist/column/surface.d.ts +4 -0
  61. package/dist/column/surface.d.ts.map +1 -0
  62. package/dist/column/surface.js +66 -0
  63. package/dist/cupola/definition.d.ts.map +1 -1
  64. package/dist/cupola/definition.js +1 -0
  65. package/dist/door/definition.d.ts.map +1 -1
  66. package/dist/door/definition.js +40 -1
  67. package/dist/door/door-math.d.ts +1 -1
  68. package/dist/door/door-math.d.ts.map +1 -1
  69. package/dist/door/door-math.js +7 -10
  70. package/dist/door/floorplan-affordances.d.ts.map +1 -1
  71. package/dist/door/floorplan-affordances.js +8 -12
  72. package/dist/door/move-tool.d.ts.map +1 -1
  73. package/dist/door/move-tool.js +3 -4
  74. package/dist/door/panel.d.ts.map +1 -1
  75. package/dist/door/panel.js +23 -52
  76. package/dist/door/tool.d.ts.map +1 -1
  77. package/dist/door/tool.js +20 -2
  78. package/dist/dormer/definition.d.ts.map +1 -1
  79. package/dist/dormer/definition.js +1 -0
  80. package/dist/downspout/definition.d.ts.map +1 -1
  81. package/dist/downspout/definition.js +1 -0
  82. package/dist/duct-segment/tool.d.ts.map +1 -1
  83. package/dist/duct-segment/tool.js +11 -0
  84. package/dist/duct-terminal/tool.d.ts.map +1 -1
  85. package/dist/duct-terminal/tool.js +77 -8
  86. package/dist/elevator/definition.d.ts.map +1 -1
  87. package/dist/elevator/definition.js +2 -0
  88. package/dist/elevator/parametrics.d.ts.map +1 -1
  89. package/dist/elevator/parametrics.js +98 -1
  90. package/dist/eyebrow-vent/definition.d.ts.map +1 -1
  91. package/dist/eyebrow-vent/definition.js +1 -0
  92. package/dist/fence/definition.d.ts.map +1 -1
  93. package/dist/fence/definition.js +1 -0
  94. package/dist/fence/tool.js +2 -2
  95. package/dist/guide/definition.d.ts.map +1 -1
  96. package/dist/guide/definition.js +1 -0
  97. package/dist/gutter/definition.d.ts.map +1 -1
  98. package/dist/gutter/definition.js +1 -0
  99. package/dist/hvac-equipment/tool.d.ts.map +1 -1
  100. package/dist/hvac-equipment/tool.js +8 -2
  101. package/dist/item/__fixtures__/main-floorplan.d.ts +26 -0
  102. package/dist/item/__fixtures__/main-floorplan.d.ts.map +1 -0
  103. package/dist/item/__fixtures__/main-floorplan.js +297 -0
  104. package/dist/item/definition.d.ts.map +1 -1
  105. package/dist/item/definition.js +3 -1
  106. package/dist/item/floorplan-move.d.ts.map +1 -1
  107. package/dist/item/floorplan-move.js +209 -130
  108. package/dist/item/floorplan.d.ts +22 -0
  109. package/dist/item/floorplan.d.ts.map +1 -1
  110. package/dist/item/floorplan.js +61 -2
  111. package/dist/item/move-tool.d.ts +1 -1
  112. package/dist/item/move-tool.d.ts.map +1 -1
  113. package/dist/item/move-tool.js +15 -7
  114. package/dist/item/renderer.d.ts.map +1 -1
  115. package/dist/item/renderer.js +8 -2
  116. package/dist/level/renderer.js +1 -1
  117. package/dist/lineset/tool.d.ts.map +1 -1
  118. package/dist/lineset/tool.js +1 -0
  119. package/dist/liquid-line/tool.d.ts.map +1 -1
  120. package/dist/liquid-line/tool.js +5 -1
  121. package/dist/measurement/definition.d.ts.map +1 -1
  122. package/dist/measurement/definition.js +1 -0
  123. package/dist/pipe-segment/tool.d.ts.map +1 -1
  124. package/dist/pipe-segment/tool.js +14 -3
  125. package/dist/pipe-trap/tool.d.ts.map +1 -1
  126. package/dist/pipe-trap/tool.js +7 -8
  127. package/dist/procedural-item/definition.d.ts.map +1 -1
  128. package/dist/procedural-item/definition.js +4 -1
  129. package/dist/ridge-vent/definition.d.ts.map +1 -1
  130. package/dist/ridge-vent/definition.js +1 -0
  131. package/dist/roof/definition.d.ts.map +1 -1
  132. package/dist/roof/definition.js +4 -1
  133. package/dist/roof/panel-model.d.ts +4 -0
  134. package/dist/roof/panel-model.d.ts.map +1 -0
  135. package/dist/roof/panel-model.js +151 -0
  136. package/dist/roof/panel.d.ts.map +1 -1
  137. package/dist/roof/panel.js +3 -4
  138. package/dist/roof/parametrics.d.ts.map +1 -1
  139. package/dist/roof/parametrics.js +16 -1
  140. package/dist/roof-segment/definition.d.ts.map +1 -1
  141. package/dist/roof-segment/definition.js +1 -0
  142. package/dist/roof-segment/parametrics.d.ts.map +1 -1
  143. package/dist/roof-segment/parametrics.js +176 -1
  144. package/dist/scan/definition.d.ts.map +1 -1
  145. package/dist/scan/definition.js +1 -0
  146. package/dist/shared/commit-opening-move.d.ts +4 -0
  147. package/dist/shared/commit-opening-move.d.ts.map +1 -0
  148. package/dist/shared/commit-opening-move.js +9 -0
  149. package/dist/shared/curtain-opening-limits.d.ts +14 -0
  150. package/dist/shared/curtain-opening-limits.d.ts.map +1 -0
  151. package/dist/shared/curtain-opening-limits.js +53 -0
  152. package/dist/shared/draft-axis-guides.d.ts +0 -6
  153. package/dist/shared/draft-axis-guides.d.ts.map +1 -1
  154. package/dist/shared/draft-axis-guides.js +1 -8
  155. package/dist/shared/hosted-resize.d.ts +12 -0
  156. package/dist/shared/hosted-resize.d.ts.map +1 -0
  157. package/dist/shared/hosted-resize.js +166 -0
  158. package/dist/shared/opening-grid-follow.d.ts +8 -0
  159. package/dist/shared/opening-grid-follow.d.ts.map +1 -0
  160. package/dist/shared/opening-grid-follow.js +12 -0
  161. package/dist/shared/opening-property-preview-host.d.ts +3 -0
  162. package/dist/shared/opening-property-preview-host.d.ts.map +1 -0
  163. package/dist/shared/opening-property-preview-host.js +21 -0
  164. package/dist/shared/opening-property-preview.d.ts +19 -0
  165. package/dist/shared/opening-property-preview.d.ts.map +1 -0
  166. package/dist/shared/opening-property-preview.js +99 -0
  167. package/dist/shared/resting-surface-plan.d.ts +2 -1
  168. package/dist/shared/resting-surface-plan.d.ts.map +1 -1
  169. package/dist/shared/resting-surface-plan.js +29 -4
  170. package/dist/shared/surface-counterparts.d.ts +12 -0
  171. package/dist/shared/surface-counterparts.d.ts.map +1 -0
  172. package/dist/shared/surface-counterparts.js +27 -0
  173. package/dist/shared/wall-attach-target.d.ts +1 -0
  174. package/dist/shared/wall-attach-target.d.ts.map +1 -1
  175. package/dist/shared/wall-attach-target.js +4 -1
  176. package/dist/shared/wall-event-from-grid.d.ts +5 -0
  177. package/dist/shared/wall-event-from-grid.d.ts.map +1 -0
  178. package/dist/shared/wall-event-from-grid.js +36 -0
  179. package/dist/shelf/definition.d.ts.map +1 -1
  180. package/dist/shelf/definition.js +3 -1
  181. package/dist/shelf/floorplan.d.ts.map +1 -1
  182. package/dist/shelf/floorplan.js +9 -2
  183. package/dist/site/renderer.d.ts.map +1 -1
  184. package/dist/site/renderer.js +9 -2
  185. package/dist/skylight/definition.d.ts.map +1 -1
  186. package/dist/skylight/definition.js +1 -0
  187. package/dist/slab/definition.d.ts.map +1 -1
  188. package/dist/slab/definition.js +4 -0
  189. package/dist/solar-panel/definition.d.ts.map +1 -1
  190. package/dist/solar-panel/definition.js +1 -0
  191. package/dist/spawn/definition.d.ts.map +1 -1
  192. package/dist/spawn/definition.js +1 -0
  193. package/dist/stair/definition.d.ts.map +1 -1
  194. package/dist/stair/definition.js +2 -0
  195. package/dist/stair/parametrics.d.ts.map +1 -1
  196. package/dist/stair/parametrics.js +164 -1
  197. package/dist/stair-segment/definition.d.ts.map +1 -1
  198. package/dist/stair-segment/definition.js +1 -0
  199. package/dist/stair-segment/parametrics.d.ts.map +1 -1
  200. package/dist/stair-segment/parametrics.js +81 -1
  201. package/dist/turbine-vent/definition.d.ts.map +1 -1
  202. package/dist/turbine-vent/definition.js +1 -0
  203. package/dist/wall/actions.d.ts +3 -0
  204. package/dist/wall/actions.d.ts.map +1 -0
  205. package/dist/wall/actions.js +56 -0
  206. package/dist/wall/curtain-opening-frame.d.ts +14 -0
  207. package/dist/wall/curtain-opening-frame.d.ts.map +1 -0
  208. package/dist/wall/curtain-opening-frame.js +111 -0
  209. package/dist/wall/curtain-panel-highlight.d.ts +3 -0
  210. package/dist/wall/curtain-panel-highlight.d.ts.map +1 -0
  211. package/dist/wall/curtain-panel-highlight.js +113 -0
  212. package/dist/wall/curtain-wall-adapter.d.ts +3 -0
  213. package/dist/wall/curtain-wall-adapter.d.ts.map +1 -0
  214. package/dist/wall/curtain-wall-adapter.js +55 -0
  215. package/dist/wall/curtain-wall-floorplan.d.ts +3 -0
  216. package/dist/wall/curtain-wall-floorplan.d.ts.map +1 -0
  217. package/dist/wall/curtain-wall-floorplan.js +91 -0
  218. package/dist/wall/curtain-wall-geometry.d.ts +4 -0
  219. package/dist/wall/curtain-wall-geometry.d.ts.map +1 -0
  220. package/dist/wall/curtain-wall-geometry.js +226 -0
  221. package/dist/wall/curtain-wall-layout.d.ts +16 -0
  222. package/dist/wall/curtain-wall-layout.d.ts.map +1 -0
  223. package/dist/wall/curtain-wall-layout.js +176 -0
  224. package/dist/wall/curtain-wall-materials.d.ts +5 -0
  225. package/dist/wall/curtain-wall-materials.d.ts.map +1 -0
  226. package/dist/wall/curtain-wall-materials.js +55 -0
  227. package/dist/wall/curtain-wall-panel.d.ts +14 -0
  228. package/dist/wall/curtain-wall-panel.d.ts.map +1 -0
  229. package/dist/wall/curtain-wall-panel.js +167 -0
  230. package/dist/wall/curtain-wall-piece-geometry.d.ts +5 -0
  231. package/dist/wall/curtain-wall-piece-geometry.d.ts.map +1 -0
  232. package/dist/wall/curtain-wall-piece-geometry.js +220 -0
  233. package/dist/wall/curtain-wall-shadow.d.ts +4 -0
  234. package/dist/wall/curtain-wall-shadow.d.ts.map +1 -0
  235. package/dist/wall/curtain-wall-shadow.js +15 -0
  236. package/dist/wall/definition.d.ts +0 -15
  237. package/dist/wall/definition.d.ts.map +1 -1
  238. package/dist/wall/definition.js +65 -4
  239. package/dist/wall/drawing-mode.d.ts +12 -0
  240. package/dist/wall/drawing-mode.d.ts.map +1 -0
  241. package/dist/wall/drawing-mode.js +38 -0
  242. package/dist/wall/floorplan-tool.d.ts +4 -0
  243. package/dist/wall/floorplan-tool.d.ts.map +1 -0
  244. package/dist/wall/floorplan-tool.js +107 -0
  245. package/dist/wall/floorplan.d.ts.map +1 -1
  246. package/dist/wall/floorplan.js +6 -2
  247. package/dist/wall/length-patch.d.ts +3 -0
  248. package/dist/wall/length-patch.d.ts.map +1 -0
  249. package/dist/wall/length-patch.js +14 -0
  250. package/dist/wall/move-tool.d.ts.map +1 -1
  251. package/dist/wall/move-tool.js +23 -1
  252. package/dist/wall/paint.d.ts.map +1 -1
  253. package/dist/wall/paint.js +39 -4
  254. package/dist/wall/panel-model.d.ts +4 -0
  255. package/dist/wall/panel-model.d.ts.map +1 -0
  256. package/dist/wall/panel-model.js +117 -0
  257. package/dist/wall/panel.d.ts.map +1 -1
  258. package/dist/wall/panel.js +20 -24
  259. package/dist/wall/property-preview.d.ts +7 -0
  260. package/dist/wall/property-preview.d.ts.map +1 -0
  261. package/dist/wall/property-preview.js +31 -0
  262. package/dist/wall/rectangle-command.d.ts +149 -0
  263. package/dist/wall/rectangle-command.d.ts.map +1 -0
  264. package/dist/wall/rectangle-command.js +22 -0
  265. package/dist/wall/rectangle-tool.d.ts +2 -0
  266. package/dist/wall/rectangle-tool.d.ts.map +1 -0
  267. package/dist/wall/rectangle-tool.js +147 -0
  268. package/dist/wall/renderer.d.ts.map +1 -1
  269. package/dist/wall/renderer.js +7 -4
  270. package/dist/wall/slots.d.ts +2 -2
  271. package/dist/wall/slots.d.ts.map +1 -1
  272. package/dist/wall/slots.js +10 -2
  273. package/dist/wall/split-floorplan-layer.d.ts +4 -0
  274. package/dist/wall/split-floorplan-layer.d.ts.map +1 -0
  275. package/dist/wall/split-floorplan-layer.js +71 -0
  276. package/dist/wall/split-pointer.d.ts +3 -0
  277. package/dist/wall/split-pointer.d.ts.map +1 -0
  278. package/dist/wall/split-pointer.js +95 -0
  279. package/dist/wall/split-preview.d.ts +55 -0
  280. package/dist/wall/split-preview.d.ts.map +1 -0
  281. package/dist/wall/split-preview.js +106 -0
  282. package/dist/wall/split-session.d.ts +16 -0
  283. package/dist/wall/split-session.d.ts.map +1 -0
  284. package/dist/wall/split-session.js +138 -0
  285. package/dist/wall/split-store.d.ts +16 -0
  286. package/dist/wall/split-store.d.ts.map +1 -0
  287. package/dist/wall/split-store.js +6 -0
  288. package/dist/wall/split-tool.d.ts +10 -0
  289. package/dist/wall/split-tool.d.ts.map +1 -0
  290. package/dist/wall/split-tool.js +95 -0
  291. package/dist/wall/system.d.ts.map +1 -1
  292. package/dist/wall/system.js +3 -1
  293. package/dist/wall/tool.d.ts.map +1 -1
  294. package/dist/wall/tool.js +14 -6
  295. package/dist/wall/wall-batch-system.d.ts.map +1 -1
  296. package/dist/wall/wall-batch-system.js +3 -0
  297. package/dist/window/definition.d.ts.map +1 -1
  298. package/dist/window/definition.js +48 -1
  299. package/dist/window/floorplan-affordances.d.ts.map +1 -1
  300. package/dist/window/floorplan-affordances.js +8 -10
  301. package/dist/window/move-tool.d.ts.map +1 -1
  302. package/dist/window/move-tool.js +4 -6
  303. package/dist/window/panel.d.ts.map +1 -1
  304. package/dist/window/panel.js +21 -59
  305. package/dist/window/tool.d.ts.map +1 -1
  306. package/dist/window/tool.js +34 -2
  307. package/dist/window/window-math.d.ts +1 -1
  308. package/dist/window/window-math.d.ts.map +1 -1
  309. package/dist/window/window-math.js +8 -11
  310. package/dist/zone/definition.d.ts.map +1 -1
  311. package/dist/zone/definition.js +1 -0
  312. package/dist/zone/quantities-panel.d.ts.map +1 -1
  313. package/dist/zone/quantities-panel.js +3 -19
  314. package/dist/zone/renderer.d.ts.map +1 -1
  315. package/dist/zone/renderer.js +1 -1
  316. package/package.json +8 -8
  317. package/dist/shared/opening-documentation-fields.d.ts +0 -16
  318. package/dist/shared/opening-documentation-fields.d.ts.map +0 -1
  319. package/dist/shared/opening-documentation-fields.js +0 -54
@@ -120,6 +120,9 @@ export function resolveWallAttachmentAtPlanPoint(wall, planPoint, maxDistance =
120
120
  itemRotation: side === 'front' ? 0 : Math.PI,
121
121
  };
122
122
  }
123
+ export function projectPlanPointToWallLocalX(wall, planPoint) {
124
+ return resolveWallAttachmentAtPlanPoint(wall, planPoint, Number.POSITIVE_INFINITY)?.localX ?? 0;
125
+ }
123
126
  /**
124
127
  * Return the closest wall attachment target in plan space, including curved
125
128
  * walls. This is deliberately separate from `findClosestWallInPlan`: doors,
@@ -240,7 +243,7 @@ export function hasWallChildOverlap(wallId, nodes, clampedX, clampedY, width, he
240
243
  if (childId === ignoreId)
241
244
  continue;
242
245
  const child = nodes[childId];
243
- if (!child)
246
+ if (!child || child.metadata.isTransient)
244
247
  continue;
245
248
  let childLeft;
246
249
  let childRight;
@@ -0,0 +1,5 @@
1
+ import { type AnyNode, type AnyNodeId, type GridEvent, type WallEvent } from '@pascal-app/core';
2
+ import { type Object3D } from 'three';
3
+ /** Canvas surface queries still reach the host when a rendered child consumes mesh events. */
4
+ export declare function wallEventFromGrid(event: GridEvent, activeLevelId: AnyNodeId | null, nodes: Record<AnyNodeId, AnyNode>, objects: ReadonlyMap<string, Object3D>): WallEvent | null;
5
+ //# sourceMappingURL=wall-event-from-grid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wall-event-from-grid.d.ts","sourceRoot":"","sources":["../../src/shared/wall-event-from-grid.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,SAAS,EAEd,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAW,KAAK,QAAQ,EAAW,MAAM,OAAO,CAAA;AAEvD,8FAA8F;AAC9F,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,EAChB,aAAa,EAAE,SAAS,GAAG,IAAI,EAC/B,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,GACrC,SAAS,GAAG,IAAI,CA8BlB"}
@@ -0,0 +1,36 @@
1
+ import { isCurvedWall, } from '@pascal-app/core';
2
+ import { Matrix3, Vector3 } from 'three';
3
+ /** Canvas surface queries still reach the host when a rendered child consumes mesh events. */
4
+ export function wallEventFromGrid(event, activeLevelId, nodes, objects) {
5
+ if (event.surfaceHit?.kind !== 'wall' || !event.surfaceNormal)
6
+ return null;
7
+ const wall = nodes[event.surfaceHit.hostId];
8
+ if (wall?.type !== 'wall' ||
9
+ wall.visible === false ||
10
+ wall.metadata.isTransient ||
11
+ wall.parentId !== activeLevelId ||
12
+ isCurvedWall(wall))
13
+ return null;
14
+ const object = objects.get(wall.id);
15
+ const frame = event.localFrameId ? objects.get(event.localFrameId) : undefined;
16
+ if (!object || (event.localFrameId && !frame))
17
+ return null;
18
+ object.updateWorldMatrix(true, false);
19
+ frame?.updateWorldMatrix(true, false);
20
+ const normal = new Vector3(...event.surfaceNormal);
21
+ if (frame)
22
+ normal.applyNormalMatrix(new Matrix3().getNormalMatrix(frame.matrixWorld));
23
+ normal.applyNormalMatrix(new Matrix3().getNormalMatrix(object.matrixWorld.clone().invert()));
24
+ if (Math.abs(normal.z) <= 0.7)
25
+ return null;
26
+ const position = object.worldToLocal(new Vector3(...event.position));
27
+ return {
28
+ node: wall,
29
+ object,
30
+ position: event.position,
31
+ localPosition: position.toArray(),
32
+ normal: normal.toArray(),
33
+ nativeEvent: event.nativeEvent,
34
+ stopPropagation: () => { },
35
+ };
36
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/shelf/definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,cAAc,EAA8B,MAAM,kBAAkB,CAAA;AAQpG,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AA4HpC,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,OAAO,SAAS,CAwJ5D,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/shelf/definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,cAAc,EAA8B,MAAM,kBAAkB,CAAA;AASpG,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AA4HpC,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,OAAO,SAAS,CAyJ5D,CAAA"}
@@ -1,3 +1,4 @@
1
+ import { restingFloorplanAffectedIds } from '../shared/resting-surface-plan';
1
2
  import { sanitizeShelfDimensions } from './dimensions';
2
3
  import { buildShelfFloorplan } from './floorplan';
3
4
  import { shelfResizeAffordance, shelfRotateAffordance } from './floorplan-affordances';
@@ -173,7 +174,7 @@ export const shelfDefinition = {
173
174
  })),
174
175
  },
175
176
  selectable: { hitVolume: 'bbox' },
176
- duplicable: true,
177
+ duplicable: { subtree: 'with-children' },
177
178
  deletable: true,
178
179
  paint: shelfPaint,
179
180
  slots: (n) => shelfSlots(n),
@@ -230,6 +231,7 @@ export const shelfDefinition = {
230
231
  ]);
231
232
  },
232
233
  floorplan: buildShelfFloorplan,
234
+ floorplanAffectedIds: restingFloorplanAffectedIds,
233
235
  // 2D move handler — Path 1 in `FloorplanRegistryMoveOverlay`. Without
234
236
  // this the overlay falls through to Path 2 which stomps the SVG
235
237
  // entry's `transform` attribute (set by the floor-plan layer to
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/shelf/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAG1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAQzC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,eAAe,GAAG,iBAAiB,CAmH7F"}
1
+ {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/shelf/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,iBAAiB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAIrF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAQzC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,eAAe,GAAG,iBAAiB,CA0H7F"}
@@ -1,3 +1,4 @@
1
+ import { restingNodePlanFrame } from '../shared/resting-surface-plan';
1
2
  import { sanitizeShelfDimensions } from './dimensions';
2
3
  // Offsets for the floor-plan selection chrome. Resize chevrons sit a
3
4
  // hair off the footprint rim; the rotate-arrow corner sits a bit further
@@ -22,8 +23,14 @@ const ROTATE_ARROW_CORNER_OFFSET = 0.22;
22
23
  */
23
24
  export function buildShelfFloorplan(node, ctx) {
24
25
  const shelf = sanitizeShelfDimensions(node);
25
- const [px, , pz] = shelf.position;
26
- const ry = shelf.rotation[1] ?? 0;
26
+ const parent = shelf.parentId ? ctx?.resolve(shelf.parentId) : undefined;
27
+ const hostedFrame = ctx && parent?.type === 'procedural-item' && parent.attachments[shelf.id] !== undefined
28
+ ? restingNodePlanFrame(shelf, ctx.resolve)
29
+ : null;
30
+ const [px, , pz] = hostedFrame?.position ?? shelf.position;
31
+ const ry = hostedFrame
32
+ ? Math.atan2(hostedFrame.axes[2][0], hostedFrame.axes[2][2])
33
+ : (shelf.rotation[1] ?? 0);
27
34
  // Floor-plan plots at `-ry` so SVG's CW-with-y-down `rotate` direction
28
35
  // ends up visually matching Three.js Y-rotation (CCW from a top-down
29
36
  // view) — same `rotation` value rotates the same way in both views.
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/site/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,QAAQ,EAQd,MAAM,kBAAkB,CAAA;AA6HzB,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,uCAmSxD,CAAA;AAED,eAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/site/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,QAAQ,EAQd,MAAM,kBAAkB,CAAA;AA8HzB,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,uCA6SxD,CAAA;AAED,eAAe,YAAY,CAAA"}
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { terrainFieldOf, unionPolygons, useLiveNodeOverrides, useLiveTerrain, useRegistry, useScene, } from '@pascal-app/core';
4
- import { backdropGradient, deepSkyColor, getSceneTheme, horizonHazeColor, NodeRenderer, useNodeEvents, useSceneAtmosphere, useSceneGroundReplacement, useViewer, } from '@pascal-app/viewer';
4
+ import { backdropGradient, deepSkyColor, getSceneTheme, horizonHazeColor, NodeRenderer, useImmersiveXRPresentation, useNodeEvents, useSceneAtmosphere, useSceneGroundReplacement, useViewer, } from '@pascal-app/viewer';
5
5
  import { useEffect, useMemo, useRef } from 'react';
6
6
  import { BufferAttribute, BufferGeometry, Path, Shape, ShapeGeometry } from 'three';
7
7
  import { cameraPosition, color, mix, positionWorld, smoothstep, float as tslFloat, vec2, } from 'three/tsl';
@@ -75,6 +75,7 @@ function addSlabHoles(shape, slabPolygons, originX = 0, originZ = 0) {
75
75
  }
76
76
  export const SiteRenderer = ({ node }) => {
77
77
  const ref = useRef(null);
78
+ const immersiveXR = useImmersiveXRPresentation();
78
79
  const atmosphere = useSceneAtmosphere();
79
80
  const groundReplaced = useSceneGroundReplacement();
80
81
  useRegistry(node.id, 'site', ref);
@@ -290,9 +291,15 @@ export const SiteRenderer = ({ node }) => {
290
291
  // inside TerrainRenderer so a stroke that starts on a site with no persisted
291
292
  // terrain still mounts the mesh.
292
293
  const showTerrain = terrainGrid !== null;
294
+ // The Site is the one kind whose `visible` flag stops at itself: hiding it
295
+ // drops the parcel's own presentation — ground fill, sculpted ground, lot
296
+ // line — while everything standing on the site keeps its own flag (the
297
+ // exporter and the 2D plan draw the same line). The horizon disc is a world
298
+ // backdrop rather than part of the parcel, so it stays either way.
299
+ const showSiteSurfaces = node.visible !== false;
293
300
  if (!(node && lineGeometry)) {
294
301
  return null;
295
302
  }
296
- return (_jsxs("group", { ref: ref, ...handlers, children: [(node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))), showTerrain && _jsx(TerrainRenderer, { material: groundMaterial, site: node }), groundGeometry && !showTerrain && (_jsx("mesh", { geometry: groundGeometry, material: groundMaterial, position: [0, -0.05, 0], receiveShadow: true, rotation: [-Math.PI / 2, 0, 0] })), horizonGeometry && horizonMaterial && fadeBounds && (_jsx("mesh", { geometry: horizonGeometry, material: horizonMaterial, position: [fadeBounds.cx, HORIZON_PLANE_Y, fadeBounds.cz], raycast: noopRaycast, receiveShadow: true, rotation: [-Math.PI / 2, 0, 0], userData: { pascalExport: 'strip' } })), _jsx("line", { frustumCulled: false, geometry: lineGeometry, renderOrder: 9, children: _jsx("lineBasicMaterial", { color: "#f59e0b", linewidth: 2, opacity: 0.6, transparent: true }) })] }));
303
+ return (_jsxs("group", { ref: ref, ...handlers, children: [(node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))), showSiteSurfaces && showTerrain && _jsx(TerrainRenderer, { material: groundMaterial, site: node }), showSiteSurfaces && groundGeometry && !showTerrain && (_jsx("mesh", { geometry: groundGeometry, material: groundMaterial, position: [0, -0.05, 0], receiveShadow: true, rotation: [-Math.PI / 2, 0, 0] })), !immersiveXR && horizonGeometry && horizonMaterial && fadeBounds && (_jsx("mesh", { geometry: horizonGeometry, material: horizonMaterial, position: [fadeBounds.cx, HORIZON_PLANE_Y, fadeBounds.cz], raycast: noopRaycast, receiveShadow: true, rotation: [-Math.PI / 2, 0, 0], userData: { pascalExport: 'strip' } })), showSiteSurfaces && (_jsx("line", { frustumCulled: false, geometry: lineGeometry, renderOrder: 9, children: _jsx("lineBasicMaterial", { color: "#f59e0b", linewidth: 2, opacity: 0.6, transparent: true }) }))] }));
297
304
  };
298
305
  export default SiteRenderer;
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/skylight/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EAIpB,MAAM,kBAAkB,CAAA;AASzB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAoMvC;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,OAAO,YAAY,CAiFlE,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/skylight/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EAIpB,MAAM,kBAAkB,CAAA;AASzB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAoMvC;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,OAAO,YAAY,CAkFlE,CAAA"}
@@ -226,6 +226,7 @@ export const skylightDefinition = {
226
226
  parametrics: skylightParametrics,
227
227
  handles: skylightHandles,
228
228
  floorplan: buildSkylightFloorplan,
229
+ rendersChildren: false,
229
230
  renderer: {
230
231
  kind: 'parametric',
231
232
  module: () => import('./renderer'),
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/slab/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EAKpB,MAAM,kBAAkB,CAAA;AA4BzB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAsNnC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,OAAO,QAAQ,CAsH1D,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/slab/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EAKpB,MAAM,kBAAkB,CAAA;AA8BzB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAsNnC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,OAAO,QAAQ,CAyH1D,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { MIN_SLAB_THICKNESS, markSlabChangeDependents, pointInPolygon2D, syncStairRises, } from '@pascal-app/core';
2
2
  import { clearStructuralElevationGuide, DRAFTING_SURFACE_EXTENSION_KEY, publishStructuralElevationGuide, resolveStructuralElevationSnap, } from '@pascal-app/editor';
3
3
  import { polygonMeasurementFeatures } from '../shared/polygon-measurement';
4
+ import { sameOutlineSurfaceCounterparts } from '../shared/surface-counterparts';
4
5
  import { applySlabBaseElevationChange, applySlabThicknessChange, applySlabTopChange, getSlabBaseElevation, slabElevationUpperBound, } from './elevation-limit';
5
6
  import { buildSlabFloorplan } from './floorplan';
6
7
  import { slabAddVertexAffordance, slabDeleteVertexAffordance, slabMoveEdgeAffordance, slabMoveVertexAffordance, } from './floorplan-affordances';
@@ -201,6 +202,9 @@ export const slabDefinition = {
201
202
  [DRAFTING_SURFACE_EXTENSION_KEY]: {
202
203
  kind: 'slab',
203
204
  },
205
+ 'pascal:editor/floorplan': {
206
+ selectionCounterparts: sameOutlineSurfaceCounterparts,
207
+ },
204
208
  },
205
209
  defaults: () => ({
206
210
  object: 'node',
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/solar-panel/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAgNzC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,EAAE,cAAc,CAAC,OAAO,cAAc,CAyDtE,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/solar-panel/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAgNzC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,EAAE,cAAc,CAAC,OAAO,cAAc,CA0DtE,CAAA"}
@@ -235,6 +235,7 @@ export const solarPanelDefinition = {
235
235
  parametrics: solarPanelParametrics,
236
236
  handles: solarPanelHandles,
237
237
  floorplan: buildSolarPanelFloorplan,
238
+ rendersChildren: false,
238
239
  renderer: {
239
240
  kind: 'parametric',
240
241
  module: () => import('./renderer'),
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/spawn/definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,cAAc,EAA8B,MAAM,kBAAkB,CAAA;AAKpG,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AA0CpC,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,OAAO,SAAS,CAuE5D,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/spawn/definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,cAAc,EAA8B,MAAM,kBAAkB,CAAA;AAKpG,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AA0CpC,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,OAAO,SAAS,CAwE5D,CAAA"}
@@ -73,6 +73,7 @@ export const spawnDefinition = {
73
73
  },
74
74
  parametrics: spawnParametrics,
75
75
  handles: [spawnRotateHandle(), spawnMoveHandle()],
76
+ rendersChildren: false,
76
77
  renderer: {
77
78
  kind: 'parametric',
78
79
  module: () => import('./renderer'),
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/stair/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EAQpB,MAAM,kBAAkB,CAAA;AAuZzB,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAGpC;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,OAAO,SAAS,CAgH5D,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/stair/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EAQpB,MAAM,kBAAkB,CAAA;AAuZzB,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAGpC;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,OAAO,SAAS,CAkH5D,CAAA"}
@@ -375,6 +375,7 @@ export const stairDefinition = {
375
375
  return rest;
376
376
  },
377
377
  capabilities: {
378
+ surfacePlacement: 'floor-only',
378
379
  selectable: { hitVolume: 'bbox' },
379
380
  // A stair has no centred box footprint: straight = a cumulative
380
381
  // `stair-segment` chain, curved / spiral = an annular sector. Hand the
@@ -401,6 +402,7 @@ export const stairDefinition = {
401
402
  },
402
403
  parametrics: stairParametrics,
403
404
  handles: stairHandles,
405
+ rendersChildren: false,
404
406
  renderer: {
405
407
  kind: 'parametric',
406
408
  module: () => import('./renderer'),
@@ -1 +1 @@
1
- {"version":3,"file":"parametrics.d.ts","sourceRoot":"","sources":["../../src/stair/parametrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEvE,eAAO,MAAM,gBAAgB,EAAE,oBAAoB,CAAC,SAAS,CAG5D,CAAA"}
1
+ {"version":3,"file":"parametrics.d.ts","sourceRoot":"","sources":["../../src/stair/parametrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEvE,eAAO,MAAM,gBAAgB,EAAE,oBAAoB,CAAC,SAAS,CAuK5D,CAAA"}
@@ -1,4 +1,167 @@
1
1
  export const stairParametrics = {
2
- groups: [],
2
+ groups: [
3
+ {
4
+ label: 'Type',
5
+ fields: [
6
+ {
7
+ key: 'stairType',
8
+ kind: 'enum',
9
+ options: ['straight', 'curved', 'spiral'],
10
+ display: 'segmented',
11
+ },
12
+ ],
13
+ },
14
+ {
15
+ label: 'Opening',
16
+ fields: [
17
+ { key: 'fromLevelId', kind: 'ref', label: 'From level', refKind: 'level' },
18
+ { key: 'toLevelId', kind: 'ref', label: 'To level', refKind: 'level' },
19
+ {
20
+ key: 'slabOpeningMode',
21
+ kind: 'enum',
22
+ label: 'Auto cutout',
23
+ options: ['none', 'destination'],
24
+ display: 'segmented',
25
+ },
26
+ {
27
+ key: 'openingOffset',
28
+ kind: 'number',
29
+ label: 'Opening offset',
30
+ unit: 'm',
31
+ min: 0,
32
+ max: 0.5,
33
+ step: 0.01,
34
+ visibleIf: (node) => node.slabOpeningMode === 'destination',
35
+ },
36
+ ],
37
+ },
38
+ {
39
+ label: 'Geometry',
40
+ fields: [
41
+ {
42
+ key: 'width',
43
+ kind: 'number',
44
+ unit: 'm',
45
+ min: 0.4,
46
+ max: 1000,
47
+ step: 0.05,
48
+ visibleIf: (node) => node.stairType !== 'straight',
49
+ },
50
+ {
51
+ key: 'stepCount',
52
+ kind: 'number',
53
+ label: 'Steps',
54
+ min: 2,
55
+ max: 32,
56
+ step: 1,
57
+ visibleIf: (node) => node.stairType !== 'straight',
58
+ },
59
+ {
60
+ key: 'fillToFloor',
61
+ kind: 'boolean',
62
+ label: 'Fill to floor',
63
+ visibleIf: (node) => node.stairType === 'curved',
64
+ },
65
+ {
66
+ key: 'thickness',
67
+ kind: 'number',
68
+ unit: 'm',
69
+ min: 0.02,
70
+ max: 1000,
71
+ step: 0.01,
72
+ visibleIf: (node) => node.stairType === 'spiral' || !node.fillToFloor,
73
+ },
74
+ {
75
+ key: 'innerRadius',
76
+ kind: 'number',
77
+ label: 'Inner radius',
78
+ unit: 'm',
79
+ min: 0.05,
80
+ max: 1000,
81
+ step: 0.05,
82
+ visibleIf: (node) => node.stairType !== 'straight',
83
+ },
84
+ {
85
+ key: 'sweepAngle',
86
+ kind: 'number',
87
+ label: 'Sweep (rad)',
88
+ min: -Math.PI * 4,
89
+ max: Math.PI * 4,
90
+ step: Math.PI / 180,
91
+ visibleIf: (node) => node.stairType !== 'straight',
92
+ },
93
+ {
94
+ key: 'showCenterColumn',
95
+ kind: 'boolean',
96
+ label: 'Center column',
97
+ visibleIf: (node) => node.stairType === 'spiral',
98
+ },
99
+ {
100
+ key: 'showStepSupports',
101
+ kind: 'boolean',
102
+ label: 'Step supports',
103
+ visibleIf: (node) => node.stairType === 'spiral',
104
+ },
105
+ {
106
+ key: 'topLandingMode',
107
+ kind: 'enum',
108
+ label: 'Top landing',
109
+ options: ['none', 'integrated'],
110
+ display: 'segmented',
111
+ visibleIf: (node) => node.stairType === 'spiral',
112
+ },
113
+ {
114
+ key: 'topLandingDepth',
115
+ kind: 'number',
116
+ label: 'Landing depth',
117
+ unit: 'm',
118
+ min: 0.3,
119
+ max: 1000,
120
+ step: 0.05,
121
+ visibleIf: (node) => node.stairType === 'spiral' && node.topLandingMode === 'integrated',
122
+ },
123
+ ],
124
+ },
125
+ {
126
+ label: 'Position',
127
+ fields: [
128
+ { key: 'position', kind: 'vec3' },
129
+ {
130
+ key: 'rotation',
131
+ kind: 'number',
132
+ label: 'Rotation (rad)',
133
+ min: -Math.PI,
134
+ max: Math.PI,
135
+ step: Math.PI / 36,
136
+ },
137
+ ],
138
+ },
139
+ {
140
+ label: 'Railing',
141
+ fields: [
142
+ {
143
+ key: 'railingMode',
144
+ kind: 'enum',
145
+ options: ['none', 'left', 'right', 'both'],
146
+ },
147
+ {
148
+ key: 'railingHeight',
149
+ kind: 'number',
150
+ label: 'Railing height',
151
+ unit: 'm',
152
+ min: 0.7,
153
+ max: 1.4,
154
+ step: 0.02,
155
+ visibleIf: (node) => node.railingMode !== 'none',
156
+ },
157
+ ],
158
+ },
159
+ ],
160
+ derive: (next, patch, previous) => patch.stairType === 'spiral' && previous?.stairType !== 'spiral'
161
+ ? {
162
+ position: [next.position[0], 0, next.position[2]],
163
+ sweepAngle: (400 * Math.PI) / 180,
164
+ }
165
+ : {},
3
166
  customPanel: () => import('./panel'),
4
167
  };
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/stair-segment/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AA2F3C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,OAAO,gBAAgB,CAgD1E,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/stair-segment/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AA2F3C;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,OAAO,gBAAgB,CAiD1E,CAAA"}
@@ -116,6 +116,7 @@ export const stairSegmentDefinition = {
116
116
  },
117
117
  parametrics: stairSegmentParametrics,
118
118
  handles: stairSegmentHandles,
119
+ rendersChildren: false,
119
120
  renderer: {
120
121
  kind: 'parametric',
121
122
  module: () => import('./renderer'),
@@ -1 +1 @@
1
- {"version":3,"file":"parametrics.d.ts","sourceRoot":"","sources":["../../src/stair-segment/parametrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE9E,eAAO,MAAM,uBAAuB,EAAE,oBAAoB,CAAC,gBAAgB,CAG1E,CAAA"}
1
+ {"version":3,"file":"parametrics.d.ts","sourceRoot":"","sources":["../../src/stair-segment/parametrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAE9E,eAAO,MAAM,uBAAuB,EAAE,oBAAoB,CAAC,gBAAgB,CAkF1E,CAAA"}
@@ -1,4 +1,84 @@
1
1
  export const stairSegmentParametrics = {
2
- groups: [],
2
+ groups: [
3
+ {
4
+ label: 'Type',
5
+ fields: [
6
+ {
7
+ key: 'segmentType',
8
+ kind: 'enum',
9
+ options: ['stair', 'landing'],
10
+ display: 'segmented',
11
+ },
12
+ {
13
+ key: 'attachmentSide',
14
+ kind: 'enum',
15
+ label: 'Attachment',
16
+ options: ['front', 'left', 'right'],
17
+ display: 'segmented',
18
+ },
19
+ ],
20
+ },
21
+ {
22
+ label: 'Dimensions',
23
+ fields: [
24
+ { key: 'width', kind: 'number', unit: 'm', min: 0.5, max: 1000, step: 0.1 },
25
+ { key: 'length', kind: 'number', unit: 'm', min: 0.5, max: 1000, step: 0.1 },
26
+ {
27
+ key: 'height',
28
+ kind: 'number',
29
+ unit: 'm',
30
+ min: 0.5,
31
+ max: 1000,
32
+ step: 0.1,
33
+ visibleIf: (node) => node.segmentType === 'stair',
34
+ },
35
+ {
36
+ key: 'stepCount',
37
+ kind: 'number',
38
+ label: 'Steps',
39
+ min: 2,
40
+ max: 30,
41
+ step: 1,
42
+ visibleIf: (node) => node.segmentType === 'stair',
43
+ },
44
+ ],
45
+ },
46
+ {
47
+ label: 'Structure',
48
+ fields: [
49
+ { key: 'fillToFloor', kind: 'boolean', label: 'Fill to floor' },
50
+ {
51
+ key: 'thickness',
52
+ kind: 'number',
53
+ unit: 'm',
54
+ min: 0.05,
55
+ max: 1000,
56
+ step: 0.05,
57
+ visibleIf: (node) => !node.fillToFloor,
58
+ },
59
+ ],
60
+ },
61
+ {
62
+ label: 'Position',
63
+ fields: [
64
+ { key: 'position', kind: 'vec3' },
65
+ {
66
+ key: 'rotation',
67
+ kind: 'number',
68
+ label: 'Rotation (rad)',
69
+ min: -Math.PI,
70
+ max: Math.PI,
71
+ step: Math.PI / 36,
72
+ },
73
+ ],
74
+ },
75
+ ],
76
+ derive: (_next, patch, previous) => {
77
+ if (!(patch.segmentType && patch.segmentType !== previous?.segmentType))
78
+ return {};
79
+ return patch.segmentType === 'landing'
80
+ ? { height: 0, length: 1, stepCount: 0 }
81
+ : { height: 2.5, length: 3, stepCount: 10 };
82
+ },
3
83
  customPanel: () => import('./panel'),
4
84
  };
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/turbine-vent/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAoD1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,OAAO,eAAe,CA2DxE,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/turbine-vent/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAoD1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,OAAO,eAAe,CA4DxE,CAAA"}
@@ -96,6 +96,7 @@ export const turbineVentDefinition = {
96
96
  parametrics: turbineVentParametrics,
97
97
  handles: turbineVentHandles,
98
98
  floorplan: buildTurbineVentFloorplan,
99
+ rendersChildren: false,
99
100
  renderer: {
100
101
  kind: 'parametric',
101
102
  module: () => import('./renderer'),
@@ -0,0 +1,3 @@
1
+ /** The wall's action-menu contributions: Split (one wall) and Merge (several). */
2
+ export default function WallActions(): import("react").JSX.Element;
3
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/wall/actions.tsx"],"names":[],"mappings":"AAgBA,kFAAkF;AAClF,MAAM,CAAC,OAAO,UAAU,WAAW,gCAOlC"}
@@ -0,0 +1,56 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { planWallMerge, runAsSingleSceneHistoryStep, useScene, } from '@pascal-app/core';
4
+ import { Tooltip, TooltipContent, TooltipTrigger, triggerSFX } from '@pascal-app/editor';
5
+ import { useViewer } from '@pascal-app/viewer';
6
+ import { FoldHorizontal, Scissors } from 'lucide-react';
7
+ import { useMemo } from 'react';
8
+ import { openWallSplit } from './split-session';
9
+ const BUTTON = 'tooltip-trigger rounded-md p-1.5 text-muted-foreground hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-50';
10
+ /** The wall's action-menu contributions: Split (one wall) and Merge (several). */
11
+ export default function WallActions() {
12
+ return (_jsxs(_Fragment, { children: [_jsx(SplitWallAction, {}), _jsx(MergeWallsAction, {})] }));
13
+ }
14
+ function SplitWallAction() {
15
+ const selected = useViewer((s) => s.selection.selectedIds);
16
+ const wall = useScene((s) => selected.length === 1 ? s.nodes[selected[0]] : undefined);
17
+ const readOnly = useScene((s) => s.readOnly);
18
+ if (wall?.type !== 'wall')
19
+ return null;
20
+ return (_jsx("button", { type: "button", "aria-label": "Split wall", title: "Split wall", disabled: readOnly, className: BUTTON, onClick: (event) => {
21
+ event.stopPropagation();
22
+ openWallSplit(wall);
23
+ }, children: _jsx(Scissors, { className: "size-4" }) }));
24
+ }
25
+ /** Joins selected walls that continue each other into one — the inverse of Split. */
26
+ function MergeWallsAction() {
27
+ const selected = useViewer((s) => s.selection.selectedIds);
28
+ const nodes = useScene((s) => s.nodes);
29
+ const readOnly = useScene((s) => s.readOnly);
30
+ // null: not a wall selection (render nothing); otherwise why it can't merge, or null.
31
+ const merge = useMemo(() => {
32
+ if (selected.length < 2 || selected.some((id) => nodes[id]?.type !== 'wall'))
33
+ return null;
34
+ try {
35
+ planWallMerge(nodes, selected);
36
+ return { reason: null };
37
+ }
38
+ catch (error) {
39
+ return { reason: error instanceof Error ? error.message : 'These walls cannot be merged.' };
40
+ }
41
+ }, [nodes, selected]);
42
+ if (!merge)
43
+ return null;
44
+ const { reason } = merge;
45
+ const button = (_jsx("button", { type: "button", "aria-label": "Merge walls", title: reason ? undefined : 'Merge walls', disabled: readOnly || reason !== null, className: BUTTON, onClick: (event) => {
46
+ event.stopPropagation();
47
+ const plan = planWallMerge(useScene.getState().nodes, selected);
48
+ runAsSingleSceneHistoryStep(useScene, () => useScene.getState().applyNodeChanges(plan.changes));
49
+ useViewer.getState().setSelection({ selectedIds: [plan.wallId] });
50
+ triggerSFX('sfx:structure-build');
51
+ }, children: _jsx(FoldHorizontal, { className: "size-4" }) }));
52
+ if (!reason)
53
+ return button;
54
+ // A disabled button gets no hover events, so the reason hangs off a wrapper.
55
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "inline-flex", children: button }) }), _jsx(TooltipContent, { side: "top", children: reason })] }));
56
+ }
@@ -0,0 +1,14 @@
1
+ import { type DoorNode, type WallNode, type WindowNode } from '@pascal-app/core';
2
+ import { type BufferGeometry, Vector2 } from 'three';
3
+ export declare function mapCurtainOpeningGeometryToWall(geometry: BufferGeometry, wall: WallNode): void;
4
+ export declare function curtainOpeningProfile(opening: DoorNode | WindowNode, width: number): {
5
+ inner: Vector2[];
6
+ outer: Vector2[];
7
+ };
8
+ export declare function buildCurtainOpeningFrame(opening: DoorNode | WindowNode, width: number, depth: number): {
9
+ frame: BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>;
10
+ cutter: BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>;
11
+ cutout: Vector2[];
12
+ };
13
+ export declare function curtainProfileSpan(points: readonly Vector2[], height: number): [number, number] | null;
14
+ //# sourceMappingURL=curtain-opening-frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"curtain-opening-frame.d.ts","sourceRoot":"","sources":["../../src/wall/curtain-opening-frame.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAIb,KAAK,QAAQ,EACb,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,KAAK,cAAc,EAAgC,OAAO,EAAE,MAAM,OAAO,CAAA;AAElF,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,QAsBvF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,QAAQ,GAAG,UAAU,EAAE,KAAK,EAAE,MAAM;;;EAqClF;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,QAAQ,GAAG,UAAU,EAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM;;;;EA+Bd;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,OAAO,EAAE,EAC1B,MAAM,EAAE,MAAM,GACb,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CASzB"}