@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
@@ -1,158 +1,14 @@
1
- import { getScaledDimensions, getSurfaceProvider, nodeRegistry, surfaceRegionContainsFootprint, } from '@pascal-app/core';
2
- import { boundsOf, boxCorners, frame, transformPoint } from '@pascal-app/core/procedural-items';
3
- function surfaceScene(nodes) {
4
- return {
5
- nodes: () => nodes,
6
- get: (id) => nodes[id],
7
- };
8
- }
1
+ import { hostedChildUpdates, planHostedEdit, withHostedChildren } from '../shared/hosted-resize';
2
+ const policy = {
3
+ host: (node) => node.type === 'cabinet',
4
+ child: (node) => node.type !== 'cabinet' && node.type !== 'cabinet-module',
5
+ };
9
6
  export function cabinetHostedChildUpdates(before, after) {
10
- const updates = [];
11
- for (const host of Object.values(before)) {
12
- if (host.type !== 'cabinet')
13
- continue;
14
- const children = host.children
15
- .map((id) => before[id])
16
- .filter((child) => child?.parentId === host.id &&
17
- child.type !== 'cabinet' &&
18
- child.type !== 'cabinet-module');
19
- if (!children.length)
20
- continue;
21
- const nextHost = after[host.id];
22
- if (nextHost?.type !== 'cabinet')
23
- return null;
24
- const provider = getSurfaceProvider(host);
25
- const nextSurfaces = provider.surfaces?.(nextHost, { scene: surfaceScene(after) }) ?? [];
26
- if (JSON.stringify(provider.surfaces?.(host, { scene: surfaceScene(before) })) ===
27
- JSON.stringify(nextSurfaces))
28
- continue;
29
- for (const child of children) {
30
- if (!child || !('position' in child) || !('rotation' in child))
31
- continue;
32
- const bounds = child.type === 'item'
33
- ? { size: getScaledDimensions(child), center: undefined }
34
- : nodeRegistry.get(child.type)?.capabilities.dragBounds?.(child, before);
35
- if (!bounds)
36
- continue;
37
- const center = bounds.center ?? [0, bounds.size[1] / 2, 0];
38
- const localBounds = {
39
- min: center.map((v, i) => v - bounds.size[i] / 2),
40
- max: center.map((v, i) => v + bounds.size[i] / 2),
41
- };
42
- const position = child.position;
43
- const rotation = (typeof child.rotation === 'number' ? [0, child.rotation, 0] : child.rotation);
44
- const projected = boundsOf(boxCorners(localBounds.min, localBounds.max).map((p) => transformPoint(frame(position, rotation), p)));
45
- const hit = {
46
- point: [
47
- (projected.min[0] + projected.max[0]) / 2,
48
- projected.min[1],
49
- (projected.min[2] + projected.max[2]) / 2,
50
- ],
51
- normalWorldY: 1,
52
- };
53
- const previousSurface = provider.resolveHit(host, hit, { scene: surfaceScene(before) });
54
- if (!previousSurface || Math.abs(previousSurface.position[1] - projected.min[1]) > 1e-5)
55
- continue;
56
- const nextSurface = nextSurfaces.find((surface) => surface.id === previousSurface.id);
57
- if (!nextSurface)
58
- return null;
59
- const nextPosition = [
60
- position[0],
61
- position[1] + nextSurface.position[1] - previousSurface.position[1],
62
- position[2],
63
- ];
64
- const surfaceLocal = [
65
- nextPosition[0] - nextSurface.position[0],
66
- nextPosition[1] - nextSurface.position[1],
67
- nextPosition[2] - nextSurface.position[2],
68
- ];
69
- if (!surfaceRegionContainsFootprint(nextSurface.region, surfaceLocal, bounds.size, rotation, localBounds))
70
- return null;
71
- if (Math.abs(nextPosition[1] - position[1]) > 1e-8)
72
- updates.push([child.id, { position: nextPosition }]);
73
- }
74
- }
75
- return updates;
7
+ return hostedChildUpdates(before, after, policy);
76
8
  }
