@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.
- package/dist/block/definition.d.ts.map +1 -1
- package/dist/block/definition.js +4 -1
- package/dist/block/face-host.d.ts.map +1 -1
- package/dist/block/face-host.js +19 -2
- package/dist/block/hosted-edit.d.ts +13 -0
- package/dist/block/hosted-edit.d.ts.map +1 -0
- package/dist/block/hosted-edit.js +205 -0
- package/dist/block/last-operation.d.ts +1 -1
- package/dist/block/last-operation.d.ts.map +1 -1
- package/dist/block/last-operation.js +6 -4
- package/dist/block/modal-session.d.ts +2 -1
- package/dist/block/modal-session.d.ts.map +1 -1
- package/dist/block/modal-session.js +2 -2
- package/dist/block/selection.d.ts.map +1 -1
- package/dist/block/selection.js +82 -24
- package/dist/block/surface.d.ts +4 -0
- package/dist/block/surface.d.ts.map +1 -0
- package/dist/block/surface.js +54 -0
- package/dist/block/use-block-face-operation.d.ts +2 -2
- package/dist/block/use-block-face-operation.d.ts.map +1 -1
- package/dist/block/use-block-face-operation.js +25 -11
- package/dist/box-vent/definition.d.ts.map +1 -1
- package/dist/box-vent/definition.js +1 -0
- package/dist/building/renderer.d.ts.map +1 -1
- package/dist/building/renderer.js +1 -1
- package/dist/cabinet/definition.d.ts.map +1 -1
- package/dist/cabinet/definition.js +3 -1
- package/dist/cabinet/hosted-resize.d.ts +3 -6
- package/dist/cabinet/hosted-resize.d.ts.map +1 -1
- package/dist/cabinet/hosted-resize.js +9 -153
- package/dist/cabinet/placement-snap.d.ts.map +1 -1
- package/dist/cabinet/placement-snap.js +4 -2
- package/dist/cabinet/surfaces.d.ts +1 -0
- package/dist/cabinet/surfaces.d.ts.map +1 -1
- package/dist/cabinet/surfaces.js +13 -1
- package/dist/cabinet/tool.d.ts.map +1 -1
- package/dist/cabinet/tool.js +8 -4
- package/dist/ceiling/definition.d.ts.map +1 -1
- package/dist/ceiling/definition.js +5 -0
- package/dist/ceiling/renderer.d.ts.map +1 -1
- package/dist/ceiling/renderer.js +3 -2
- package/dist/chimney/definition.d.ts.map +1 -1
- package/dist/chimney/definition.js +1 -0
- package/dist/column/definition.d.ts.map +1 -1
- package/dist/column/definition.js +10 -3
- package/dist/column/floorplan-affordances.d.ts.map +1 -1
- package/dist/column/floorplan-affordances.js +6 -1
- package/dist/column/floorplan.d.ts.map +1 -1
- package/dist/column/floorplan.js +23 -11
- package/dist/column/hosted-resize.d.ts +116 -0
- package/dist/column/hosted-resize.d.ts.map +1 -0
- package/dist/column/hosted-resize.js +9 -0
- package/dist/column/panel.d.ts.map +1 -1
- package/dist/column/panel.js +6 -3
- package/dist/column/renderer.d.ts.map +1 -1
- package/dist/column/renderer.js +25 -103
- package/dist/column/shape.d.ts +22 -0
- package/dist/column/shape.d.ts.map +1 -0
- package/dist/column/shape.js +120 -0
- package/dist/column/surface.d.ts +4 -0
- package/dist/column/surface.d.ts.map +1 -0
- package/dist/column/surface.js +66 -0
- package/dist/cupola/definition.d.ts.map +1 -1
- package/dist/cupola/definition.js +1 -0
- package/dist/door/definition.d.ts.map +1 -1
- package/dist/door/definition.js +40 -1
- package/dist/door/door-math.d.ts +1 -1
- package/dist/door/door-math.d.ts.map +1 -1
- package/dist/door/door-math.js +7 -10
- package/dist/door/floorplan-affordances.d.ts.map +1 -1
- package/dist/door/floorplan-affordances.js +8 -12
- package/dist/door/move-tool.d.ts.map +1 -1
- package/dist/door/move-tool.js +3 -4
- package/dist/door/panel.d.ts.map +1 -1
- package/dist/door/panel.js +23 -52
- package/dist/door/tool.d.ts.map +1 -1
- package/dist/door/tool.js +20 -2
- package/dist/dormer/definition.d.ts.map +1 -1
- package/dist/dormer/definition.js +1 -0
- package/dist/downspout/definition.d.ts.map +1 -1
- package/dist/downspout/definition.js +1 -0
- package/dist/duct-segment/tool.d.ts.map +1 -1
- package/dist/duct-segment/tool.js +11 -0
- package/dist/duct-terminal/tool.d.ts.map +1 -1
- package/dist/duct-terminal/tool.js +77 -8
- package/dist/elevator/definition.d.ts.map +1 -1
- package/dist/elevator/definition.js +2 -0
- package/dist/elevator/parametrics.d.ts.map +1 -1
- package/dist/elevator/parametrics.js +98 -1
- package/dist/eyebrow-vent/definition.d.ts.map +1 -1
- package/dist/eyebrow-vent/definition.js +1 -0
- package/dist/fence/definition.d.ts.map +1 -1
- package/dist/fence/definition.js +1 -0
- package/dist/fence/tool.js +2 -2
- package/dist/guide/definition.d.ts.map +1 -1
- package/dist/guide/definition.js +1 -0
- package/dist/gutter/definition.d.ts.map +1 -1
- package/dist/gutter/definition.js +1 -0
- package/dist/hvac-equipment/tool.d.ts.map +1 -1
- package/dist/hvac-equipment/tool.js +8 -2
- package/dist/item/__fixtures__/main-floorplan.d.ts +26 -0
- package/dist/item/__fixtures__/main-floorplan.d.ts.map +1 -0
- package/dist/item/__fixtures__/main-floorplan.js +297 -0
- package/dist/item/definition.d.ts.map +1 -1
- package/dist/item/definition.js +3 -1
- package/dist/item/floorplan-move.d.ts.map +1 -1
- package/dist/item/floorplan-move.js +209 -130
- package/dist/item/floorplan.d.ts +22 -0
- package/dist/item/floorplan.d.ts.map +1 -1
- package/dist/item/floorplan.js +61 -2
- package/dist/item/move-tool.d.ts +1 -1
- package/dist/item/move-tool.d.ts.map +1 -1
- package/dist/item/move-tool.js +15 -7
- package/dist/item/renderer.d.ts.map +1 -1
- package/dist/item/renderer.js +8 -2
- package/dist/level/renderer.js +1 -1
- package/dist/lineset/tool.d.ts.map +1 -1
- package/dist/lineset/tool.js +1 -0
- package/dist/liquid-line/tool.d.ts.map +1 -1
- package/dist/liquid-line/tool.js +5 -1
- package/dist/measurement/definition.d.ts.map +1 -1
- package/dist/measurement/definition.js +1 -0
- package/dist/pipe-segment/tool.d.ts.map +1 -1
- package/dist/pipe-segment/tool.js +14 -3
- package/dist/pipe-trap/tool.d.ts.map +1 -1
- package/dist/pipe-trap/tool.js +7 -8
- package/dist/procedural-item/definition.d.ts.map +1 -1
- package/dist/procedural-item/definition.js +4 -1
- package/dist/ridge-vent/definition.d.ts.map +1 -1
- package/dist/ridge-vent/definition.js +1 -0
- package/dist/roof/definition.d.ts.map +1 -1
- package/dist/roof/definition.js +4 -1
- package/dist/roof/panel-model.d.ts +4 -0
- package/dist/roof/panel-model.d.ts.map +1 -0
- package/dist/roof/panel-model.js +151 -0
- package/dist/roof/panel.d.ts.map +1 -1
- package/dist/roof/panel.js +3 -4
- package/dist/roof/parametrics.d.ts.map +1 -1
- package/dist/roof/parametrics.js +16 -1
- package/dist/roof-segment/definition.d.ts.map +1 -1
- package/dist/roof-segment/definition.js +1 -0
- package/dist/roof-segment/parametrics.d.ts.map +1 -1
- package/dist/roof-segment/parametrics.js +176 -1
- package/dist/scan/definition.d.ts.map +1 -1
- package/dist/scan/definition.js +1 -0
- package/dist/shared/commit-opening-move.d.ts +4 -0
- package/dist/shared/commit-opening-move.d.ts.map +1 -0
- package/dist/shared/commit-opening-move.js +9 -0
- package/dist/shared/curtain-opening-limits.d.ts +14 -0
- package/dist/shared/curtain-opening-limits.d.ts.map +1 -0
- package/dist/shared/curtain-opening-limits.js +53 -0
- package/dist/shared/draft-axis-guides.d.ts +0 -6
- package/dist/shared/draft-axis-guides.d.ts.map +1 -1
- package/dist/shared/draft-axis-guides.js +1 -8
- package/dist/shared/hosted-resize.d.ts +12 -0
- package/dist/shared/hosted-resize.d.ts.map +1 -0
- package/dist/shared/hosted-resize.js +166 -0
- package/dist/shared/opening-grid-follow.d.ts +8 -0
- package/dist/shared/opening-grid-follow.d.ts.map +1 -0
- package/dist/shared/opening-grid-follow.js +12 -0
- package/dist/shared/opening-property-preview-host.d.ts +3 -0
- package/dist/shared/opening-property-preview-host.d.ts.map +1 -0
- package/dist/shared/opening-property-preview-host.js +21 -0
- package/dist/shared/opening-property-preview.d.ts +19 -0
- package/dist/shared/opening-property-preview.d.ts.map +1 -0
- package/dist/shared/opening-property-preview.js +99 -0
- package/dist/shared/resting-surface-plan.d.ts +2 -1
- package/dist/shared/resting-surface-plan.d.ts.map +1 -1
- package/dist/shared/resting-surface-plan.js +29 -4
- package/dist/shared/surface-counterparts.d.ts +12 -0
- package/dist/shared/surface-counterparts.d.ts.map +1 -0
- package/dist/shared/surface-counterparts.js +27 -0
- package/dist/shared/wall-attach-target.d.ts +1 -0
- package/dist/shared/wall-attach-target.d.ts.map +1 -1
- package/dist/shared/wall-attach-target.js +4 -1
- package/dist/shared/wall-event-from-grid.d.ts +5 -0
- package/dist/shared/wall-event-from-grid.d.ts.map +1 -0
- package/dist/shared/wall-event-from-grid.js +36 -0
- package/dist/shelf/definition.d.ts.map +1 -1
- package/dist/shelf/definition.js +3 -1
- package/dist/shelf/floorplan.d.ts.map +1 -1
- package/dist/shelf/floorplan.js +9 -2
- package/dist/site/renderer.d.ts.map +1 -1
- package/dist/site/renderer.js +9 -2
- package/dist/skylight/definition.d.ts.map +1 -1
- package/dist/skylight/definition.js +1 -0
- package/dist/slab/definition.d.ts.map +1 -1
- package/dist/slab/definition.js +4 -0
- package/dist/solar-panel/definition.d.ts.map +1 -1
- package/dist/solar-panel/definition.js +1 -0
- package/dist/spawn/definition.d.ts.map +1 -1
- package/dist/spawn/definition.js +1 -0
- package/dist/stair/definition.d.ts.map +1 -1
- package/dist/stair/definition.js +2 -0
- package/dist/stair/parametrics.d.ts.map +1 -1
- package/dist/stair/parametrics.js +164 -1
- package/dist/stair-segment/definition.d.ts.map +1 -1
- package/dist/stair-segment/definition.js +1 -0
- package/dist/stair-segment/parametrics.d.ts.map +1 -1
- package/dist/stair-segment/parametrics.js +81 -1
- package/dist/turbine-vent/definition.d.ts.map +1 -1
- package/dist/turbine-vent/definition.js +1 -0
- package/dist/wall/actions.d.ts +3 -0
- package/dist/wall/actions.d.ts.map +1 -0
- package/dist/wall/actions.js +56 -0
- package/dist/wall/curtain-opening-frame.d.ts +14 -0
- package/dist/wall/curtain-opening-frame.d.ts.map +1 -0
- package/dist/wall/curtain-opening-frame.js +111 -0
- package/dist/wall/curtain-panel-highlight.d.ts +3 -0
- package/dist/wall/curtain-panel-highlight.d.ts.map +1 -0
- package/dist/wall/curtain-panel-highlight.js +113 -0
- package/dist/wall/curtain-wall-adapter.d.ts +3 -0
- package/dist/wall/curtain-wall-adapter.d.ts.map +1 -0
- package/dist/wall/curtain-wall-adapter.js +55 -0
- package/dist/wall/curtain-wall-floorplan.d.ts +3 -0
- package/dist/wall/curtain-wall-floorplan.d.ts.map +1 -0
- package/dist/wall/curtain-wall-floorplan.js +91 -0
- package/dist/wall/curtain-wall-geometry.d.ts +4 -0
- package/dist/wall/curtain-wall-geometry.d.ts.map +1 -0
- package/dist/wall/curtain-wall-geometry.js +226 -0
- package/dist/wall/curtain-wall-layout.d.ts +16 -0
- package/dist/wall/curtain-wall-layout.d.ts.map +1 -0
- package/dist/wall/curtain-wall-layout.js +176 -0
- package/dist/wall/curtain-wall-materials.d.ts +5 -0
- package/dist/wall/curtain-wall-materials.d.ts.map +1 -0
- package/dist/wall/curtain-wall-materials.js +55 -0
- package/dist/wall/curtain-wall-panel.d.ts +14 -0
- package/dist/wall/curtain-wall-panel.d.ts.map +1 -0
- package/dist/wall/curtain-wall-panel.js +167 -0
- package/dist/wall/curtain-wall-piece-geometry.d.ts +5 -0
- package/dist/wall/curtain-wall-piece-geometry.d.ts.map +1 -0
- package/dist/wall/curtain-wall-piece-geometry.js +220 -0
- package/dist/wall/curtain-wall-shadow.d.ts +4 -0
- package/dist/wall/curtain-wall-shadow.d.ts.map +1 -0
- package/dist/wall/curtain-wall-shadow.js +15 -0
- package/dist/wall/definition.d.ts +0 -15
- package/dist/wall/definition.d.ts.map +1 -1
- package/dist/wall/definition.js +65 -4
- package/dist/wall/drawing-mode.d.ts +12 -0
- package/dist/wall/drawing-mode.d.ts.map +1 -0
- package/dist/wall/drawing-mode.js +38 -0
- package/dist/wall/floorplan-tool.d.ts +4 -0
- package/dist/wall/floorplan-tool.d.ts.map +1 -0
- package/dist/wall/floorplan-tool.js +107 -0
- package/dist/wall/floorplan.d.ts.map +1 -1
- package/dist/wall/floorplan.js +6 -2
- package/dist/wall/length-patch.d.ts +3 -0
- package/dist/wall/length-patch.d.ts.map +1 -0
- package/dist/wall/length-patch.js +14 -0
- package/dist/wall/move-tool.d.ts.map +1 -1
- package/dist/wall/move-tool.js +23 -1
- package/dist/wall/paint.d.ts.map +1 -1
- package/dist/wall/paint.js +39 -4
- package/dist/wall/panel-model.d.ts +4 -0
- package/dist/wall/panel-model.d.ts.map +1 -0
- package/dist/wall/panel-model.js +117 -0
- package/dist/wall/panel.d.ts.map +1 -1
- package/dist/wall/panel.js +20 -24
- package/dist/wall/property-preview.d.ts +7 -0
- package/dist/wall/property-preview.d.ts.map +1 -0
- package/dist/wall/property-preview.js +31 -0
- package/dist/wall/rectangle-command.d.ts +149 -0
- package/dist/wall/rectangle-command.d.ts.map +1 -0
- package/dist/wall/rectangle-command.js +22 -0
- package/dist/wall/rectangle-tool.d.ts +2 -0
- package/dist/wall/rectangle-tool.d.ts.map +1 -0
- package/dist/wall/rectangle-tool.js +147 -0
- package/dist/wall/renderer.d.ts.map +1 -1
- package/dist/wall/renderer.js +7 -4
- package/dist/wall/slots.d.ts +2 -2
- package/dist/wall/slots.d.ts.map +1 -1
- package/dist/wall/slots.js +10 -2
- package/dist/wall/split-floorplan-layer.d.ts +4 -0
- package/dist/wall/split-floorplan-layer.d.ts.map +1 -0
- package/dist/wall/split-floorplan-layer.js +71 -0
- package/dist/wall/split-pointer.d.ts +3 -0
- package/dist/wall/split-pointer.d.ts.map +1 -0
- package/dist/wall/split-pointer.js +95 -0
- package/dist/wall/split-preview.d.ts +55 -0
- package/dist/wall/split-preview.d.ts.map +1 -0
- package/dist/wall/split-preview.js +106 -0
- package/dist/wall/split-session.d.ts +16 -0
- package/dist/wall/split-session.d.ts.map +1 -0
- package/dist/wall/split-session.js +138 -0
- package/dist/wall/split-store.d.ts +16 -0
- package/dist/wall/split-store.d.ts.map +1 -0
- package/dist/wall/split-store.js +6 -0
- package/dist/wall/split-tool.d.ts +10 -0
- package/dist/wall/split-tool.d.ts.map +1 -0
- package/dist/wall/split-tool.js +95 -0
- package/dist/wall/system.d.ts.map +1 -1
- package/dist/wall/system.js +3 -1
- package/dist/wall/tool.d.ts.map +1 -1
- package/dist/wall/tool.js +14 -6
- package/dist/wall/wall-batch-system.d.ts.map +1 -1
- package/dist/wall/wall-batch-system.js +3 -0
- package/dist/window/definition.d.ts.map +1 -1
- package/dist/window/definition.js +48 -1
- package/dist/window/floorplan-affordances.d.ts.map +1 -1
- package/dist/window/floorplan-affordances.js +8 -10
- package/dist/window/move-tool.d.ts.map +1 -1
- package/dist/window/move-tool.js +4 -6
- package/dist/window/panel.d.ts.map +1 -1
- package/dist/window/panel.js +21 -59
- package/dist/window/tool.d.ts.map +1 -1
- package/dist/window/tool.js +34 -2
- package/dist/window/window-math.d.ts +1 -1
- package/dist/window/window-math.d.ts.map +1 -1
- package/dist/window/window-math.js +8 -11
- package/dist/zone/definition.d.ts.map +1 -1
- package/dist/zone/definition.js +1 -0
- package/dist/zone/quantities-panel.d.ts.map +1 -1
- package/dist/zone/quantities-panel.js +3 -19
- package/dist/zone/renderer.d.ts.map +1 -1
- package/dist/zone/renderer.js +1 -1
- package/package.json +8 -8
- package/dist/shared/opening-documentation-fields.d.ts +0 -16
- package/dist/shared/opening-documentation-fields.d.ts.map +0 -1
- package/dist/shared/opening-documentation-fields.js +0 -54
|
@@ -1,103 +1,19 @@
|
|
|
1
|
-
import { collectAlignmentAnchors, createSceneApi, getBlockFaceFrame,
|
|
2
|
-
import { boundsOf, boxCorners, frame, transformPoint } from '@pascal-app/core/procedural-items';
|
|
3
|
-
import { applyFloorplanAlignment, isGridSnapActive, isMagneticSnapActive, useEditor, } from '@pascal-app/editor';
|
|
1
|
+
import { cascadeDirty, clearFaceHostItemFields, collectAlignmentAnchors, collectDescendants, createSceneApi, getBlockFaceFrame, getScaledDimensions, getSurfaceProvider, movingFootprintAnchors, nodeRegistry, resolveSupportSlabPatch, resolveSurfacePlacement, surfaceRegionContainsPoint, useLiveNodeOverrides, useScene, } from '@pascal-app/core';
|
|
2
|
+
import { boundsOf, boxCorners, composeFrames, frame, transformPoint, } from '@pascal-app/core/procedural-items';
|
|
3
|
+
import { applyFloorplanAlignment, isGridSnapActive, isMagneticSnapActive, surfaceAttachmentId, surfaceFramePose, updateSurfaceNode, useEditor, useInteractionScope, } from '@pascal-app/editor';
|
|
4
4
|
import { createFloorplanCursorResolver } from '../shared/floorplan-cursor';
|
|
5
5
|
import { restingNodePlanFrame } from '../shared/resting-surface-plan';
|
|
6
6
|
import { findClosestWallInPlan, snapLocalXToNeighbors } from '../shared/wall-attach-target';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
return
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const cached = cache.get(item.id);
|
|
14
|
-
if (cached)
|
|
15
|
-
return cached;
|
|
16
|
-
const localRotation = item.rotation[1] ?? 0;
|
|
17
|
-
let result = {
|
|
18
|
-
point: [item.position[0], item.position[2]],
|
|
19
|
-
rotation: localRotation,
|
|
7
|
+
import { resolveItemTransform } from './floorplan';
|
|
8
|
+
function resolveItemPlanTransform(item, nodes) {
|
|
9
|
+
const pose = resolveItemTransform(item, { resolve: (id) => nodes[id] });
|
|
10
|
+
return {
|
|
11
|
+
point: pose ? [pose.x, pose.y] : [item.position[0], item.position[2]],
|
|
12
|
+
rotation: pose?.rotation ?? item.rotation[1],
|
|
20
13
|
};
|
|
21
|
-
const parent = item.parentId ? nodes[item.parentId] : null;
|
|
22
|
-
if (parent?.type === 'wall') {
|
|
23
|
-
const wallRotation = -Math.atan2(parent.end[1] - parent.start[1], parent.end[0] - parent.start[0]);
|
|
24
|
-
const wallLocalZ = item.asset.attachTo === 'wall-side'
|
|
25
|
-
? ((parent.thickness ?? 0.1) / 2) * (item.side === 'front' ? 1 : -1)
|
|
26
|
-
: item.position[2];
|
|
27
|
-
const [offsetX, offsetZ] = rotateVec(item.position[0], wallLocalZ, wallRotation);
|
|
28
|
-
result = {
|
|
29
|
-
point: [parent.start[0] + offsetX, parent.start[1] + offsetZ],
|
|
30
|
-
rotation: wallRotation + localRotation,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
else if (parent?.type === 'cabinet' ||
|
|
34
|
-
parent?.type === 'cabinet-module' ||
|
|
35
|
-
parent?.type === 'procedural-item') {
|
|
36
|
-
const f = restingNodePlanFrame(item, (id) => nodes[id]);
|
|
37
|
-
result = {
|
|
38
|
-
point: [f.position[0], f.position[2]],
|
|
39
|
-
rotation: Math.atan2(f.axes[2][0], f.axes[2][2]),
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
else if (parent?.type === 'shelf') {
|
|
43
|
-
const shelf = parent;
|
|
44
|
-
const [offsetX, offsetZ] = rotateVec(item.position[0], item.position[2], shelf.rotation[1] ?? 0);
|
|
45
|
-
result = {
|
|
46
|
-
point: [shelf.position[0] + offsetX, shelf.position[2] + offsetZ],
|
|
47
|
-
rotation: (shelf.rotation[1] ?? 0) + localRotation,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
else if (parent?.type === 'item') {
|
|
51
|
-
const parentTransform = resolveItemPlanTransform(parent, nodes, cache);
|
|
52
|
-
const [offsetX, offsetZ] = rotateVec(item.position[0], item.position[2], parentTransform.rotation);
|
|
53
|
-
result = {
|
|
54
|
-
point: [parentTransform.point[0] + offsetX, parentTransform.point[1] + offsetZ],
|
|
55
|
-
rotation: parentTransform.rotation + localRotation,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
else if (parent?.type === 'roof-segment') {
|
|
59
|
-
// Roof-hosted wall item: FACE-LOCAL position mapped through the face
|
|
60
|
-
// frame, then composed through the segment's and roof's yaw +
|
|
61
|
-
// position into level-local plan coords — without this the drag seed
|
|
62
|
-
// jumps off the roof at move start.
|
|
63
|
-
const segment = parent;
|
|
64
|
-
const roof = segment.parentId
|
|
65
|
-
? nodes[segment.parentId]
|
|
66
|
-
: undefined;
|
|
67
|
-
if (roof?.type === 'roof' && item.roofFace) {
|
|
68
|
-
const frame = getRoofWallFaceFrame(segment, item.roofFace);
|
|
69
|
-
const segLocal = roofFacePointToSegment(segment, item.roofFace, item.position);
|
|
70
|
-
const [sx, sz] = rotateVec(segLocal[0], segLocal[2], segment.rotation ?? 0);
|
|
71
|
-
const [rx, rz] = rotateVec(sx + segment.position[0], sz + segment.position[2], roof.rotation ?? 0);
|
|
72
|
-
result = {
|
|
73
|
-
point: [rx + roof.position[0], rz + roof.position[2]],
|
|
74
|
-
rotation: (roof.rotation ?? 0) + (segment.rotation ?? 0) + frame.yaw + localRotation,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
else if (parent?.type === 'block' && item.blockFaceId) {
|
|
79
|
-
const frame = getBlockFaceFrame(parent.topology, item.blockFaceId);
|
|
80
|
-
if (frame) {
|
|
81
|
-
const localX = frame.origin[0] +
|
|
82
|
-
frame.xAxis[0] * item.position[0] +
|
|
83
|
-
frame.yAxis[0] * item.position[1] +
|
|
84
|
-
frame.normal[0] * item.position[2];
|
|
85
|
-
const localZ = frame.origin[2] +
|
|
86
|
-
frame.xAxis[2] * item.position[0] +
|
|
87
|
-
frame.yAxis[2] * item.position[1] +
|
|
88
|
-
frame.normal[2] * item.position[2];
|
|
89
|
-
const [offsetX, offsetZ] = rotateVec(localX, localZ, parent.rotation ?? 0);
|
|
90
|
-
result = {
|
|
91
|
-
point: [parent.position[0] + offsetX, parent.position[2] + offsetZ],
|
|
92
|
-
rotation: (parent.rotation ?? 0) - Math.atan2(frame.xAxis[2], frame.xAxis[0]) + localRotation,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
cache.set(item.id, result);
|
|
97
|
-
return result;
|
|
98
14
|
}
|
|
99
|
-
function resolveItemPlanPoint(item, nodes
|
|
100
|
-
return resolveItemPlanTransform(item, nodes
|
|
15
|
+
function resolveItemPlanPoint(item, nodes) {
|
|
16
|
+
return resolveItemPlanTransform(item, nodes).point;
|
|
101
17
|
}
|
|
102
18
|
function createPlanarMovePointResolver(originalPlanPoint, node) {
|
|
103
19
|
const resolveCursor = createFloorplanCursorResolver({
|
|
@@ -211,49 +127,185 @@ function buildWallItemSession(node, startLevelId) {
|
|
|
211
127
|
* bookkeeping and the item drops out of the level→children DFS the
|
|
212
128
|
* floor-plan layer walks → the polygon stops rendering mid-drag.
|
|
213
129
|
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* in `use-placement-coordinator.tsx`). Snap to the 0.5m grid unless the
|
|
217
|
-
* user holds Shift.
|
|
130
|
+
* A host exit converts the pose to the level frame, matching the 3D
|
|
131
|
+
* `detachItemSurfaceToFloor`; inherited support lift stays out of stored Y.
|
|
218
132
|
*/
|
|
219
133
|
function buildFloorItemSession(node, startLevelId, nodes) {
|
|
220
|
-
const
|
|
221
|
-
const
|
|
134
|
+
const host = node.parentId ? nodes[node.parentId] : undefined;
|
|
135
|
+
const hosted = host && host.type !== 'level';
|
|
136
|
+
const surfaceId = surfaceAttachmentId(node);
|
|
137
|
+
const hostPose = surfaceFramePose(node.parentId, surfaceId, node, false);
|
|
138
|
+
const planTransform = resolveItemPlanTransform(node, nodes);
|
|
139
|
+
const levelFrame = hosted ? restingNodePlanFrame(node, (id) => nodes[id]) : null;
|
|
140
|
+
const rotationY = levelFrame
|
|
141
|
+
? Math.atan2(levelFrame.axes[2][0], levelFrame.axes[2][2])
|
|
142
|
+
: planTransform.rotation;
|
|
143
|
+
const levelRotation = [...node.rotation];
|
|
144
|
+
if (hosted) {
|
|
145
|
+
if (node.type === 'procedural-item') {
|
|
146
|
+
// Generated footprints use the query frame; catalog footprints keep their legacy plan mapping.
|
|
147
|
+
planTransform.point = [levelFrame.position[0], levelFrame.position[2]];
|
|
148
|
+
const hostFrame = restingNodePlanFrame(host, (id) => nodes[id]);
|
|
149
|
+
const headingPose = surfaceFramePose(node.parentId, surfaceId, { position: node.position, rotation: [0, node.rotation[1], 0] }, false);
|
|
150
|
+
levelRotation[1] =
|
|
151
|
+
headingPose.rotation[1] + Math.atan2(hostFrame.axes[2][0], hostFrame.axes[2][2]);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
levelRotation.splice(0, 3, hostPose.rotation[0], rotationY, hostPose.rotation[2]);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (host?.type === 'block' && node.blockFaceId)
|
|
158
|
+
levelRotation.splice(0, 3, 0, rotationY, 0);
|
|
159
|
+
const resolvePlanPoint = createPlanarMovePointResolver(planTransform.point, node);
|
|
222
160
|
// Alignment candidates gathered once — scene is stable during the drag.
|
|
223
161
|
const candidates = collectAlignmentAnchors(nodes, node.id);
|
|
162
|
+
const scene = createSceneApi(useScene);
|
|
163
|
+
const bounds = nodeRegistry.get(node.type)?.capabilities.dragBounds?.(node, nodes);
|
|
164
|
+
const dimensions = bounds?.size ?? getScaledDimensions(node);
|
|
165
|
+
const localBounds = bounds?.center
|
|
166
|
+
? {
|
|
167
|
+
min: bounds.center.map((v, i) => v - dimensions[i] / 2),
|
|
168
|
+
max: bounds.center.map((v, i) => v + dimensions[i] / 2),
|
|
169
|
+
}
|
|
170
|
+
: {
|
|
171
|
+
min: [-dimensions[0] / 2, 0, -dimensions[2] / 2],
|
|
172
|
+
max: [dimensions[0] / 2, dimensions[1], dimensions[2] / 2],
|
|
173
|
+
};
|
|
174
|
+
const originalBounds = boundsOf(boxCorners(localBounds.min, localBounds.max).map((p) => transformPoint(frame(hostPose.position, hostPose.rotation), p)));
|
|
175
|
+
const originalPlacement = hosted
|
|
176
|
+
? resolveSurfacePlacement({
|
|
177
|
+
host,
|
|
178
|
+
childKind: node.type,
|
|
179
|
+
childId: node.id,
|
|
180
|
+
childFootprint: {
|
|
181
|
+
size: dimensions,
|
|
182
|
+
rotationY: hostPose.rotation[1],
|
|
183
|
+
rotation: hostPose.rotation,
|
|
184
|
+
localBounds,
|
|
185
|
+
},
|
|
186
|
+
hit: {
|
|
187
|
+
point: [
|
|
188
|
+
(originalBounds.min[0] + originalBounds.max[0]) / 2,
|
|
189
|
+
originalBounds.min[1],
|
|
190
|
+
(originalBounds.min[2] + originalBounds.max[2]) / 2,
|
|
191
|
+
],
|
|
192
|
+
normalWorldY: 1,
|
|
193
|
+
},
|
|
194
|
+
origin: hostPose.position,
|
|
195
|
+
scene,
|
|
196
|
+
})
|
|
197
|
+
: null;
|
|
198
|
+
const retainedSurfaceId = hosted && originalPlacement
|
|
199
|
+
? getSurfaceProvider(host)
|
|
200
|
+
.surfaces?.(host, { scene })
|
|
201
|
+
.find((s) => s.id === (surfaceId ?? originalPlacement.surfaceId))?.id
|
|
202
|
+
: undefined;
|
|
203
|
+
const markMoved = () => {
|
|
204
|
+
for (const id of new Set([
|
|
205
|
+
...cascadeDirty(node.id, { scene }),
|
|
206
|
+
...collectDescendants(node.id, { scene }),
|
|
207
|
+
]))
|
|
208
|
+
useScene.getState().markDirty(id);
|
|
209
|
+
};
|
|
224
210
|
let lastPatch = null;
|
|
225
|
-
|
|
211
|
+
let lastInput = null;
|
|
212
|
+
let commitBlocked = false;
|
|
213
|
+
let lastHostedPatch = {
|
|
214
|
+
parentId: node.parentId,
|
|
215
|
+
position: [...node.position],
|
|
216
|
+
rotation: [...node.rotation],
|
|
217
|
+
supportSlabId: undefined,
|
|
218
|
+
};
|
|
219
|
+
const session = {
|
|
226
220
|
affectedIds: [node.id],
|
|
227
|
-
apply(
|
|
221
|
+
apply(input) {
|
|
222
|
+
lastInput = input;
|
|
223
|
+
commitBlocked = false;
|
|
224
|
+
const { planPoint } = input;
|
|
228
225
|
const gridSnapped = resolvePlanPoint(planPoint);
|
|
229
226
|
// Figma-style alignment layered on the grid snap, mode-driven (matching 3D):
|
|
230
227
|
// guides are DISPLAYED in every snapping mode; the magnetic pull onto them
|
|
231
228
|
// is applied only in "lines" mode (`applySnap`).
|
|
232
229
|
const { point: snapped } = applyFloorplanAlignment(gridSnapped, movingFootprintAnchors(node, gridSnapped[0], gridSnapped[1], rotationY), candidates, { applySnap: isMagneticSnapActive() });
|
|
233
|
-
const
|
|
234
|
-
if (
|
|
230
|
+
const liveHost = host && useScene.getState().nodes[host.id];
|
|
231
|
+
if (liveHost?.type === 'block' && node.blockFaceId) {
|
|
232
|
+
const face = getBlockFaceFrame(liveHost.topology, node.blockFaceId);
|
|
233
|
+
if (face && face.normal[1] > 0.99) {
|
|
234
|
+
const hostFrame = restingNodePlanFrame(liveHost, (id) => useScene.getState().nodes[id]);
|
|
235
|
+
const surface = composeFrames(hostFrame, {
|
|
236
|
+
position: face.origin,
|
|
237
|
+
axes: [face.xAxis, face.yAxis, face.normal],
|
|
238
|
+
});
|
|
239
|
+
const delta = [snapped[0] - surface.position[0], 0, snapped[1] - surface.position[2]];
|
|
240
|
+
const uv = surface.axes
|
|
241
|
+
.slice(0, 2)
|
|
242
|
+
.map((axis) => axis.reduce((sum, v, i) => sum + v * delta[i], 0));
|
|
243
|
+
const vertices = new Map(liveHost.topology.vertices.map((v) => [v.id, v.position]));
|
|
244
|
+
const polygon = liveHost.topology.faces
|
|
245
|
+
.find((f) => f.id === node.blockFaceId)
|
|
246
|
+
.vertexIds.map((id) => {
|
|
247
|
+
const v = vertices.get(id);
|
|
248
|
+
const delta = v.map((value, i) => value - face.origin[i]);
|
|
249
|
+
return [face.xAxis, face.yAxis].map((axis) => axis.reduce((sum, value, i) => sum + value * delta[i], 0));
|
|
250
|
+
});
|
|
251
|
+
if (surfaceRegionContainsPoint({ kind: 'polygon', points: polygon }, uv)) {
|
|
252
|
+
lastPatch = {
|
|
253
|
+
parentId: liveHost.id,
|
|
254
|
+
position: [uv[0], uv[1], node.position[2]],
|
|
255
|
+
rotation: [...node.rotation],
|
|
256
|
+
supportSlabId: undefined,
|
|
257
|
+
};
|
|
258
|
+
useLiveNodeOverrides.getState().set(node.id, lastPatch);
|
|
259
|
+
markMoved();
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (hosted && liveHost && (liveHost.type !== 'block' || !node.blockFaceId)) {
|
|
265
|
+
const host = liveHost;
|
|
266
|
+
const retainedSurface = getSurfaceProvider(host)
|
|
267
|
+
.surfaces?.(host, { scene })
|
|
268
|
+
.find((s) => s.id === retainedSurfaceId);
|
|
235
269
|
const hostFrame = restingNodePlanFrame(host, (id) => useScene.getState().nodes[id]);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const bounds = nodeRegistry.get(node.type)?.capabilities.dragBounds?.(node, nodes);
|
|
244
|
-
const dimensions = bounds?.size ?? getScaledDimensions(node);
|
|
245
|
-
const center = bounds?.center;
|
|
246
|
-
const localBounds = center
|
|
247
|
-
? {
|
|
248
|
-
min: center.map((v, i) => v - dimensions[i] / 2),
|
|
249
|
-
max: center.map((v, i) => v + dimensions[i] / 2),
|
|
270
|
+
if (host.type === 'item') {
|
|
271
|
+
const plan = resolveItemTransform(host, {
|
|
272
|
+
resolve: (id) => useScene.getState().nodes[id],
|
|
273
|
+
});
|
|
274
|
+
if (plan) {
|
|
275
|
+
hostFrame.position[0] = plan.x;
|
|
276
|
+
hostFrame.position[2] = plan.y;
|
|
250
277
|
}
|
|
251
|
-
|
|
252
|
-
const
|
|
278
|
+
}
|
|
279
|
+
const surfaceFrame = composeFrames(hostFrame, frame(retainedSurface ? [...retainedSurface.position] : [0, hostPose.position[1], 0], retainedSurface ? [...(retainedSurface.rotation ?? [0, 0, 0])] : [0, 0, 0]));
|
|
280
|
+
const normal = surfaceFrame.axes[1];
|
|
281
|
+
const localRotation = originalPlacement?.surfaceLocal?.rotation ?? hostPose.rotation;
|
|
282
|
+
const bottomOffset = retainedSurface
|
|
283
|
+
? -Math.min(...boxCorners(localBounds.min, localBounds.max).map((p) => transformPoint(frame([0, 0, 0], [...localRotation]), p)[1]))
|
|
284
|
+
: 0;
|
|
285
|
+
const y = surfaceFrame.position[1] +
|
|
286
|
+
(bottomOffset -
|
|
287
|
+
normal[0] * (snapped[0] - surfaceFrame.position[0]) -
|
|
288
|
+
normal[2] * (snapped[1] - surfaceFrame.position[2])) /
|
|
289
|
+
normal[1];
|
|
290
|
+
const delta = [
|
|
291
|
+
snapped[0] - hostFrame.position[0],
|
|
292
|
+
y - hostFrame.position[1],
|
|
293
|
+
snapped[1] - hostFrame.position[2],
|
|
294
|
+
];
|
|
295
|
+
const local = hostFrame.axes.map((axis) => axis.reduce((sum, v, i) => sum + v * delta[i], 0));
|
|
296
|
+
const projected = boundsOf(boxCorners(localBounds?.min ?? [-dimensions[0] / 2, 0, -dimensions[2] / 2], localBounds?.max ?? [dimensions[0] / 2, dimensions[1], dimensions[2] / 2]).map((p) => transformPoint(frame(local, hostPose.rotation), p)));
|
|
297
|
+
let occupied = false;
|
|
253
298
|
const pose = resolveSurfacePlacement({
|
|
254
299
|
host,
|
|
300
|
+
surface: retainedSurface,
|
|
255
301
|
childKind: node.type,
|
|
256
|
-
|
|
302
|
+
childId: node.id,
|
|
303
|
+
childFootprint: {
|
|
304
|
+
size: dimensions,
|
|
305
|
+
rotationY: hostPose.rotation[1],
|
|
306
|
+
rotation: hostPose.rotation,
|
|
307
|
+
localBounds,
|
|
308
|
+
},
|
|
257
309
|
hit: {
|
|
258
310
|
point: [
|
|
259
311
|
(projected.min[0] + projected.max[0]) / 2,
|
|
@@ -264,36 +316,63 @@ function buildFloorItemSession(node, startLevelId, nodes) {
|
|
|
264
316
|
},
|
|
265
317
|
origin: local,
|
|
266
318
|
scene: createSceneApi(useScene),
|
|
319
|
+
onReject: (reason) => {
|
|
320
|
+
occupied = reason === 'surface-occupied';
|
|
321
|
+
},
|
|
267
322
|
});
|
|
268
|
-
if (
|
|
269
|
-
|
|
323
|
+
if (occupied) {
|
|
324
|
+
commitBlocked = useInteractionScope.getState().ownedSubtree?.creation.rootId === node.id;
|
|
325
|
+
lastPatch = lastHostedPatch;
|
|
326
|
+
useLiveNodeOverrides.getState().set(node.id, lastPatch);
|
|
327
|
+
markMoved();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (pose &&
|
|
331
|
+
(pose.childFrame === 'surface-local' ? pose.surfaceId === surfaceId : surfaceId === null)) {
|
|
332
|
+
const stored = pose.childFrame === 'surface-local' ? pose.surfaceLocal : pose;
|
|
333
|
+
lastPatch = {
|
|
334
|
+
parentId: host.id,
|
|
335
|
+
position: [...stored.position],
|
|
336
|
+
rotation: [...node.rotation],
|
|
337
|
+
supportSlabId: undefined,
|
|
338
|
+
};
|
|
339
|
+
lastHostedPatch = lastPatch;
|
|
270
340
|
useLiveNodeOverrides.getState().set(node.id, lastPatch);
|
|
271
|
-
|
|
341
|
+
markMoved();
|
|
272
342
|
return;
|
|
273
343
|
}
|
|
274
344
|
}
|
|
275
|
-
const
|
|
276
|
-
|
|
345
|
+
const nextPosition = [
|
|
346
|
+
snapped[0],
|
|
347
|
+
hosted ? 0 : node.position[1],
|
|
348
|
+
snapped[1],
|
|
349
|
+
];
|
|
277
350
|
lastPatch = {
|
|
351
|
+
...(node.blockFaceId ? clearFaceHostItemFields(host) : {}),
|
|
278
352
|
position: nextPosition,
|
|
353
|
+
rotation: levelRotation,
|
|
279
354
|
// Keep parent as the level we resolved at session-start. If
|
|
280
355
|
// somehow it's null (e.g. orphaned item), fall back to the
|
|
281
356
|
// existing parent so we don't write `null` and detach.
|
|
282
357
|
parentId: startLevelId ?? node.parentId,
|
|
283
358
|
};
|
|
359
|
+
Object.assign(lastPatch, resolveSupportSlabPatch({ ...node, ...lastPatch }, nodes));
|
|
284
360
|
useLiveNodeOverrides.getState().set(node.id, lastPatch);
|
|
285
|
-
|
|
361
|
+
markMoved();
|
|
286
362
|
},
|
|
287
363
|
canCommit() {
|
|
288
|
-
return lastPatch !== null;
|
|
364
|
+
return lastPatch !== null && !commitBlocked;
|
|
289
365
|
},
|
|
290
366
|
commit() {
|
|
367
|
+
if (lastInput)
|
|
368
|
+
session.apply(lastInput);
|
|
291
369
|
if (!lastPatch)
|
|
292
370
|
return;
|
|
371
|
+
updateSurfaceNode(node.id, lastPatch);
|
|
293
372
|
useLiveNodeOverrides.getState().clear(node.id);
|
|
294
|
-
useScene.getState().updateNodes([{ id: node.id, data: lastPatch }]);
|
|
295
373
|
},
|
|
296
374
|
};
|
|
375
|
+
return session;
|
|
297
376
|
}
|
|
298
377
|
/**
|
|
299
378
|
* Ceiling items reparent to whichever ceiling polygon contains the
|
package/dist/item/floorplan.d.ts
CHANGED
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import { type FloorplanGeometry, type GeometryContext, type ItemNode } from '@pascal-app/core';
|
|
2
|
+
/**
|
|
3
|
+
* Stage C floor-plan builder for item.
|
|
4
|
+
*
|
|
5
|
+
* Items can be parented to a wall, ceiling, slab, or another item.
|
|
6
|
+
* Position is in the parent's local frame, so we walk the parent chain
|
|
7
|
+
* via `ctx.resolve` to compute the world-space (level-local) transform.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors `getItemFloorplanTransform` from editor/lib/floorplan/items.ts
|
|
10
|
+
* but uses the registry's resolve callback instead of a node map. Logic
|
|
11
|
+
* is identical so visual output matches the legacy.
|
|
12
|
+
*
|
|
13
|
+
* Returns a rotated rectangle of width × depth at the resolved position.
|
|
14
|
+
* Phase 5 follow-up may render `asset.floorPlanUrl` as a custom image
|
|
15
|
+
* overlay when present.
|
|
16
|
+
*/
|
|
17
|
+
type Transform = {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
rotation: number;
|
|
21
|
+
};
|
|
22
|
+
export declare function resolveItemTransform(item: ItemNode, ctx: GeometryContext, cache?: Map<`roof_${string}` | `rseg_${string}` | `site_${string}` | `building_${string}` | `level_${string}` | `elevator_${string}` | `unit_${string}` | `zone_${string}` | `block_${string}` | `ceiling_${string}` | `item_${string}` | `procedural-item_${string}` | `column_${string}` | `construction-dimension_${string}` | `wall_${string}` | `slab_${string}` | `fence_${string}` | `structural-grid_${string}` | `imesh_${string}` | `stair_${string}` | `scan_${string}` | `guide_${string}` | `measurement_${string}` | `spawn_${string}` | `shelf_${string}` | `duct-segment_${string}` | `duct-fitting_${string}` | `duct-terminal_${string}` | `hvac-equipment_${string}` | `lineset_${string}` | `liquid-line_${string}` | `pipe-segment_${string}` | `pipe-fitting_${string}` | `pipe-trap_${string}` | `leanto_${string}` | `door_${string}` | `window_${string}` | `cabinet_${string}` | `cabinet-module_${string}` | `sseg_${string}` | `bvent_${string}` | `rvent_${string}` | `tvent_${string}` | `cupola_${string}` | `eyebrow-vent_${string}` | `gutter_${string}` | `chimney_${string}` | `solarpanel_${string}` | `skylight_${string}` | `dormer_${string}` | `downspout_${string}`, Transform | null>): Transform | null;
|
|
2
23
|
export declare function buildItemContextualDimensions(node: ItemNode, ctx: GeometryContext): FloorplanGeometry | null;
|
|
3
24
|
export declare function buildItemFloorplan(node: ItemNode, ctx: GeometryContext): FloorplanGeometry | null;
|
|
25
|
+
export {};
|
|
4
26
|
//# sourceMappingURL=floorplan.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/item/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EAEtB,KAAK,eAAe,
|
|
1
|
+
{"version":3,"file":"floorplan.d.ts","sourceRoot":"","sources":["../../src/item/floorplan.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EAEtB,KAAK,eAAe,EAKpB,KAAK,QAAQ,EAId,MAAM,kBAAkB,CAAA;AAIzB;;;;;;;;;;;;;;GAcG;AACH,KAAK,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAA;AA2D3D,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,eAAe,EACpB,KAAK,0pCAAyC,GAC7C,SAAS,GAAG,IAAI,CAwKlB;AAED,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,eAAe,GACnB,iBAAiB,GAAG,IAAI,CA+C1B;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,eAAe,GAAG,iBAAiB,GAAG,IAAI,CAmGjG"}
|
package/dist/item/floorplan.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getBlockFaceFrame, getRoofWallFaceFrame, getScaledDimensions, roofFacePointToSegment, useLiveTransforms, } from '@pascal-app/core';
|
|
1
|
+
import { getBlockFaceFrame, getEffectiveNode, getRoofWallFaceFrame, getScaledDimensions, roofFacePointToSegment, useLiveTransforms, } from '@pascal-app/core';
|
|
2
2
|
import { formatLinearMeasurement, readFloorplanMetricNotationOverride } from '@pascal-app/editor';
|
|
3
3
|
import { restingNodePlanFrame } from '../shared/resting-surface-plan';
|
|
4
4
|
// Plan-space rotation convention used by the legacy `rotatePlanVector`
|
|
@@ -13,10 +13,57 @@ function rotateVec(x, y, angle) {
|
|
|
13
13
|
const s = Math.sin(angle);
|
|
14
14
|
return [x * c + y * s, -x * s + y * c];
|
|
15
15
|
}
|
|
16
|
-
function
|
|
16
|
+
function needsFullAncestorFrame(item, ctx) {
|
|
17
|
+
let id = item.parentId;
|
|
18
|
+
const visited = new Set([item.id]);
|
|
19
|
+
while (id && !visited.has(id)) {
|
|
20
|
+
visited.add(id);
|
|
21
|
+
const parent = ctx.resolve(id);
|
|
22
|
+
if (!parent || parent.type === 'level')
|
|
23
|
+
break;
|
|
24
|
+
if (![
|
|
25
|
+
'wall',
|
|
26
|
+
'ceiling',
|
|
27
|
+
'roof',
|
|
28
|
+
'roof-segment',
|
|
29
|
+
'item',
|
|
30
|
+
'shelf',
|
|
31
|
+
'cabinet',
|
|
32
|
+
'cabinet-module',
|
|
33
|
+
'procedural-item',
|
|
34
|
+
'block',
|
|
35
|
+
'slab',
|
|
36
|
+
].includes(parent.type)) {
|
|
37
|
+
const effective = getEffectiveNode(parent);
|
|
38
|
+
const live = useLiveTransforms.getState().get(parent.id);
|
|
39
|
+
const position = live?.position ?? effective.position;
|
|
40
|
+
const rotation = effective.rotation;
|
|
41
|
+
// Main's level-local fallback already handles identity plugins directly on a level.
|
|
42
|
+
if (parent.type === 'column' ||
|
|
43
|
+
(parent.parentId && ctx.resolve(parent.parentId)?.type !== 'level') ||
|
|
44
|
+
position?.some((v) => v !== 0) ||
|
|
45
|
+
live?.rotation ||
|
|
46
|
+
(Array.isArray(rotation) ? rotation.some((v) => v !== 0) : rotation))
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
id = parent.parentId;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
export function resolveItemTransform(item, ctx, cache = new Map()) {
|
|
17
54
|
const cached = cache.get(item.id);
|
|
18
55
|
if (cached !== undefined)
|
|
19
56
|
return cached;
|
|
57
|
+
if (needsFullAncestorFrame(item, ctx)) {
|
|
58
|
+
const f = restingNodePlanFrame(item, ctx.resolve);
|
|
59
|
+
const result = {
|
|
60
|
+
x: f.position[0],
|
|
61
|
+
y: f.position[2],
|
|
62
|
+
rotation: Math.atan2(f.axes[2][0], f.axes[2][2]),
|
|
63
|
+
};
|
|
64
|
+
cache.set(item.id, result);
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
20
67
|
const localRotation = item.rotation[1] ?? 0;
|
|
21
68
|
let result = null;
|
|
22
69
|
const parentNode = item.parentId
|
|
@@ -75,6 +122,18 @@ function resolveItemTransform(item, ctx, cache = new Map()) {
|
|
|
75
122
|
// shelf's own entry follows via the layer's effectiveNode override.
|
|
76
123
|
const shelf = parentNode;
|
|
77
124
|
const live = useLiveTransforms.getState().get(shelf.id);
|
|
125
|
+
if (shelf.parentId &&
|
|
126
|
+
['item', 'shelf', 'cabinet', 'cabinet-module', 'procedural-item'].includes(ctx.resolve(shelf.parentId)?.type ?? '')) {
|
|
127
|
+
const parentT = resolveItemTransform({
|
|
128
|
+
...shelf,
|
|
129
|
+
position: live?.position ?? shelf.position,
|
|
130
|
+
rotation: [0, live?.rotation ?? shelf.rotation[1], 0],
|
|
131
|
+
}, ctx, cache);
|
|
132
|
+
if (!parentT)
|
|
133
|
+
return null;
|
|
134
|
+
const [x, y] = rotateVec(item.position[0], item.position[2], parentT.rotation);
|
|
135
|
+
return { x: parentT.x + x, y: parentT.y + y, rotation: parentT.rotation + localRotation };
|
|
136
|
+
}
|
|
78
137
|
const shelfX = live?.position[0] ?? shelf.position[0];
|
|
79
138
|
const shelfZ = live?.position[2] ?? shelf.position[2];
|
|
80
139
|
const shelfRotationY = live?.rotation ?? shelf.rotation[1] ?? 0;
|
package/dist/item/move-tool.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ import { type PlacementState } from '@pascal-app/editor';
|
|
|
26
26
|
* and dropped from editor.
|
|
27
27
|
*/
|
|
28
28
|
export declare function getInitialState(node: ItemNode, parent?: AnyNode | undefined): PlacementState;
|
|
29
|
-
export declare function MoveItemTool({ node }: {
|
|
29
|
+
export declare function MoveItemTool({ node: source }: {
|
|
30
30
|
node: ItemNode;
|
|
31
31
|
}): import("react").JSX.Element;
|
|
32
32
|
export default MoveItemTool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"move-tool.d.ts","sourceRoot":"","sources":["../../src/item/move-tool.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"move-tool.d.ts","sourceRoot":"","sources":["../../src/item/move-tool.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,OAAO,EAGZ,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,KAAK,cAAc,EAKpB,MAAM,oBAAoB,CAAA;AAI3B;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,QAAQ,EACd,MAAM,GAAE,OAAO,GAAG,SAEL,GACZ,cAAc,CAiFhB;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,+BAkEhE;AAED,eAAe,YAAY,CAAA"}
|
package/dist/item/move-tool.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useScene } from '@pascal-app/core';
|
|
3
|
+
import { canHostSurfaceChild, useScene, } from '@pascal-app/core';
|
|
4
4
|
import { triggerSFX, useDraftNode, useEditor, usePlacementCoordinator, } from '@pascal-app/editor';
|
|
5
|
+
import { useMemo } from 'react';
|
|
5
6
|
import { Vector3 } from 'three';
|
|
6
7
|
/**
|
|
7
8
|
* Phase 5 Stage D — item's registry-driven 3D move affordance.
|
|
@@ -78,7 +79,10 @@ export function getInitialState(node, parent = node.parentId
|
|
|
78
79
|
// handler — which preserves the grab offset — instead of a fresh `enter()`
|
|
79
80
|
// that snaps the item's origin under the cursor. Without this the item
|
|
80
81
|
// teleports the instant it's grabbed.
|
|
81
|
-
if (parent?.type === 'item' ||
|
|
82
|
+
if (parent?.type === 'item' ||
|
|
83
|
+
parent?.type === 'cabinet' ||
|
|
84
|
+
parent?.type === 'procedural-item' ||
|
|
85
|
+
(parent && parent.type !== 'shelf' && canHostSurfaceChild(parent, node.type, node.id))) {
|
|
82
86
|
return {
|
|
83
87
|
surface: 'item-surface',
|
|
84
88
|
wallId: null,
|
|
@@ -107,12 +111,16 @@ export function getInitialState(node, parent = node.parentId
|
|
|
107
111
|
shelfId: null,
|
|
108
112
|
};
|
|
109
113
|
}
|
|
110
|
-
export function MoveItemTool({ node }) {
|
|
114
|
+
export function MoveItemTool({ node: source }) {
|
|
115
|
+
const node = useMemo(() => source.metadata?.isNew
|
|
116
|
+
? (useScene.getState().nodes[source.id] ?? source)
|
|
117
|
+
: source, [source]);
|
|
111
118
|
const draftNode = useDraftNode();
|
|
112
119
|
const meta = typeof node.metadata === 'object' && node.metadata !== null
|
|
113
120
|
? node.metadata
|
|
114
121
|
: {};
|
|
115
122
|
const isNew = !!meta.isNew;
|
|
123
|
+
const isSceneDraft = useMemo(() => isNew && !!useScene.getState().nodes[node.id], [isNew, node.id]);
|
|
116
124
|
const cursor = usePlacementCoordinator({
|
|
117
125
|
asset: node.asset,
|
|
118
126
|
draftNode,
|
|
@@ -120,7 +128,7 @@ export function MoveItemTool({ node }) {
|
|
|
120
128
|
// items create their draft lazily inside the coordinator).
|
|
121
129
|
slots: node.slots,
|
|
122
130
|
// Duplicates start fresh in floor mode; wall/ceiling draft is created lazily by ensureDraft.
|
|
123
|
-
initialState: isNew
|
|
131
|
+
initialState: isNew && !isSceneDraft
|
|
124
132
|
? {
|
|
125
133
|
surface: 'floor',
|
|
126
134
|
wallId: null,
|
|
@@ -132,9 +140,9 @@ export function MoveItemTool({ node }) {
|
|
|
132
140
|
: getInitialState(node),
|
|
133
141
|
// Preserve the original item's scale so Y-position calculations use the correct height.
|
|
134
142
|
defaultScale: isNew ? node.scale : undefined,
|
|
135
|
-
preserveDragOffset:
|
|
143
|
+
preserveDragOffset: !isSceneDraft,
|
|
136
144
|
initDraft: (gridPosition) => {
|
|
137
|
-
if (isNew) {
|
|
145
|
+
if (isNew && !isSceneDraft) {
|
|
138
146
|
// Duplicate: floor items get a draft immediately; wall/ceiling
|
|
139
147
|
// items are created lazily on surface entry.
|
|
140
148
|
gridPosition.copy(new Vector3(...node.position));
|
|
@@ -144,7 +152,7 @@ export function MoveItemTool({ node }) {
|
|
|
144
152
|
}
|
|
145
153
|
else {
|
|
146
154
|
draftNode.adopt(node);
|
|
147
|
-
gridPosition.copy(new Vector3(...
|
|
155
|
+
gridPosition.copy(new Vector3(...draftNode.current.position));
|
|
148
156
|
}
|
|
149
157
|
},
|
|
150
158
|
onCommitted: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/item/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,QAAQ,EAYd,MAAM,kBAAkB,CAAA;AA6ZzB,eAAO,MAAM,YAAY,GAAI,qBAAqB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/item/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,QAAQ,EAYd,MAAM,kBAAkB,CAAA;AA6ZzB,eAAO,MAAM,YAAY,GAAI,qBAAqB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,gCAmEnE,CAAA;AA+CD,eAAO,MAAM,WAAW,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,gCAUvD,CAAA;AA4RD,eAAe,YAAY,CAAA"}
|
package/dist/item/renderer.js
CHANGED
|
@@ -292,8 +292,14 @@ export const ItemRenderer = ({ node: storeNode }) => {
|
|
|
292
292
|
setSettled(true);
|
|
293
293
|
}, [roomClearPreview, setSettled]);
|
|
294
294
|
const content = (_jsx("group", { position: node.position, ref: ref, rotation: node.rotation, visible: node.visible, children: roomClearPreview ? (_jsx(ClearPreviewModel, { node: node })) : (_jsxs(_Fragment, { children: [_jsx(ModelWithRetry, { node: node, setSettled: setSettled }, node.asset.src ?? 'no-src'), node.children?.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] })) }));
|
|
295
|
-
|
|
296
|
-
|
|
295
|
+
// A floor-exit override is still mounted beneath the stored block until commit.
|
|
296
|
+
const faceNode = liveOverrides?.parentId &&
|
|
297
|
+
useScene.getState().nodes[liveOverrides.parentId]?.type === 'level' &&
|
|
298
|
+
useScene.getState().nodes[storeNode.parentId]?.type === 'block'
|
|
299
|
+
? storeNode
|
|
300
|
+
: node;
|
|
301
|
+
if (faceNode.blockFaceId && faceNode.parentId) {
|
|
302
|
+
return (_jsx(BlockFaceHostFrame, { blockId: faceNode.parentId, faceId: faceNode.blockFaceId, children: content }));
|
|
297
303
|
}
|
|
298
304
|
if (!node.roofSegmentId)
|
|
299
305
|
return content;
|
package/dist/level/renderer.js
CHANGED
|
@@ -7,6 +7,6 @@ export const LevelRenderer = ({ node }) => {
|
|
|
7
7
|
const ref = useRef(null);
|
|
8
8
|
useRegistry(node.id, node.type, ref);
|
|
9
9
|
const handlers = useNodeEvents(node, 'level');
|
|
10
|
-
return (_jsx("group", { ref: ref, ...handlers, children: node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) }));
|
|
10
|
+
return (_jsx("group", { ref: ref, visible: node.visible !== false, ...handlers, children: node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) }));
|
|
11
11
|
};
|
|
12
12
|
export default LevelRenderer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/lineset/tool.tsx"],"names":[],"mappings":"AAwFA,QAAA,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/lineset/tool.tsx"],"names":[],"mappings":"AAwFA,QAAA,MAAM,WAAW,0CA6QhB,CAAA;AAgCD,eAAe,WAAW,CAAA"}
|
package/dist/lineset/tool.js
CHANGED