@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
@@ -121,6 +121,7 @@ export const MoveWallTool = ({ node }) => {
121
121
  const levelId = node.parentId ?? null;
122
122
  pauseSceneHistory(useScene);
123
123
  let shouldRestoreOnCleanup = true;
124
+ let active = true;
124
125
  // Wall ids that currently carry a live position override. Cleared on commit
125
126
  // (after the final store write lands) and on cancel / external unmount.
126
127
  const touchedWallIds = new Set();
@@ -284,6 +285,13 @@ export const MoveWallTool = ({ node }) => {
284
285
  return linkedOriginalsRef.current.map((wall) => movedById.get(wall.id) ?? { id: wall.id, start: wall.start, end: wall.end });
285
286
  };
286
287
  const applyPreview = (nextStart, nextEnd) => {
288
+ const previous = previewRef.current ?? { start: originalStart, end: originalEnd };
289
+ if (nextStart[0] === previous.start[0] &&
290
+ nextStart[1] === previous.start[1] &&
291
+ nextEnd[0] === previous.end[0] &&
292
+ nextEnd[1] === previous.end[1])
293
+ return;
294
+ hasDraggedRef.current = true;
287
295
  previewRef.current = { start: nextStart, end: nextEnd };
288
296
  const centerX = (nextStart[0] + nextEnd[0]) / 2;
289
297
  const centerZ = (nextStart[1] + nextEnd[1]) / 2;
@@ -324,15 +332,24 @@ export const MoveWallTool = ({ node }) => {
324
332
  clearSurfaceOverrides();
325
333
  };
326
334
  const onGridMove = (event) => {
335
+ if (!active)
336
+ return;
327
337
  const rawX = event.localPosition[0];
328
338
  const rawZ = event.localPosition[2];
329
339
  const snapStep = getSegmentGridStep();
330
340
  // Anchor at the raw cursor so the displacement is measured in
331
341
  // continuous space.
332
342
  const anchor = dragAnchorRef.current ?? [rawX, rawZ];
343
+ const firstMove = dragAnchorRef.current === null;
333
344
  dragAnchorRef.current = anchor;
345
+ // Seeding a controller grab is not movement; don't snap an off-grid wall
346
+ // to the lattice before the user has moved their hand.
347
+ if (firstMove)
348
+ return;
334
349
  const rawDeltaX = rawX - anchor[0];
335
350
  const rawDeltaZ = rawZ - anchor[1];
351
+ if (!hasDraggedRef.current && Math.hypot(rawDeltaX, rawDeltaZ) < 1e-6)
352
+ return;
336
353
  // When the move is axis-locked (side-handle drag), snap the wall
337
354
  // center's absolute perpendicular offset to a multiple of
338
355
  // `snapStep`. For axis-aligned walls this puts the wall on grid
@@ -378,7 +395,6 @@ export const MoveWallTool = ({ node }) => {
378
395
  previousGridPosRef.current = constrainedGridPos;
379
396
  const nextCenter = [originalCenter[0] + deltaX, originalCenter[1] + deltaZ];
380
397
  const nextWall = buildWallFromCenter(nextCenter);
381
- hasDraggedRef.current = true;
382
398
  applyPreview(nextWall.start, nextWall.end);
383
399
  };
384
400
  const commitPreview = () => {
@@ -459,6 +475,9 @@ export const MoveWallTool = ({ node }) => {
459
475
  exitMoveMode();
460
476
  };
461
477
  const onPointerUp = () => {
478
+ if (!active)
479
+ return;
480
+ active = false;
462
481
  // Press-release without drag: dismiss the tool without committing.
463
482
  // This is the same UX as MoveWallEndpointTool / WallHeightArrowHandle
464
483
  // — pointer-down on the affordance starts the move, drag updates the
@@ -496,6 +515,9 @@ export const MoveWallTool = ({ node }) => {
496
515
  applyPreview(nextWall.start, nextWall.end);
497
516
  };
498
517
  const onCancel = () => {
518
+ if (!active)
519
+ return;
520
+ active = false;
499
521
  shouldRestoreOnCleanup = false;
500
522
  restoreOriginal();
501
523
  useViewer.getState().setSelection({ selectedIds: [nodeId] });
@@ -1 +1 @@
1
- {"version":3,"file":"paint.d.ts","sourceRoot":"","sources":["../../src/wall/paint.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,eAAe,EAOpB,KAAK,QAAQ,EAGd,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAA2C,KAAK,GAAG,EAAa,MAAM,OAAO,CAAA;AAqDpF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,IAAI,EAAE,QAAQ,CAAA;IACd,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,CAAA;IAC/C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IACrD,aAAa,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IAC5D,GAAG,CAAC,EAAE,GAAG,CAAA;CACV,GAAG,MAAM,GAAG,IAAI,CA+DhB;AAqCD;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,eAoCtB,CAAA"}
1
+ {"version":3,"file":"paint.d.ts","sourceRoot":"","sources":["../../src/wall/paint.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,eAAe,EAOpB,KAAK,QAAQ,EAGd,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAA2C,KAAK,GAAG,EAAa,MAAM,OAAO,CAAA;AAqDpF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,IAAI,EAAE,QAAQ,CAAA;IACd,SAAS,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,CAAA;IAC/C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IACrD,aAAa,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IAC5D,GAAG,CAAC,EAAE,GAAG,CAAA;CACV,GAAG,MAAM,GAAG,IAAI,CAwEhB;AAsCD;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,eA8DtB,CAAA"}
@@ -1,4 +1,4 @@
1
- import { getEffectiveWallSurfaceMaterial, getWallBandSlotId, getWallFaceBandConfig, getWallFaceBandForHeight, getWallSurfaceSideFromBandSlot, parseMaterialRef, sceneRegistry, useScene, WALL_SURFACE_SLOT_DEFAULTS, } from '@pascal-app/core';
1
+ import { getCurtainWallConfig, getEffectiveWallSurfaceMaterial, getWallBandSlotId, getWallFaceBandConfig, getWallFaceBandForHeight, getWallSurfaceSideFromBandSlot, parseMaterialRef, sceneRegistry, useScene, WALL_SURFACE_SLOT_DEFAULTS, } from '@pascal-app/core';
2
2
  import { setSurfaceRaycastLayers } from '@pascal-app/viewer';
3
3
  import { Raycaster } from 'three';
4
4
  import { buildSlotPreviewMaterial, createSlotPaintCapability, previewSlotByUserData, } from '../shared/slot-paint';
@@ -57,6 +57,14 @@ function resolveWallSlotByRay(node, ray) {
57
57
  */
58
58
  export function resolveWallRole(args) {
59
59
  const { node, hitObject, materialIndex, normal, localPosition, ray } = args;
60
+ if (node.wallType === 'curtain') {
61
+ const root = sceneRegistry.nodes.get(node.id);
62
+ if (!(root && ray))
63
+ return null;
64
+ wallSlotRaycaster.ray.copy(ray);
65
+ const hit = wallSlotRaycaster.intersectObject(root, false)[0];
66
+ return (['curtain-frame', 'curtain-glass', 'curtain-solid'][hit?.face?.materialIndex ?? -1] ?? null);
67
+ }
60
68
  const directSlotId = hitObject?.userData?.slotId;
61
69
  if (typeof directSlotId === 'string' && WALL_SLOT_IDS.has(directSlotId)) {
62
70
  return directSlotId;
@@ -113,11 +121,12 @@ export function resolveWallRole(args) {
113
121
  */
114
122
  function applyWallPreview(args) {
115
123
  const { role, material, materialPreset } = args;
116
- if (!(role in WALL_ARRAY_SLOT_INDEX)) {
124
+ const curtainIndex = ['curtain-frame', 'curtain-glass', 'curtain-solid'].indexOf(role);
125
+ if (curtainIndex < 0 && !(role in WALL_ARRAY_SLOT_INDEX)) {
117
126
  return previewSlotByUserData(args);
118
127
  }
119
- const index = WALL_ARRAY_SLOT_INDEX[role];
120
- if (!index)
128
+ const index = curtainIndex >= 0 ? curtainIndex : WALL_ARRAY_SLOT_INDEX[role];
129
+ if (index === undefined)
121
130
  return previewSlotByUserData(args);
122
131
  const mesh = sceneRegistry.nodes.get(args.node.id);
123
132
  if (!(mesh && mesh.isMesh))
@@ -155,6 +164,32 @@ export const wallPaint = createSlotPaintCapability({
155
164
  }),
156
165
  applyPreview: applyWallPreview,
157
166
  legacyEffective: (node, role) => {
167
+ if (node.type === 'wall' && node.wallType === 'curtain') {
168
+ const config = getCurtainWallConfig(node);
169
+ const color = role === 'curtain-frame'
170
+ ? config.frameColor
171
+ : role === 'curtain-glass'
172
+ ? config.glassColor
173
+ : role === 'curtain-solid'
174
+ ? config.solidColor
175
+ : undefined;
176
+ if (!color)
177
+ return null;
178
+ return {
179
+ materialPreset: undefined,
180
+ material: {
181
+ preset: 'custom',
182
+ properties: {
183
+ color,
184
+ roughness: role === 'curtain-glass' ? config.glassRoughness : 0.4,
185
+ metalness: role === 'curtain-frame' ? 0.65 : 0,
186
+ opacity: role === 'curtain-glass' ? config.glassOpacity : 1,
187
+ transparent: role === 'curtain-glass',
188
+ side: 'front',
189
+ },
190
+ },
191
+ };
192
+ }
158
193
  const side = getWallSurfaceSideFromBandSlot(role);
159
194
  if (!side && role in WALL_SURFACE_SLOT_DEFAULTS) {
160
195
  return {
@@ -0,0 +1,4 @@
1
+ import { type AnyNode, type AnyNodeId, type WallNode } from '@pascal-app/core';
2
+ import { type PanelRow } from '@pascal-app/editor';
3
+ export declare function wallSettings(node: WallNode, nodes: Record<AnyNodeId, AnyNode>, update: (patch: Partial<WallNode>) => void, reshape?: () => void): PanelRow[];
4
+ //# sourceMappingURL=panel-model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"panel-model.d.ts","sourceRoot":"","sources":["../../src/wall/panel-model.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,SAAS,EAad,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAEL,KAAK,QAAQ,EAGd,MAAM,oBAAoB,CAAA;AAiB3B,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EACjC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,KAAK,IAAI,EAC1C,OAAO,GAAE,MAAM,IAId,GACA,QAAQ,EAAE,CAmNZ"}
@@ -0,0 +1,117 @@
1
+ import { buildWallFaceBandCountPatch, GROUND_SUPPORT_ID, getClampedWallCurveOffset, getMaxWallCurveOffset, getWallCurveLength, getWallFaceBandConfig, normalizeWallCurveOffset, terrainSupportLift, WALL_CHAIR_RAIL_DEFAULT, WALL_CROWN_DEFAULT, WALL_FACE_BAND_DEFAULT, WALL_SKIRTING_DEFAULT, } from '@pascal-app/core';
2
+ import { curveReshapeScope, triggerSFX, useInteractionScope, } from '@pascal-app/editor';
3
+ import { useViewer } from '@pascal-app/viewer';
4
+ import { resolveWallOpeningCeiling } from '../shared/wall-opening-ceiling';
5
+ import { hasWallCurveBlockingChildren } from './curve-eligibility';
6
+ import { buildWallLengthPatch } from './length-patch';
7
+ const profiles = {
8
+ skirting: ['flat', 'base-modern', 'base-colonial', 'base-shoe', 'base-ogee'],
9
+ crown: ['flat', 'crown-cove', 'crown-ogee', 'crown-craftsman', 'crown-layered'],
10
+ chairRail: ['flat', 'rail-rounded', 'rail-ogee', 'rail-picture', 'rail-stepped'],
11
+ };
12
+ const pretty = (value) => value
13
+ .replace(/^(base|crown|rail)-/, '')
14
+ .replace(/(^|[- ])\w/g, (s) => s.replace('-', ' ').toUpperCase());
15
+ export function wallSettings(node, nodes, update, reshape = () => {
16
+ triggerSFX('sfx:item-pick');
17
+ useInteractionScope.getState().begin(curveReshapeScope(node.id));
18
+ useViewer.getState().setSelection({ selectedIds: [] });
19
+ }) {
20
+ const rows = [];
21
+ const height = resolveWallOpeningCeiling(node, nodes);
22
+ const repairBase = () => ({
23
+ supportOffset: undefined,
24
+ ...(node.supportSlabId === GROUND_SUPPORT_ID &&
25
+ !(node.parentId && terrainSupportLift(nodes, node.parentId, ...node.start) != null)
26
+ ? { supportSlabId: undefined }
27
+ : {}),
28
+ });
29
+ const number = (section, id, label, value, min, max, step, onChange, unit = 'm') => rows.push({
30
+ section,
31
+ id,
32
+ kind: 'stepper',
33
+ label,
34
+ value,
35
+ min,
36
+ max,
37
+ step,
38
+ unit,
39
+ onChange: (next) => onChange(Math.max(min, Math.min(max, next))),
40
+ });
41
+ const choice = (section, id, label, value, onSelect) => rows.push({ section, id, kind: 'choice', label, value, onSelect });
42
+ const cycle = (section, id, label, value, values, onChange) => {
43
+ const change = (direction) => onChange(values[(Math.max(0, values.indexOf(value)) + direction + values.length) % values.length]);
44
+ rows.push({
45
+ section,
46
+ id,
47
+ kind: 'cycle',
48
+ label,
49
+ value: pretty(value),
50
+ next: () => change(1),
51
+ previous: () => change(-1),
52
+ });
53
+ };
54
+ const length = getWallCurveLength(node);
55
+ number('Dimensions', 'wall-length', 'Length', length, 0.1, 1000, 0.05, (next) => {
56
+ update(buildWallLengthPatch(node, next));
57
+ });
58
+ choice('Dimensions', 'wall-top', 'Top', node.height == null ? 'Follows level' : 'Custom height', () => update(node.height == null
59
+ ? { height: Math.max(0.1, height) }
60
+ : { height: undefined, ...repairBase() }));
61
+ if (node.height == null)
62
+ choice('Dimensions', 'wall-height-auto', 'Current height', `${Number(height.toFixed(3))} m`);
63
+ else
64
+ number('Dimensions', 'height', 'Height', node.height, 0.1, 1000, 0.05, (height) => update({ height }));
65
+ choice('Dimensions', 'wall-bottom', 'Bottom', node.fillToTerrain ? 'Fill to terrain' : 'Auto', () => update(node.fillToTerrain
66
+ ? { fillToTerrain: undefined, ...repairBase() }
67
+ : { fillToTerrain: true }));
68
+ number('Dimensions', 'thickness', 'Thickness', node.thickness ?? 0.1, 0.05, 1000, 0.01, (thickness) => update({ thickness }));
69
+ const blockedCurve = hasWallCurveBlockingChildren((node.children ?? []).flatMap((id) => nodes[id] ? [nodes[id]] : []));
70
+ if (!blockedCurve) {
71
+ const limit = Math.max(0.01, getMaxWallCurveOffset(node));
72
+ number('Dimensions', 'curveOffset', 'Curve', getClampedWallCurveOffset(node), -limit, limit, 0.05, (next) => update({ curveOffset: normalizeWallCurveOffset(node, next) }));
73
+ if (reshape)
74
+ rows.push({
75
+ section: 'Dimensions',
76
+ id: 'wall-reshape',
77
+ kind: 'action',
78
+ label: 'Reshape curve in scene',
79
+ onSelect: reshape,
80
+ });
81
+ }
82
+ const bands = getWallFaceBandConfig(node, height);
83
+ number('Wall bands', 'wall-band-count', 'Bands', bands.count, 1, 4, 1, (count) => update(buildWallFaceBandCountPatch(node, Math.round(count))), '');
84
+ const band = (key, label, max) => number('Wall bands', `wall-band-${key}`, label, bands[key], 0, Math.max(0, max), 0.01, (value) => update({
85
+ faceBands: {
86
+ ...WALL_FACE_BAND_DEFAULT,
87
+ ...node.faceBands,
88
+ enabled: bands.count > 1,
89
+ count: bands.count,
90
+ [key]: value,
91
+ },
92
+ }));
93
+ if (bands.count >= 2)
94
+ band('lowerHeight', 'Lower', height);
95
+ if (bands.count >= 3)
96
+ band('middleHeight', 'Middle', height - bands.lowerHeight);
97
+ if (bands.count >= 4)
98
+ band('upperHeight', 'Upper', height - bands.lowerHeight - bands.middleHeight);
99
+ for (const [key, title, defaults] of [
100
+ ['skirting', 'Skirting', WALL_SKIRTING_DEFAULT],
101
+ ['crown', 'Crown molding', WALL_CROWN_DEFAULT],
102
+ ['chairRail', 'Chair rail', WALL_CHAIR_RAIL_DEFAULT],
103
+ ]) {
104
+ const trim = { ...defaults, ...node[key] };
105
+ const patch = (value) => update({ [key]: { ...trim, ...value } });
106
+ choice(title, `${key}-enabled`, title, trim.enabled ? 'On' : 'Off', () => patch({ enabled: !trim.enabled }));
107
+ if (!trim.enabled)
108
+ continue;
109
+ cycle(title, `${key}-sides`, 'Sides', trim.sides, ['interior', 'exterior', 'both'], (sides) => patch({ sides: sides }));
110
+ cycle(title, `${key}-profile`, 'Profile', trim.profile, profiles[key], (profile) => patch({ profile: profile }));
111
+ number(title, `${key}-height`, 'Height', trim.height, 0.01, Math.max(0.05, height), 0.01, (height) => patch({ height }));
112
+ number(title, `${key}-proud`, 'Projection', trim.proud, 0.001, 0.2, 0.005, (proud) => patch({ proud }));
113
+ if (key === 'chairRail')
114
+ number(title, `${key}-offset`, 'Offset', trim.offsetY ?? 0, 0, Math.max(0.05, height - trim.height), 0.01, (offsetY) => patch({ offsetY }));
115
+ }
116
+ return rows;
117
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/wall/panel.tsx"],"names":[],"mappings":"AA0FA,MAAM,CAAC,OAAO,UAAU,SAAS,uCA8ThC"}
1
+ {"version":3,"file":"panel.d.ts","sourceRoot":"","sources":["../../src/wall/panel.tsx"],"names":[],"mappings":"AA6FA,MAAM,CAAC,OAAO,UAAU,SAAS,uCAiWhC"}
@@ -4,9 +4,12 @@ import { buildWallFaceBandCountPatch, GROUND_SUPPORT_ID, getClampedWallCurveOffs
4
4
  import { ActionButton, ActionGroup, curveReshapeScope, formatLinearMeasurement, getLinearUnitLabel, linearControlValueToMeters, metersToLinearUnit, PanelSection, PanelWrapper, SegmentedControl, SliderControl, triggerSFX, useInteractionScope, } from '@pascal-app/editor';
5
5
  import { useViewer } from '@pascal-app/viewer';
6
6
  import { Spline } from 'lucide-react';
7
- import { useCallback, useMemo, useRef } from 'react';
7
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
8
8
  import { resolveWallOpeningCeiling } from '../shared/wall-opening-ceiling';
9
+ import { CurtainWallPanel } from './curtain-wall-panel';
9
10
  import { hasWallCurveBlockingChildren } from './curve-eligibility';
11
+ import { buildWallLengthPatch } from './length-patch';
12
+ import { createWallPropertyPreview } from './property-preview';
10
13
  /**
11
14
  * Base half of the plane-bound repair: a stamped draft offset goes, and a
12
15
  * ground host is dropped unless sculpted terrain actually supports it — a
@@ -90,28 +93,18 @@ export default function WallPanel() {
90
93
  // "Maximum update depth exceeded" cascade. Same fix in fence-panel.tsx.
91
94
  const nodeRef = useRef(node);
92
95
  nodeRef.current = node;
93
- const handleUpdate = useCallback((updates) => {
94
- if (!selectedId)
95
- return;
96
- useScene.getState().updateNode(selectedId, updates);
97
- }, [selectedId]);
96
+ const propertyPreview = useMemo(() => (selectedId ? createWallPropertyPreview(selectedId) : undefined), [selectedId]);
97
+ useEffect(() => () => propertyPreview?.cancel(), [propertyPreview]);
98
+ const handleUpdate = useCallback((updates) => propertyPreview?.commit(updates), [propertyPreview]);
99
+ const handlePreview = useCallback((updates) => propertyPreview?.preview(updates), [propertyPreview]);
100
+ const handleCommit = useCallback(() => propertyPreview?.commit(), [propertyPreview]);
101
+ const handleCancel = useCallback(() => propertyPreview?.cancel(), [propertyPreview]);
98
102
  const handleUpdateLength = useCallback((newLength) => {
99
103
  const n = nodeRef.current;
100
104
  if (!n || newLength <= 0)
101
105
  return;
102
- const dx = n.end[0] - n.start[0];
103
- const dz = n.end[1] - n.start[1];
104
- const currentLength = Math.sqrt(dx * dx + dz * dz);
105
- if (currentLength === 0)
106
- return;
107
- const dirX = dx / currentLength;
108
- const dirZ = dz / currentLength;
109
- const newEnd = [
110
- n.start[0] + dirX * newLength,
111
- n.start[1] + dirZ * newLength,
112
- ];
113
- handleUpdate({ end: newEnd });
114
- }, [handleUpdate]);
106
+ handlePreview(buildWallLengthPatch(n, newLength));
107
+ }, [handlePreview]);
115
108
  const handleTopModeChange = useCallback((mode) => {
116
109
  const n = nodeRef.current;
117
110
  if (!n)
@@ -174,25 +167,28 @@ export default function WallPanel() {
174
167
  const skirting = { ...WALL_SKIRTING_DEFAULT, ...(node.skirting ?? {}) };
175
168
  const crown = { ...WALL_CROWN_DEFAULT, ...(node.crown ?? {}) };
176
169
  const chairRail = { ...WALL_CHAIR_RAIL_DEFAULT, ...(node.chairRail ?? {}) };
177
- return (_jsxs(PanelWrapper, { icon: "/icons/wall.webp", onClose: handleClose, title: node.name || 'Wall', width: 280, children: [_jsxs(PanelSection, { title: "Dimensions", children: [_jsx(SliderControl, { label: "Length", max: metersToLinearUnit(1000, unit), min: metersToLinearUnit(0.1, unit), onChange: (value) => handleUpdateLength(linearControlValueToMeters(value, unit, { maxMeters: 1000, minMeters: 0.1 })), precision: 2, step: unit === 'imperial' ? 0.1 : 0.01, unit: unitLabel, value: displayLength }), _jsx("div", { className: "px-1 font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Top" }), _jsx(SegmentedControl, { onChange: handleTopModeChange, options: [
170
+ return (_jsxs(PanelWrapper, { icon: "/icons/wall.webp", onClose: handleClose, title: node.name || 'Wall', width: 280, children: [_jsx(PanelSection, { title: "Wall type", children: _jsx(SegmentedControl, { onChange: (wallType) => handleUpdate({ wallType }), options: [
171
+ { label: 'Standard', value: 'standard' },
172
+ { label: 'Curtain wall', value: 'curtain' },
173
+ ], value: node.wallType ?? 'standard' }) }), _jsxs(PanelSection, { title: "Dimensions", children: [_jsx(SliderControl, { onCommit: handleCommit, onCancel: handleCancel, restoreOnCommit: false, previewWhileTyping: true, label: "Length", max: metersToLinearUnit(1000, unit), min: metersToLinearUnit(0.1, unit), onChange: (value) => handleUpdateLength(linearControlValueToMeters(value, unit, { maxMeters: 1000, minMeters: 0.1 })), precision: 2, step: unit === 'imperial' ? 0.1 : 0.01, unit: unitLabel, value: displayLength }), _jsx("div", { className: "px-1 font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Top" }), _jsx(SegmentedControl, { onChange: handleTopModeChange, options: [
178
174
  { label: 'Follows level', value: 'storey' },
179
175
  { label: 'Custom height', value: 'custom' },
180
- ], value: isPlaneBound ? 'storey' : 'custom' }), isPlaneBound ? (_jsxs("div", { className: "px-1 text-[11px] text-muted-foreground", children: ["Currently ", formatLinearMeasurement(height, unit)] })) : (_jsx(SliderControl, { label: "Height", max: metersToLinearUnit(1000, unit), min: metersToLinearUnit(0.1, unit), onChange: (v) => handleUpdate({
176
+ ], value: isPlaneBound ? 'storey' : 'custom' }), isPlaneBound ? (_jsxs("div", { className: "px-1 text-[11px] text-muted-foreground", children: ["Currently ", formatLinearMeasurement(height, unit)] })) : (_jsx(SliderControl, { onCommit: handleCommit, onCancel: handleCancel, restoreOnCommit: false, previewWhileTyping: true, label: "Height", max: metersToLinearUnit(1000, unit), min: metersToLinearUnit(0.1, unit), onChange: (v) => handlePreview({
181
177
  height: linearControlValueToMeters(v, unit, { maxMeters: 1000, minMeters: 0.1 }),
182
178
  }), precision: 2, step: 0.1, unit: unitLabel, value: Math.round(displayHeight * 100) / 100 })), _jsx("div", { className: "px-1 font-medium text-[10px] text-muted-foreground/80 uppercase tracking-wider", children: "Bottom" }), _jsx(SegmentedControl, { onChange: handleInfillChange, options: [
183
179
  { label: 'Auto', value: 'auto' },
184
180
  { label: 'Fill to terrain', value: 'terrain' },
185
- ], value: followsTerrain ? 'terrain' : 'auto' }), followsTerrain && (_jsx("div", { className: "px-1 text-[11px] text-muted-foreground", children: "Extends downward to meet the terrain. Height and top stay unchanged." })), _jsx(SliderControl, { label: "Thickness", max: metersToLinearUnit(1000, unit), min: metersToLinearUnit(0.05, unit), onChange: (v) => handleUpdate({
181
+ ], value: followsTerrain ? 'terrain' : 'auto' }), followsTerrain && (_jsx("div", { className: "px-1 text-[11px] text-muted-foreground", children: "Extends downward to meet the terrain. Height and top stay unchanged." })), _jsx(SliderControl, { onCommit: handleCommit, onCancel: handleCancel, restoreOnCommit: false, previewWhileTyping: true, label: node.wallType === 'curtain' ? 'Frame depth' : 'Thickness', max: metersToLinearUnit(1000, unit), min: metersToLinearUnit(0.05, unit), onChange: (v) => handlePreview({
186
182
  thickness: linearControlValueToMeters(v, unit, {
187
183
  maxMeters: 1000,
188
184
  minMeters: 0.05,
189
185
  }),
190
- }), precision: 3, step: 0.01, unit: unitLabel, value: Math.round(displayThickness * 1000) / 1000 }), !hasWallChildrenBlockingCurve && (_jsx(SliderControl, { label: "Curve", max: Math.max(metersToLinearUnit(0.01, unit), displayMaxCurveOffset), min: -Math.max(metersToLinearUnit(0.01, unit), displayMaxCurveOffset), onChange: (v) => handleUpdate({
186
+ }), precision: 3, step: 0.01, unit: unitLabel, value: Math.round(displayThickness * 1000) / 1000 }), !hasWallChildrenBlockingCurve && (_jsx(SliderControl, { onCommit: handleCommit, onCancel: handleCancel, restoreOnCommit: false, previewWhileTyping: true, label: "Curve", max: Math.max(metersToLinearUnit(0.01, unit), displayMaxCurveOffset), min: -Math.max(metersToLinearUnit(0.01, unit), displayMaxCurveOffset), onChange: (v) => handlePreview({
191
187
  curveOffset: normalizeWallCurveOffset(node, linearControlValueToMeters(v, unit, {
192
188
  maxMeters: curveOffsetLimit,
193
189
  minMeters: -curveOffsetLimit,
194
190
  })),
195
- }), precision: 2, step: 0.1, unit: unitLabel, value: Math.round(displayCurveOffset * 100) / 100 }))] }), _jsx(WallFaceBandSection, { node: node, onUpdate: handleUpdate, unit: unit, unitLabel: unitLabel, wallHeightMeters: wallHeightMeters }), _jsx(WallTrimSection, { node: node, onUpdate: handleUpdate, title: "Skirting", trimKey: "skirting", trimValue: skirting, unit: unit, unitLabel: unitLabel, wallHeightMeters: wallHeightMeters }), _jsx(WallTrimSection, { node: node, onUpdate: handleUpdate, title: "Crown molding", trimKey: "crown", trimValue: crown, unit: unit, unitLabel: unitLabel, wallHeightMeters: wallHeightMeters }), _jsx(WallTrimSection, { node: node, onUpdate: handleUpdate, title: "Chair rail", trimKey: "chairRail", trimValue: chairRail, unit: unit, unitLabel: unitLabel, wallHeightMeters: wallHeightMeters }), !hasWallChildrenBlockingCurve && (_jsx(PanelSection, { title: "Actions", children: _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Spline, { className: "h-3.5 w-3.5" }), label: "Curve", onClick: handleCurve }) }) }))] }));
191
+ }), precision: 2, step: 0.1, unit: unitLabel, value: Math.round(displayCurveOffset * 100) / 100 }))] }), node.wallType === 'curtain' ? (_jsx(CurtainWallPanel, { height: height, node: node, onUpdate: handleUpdate, onPreview: handlePreview, onCommit: handleCommit, onCancel: handleCancel, unit: unit }, node.id)) : (_jsxs(_Fragment, { children: [_jsx(WallFaceBandSection, { node: node, onUpdate: handleUpdate, unit: unit, unitLabel: unitLabel, wallHeightMeters: wallHeightMeters }), _jsx(WallTrimSection, { node: node, onUpdate: handleUpdate, title: "Skirting", trimKey: "skirting", trimValue: skirting, unit: unit, unitLabel: unitLabel, wallHeightMeters: wallHeightMeters }), _jsx(WallTrimSection, { node: node, onUpdate: handleUpdate, title: "Crown molding", trimKey: "crown", trimValue: crown, unit: unit, unitLabel: unitLabel, wallHeightMeters: wallHeightMeters }), _jsx(WallTrimSection, { node: node, onUpdate: handleUpdate, title: "Chair rail", trimKey: "chairRail", trimValue: chairRail, unit: unit, unitLabel: unitLabel, wallHeightMeters: wallHeightMeters })] })), !hasWallChildrenBlockingCurve && (_jsx(PanelSection, { title: "Actions", children: _jsx(ActionGroup, { children: _jsx(ActionButton, { icon: _jsx(Spline, { className: "h-3.5 w-3.5" }), label: "Curve", onClick: handleCurve }) }) }))] }));
196
192
  }
197
193
  function WallFaceBandSection({ node, onUpdate, unit, unitLabel, wallHeightMeters, }) {
198
194
  const bandConfig = getWallFaceBandConfig(node, wallHeightMeters);
@@ -0,0 +1,7 @@
1
+ import { type AnyNodeId, type WallNode } from '@pascal-app/core';
2
+ export declare function createWallPropertyPreview(id: AnyNodeId): {
3
+ preview(patch: Partial<WallNode>): void;
4
+ commit(patch?: Partial<WallNode>): void;
5
+ cancel: () => void;
6
+ };
7
+ //# sourceMappingURL=property-preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-preview.d.ts","sourceRoot":"","sources":["../../src/wall/property-preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAkC,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEhG,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,SAAS;mBASpC,OAAO,CAAC,QAAQ,CAAC;mBAKjB,OAAO,CAAC,QAAQ,CAAC;;EAiBnC"}
@@ -0,0 +1,31 @@
1
+ import { useLiveNodeOverrides, useScene } from '@pascal-app/core';
2
+ export function createWallPropertyPreview(id) {
3
+ let pending;
4
+ const clear = () => {
5
+ if (!pending)
6
+ return;
7
+ useLiveNodeOverrides.getState().clearFields(id, Object.keys(pending));
8
+ pending = undefined;
9
+ useScene.getState().markDirty(id);
10
+ };
11
+ return {
12
+ preview(patch) {
13
+ pending = { ...pending, ...patch };
14
+ useLiveNodeOverrides.getState().set(id, patch);
15
+ useScene.getState().markDirty(id);
16
+ },
17
+ commit(patch) {
18
+ const live = useLiveNodeOverrides.getState().get(id);
19
+ const current = pending && live
20
+ ? Object.fromEntries(Object.keys(pending)
21
+ .filter((key) => key in live)
22
+ .map((key) => [key, live[key]]))
23
+ : undefined;
24
+ const updates = { ...current, ...patch };
25
+ if (Object.keys(updates).length && useScene.getState().nodes[id]?.type === 'wall')
26
+ useScene.getState().updateNode(id, updates);
27
+ clear();
28
+ },
29
+ cancel: clear,
30
+ };
31
+ }
@@ -0,0 +1,149 @@
1
+ import { type AnyNodeId, type WallConstructionOptions, type WallNode, type WallPlanPoint } from '@pascal-app/core';
2
+ export declare function createWallRectangle(levelId: AnyNodeId, start: WallPlanPoint, end: WallPlanPoint, defaults?: Partial<WallNode>, options?: WallConstructionOptions): {
3
+ object: "node";
4
+ parentId: string | null;
5
+ visible: boolean;
6
+ metadata: Record<string, unknown>;
7
+ id: `wall_${string}`;
8
+ type: "wall";
9
+ children: (`item_${string}` | `procedural-item_${string}` | `leanto_${string}` | `door_${string}` | `window_${string}`)[];
10
+ start: [number, number];
11
+ end: [number, number];
12
+ frontSide: "unknown" | "interior" | "exterior";
13
+ backSide: "unknown" | "interior" | "exterior";
14
+ name?: string | undefined;
15
+ camera?: {
16
+ position: [number, number, number];
17
+ target: [number, number, number];
18
+ mode: "perspective" | "orthographic";
19
+ fov?: number | undefined;
20
+ zoom?: number | undefined;
21
+ } | undefined;
22
+ wallType?: "standard" | "curtain" | undefined;
23
+ curtainWall?: {
24
+ construction: "stick" | "unitized";
25
+ framing: "capped" | "vertical-caps" | "horizontal-caps" | "structural-glazing";
26
+ columns: {
27
+ layout: "count" | "maximum-spacing" | "fixed-spacing";
28
+ count: number;
29
+ spacing: number;
30
+ alignment: "center" | "start" | "end";
31
+ };
32
+ rows: {
33
+ layout: "count" | "maximum-spacing" | "fixed-spacing";
34
+ count: number;
35
+ spacing: number;
36
+ alignment: "center" | "start" | "end";
37
+ };
38
+ mullionWidth: number;
39
+ transomWidth: number;
40
+ perimeterWidth: number;
41
+ jointWidth: number;
42
+ glassThickness: number;
43
+ panelType: "glass" | "solid" | "empty";
44
+ spandrel: "top" | "none" | "bottom";
45
+ frameColor: string;
46
+ glassColor: string;
47
+ solidColor: string;
48
+ glassOpacity: number;
49
+ glassRoughness: number;
50
+ panels: {
51
+ column: number;
52
+ row: number;
53
+ type: "glass" | "solid" | "empty";
54
+ }[];
55
+ } | undefined;
56
+ material?: {
57
+ id?: string | undefined;
58
+ preset?: "custom" | "white" | "brick" | "concrete" | "wood" | "glass" | "metal" | "plaster" | "tile" | "marble" | undefined;
59
+ properties?: {
60
+ color: string;
61
+ roughness: number;
62
+ metalness: number;
63
+ opacity: number;
64
+ transparent: boolean;
65
+ side: "front" | "back" | "double";
66
+ } | undefined;
67
+ texture?: {
68
+ url: string;
69
+ repeat?: [number, number] | undefined;
70
+ scale?: number | undefined;
71
+ } | undefined;
72
+ } | undefined;
73
+ materialPreset?: string | undefined;
74
+ interiorMaterial?: {
75
+ id?: string | undefined;
76
+ preset?: "custom" | "white" | "brick" | "concrete" | "wood" | "glass" | "metal" | "plaster" | "tile" | "marble" | undefined;
77
+ properties?: {
78
+ color: string;
79
+ roughness: number;
80
+ metalness: number;
81
+ opacity: number;
82
+ transparent: boolean;
83
+ side: "front" | "back" | "double";
84
+ } | undefined;
85
+ texture?: {
86
+ url: string;
87
+ repeat?: [number, number] | undefined;
88
+ scale?: number | undefined;
89
+ } | undefined;
90
+ } | undefined;
91
+ interiorMaterialPreset?: string | undefined;
92
+ exteriorMaterial?: {
93
+ id?: string | undefined;
94
+ preset?: "custom" | "white" | "brick" | "concrete" | "wood" | "glass" | "metal" | "plaster" | "tile" | "marble" | undefined;
95
+ properties?: {
96
+ color: string;
97
+ roughness: number;
98
+ metalness: number;
99
+ opacity: number;
100
+ transparent: boolean;
101
+ side: "front" | "back" | "double";
102
+ } | undefined;
103
+ texture?: {
104
+ url: string;
105
+ repeat?: [number, number] | undefined;
106
+ scale?: number | undefined;
107
+ } | undefined;
108
+ } | undefined;
109
+ exteriorMaterialPreset?: string | undefined;
110
+ slots?: Record<string, string> | undefined;
111
+ thickness?: number | undefined;
112
+ height?: number | undefined;
113
+ curveOffset?: number | undefined;
114
+ supportSlabId?: string | undefined;
115
+ supportOffset?: number | undefined;
116
+ fillToTerrain?: boolean | undefined;
117
+ faceBands?: {
118
+ enabled: boolean;
119
+ count: number;
120
+ lowerHeight: number;
121
+ middleHeight: number;
122
+ upperHeight: number;
123
+ } | undefined;
124
+ skirting?: {
125
+ enabled: boolean;
126
+ sides: "interior" | "exterior" | "both";
127
+ height: number;
128
+ proud: number;
129
+ profile: "flat" | "bevel" | "triangle" | "cove" | "bullnose" | "base-modern" | "base-colonial" | "base-shoe" | "base-ogee" | "crown-cove" | "crown-ogee" | "crown-craftsman" | "crown-layered" | "rail-rounded" | "rail-ogee" | "rail-picture" | "rail-stepped";
130
+ offsetY?: number | undefined;
131
+ } | undefined;
132
+ crown?: {
133
+ enabled: boolean;
134
+ sides: "interior" | "exterior" | "both";
135
+ height: number;
136
+ proud: number;
137
+ profile: "flat" | "bevel" | "triangle" | "cove" | "bullnose" | "base-modern" | "base-colonial" | "base-shoe" | "base-ogee" | "crown-cove" | "crown-ogee" | "crown-craftsman" | "crown-layered" | "rail-rounded" | "rail-ogee" | "rail-picture" | "rail-stepped";
138
+ offsetY?: number | undefined;
139
+ } | undefined;
140
+ chairRail?: {
141
+ enabled: boolean;
142
+ sides: "interior" | "exterior" | "both";
143
+ height: number;
144
+ proud: number;
145
+ profile: "flat" | "bevel" | "triangle" | "cove" | "bullnose" | "base-modern" | "base-colonial" | "base-shoe" | "base-ogee" | "crown-cove" | "crown-ogee" | "crown-craftsman" | "crown-layered" | "rail-rounded" | "rail-ogee" | "rail-picture" | "rail-stepped";
146
+ offsetY?: number | undefined;
147
+ } | undefined;
148
+ }[];
149
+ //# sourceMappingURL=rectangle-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rectangle-command.d.ts","sourceRoot":"","sources":["../../src/wall/rectangle-command.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAKd,KAAK,uBAAuB,EAC5B,KAAK,QAAQ,EACb,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAA;AAEzB,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,EAClB,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,aAAa,EAClB,QAAQ,GAAE,OAAO,CAAC,QAAQ,CAAM,EAChC,OAAO,CAAC,EAAE,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAmBlC"}
@@ -0,0 +1,22 @@
1
+ import { planWallRectangle, resolveWallConstruction, runAsSingleSceneHistoryStep, useScene, } from '@pascal-app/core';
2
+ export function createWallRectangle(levelId, start, end, defaults = {}, options) {
3
+ const scene = useScene.getState();
4
+ if (scene.readOnly)
5
+ throw Error('This scene is read-only.');
6
+ const plan = planWallRectangle(scene.nodes, { levelId, start, end, wallDefaults: defaults });
7
+ if (!plan.changes.create.length && !plan.changes.update.length && !plan.changes.delete.length)
8
+ return [];
9
+ const construction = resolveWallConstruction(scene.nodes, levelId, plan.walls, options);
10
+ const walls = new Map(construction.walls.map((w) => [w.id, w]));
11
+ const changes = {
12
+ ...plan.changes,
13
+ create: plan.changes.create.map((op) => ({
14
+ ...op,
15
+ node: walls.get(op.node.id) ?? op.node,
16
+ })),
17
+ };
18
+ if (construction.sourceSupportUpdate)
19
+ changes.update.push(construction.sourceSupportUpdate);
20
+ runAsSingleSceneHistoryStep(useScene, () => scene.applyNodeChanges(changes));
21
+ return construction.walls;
22
+ }
@@ -0,0 +1,2 @@
1
+ export default function RectangleWallTool(): import("react").JSX.Element;
2
+ //# sourceMappingURL=rectangle-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rectangle-tool.d.ts","sourceRoot":"","sources":["../../src/wall/rectangle-tool.tsx"],"names":[],"mappings":"AAmCA,MAAM,CAAC,OAAO,UAAU,iBAAiB,gCAkMxC"}