77
- export function planCabinetHostedEdit(sceneApi, apply) {
78
- const before = sceneApi.nodes();
79
- const after = { ...before };
80
- const updates = new Map();
81
- let supported = true;
82
- const staged = {
83
- ...sceneApi,
84
- ...surfaceScene(after),
85
- update(id, data) {
86
- if (!after[id])
87
- return;
88
- after[id] = { ...after[id], ...data };
89
- updates.set(id, { ...updates.get(id), ...data });
90
- },
91
- upsert(node) {
92
- supported = false;
93
- return node.id;
94
- },
95
- delete() {
96
- supported = false;
97
- },
98
- markDirty() { },
99
- pauseHistory() { },
100
- resumeHistory() { },
101
- };
102
- apply(staged);
103
- if (!supported)
104
- return null;
105
- const children = cabinetHostedChildUpdates(before, after);
106
- if (!children)
107
- return null;
108
- for (const [id, data] of children)
109
- updates.set(id, data);
110
- return [...updates];
9
+ export function planCabinetHostedEdit(scene, apply) {
10
+ return planHostedEdit(scene, apply, policy);
111
11
  }
112
12
  export function withCabinetHostedChildren(descriptor) {
113
- if (descriptor.kind !== 'linear-resize')
114
- return descriptor;
115
- const plan = (node, patch, sceneApi, modifiers) => planCabinetHostedEdit(sceneApi, (staged) => {
116
- if (descriptor.commit)
117
- descriptor.commit(node, patch, staged, modifiers);
118
- else
119
- staged.update(descriptor.overrideTarget?.(node, staged) ?? node.id, patch);
120
- });
121
- const hasHostedChildren = (scene) => Object.values(scene?.nodes() ?? {}).some((node) => node.type === 'cabinet' &&
122
- node.children.some((id) => {
123
- const child = scene.get(id);
124
- return child && child.type !== 'cabinet' && child.type !== 'cabinet-module';
125
- }));
126
- return {
127
- ...descriptor,
128
- apply(node, value, sceneApi, modifiers) {
129
- const patch = descriptor.apply(node, value, sceneApi, modifiers);
130
- if (!hasHostedChildren(sceneApi) || plan(node, patch, sceneApi, modifiers))
131
- return patch;
132
- const target = sceneApi.get(descriptor.overrideTarget?.(node, sceneApi) ?? node.id) ?? node;
133
- return Object.fromEntries(Object.keys(patch).map((key) => [key, target[key]]));
134
- },
135
- previewOverrides(node, value, sceneApi, modifiers) {
136
- if (!hasHostedChildren(sceneApi))
137
- return descriptor.previewOverrides?.(node, value, sceneApi, modifiers) ?? [];
138
- const patch = descriptor.apply(node, value, sceneApi, modifiers);
139
- return plan(node, patch, sceneApi, modifiers) ?? [];
140
- },
141
- commit(node, patch, sceneApi, modifiers) {
142
- if (!hasHostedChildren(sceneApi)) {
143
- if (descriptor.commit)
144
- descriptor.commit(node, patch, sceneApi, modifiers);
145
- else
146
- sceneApi.update(descriptor.overrideTarget?.(node, sceneApi) ?? node.id, patch);
147
- return;
148
- }
149
- const updates = plan(node, patch, sceneApi, modifiers);
150
- if (!updates)
151
- return;
152
- for (const [id, data] of updates) {
153
- sceneApi.update(id, data);
154
- sceneApi.markDirty(id);
155
- }
156
- },
157
- };
13
+ return descriptor.kind === 'linear-resize' ? withHostedChildren(descriptor, policy) : descriptor;
158
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"placement-snap.d.ts","sourceRoot":"","sources":["../../src/cabinet/placement-snap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAE/C,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK9F;AAED;;wEAEwE;AACxE,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC;IAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAQnD;AAED,wBAAgB,0BAA0B,CAAC,EACzC,GAAG,EACH,UAAU,EACV,eAAwB,EACxB,GAAG,EACH,IAAI,GACL,EAAE;IACD,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAgB3B;AAED,wBAAgB,iCAAiC,CAAC,EAChD,GAAG,EACH,UAAU,EACV,eAAwB,EACxB,GAAG,EACH,IAAI,EACJ,KAAK,GACN,EAAE;IACD,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE;QAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CACzD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAwB3B"}
1
+ {"version":3,"file":"placement-snap.d.ts","sourceRoot":"","sources":["../../src/cabinet/placement-snap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAE/C,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK9F;AAED;;wEAEwE;AACxE,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC;IAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAUnD;AAED,wBAAgB,0BAA0B,CAAC,EACzC,GAAG,EACH,UAAU,EACV,eAAwB,EACxB,GAAG,EACH,IAAI,GACL,EAAE;IACD,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;CACb,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAgB3B;AAED,wBAAgB,iCAAiC,CAAC,EAChD,GAAG,EACH,UAAU,EACV,eAAwB,EACxB,GAAG,EACH,IAAI,EACJ,KAAK,GACN,EAAE;IACD,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE;QAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CACzD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAwB3B"}
@@ -13,9 +13,11 @@ export function resolveCabinetLevelPlanFrame(levelId, nodes) {
13
13
  const building = level?.parentId ? nodes[level.parentId] : undefined;
14
14
  if (building?.type !== 'building')
15
15
  return { position: [0, 0], rotationY: 0 };
16
+ const position = Array.isArray(building.position) ? building.position : [0, 0, 0];
17
+ const rotation = Array.isArray(building.rotation) ? building.rotation : [0, 0, 0];
16
18
  return {
17
- position: [building.position[0], building.position[2]],
18
- rotationY: building.rotation[1],
19
+ position: [position[0] ?? 0, position[2] ?? 0],
20
+ rotationY: rotation[1] ?? 0,
19
21
  };
20
22
  }
21
23
  export function resolveCabinetGridPosition({ raw, dimensions, footprintOffset = [0, 0], yaw, step, }) {
@@ -1,4 +1,5 @@
1
1
  import { type CabinetNode, type DeclaredHostSurface, type GeometryContext, type SurfaceProvider } from '@pascal-app/core';
2
2
  export declare function getCabinetSurfaces(node: CabinetNode, ctx?: GeometryContext): DeclaredHostSurface[];
3
3
  export declare const cabinetSurfaceProvider: SurfaceProvider;
4
+ export declare const cabinetModuleSurfaceProvider: SurfaceProvider;
4
5
  //# sourceMappingURL=surfaces.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"surfaces.d.ts","sourceRoot":"","sources":["../../src/cabinet/surfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAGpB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAA;AA8BzB,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,WAAW,EACjB,GAAG,CAAC,EAAE,eAAe,GACpB,mBAAmB,EAAE,CA0CvB;AAgBD,eAAO,MAAM,sBAAsB,EAAE,eAkCpC,CAAA"}
1
+ {"version":3,"file":"surfaces.d.ts","sourceRoot":"","sources":["../../src/cabinet/surfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAIpB,KAAK,eAAe,EACrB,MAAM,kBAAkB,CAAA;AA8BzB,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,WAAW,EACjB,GAAG,CAAC,EAAE,eAAe,GACpB,mBAAmB,EAAE,CA0CvB;AAgBD,eAAO,MAAM,sBAAsB,EAAE,eAkCpC,CAAA;AAED,eAAO,MAAM,4BAA4B,EAAE,eAU1C,CAAA"}
@@ -1,4 +1,4 @@
1
- import { pointInPolygon2D, } from '@pascal-app/core';
1
+ import { hitDerivedSurfaceProvider, pointInPolygon2D, } from '@pascal-app/core';
2
2
  import { cooktopFootprint, sinkFaucetFootprint, sinkOpening } from './appliance-layout';
3
3
  import { getCabinetCountertopLayout } from './countertop-layout';
4
4
  import { isCooktopCompartmentType } from './stack';
@@ -99,3 +99,15 @@ export const cabinetSurfaceProvider = {
99
99
  return nearest;
100
100
  },
101
101
  };
102
+ export const cabinetModuleSurfaceProvider = {
103
+ childFrame: 'host-local',
104
+ // Catalog placement has always targeted runs; standalone module hosting belongs to registry movers.
105
+ accepts: (_host, childKind) => childKind !== 'item',
106
+ resolveHit(host, hit, ctx) {
107
+ const parent = host.parentId ? ctx.scene.get(host.parentId) : undefined;
108
+ // A run owns its counter and cutouts; its module meshes must let the hit bubble.
109
+ if (parent?.type === 'cabinet')
110
+ return null;
111
+ return hitDerivedSurfaceProvider.resolveHit(host, hit, ctx);
112
+ },
113
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/cabinet/tool.tsx"],"names":[],"mappings":"AAoZA,QAAA,MAAM,WAAW,0CAy/ChB,CAAA;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/cabinet/tool.tsx"],"names":[],"mappings":"AAoZA,QAAA,MAAM,WAAW,0CA6/ChB,CAAA;AAED,eAAe,WAAW,CAAA"}
@@ -200,6 +200,7 @@ const CabinetTool = () => {
200
200
  const [placement, setPlacement] = useState(null);
201
201
  const [draftSegments, setDraftSegments] = useState([]);
202
202
  const [yaw, setYaw] = useState(0);
203
+ const toolDefaults = useEditor((state) => state.toolDefaults.cabinet);
203
204
  const placementType = useCabinetPlacementType((s) => s.type);
204
205
  const islandMode = placementType === 'island';
205
206
  const yawRef = useRef(0);
@@ -226,6 +227,7 @@ const CabinetTool = () => {
226
227
  return CabinetModuleNode.parse({
227
228
  ...cabinetModuleDefinition.defaults(),
228
229
  ...DEFAULT_PLACEMENT_PRESET.createPatch(),
230
+ ...toolDefaults,
229
231
  showPlinth: runDefaults.showPlinth,
230
232
  plinthHeight: runDefaults.plinthHeight,
231
233
  toeKickDepth: runDefaults.toeKickDepth,
@@ -234,7 +236,7 @@ const CabinetTool = () => {
234
236
  countertopOverhang: runDefaults.countertopOverhang,
235
237
  countertopBackOverhang: runDefaults.countertopBackOverhang,
236
238
  });
237
- }, []);
239
+ }, [toolDefaults]);
238
240
  const [previewSize, setPreviewSize] = useState(() => ({
239
241
  depth: previewNodeTemplate.depth,
240
242
  height: previewNodeTemplate.carcassHeight,
@@ -839,12 +841,13 @@ const CabinetTool = () => {
839
841
  const island = islandModeRef.current;
840
842
  const cabinet = CabinetNode.parse({
841
843
  ...cabinetDefinition.defaults(),
844
+ ...toolDefaults,
842
845
  name: island ? 'Kitchen Island' : 'Modular Cabinet',
843
846
  position,
844
847
  rotation: yaw,
845
848
  parentId: activeLevelId,
846
- depth: patch.depth ?? cabinetDefinition.defaults().depth,
847
- carcassHeight: patch.carcassHeight ?? cabinetDefinition.defaults().carcassHeight,
849
+ depth: previewNode.depth,
850
+ carcassHeight: previewNode.carcassHeight,
848
851
  ...(island && {
849
852
  countertopBackOverhang: ISLAND_SEATING_OVERHANG,
850
853
  withFinishedBack: true,
@@ -853,6 +856,7 @@ const CabinetTool = () => {
853
856
  const buildModule = (localX, width, index) => CabinetModuleNode.parse({
854
857
  ...cabinetModuleDefinition.defaults(),
855
858
  ...patch,
859
+ ...toolDefaults,
856
860
  name: index === 0 ? (patch.name ?? 'Base Cabinet') : `Base Cabinet ${index + 1}`,
857
861
  parentId: cabinet.id,
858
862
  position: [localX, runModuleBaseY(cabinet.plinthHeight, cabinet.showPlinth), 0],
@@ -1387,7 +1391,7 @@ const CabinetTool = () => {
1387
1391
  useAlignmentGuides.getState().clear();
1388
1392
  useCabinetPlacementStatus.getState().setBlocked(false);
1389
1393
  };
1390
- }, [activeLevelId, metricNotation, publishFloorplanPreview, unit]);
1394
+ }, [activeLevelId, metricNotation, previewNode, publishFloorplanPreview, toolDefaults, unit]);
1391
1395
  if (!activeLevelId || !placement)
1392
1396
  return null;
1393
1397
  const stretch = placement.stretch;
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/ceiling/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EAIpB,MAAM,kBAAkB,CAAA;AAmBzB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAmFtC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,CAiHhE,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/ceiling/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,cAAc,EAIpB,MAAM,kBAAkB,CAAA;AAqBzB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAmFtC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,CAqHhE,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { getCeilingClampBound, resolveCeilingHeight, useScene, } 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 { buildCeilingFloorplan } from './floorplan';
5
6
  import { ceilingAddVertexAffordance, ceilingDeleteVertexAffordance, ceilingMoveEdgeAffordance, ceilingMoveVertexAffordance, } from './floorplan-affordances';
6
7
  import { ceilingFloorplanMoveTarget } from './floorplan-move';
@@ -105,6 +106,9 @@ export const ceilingDefinition = {
105
106
  kind: 'ceiling',
106
107
  raycast: 'underside',
107
108
  },
109
+ 'pascal:editor/floorplan': {
110
+ selectionCounterparts: sameOutlineSurfaceCounterparts,
111
+ },
108
112
  },
109
113
  // Height-less on purpose: a new ceiling follows the level top until the
110
114
  // user gives it an explicit custom height.
@@ -122,6 +126,7 @@ export const ceilingDefinition = {
122
126
  capabilities: {
123
127
  selectable: { hitVolume: 'bbox' },
124
128
  surfaces: {
129
+ hosting: false,
125
130
  top: {
126
131
  height: (n) => resolveCeilingHeight(n, useScene.getState().nodes),
127
132
  },
@@ -1 +1 @@
1
- {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/ceiling/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAA;AAiBzB,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,gCAwG9D,CAAA;AAED,eAAe,eAAe,CAAA"}
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/ceiling/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,WAAW,EAOjB,MAAM,kBAAkB,CAAA;AAkBzB,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,gCA2G9D,CAAA;AAED,eAAe,eAAe,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 { getMaterialPresetByRef, resolveCeilingHeight, resolveMaterial, useLiveTransforms, useRegistry, useScene, } from '@pascal-app/core';
4
- import { createSurfaceRoleMaterial, NodeRenderer, resolveSurfaceColor, useViewer, } from '@pascal-app/viewer';
4
+ import { createSurfaceRoleMaterial, NodeRenderer, resolveSurfaceColor, useNodeEvents, useViewer, } from '@pascal-app/viewer';
5
5
  import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
6
6
  import { BackSide } from 'three/webgpu';
7
7
  import { createPlaceholderGeometry } from '../shared/placeholder-geometry';
@@ -12,6 +12,7 @@ function createEmptyGeometry() {
12
12
  }
13
13
  export const CeilingRenderer = ({ node }) => {
14
14
  const ref = useRef(null);
15
+ const handlers = useNodeEvents(node, 'ceiling');
15
16
  const placeholderGeometry = useMemo(createEmptyGeometry, []);
16
17
  const gridPlaceholderGeometry = useMemo(createEmptyGeometry, []);
17
18
  useRegistry(node.id, 'ceiling', ref);
@@ -86,6 +87,6 @@ export const CeilingRenderer = ({ node }) => {
86
87
  node.material?.properties,
87
88
  node.material?.texture,
88
89
  ]);
89
- return (_jsxs("mesh", { geometry: placeholderGeometry, material: materials.bottomMaterial, position: position, ref: ref, children: [_jsx("mesh", { geometry: gridPlaceholderGeometry, material: materials.topMaterial, name: "ceiling-grid", scale: 0, visible: false }), (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
90
+ return (_jsxs("mesh", { geometry: placeholderGeometry, material: materials.bottomMaterial, position: position, ref: ref, visible: node.visible !== false, ...handlers, children: [_jsx("mesh", { geometry: gridPlaceholderGeometry, material: materials.topMaterial, name: "ceiling-grid", scale: 0, visible: false }), (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
90
91
  };
91
92
  export default CeilingRenderer;
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/chimney/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAqUtC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,CAqEhE,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/chimney/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAqUtC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,CAsEhE,CAAA"}
@@ -365,6 +365,7 @@ export const chimneyDefinition = {
365
365
  parametrics: chimneyParametrics,
366
366
  handles: chimneyHandles,
367
367
  floorplan: buildChimneyFloorplan,
368
+ rendersChildren: false,
368
369
  renderer: {
369
370
  kind: 'parametric',
370
371
  module: () => import('./renderer'),
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/column/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAA;AAUzB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AA2TrC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,OAAO,UAAU,CAiG9D,CAAA"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/column/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAA;AAYzB,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AA4TrC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,OAAO,UAAU,CAqG9D,CAAA"}
@@ -1,12 +1,15 @@
1
1
  import { ColumnNode as ColumnNodeSchema, } from '@pascal-app/core';
2
+ import { withHostedChildren } from '../shared/hosted-resize';
2
3
  import { collectStructuralGridAxes, resolveStructuralGridSnap, } from '../structural-grid/coordination';
3
4
  import { buildColumnFloorplan, computeColumnFloorplanLevelData } from './floorplan';
4
5
  import { columnResizeAffordance, columnRotateAffordance } from './floorplan-affordances';
5
6
  import { columnFloorplanMoveTarget } from './floorplan-move';
7
+ import { columnHostedPolicy } from './hosted-resize';
6
8
  import { columnPaint } from './paint';
7
9
  import { columnParametrics } from './parametrics';
8
10
  import { ColumnNode } from './schema';
9
11
  import { columnSlots } from './slots';
12
+ import { columnSurfaceProvider } from './surface';
10
13
  // Limits + offsets shared with the in-world arrows. Mirrors the floors
11
14
  // the renderer clamps to (`Math.max(0.2, node.height)` etc.) so a drag
12
15
  // can't push values past what the renderer will accept.
@@ -288,7 +291,7 @@ function columnHandles(node) {
288
291
  handles.push(columnRotateHandle());
289
292
  if (!managedByLeanTo)
290
293
  handles.push(columnMoveHandle());
291
- return handles;
294
+ return handles.map((handle) => withHostedChildren(handle, columnHostedPolicy));
292
295
  }
293
296
  function resolveColumnStructuralGridMoveSnap({ candidatePosition, nodes, levelId, }) {
294
297
  const snap = resolveStructuralGridSnap([candidatePosition[0], candidatePosition[2]], collectStructuralGridAxes(nodes, levelId));
@@ -325,9 +328,13 @@ export const columnDefinition = {
325
328
  return rest;
326
329
  },
327
330
  capabilities: {
331
+ surfacePlacement: 'floor-only',
328
332
  selectable: { hitVolume: 'bbox' },
329
- surfaces: { top: { height: (node) => node.height } },
330
- duplicable: true,
333
+ surfaces: {
334
+ top: { height: (node) => node.height },
335
+ hosting: columnSurfaceProvider,
336
+ },
337
+ duplicable: { subtree: true },
331
338
  deletable: true,
332
339
  // Generic 3D translate-on-XZ via `MoveRegistryNodeTool` (grid snap + the
333
340
  // mode-driven snapping the overhaul standardised). 2D move keeps using
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan-affordances.d.ts","sourceRoot":"","sources":["../../src/column/floorplan-affordances.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,mBAAmB,EAGzB,MAAM,kBAAkB,CAAA;AAazB;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EACC,OAAO,GACP,OAAO,GACP,SAAS,GACT,QAAQ,GACR,aAAa,GACb,aAAa,GACb,qBAAqB,GACrB,kBAAkB,CAAA;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC3B,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,UAAU,CAoFlE,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,UAAU,CAgClE,CAAA"}
1
+ {"version":3,"file":"floorplan-affordances.d.ts","sourceRoot":"","sources":["../../src/column/floorplan-affordances.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,mBAAmB,EAGzB,MAAM,kBAAkB,CAAA;AAczB;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,EACC,OAAO,GACP,OAAO,GACP,SAAS,GACT,QAAQ,GACR,aAAa,GACb,aAAa,GACb,qBAAqB,GACrB,kBAAkB,CAAA;IACtB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC3B,CAAA;AAED,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,UAAU,CAsFlE,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,EAAE,mBAAmB,CAAC,UAAU,CAgClE,CAAA"}
@@ -1,6 +1,7 @@
1
1
  import { useLiveNodeOverrides, useScene, } from '@pascal-app/core';
2
2
  import { isAngleSnapActive } from '@pascal-app/editor';
3
3
  import { rotateAffordanceDelta } from '../shared/rotate-affordance';
4
+ import { planColumnEdit } from './hosted-resize';
4
5
  // Floor minimums — mirror the 3D handles in `column/definition.ts` so a
5
6
  // drag can't push a value past what the renderer accepts.
6
7
  const MIN_COLUMN_WIDTH = 0.1;
@@ -24,6 +25,8 @@ export const columnResizeAffordance = {
24
25
  const initialBraceTopSpread = node.braceTopSpread ?? 0.12;
25
26
  let lastPatch = {};
26
27
  const previewPatch = (patch) => {
28
+ if (!planColumnEdit(columnId, patch))
29
+ return;
27
30
  lastPatch = patch;
28
31
  useLiveNodeOverrides.getState().set(columnId, patch);
29
32
  useScene.getState().markDirty(columnId);
@@ -82,7 +85,9 @@ export const columnResizeAffordance = {
82
85
  commit() {
83
86
  if (Object.keys(lastPatch).length > 0) {
84
87
  useLiveNodeOverrides.getState().clear(columnId);
85
- useScene.getState().updateNode(columnId, lastPatch);
88
+ const updates = planColumnEdit(columnId, lastPatch);
89
+ if (updates)
90
+ useScene.getState().updateNodes(updates.map(([id, data]) => ({ id, data })));
86
91
  }
87
92
  },
88
93
  };
@@ -1 +1 @@
1
- {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/column/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,kBAAkB,EACnB,MAAM,kBAAkB,CAAA;AAsBzB,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,EAAE,kBAAkB,EAAE,CAAA;CACtC,CAAA;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,SAAS,UAAU,EAAE,CAAA;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B,GAAG,wBAAwB,CAI3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,eAAe,GACnB,iBAAiB,GAAG,IAAI,CAiL1B;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,EAAE,CAE9E"}
1
+ {"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/column/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,kBAAkB,EACnB,MAAM,kBAAkB,CAAA;AAsBzB,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,EAAE,kBAAkB,EAAE,CAAA;CACtC,CAAA;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,SAAS,UAAU,EAAE,CAAA;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B,GAAG,wBAAwB,CAI3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,UAAU,EAChB,GAAG,EAAE,eAAe,GACnB,iBAAiB,GAAG,IAAI,CA6L1B;AAED,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,EAAE,CAE9E"}
@@ -40,17 +40,29 @@ export function buildColumnFloorplan(node, ctx) {
40
40
  const showSelectedChrome = isSelected || isHighlighted;
41
41
  const stroke = showSelectedChrome && palette ? palette.selectedStroke : '#374151';
42
42
  const fill = showSelectedChrome ? '#fed7aa' : '#9ca3af';
43
- const children = [
44
- {
45
- kind: 'polygon',
46
- points,
47
- fill,
48
- stroke,
49
- strokeWidth: showSelectedChrome ? 0.03 : 0.02,
50
- opacity: 0.92,
51
- metadata: floorplanGeometryMetadata({ annotationObstacle: 'bounds' }),
52
- },
53
- ];
43
+ const body = {
44
+ kind: 'polygon',
45
+ points,
46
+ fill,
47
+ stroke,
48
+ strokeWidth: showSelectedChrome ? 0.03 : 0.02,
49
+ opacity: 0.92,
50
+ metadata: floorplanGeometryMetadata({ annotationObstacle: 'bounds' }),
51
+ };
52
+ const children = node.children.length
53
+ ? [
54
+ // Occupied columns keep label bounds in the overlay without covering their children.
55
+ {
56
+ kind: 'polygon',
57
+ points,
58
+ fill: 'none',
59
+ stroke: 'none',
60
+ pointerEvents: 'none',
61
+ metadata: body.metadata,
62
+ },
63
+ { ...body, metadata: undefined },
64
+ ]
65
+ : [body];
54
66
  const { halfX, halfZ } = columnPlanHalfExtents(node);
55
67
  const centerMarkHalf = Math.min(0.09, Math.max(0.035, Math.min(halfX, halfZ) * 0.45));
56
68
  const centerX = node.position[0];
@@ -0,0 +1,116 @@
1
+ import { type AnyNode, type AnyNodeId } from '@pascal-app/core';
2
+ export declare const columnHostedPolicy: {
3
+ host: (node: AnyNode) => node is {
4
+ object: "node";
5
+ style: "plain" | "faceted" | "fluted" | "lathe-turned" | "dravidian-carved" | "cluster";
6
+ metadata: Record<string, unknown>;
7
+ id: `column_${string}`;
8
+ parentId: string | null;
9
+ visible: boolean;
10
+ position: [number, number, number];
11
+ rotation: number;
12
+ children: string[];
13
+ width: number;
14
+ depth: number;
15
+ radius: number;
16
+ height: number;
17
+ crossSection: "round" | "square" | "rectangular" | "octagonal" | "sixteen-sided";
18
+ edgeSoftness: number;
19
+ baseHeight: number;
20
+ capitalHeight: number;
21
+ shaftProfile: "straight" | "tapered" | "bulged" | "baluster" | "hourglass";
22
+ shaftTaper: number;
23
+ shaftBulge: number;
24
+ shaftStartScale: number;
25
+ shaftEndScale: number;
26
+ shaftSegmentCount: number;
27
+ shaftTwistStep: number;
28
+ shaftCornerRadius: number;
29
+ shaftDetail: "none" | "fluted" | "lathe-turned" | "spiral" | "panelled";
30
+ baseStyle: "none" | "simple-square" | "round-rings" | "square-plinth" | "stepped-square" | "lotus" | "ribbed-lotus" | "panelled-pedestal";
31
+ baseWidthScale: number;
32
+ baseDepthScale: number;
33
+ baseTierCount: number;
34
+ baseStepSpread: number;
35
+ basePlinthHeightRatio: number;
36
+ baseRoundBandScale: number;
37
+ baseNeckScale: number;
38
+ baseRoundBandCount: number;
39
+ baseRibCount: number;
40
+ baseCarvingLevel: number;
41
+ basePanelInset: number;
42
+ capitalStyle: "none" | "simple" | "simple-slab" | "rounded" | "stepped" | "doric" | "volute" | "ionic-volute" | "leaf-carved" | "corinthian-leaf" | "south-indian-bracket" | "wood-bracket";
43
+ capitalWidthScale: number;
44
+ capitalDepthScale: number;
45
+ capitalTierCount: number;
46
+ capitalStepSpread: number;
47
+ capitalBandCount: number;
48
+ voluteSize: number;
49
+ voluteCount: number;
50
+ leafCount: number;
51
+ leafRows: number;
52
+ bracketDepth: number;
53
+ bracketTierCount: number;
54
+ pendantCount: number;
55
+ capitalCarvingLevel: number;
56
+ ringCount: number;
57
+ ringPlacement: "top" | "ends" | "even" | "bottom";
58
+ ringThickness: number;
59
+ ringSpread: number;
60
+ fluteCount: number;
61
+ fluteDepth: number;
62
+ fluteWidth: number;
63
+ spiralTwist: number;
64
+ spiralRibCount: number;
65
+ panelCount: number;
66
+ panelInsetDepth: number;
67
+ panelShape: "rectangle" | "arched" | "diamond";
68
+ latheRingCount: number;
69
+ latheRingSpacing: "top" | "ends" | "even" | "bottom";
70
+ carvingLevel: number;
71
+ carvingPlacement: "base" | "shaft" | "capital" | "all";
72
+ lowerBandEnabled: boolean;
73
+ lowerBandHeight: number;
74
+ lowerBandCarvingLevel: number;
75
+ dentilCount: number;
76
+ beadCount: number;
77
+ supportStyle: "vertical" | "a-frame" | "y-frame" | "v-frame" | "x-brace" | "k-brace" | "single-strut" | "tripod" | "trestle" | "portal-frame" | "box-frame";
78
+ braceWidth: number;
79
+ braceDepth: number;
80
+ braceBottomSpread: number;
81
+ braceTopSpread: number;
82
+ bracePlateEnabled: boolean;
83
+ type: "column";
84
+ material?: {
85
+ id?: string | undefined;
86
+ preset?: "custom" | "white" | "brick" | "concrete" | "wood" | "glass" | "metal" | "plaster" | "tile" | "marble" | undefined;
87
+ properties?: {
88
+ color: string;
89
+ roughness: number;
90
+ metalness: number;
91
+ opacity: number;
92
+ transparent: boolean;
93
+ side: "front" | "back" | "double";
94
+ } | undefined;
95
+ texture?: {
96
+ url: string;
97
+ repeat?: [number, number] | undefined;
98
+ scale?: number | undefined;
99
+ } | undefined;
100
+ } | undefined;
101
+ name?: string | undefined;
102
+ camera?: {
103
+ position: [number, number, number];
104
+ target: [number, number, number];
105
+ mode: "perspective" | "orthographic";
106
+ fov?: number | undefined;
107
+ zoom?: number | undefined;
108
+ } | undefined;
109
+ materialPreset?: string | undefined;
110
+ slots?: Record<string, string> | undefined;
111
+ supportSlabId?: string | undefined;
112
+ };
113
+ child: (_node: AnyNode) => boolean;
114
+ };
115
+ export declare function planColumnEdit(id: AnyNodeId, patch: Partial<AnyNode>): import("../shared/hosted-resize").HostedUpdate[] | null;
116
+ //# sourceMappingURL=hosted-resize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-resize.d.ts","sourceRoot":"","sources":["../../src/column/hosted-resize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAA4B,MAAM,kBAAkB,CAAA;AAGzF,eAAO,MAAM,kBAAkB;iBAChB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBACL,OAAO;CACvB,CAAA;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,2DAMpE"}
@@ -0,0 +1,9 @@
1
+ import { createSceneApi, useScene } from '@pascal-app/core';
2
+ import { planHostedEdit } from '../shared/hosted-resize';
3
+ export const columnHostedPolicy = {
4
+ host: (node) => node.type === 'column',
5
+ child: (_node) => true,
6
+ };
7
+ export function planColumnEdit(id, patch) {
8
+ return planHostedEdit(createSceneApi(useScene), (scene) => scene.update(id, patch), columnHostedPolicy);
9
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/column/panel.tsx"],"names":[],"mappings":"AAgSA,MAAM,CAAC,OAAO,UAAU,WAAW,uCAo1BlC"}
1
+ {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/column/panel.tsx"],"names":[],"mappings":"AAkSA,MAAM,CAAC,OAAO,UAAU,WAAW,uCAq1BlC"}