@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
package/dist/block/selection.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { emitter, sceneRegistry,
|
|
3
|
+
import { emitter, sceneRegistry, } from '@pascal-app/core';
|
|
4
4
|
import { cn, EDITOR_LAYER, getFloatingMenuScale, isAngleSnapActive, isGridSnapActive, isMagneticSnapActive, markToolCancelConsumed, meshEditScope, NodeActionMenu, swallowNextClick, triggerSFX, useEditor, useInteractionScope, } from '@pascal-app/editor';
|
|
5
5
|
import { Html } from '@react-three/drei';
|
|
6
6
|
import { createPortal, useFrame, useThree } from '@react-three/fiber';
|
|
@@ -13,6 +13,7 @@ import useBlockEditSession from './edit-session';
|
|
|
13
13
|
import { triangulateBlockFace } from './geometry';
|
|
14
14
|
import { blockGeometrySnapThreshold, resolveBlockGeometrySnap } from './geometry-snap';
|
|
15
15
|
import { BLOCK_WHEEL_OPTIONS, consumeBlockGestureWheel } from './gesture-wheel';
|
|
16
|
+
import { BLOCK_SUPPORT_REFUSAL, commitBlockTopologyEdit, createBlockTopologyPreview, } from './hosted-edit';
|
|
16
17
|
import { blockSfx } from './interaction-sfx';
|
|
17
18
|
import { commitBlockOperation, repeatCommittedBlockOperation, replaceCommittedBlockOperation, } from './last-operation';
|
|
18
19
|
import { resolveLoopCutPointerAction, resolveLoopCutSlideFactor } from './loop-cut-interaction';
|
|
@@ -645,6 +646,7 @@ function LastOperationPanel({ operation, onChange, onClose, onRepeat, }) {
|
|
|
645
646
|
}
|
|
646
647
|
function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, target, mirrorTarget, }) {
|
|
647
648
|
const { camera, gl } = useThree();
|
|
649
|
+
const topologyPreview = useMemo(() => createBlockTopologyPreview(node.id, sceneApi), [node.id, sceneApi]);
|
|
648
650
|
const outerRef = useRef(null);
|
|
649
651
|
const menuScaleRef = useRef(null);
|
|
650
652
|
const menuWorldPositionRef = useRef(new Vector3());
|
|
@@ -726,7 +728,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
726
728
|
const exitEditMode = useCallback(() => {
|
|
727
729
|
cancelDragRef.current?.();
|
|
728
730
|
cancelDragRef.current = null;
|
|
729
|
-
|
|
731
|
+
topologyPreview.clear();
|
|
730
732
|
sceneApi.markDirty(node.id);
|
|
731
733
|
endOwnedScope();
|
|
732
734
|
useBlockEditSession.getState().end(node.id);
|
|
@@ -743,22 +745,22 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
743
745
|
setToolbarPanel(null);
|
|
744
746
|
setError(null);
|
|
745
747
|
playBlockSfx('finish');
|
|
746
|
-
}, [endOwnedScope, node.id, sceneApi.markDirty]);
|
|
748
|
+
}, [endOwnedScope, node.id, sceneApi.markDirty, topologyPreview]);
|
|
747
749
|
useEffect(() => () => {
|
|
748
750
|
cancelDragRef.current?.();
|
|
749
|
-
|
|
751
|
+
topologyPreview.clear();
|
|
750
752
|
sceneApi.markDirty(node.id);
|
|
751
753
|
endOwnedScope();
|
|
752
754
|
useBlockEditSession.getState().end(node.id);
|
|
753
755
|
if (document.body.style.cursor === 'grabbing')
|
|
754
756
|
document.body.style.cursor = '';
|
|
755
|
-
}, [endOwnedScope, node.id, sceneApi.markDirty]);
|
|
757
|
+
}, [endOwnedScope, node.id, sceneApi.markDirty, topologyPreview]);
|
|
756
758
|
useEffect(() => {
|
|
757
759
|
if (editing)
|
|
758
760
|
return;
|
|
759
761
|
cancelDragRef.current?.();
|
|
760
762
|
cancelDragRef.current = null;
|
|
761
|
-
|
|
763
|
+
topologyPreview.clear();
|
|
762
764
|
sceneApi.markDirty(node.id);
|
|
763
765
|
setPreviewTopology(null);
|
|
764
766
|
setToolbarPanel(null);
|
|
@@ -771,7 +773,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
771
773
|
setActiveFaceOperation(null);
|
|
772
774
|
setFaceOperationValue('');
|
|
773
775
|
useBlockEditSession.getState().end(node.id);
|
|
774
|
-
}, [editing, node.id, sceneApi.markDirty]);
|
|
776
|
+
}, [editing, node.id, sceneApi.markDirty, topologyPreview]);
|
|
775
777
|
useEffect(() => {
|
|
776
778
|
if (!editing)
|
|
777
779
|
return;
|
|
@@ -1192,15 +1194,18 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1192
1194
|
setError(result.error);
|
|
1193
1195
|
return;
|
|
1194
1196
|
}
|
|
1197
|
+
if (!topologyPreview.set(result.topology)) {
|
|
1198
|
+
setError(BLOCK_SUPPORT_REFUSAL);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1195
1201
|
latestTopology = result.topology;
|
|
1196
1202
|
latestCommand = command;
|
|
1197
1203
|
setPreviewTopology(result.topology);
|
|
1198
|
-
useLiveNodeOverrides.getState().set(node.id, { topology: result.topology });
|
|
1199
1204
|
sceneApi.markDirty(node.id);
|
|
1200
1205
|
setError(null);
|
|
1201
1206
|
};
|
|
1202
1207
|
const complete = (commit) => {
|
|
1203
|
-
|
|
1208
|
+
topologyPreview.clear();
|
|
1204
1209
|
sceneApi.markDirty(node.id);
|
|
1205
1210
|
setPreviewTopology(null);
|
|
1206
1211
|
setActiveTransform(null);
|
|
@@ -1291,6 +1296,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1291
1296
|
setModalFeedbackMode('free');
|
|
1292
1297
|
setError(null);
|
|
1293
1298
|
beginBlockModalSession({
|
|
1299
|
+
canCommit: () => topologyPreview.valid,
|
|
1294
1300
|
beginInputDrag: interactionApi.beginInputDrag,
|
|
1295
1301
|
cancelRef: cancelDragRef,
|
|
1296
1302
|
cursor: operation === 'translate' ? 'move' : 'crosshair',
|
|
@@ -1314,6 +1320,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1314
1320
|
selection,
|
|
1315
1321
|
target,
|
|
1316
1322
|
sceneApi.markDirty,
|
|
1323
|
+
topologyPreview,
|
|
1317
1324
|
]);
|
|
1318
1325
|
const beginTranslationDrag = useCallback((constraint, event) => {
|
|
1319
1326
|
if (!ownsEditSession() || selectedIds.length === 0 || cancelDragRef.current)
|
|
@@ -1409,10 +1416,16 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1409
1416
|
setError(result.error);
|
|
1410
1417
|
return;
|
|
1411
1418
|
}
|
|
1419
|
+
const wasSupportRefused = !topologyPreview.valid;
|
|
1420
|
+
if (!topologyPreview.set(result.topology)) {
|
|
1421
|
+
setError(BLOCK_SUPPORT_REFUSAL);
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
if (wasSupportRefused)
|
|
1425
|
+
setError(null);
|
|
1412
1426
|
latestDelta = delta;
|
|
1413
1427
|
latestTopology = result.topology;
|
|
1414
1428
|
setPreviewTopology(result.topology);
|
|
1415
|
-
useLiveNodeOverrides.getState().set(node.id, { topology: result.topology });
|
|
1416
1429
|
sceneApi.markDirty(node.id);
|
|
1417
1430
|
};
|
|
1418
1431
|
const finish = (commit) => {
|
|
@@ -1424,7 +1437,13 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1424
1437
|
window.removeEventListener('pointercancel', onPointerCancel);
|
|
1425
1438
|
window.removeEventListener('blur', onPointerCancel);
|
|
1426
1439
|
cancelDragRef.current = null;
|
|
1427
|
-
|
|
1440
|
+
if (!topologyPreview.valid) {
|
|
1441
|
+
// Release keeps the last visible preview; an entirely refused drag cancels.
|
|
1442
|
+
if (!latestTopology)
|
|
1443
|
+
commit = false;
|
|
1444
|
+
setError(null);
|
|
1445
|
+
}
|
|
1446
|
+
topologyPreview.clear();
|
|
1428
1447
|
sceneApi.markDirty(node.id);
|
|
1429
1448
|
restoreInputDragging();
|
|
1430
1449
|
document.body.style.cursor = previousCursor;
|
|
@@ -1465,6 +1484,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1465
1484
|
selection,
|
|
1466
1485
|
target,
|
|
1467
1486
|
sceneApi.markDirty,
|
|
1487
|
+
topologyPreview,
|
|
1468
1488
|
]);
|
|
1469
1489
|
const beginRotationDrag = useCallback((axis, event) => {
|
|
1470
1490
|
if (!ownsEditSession() || selectedIds.length === 0 || cancelDragRef.current)
|
|
@@ -1541,10 +1561,16 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1541
1561
|
setError(result.error);
|
|
1542
1562
|
return;
|
|
1543
1563
|
}
|
|
1564
|
+
const wasSupportRefused = !topologyPreview.valid;
|
|
1565
|
+
if (!topologyPreview.set(result.topology)) {
|
|
1566
|
+
setError(BLOCK_SUPPORT_REFUSAL);
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
if (wasSupportRefused)
|
|
1570
|
+
setError(null);
|
|
1544
1571
|
latestAngle = angle;
|
|
1545
1572
|
latestTopology = result.topology;
|
|
1546
1573
|
setPreviewTopology(result.topology);
|
|
1547
|
-
useLiveNodeOverrides.getState().set(node.id, { topology: result.topology });
|
|
1548
1574
|
sceneApi.markDirty(node.id);
|
|
1549
1575
|
};
|
|
1550
1576
|
const finish = (commit) => {
|
|
@@ -1556,7 +1582,12 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1556
1582
|
window.removeEventListener('pointercancel', onPointerCancel);
|
|
1557
1583
|
window.removeEventListener('blur', onPointerCancel);
|
|
1558
1584
|
cancelDragRef.current = null;
|
|
1559
|
-
|
|
1585
|
+
if (!topologyPreview.valid) {
|
|
1586
|
+
if (!latestTopology)
|
|
1587
|
+
commit = false;
|
|
1588
|
+
setError(null);
|
|
1589
|
+
}
|
|
1590
|
+
topologyPreview.clear();
|
|
1560
1591
|
sceneApi.markDirty(node.id);
|
|
1561
1592
|
restoreInputDragging();
|
|
1562
1593
|
document.body.style.cursor = previousCursor;
|
|
@@ -1600,6 +1631,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1600
1631
|
selection,
|
|
1601
1632
|
target,
|
|
1602
1633
|
sceneApi.markDirty,
|
|
1634
|
+
topologyPreview,
|
|
1603
1635
|
]);
|
|
1604
1636
|
const beginScaleDrag = useCallback((axis, event) => {
|
|
1605
1637
|
if (!ownsEditSession() || selectedIds.length === 0 || cancelDragRef.current)
|
|
@@ -1659,10 +1691,13 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1659
1691
|
setError(result.error);
|
|
1660
1692
|
return;
|
|
1661
1693
|
}
|
|
1694
|
+
if (!topologyPreview.set(result.topology)) {
|
|
1695
|
+
setError(BLOCK_SUPPORT_REFUSAL);
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1662
1698
|
latestFactor = factor;
|
|
1663
1699
|
latestTopology = result.topology;
|
|
1664
1700
|
setPreviewTopology(result.topology);
|
|
1665
|
-
useLiveNodeOverrides.getState().set(node.id, { topology: result.topology });
|
|
1666
1701
|
sceneApi.markDirty(node.id);
|
|
1667
1702
|
setError(null);
|
|
1668
1703
|
};
|
|
@@ -1675,7 +1710,12 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1675
1710
|
window.removeEventListener('pointercancel', onPointerCancel);
|
|
1676
1711
|
window.removeEventListener('blur', onPointerCancel);
|
|
1677
1712
|
cancelDragRef.current = null;
|
|
1678
|
-
|
|
1713
|
+
if (!topologyPreview.valid) {
|
|
1714
|
+
if (!latestTopology)
|
|
1715
|
+
commit = false;
|
|
1716
|
+
setError(null);
|
|
1717
|
+
}
|
|
1718
|
+
topologyPreview.clear();
|
|
1679
1719
|
sceneApi.markDirty(node.id);
|
|
1680
1720
|
restoreInputDragging();
|
|
1681
1721
|
document.body.style.cursor = previousCursor;
|
|
@@ -1719,6 +1759,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1719
1759
|
selection,
|
|
1720
1760
|
target,
|
|
1721
1761
|
sceneApi.markDirty,
|
|
1762
|
+
topologyPreview,
|
|
1722
1763
|
]);
|
|
1723
1764
|
const beginUniformScaleModal = useCallback(() => {
|
|
1724
1765
|
if (!ownsEditSession() || selectedIds.length === 0 || cancelDragRef.current)
|
|
@@ -1771,17 +1812,20 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1771
1812
|
setError(result.error);
|
|
1772
1813
|
return;
|
|
1773
1814
|
}
|
|
1774
|
-
latestFactor = factor;
|
|
1775
1815
|
setTransformNumericInput(typedInput || blockTransformDisplayValue('scale', factor));
|
|
1776
1816
|
setModalFeedbackMode(typedInput ? 'exact' : snapStep > 0 ? 'grid' : 'free');
|
|
1817
|
+
if (!topologyPreview.set(result.topology)) {
|
|
1818
|
+
setError(BLOCK_SUPPORT_REFUSAL);
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
latestFactor = factor;
|
|
1777
1822
|
latestTopology = result.topology;
|
|
1778
1823
|
setPreviewTopology(result.topology);
|
|
1779
|
-
useLiveNodeOverrides.getState().set(node.id, { topology: result.topology });
|
|
1780
1824
|
sceneApi.markDirty(node.id);
|
|
1781
1825
|
setError(null);
|
|
1782
1826
|
};
|
|
1783
1827
|
const complete = (commit) => {
|
|
1784
|
-
|
|
1828
|
+
topologyPreview.clear();
|
|
1785
1829
|
sceneApi.markDirty(node.id);
|
|
1786
1830
|
setPreviewTopology(null);
|
|
1787
1831
|
setActiveTransform(null);
|
|
@@ -1857,6 +1901,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1857
1901
|
setModalFeedbackMode('free');
|
|
1858
1902
|
setError(null);
|
|
1859
1903
|
beginBlockModalSession({
|
|
1904
|
+
canCommit: () => topologyPreview.valid,
|
|
1860
1905
|
beginInputDrag: interactionApi.beginInputDrag,
|
|
1861
1906
|
cancelRef: cancelDragRef,
|
|
1862
1907
|
cursor: 'nwse-resize',
|
|
@@ -1867,6 +1912,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1867
1912
|
});
|
|
1868
1913
|
return true;
|
|
1869
1914
|
}, [
|
|
1915
|
+
topologyPreview,
|
|
1870
1916
|
camera,
|
|
1871
1917
|
commitAdjustableOperation,
|
|
1872
1918
|
displayTopology,
|
|
@@ -1926,13 +1972,16 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1926
1972
|
setError(result.error);
|
|
1927
1973
|
return false;
|
|
1928
1974
|
}
|
|
1975
|
+
setBevelWidth(width);
|
|
1976
|
+
if (!topologyPreview.set(result.topology)) {
|
|
1977
|
+
setError(BLOCK_SUPPORT_REFUSAL);
|
|
1978
|
+
return false;
|
|
1979
|
+
}
|
|
1929
1980
|
activeSegments = segments;
|
|
1930
1981
|
latestWidth = width;
|
|
1931
|
-
setBevelWidth(width);
|
|
1932
1982
|
latestTopology = result.topology;
|
|
1933
1983
|
latestSelection = result.selection;
|
|
1934
1984
|
setPreviewTopology(result.topology);
|
|
1935
|
-
useLiveNodeOverrides.getState().set(node.id, { topology: result.topology });
|
|
1936
1985
|
sceneApi.markDirty(node.id);
|
|
1937
1986
|
setError(null);
|
|
1938
1987
|
return true;
|
|
@@ -1976,7 +2025,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
1976
2025
|
window.removeEventListener('pointercancel', onPointerCancel);
|
|
1977
2026
|
window.removeEventListener('blur', onPointerCancel);
|
|
1978
2027
|
cancelDragRef.current = null;
|
|
1979
|
-
|
|
2028
|
+
topologyPreview.clear();
|
|
1980
2029
|
sceneApi.markDirty(node.id);
|
|
1981
2030
|
restoreInputDragging();
|
|
1982
2031
|
document.body.style.cursor = previousCursor;
|
|
@@ -2022,6 +2071,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
2022
2071
|
selectedIds,
|
|
2023
2072
|
target,
|
|
2024
2073
|
sceneApi.markDirty,
|
|
2074
|
+
topologyPreview,
|
|
2025
2075
|
]);
|
|
2026
2076
|
const previewLoopCut = useCallback((edgeId) => {
|
|
2027
2077
|
if (cancelDragRef.current)
|
|
@@ -2124,13 +2174,16 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
2124
2174
|
setError(result.ok ? 'Could not preview loop cut' : result.error);
|
|
2125
2175
|
return false;
|
|
2126
2176
|
}
|
|
2177
|
+
if (!topologyPreview.set(result.topology)) {
|
|
2178
|
+
setError(BLOCK_SUPPORT_REFUSAL);
|
|
2179
|
+
return false;
|
|
2180
|
+
}
|
|
2127
2181
|
latestFactor = effectiveFactor;
|
|
2128
2182
|
latestTopology = result.topology;
|
|
2129
2183
|
latestSelection = result.selection;
|
|
2130
2184
|
setPreviewTopology(result.topology);
|
|
2131
2185
|
setLoopCutSegments(segments);
|
|
2132
2186
|
setLoopCutFactor(effectiveFactor);
|
|
2133
|
-
useLiveNodeOverrides.getState().set(node.id, { topology: result.topology });
|
|
2134
2187
|
sceneApi.markDirty(node.id);
|
|
2135
2188
|
setError(null);
|
|
2136
2189
|
return true;
|
|
@@ -2174,7 +2227,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
2174
2227
|
window.removeEventListener('pointercancel', onPointerCancel);
|
|
2175
2228
|
window.removeEventListener('blur', onPointerCancel);
|
|
2176
2229
|
cancelDragRef.current = null;
|
|
2177
|
-
|
|
2230
|
+
topologyPreview.clear();
|
|
2178
2231
|
sceneApi.markDirty(node.id);
|
|
2179
2232
|
restoreInputDragging();
|
|
2180
2233
|
document.body.style.cursor = previousCursor;
|
|
@@ -2233,6 +2286,7 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
2233
2286
|
ownsEditSession,
|
|
2234
2287
|
target,
|
|
2235
2288
|
sceneApi.markDirty,
|
|
2289
|
+
topologyPreview,
|
|
2236
2290
|
]);
|
|
2237
2291
|
const beginFaceOperationModal = useBlockFaceOperation({
|
|
2238
2292
|
camera,
|
|
@@ -2270,7 +2324,11 @@ function BlockEditor({ historyApi, interactionApi, node, readOnly, sceneApi, tar
|
|
|
2270
2324
|
setError(result.error);
|
|
2271
2325
|
return;
|
|
2272
2326
|
}
|
|
2273
|
-
sceneApi
|
|
2327
|
+
if (!commitBlockTopologyEdit(sceneApi, node.id, result.topology)) {
|
|
2328
|
+
useInteractionScope.getState().begin(meshEditScope(node.id));
|
|
2329
|
+
setError(BLOCK_SUPPORT_REFUSAL);
|
|
2330
|
+
return;
|
|
2331
|
+
}
|
|
2274
2332
|
const session = useBlockEditSession.getState();
|
|
2275
2333
|
session.setSelection(node.id, {
|
|
2276
2334
|
...result.selection,
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type BlockNode, type DeclaredHostSurface, type SurfaceProvider } from '@pascal-app/core';
|
|
2
|
+
export declare function blockTopSurfaces(host: BlockNode): DeclaredHostSurface[];
|
|
3
|
+
export declare const blockSurfaceProvider: SurfaceProvider;
|
|
4
|
+
//# sourceMappingURL=surface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../../src/block/surface.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,SAAS,EAEd,KAAK,mBAAmB,EAExB,KAAK,eAAe,EAErB,MAAM,kBAAkB,CAAA;AAIzB,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,mBAAmB,EAAE,CAwBvE;AAED,eAAO,MAAM,oBAAoB,EAAE,eA0BlC,CAAA"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getBlockFaceFrame, surfaceRegionContainsPoint, } from '@pascal-app/core';
|
|
2
|
+
const topSurfaces = new WeakMap();
|
|
3
|
+
export function blockTopSurfaces(host) {
|
|
4
|
+
const cached = topSurfaces.get(host.topology);
|
|
5
|
+
if (cached)
|
|
6
|
+
return cached;
|
|
7
|
+
const vertices = new Map(host.topology.vertices.map((v) => [v.id, v.position]));
|
|
8
|
+
const surfaces = host.topology.faces.flatMap((face) => {
|
|
9
|
+
const frame = getBlockFaceFrame(host.topology, face.id);
|
|
10
|
+
if (!frame || frame.normal[1] < 0.75)
|
|
11
|
+
return [];
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
id: face.id,
|
|
15
|
+
position: [0, frame.origin[1], 0],
|
|
16
|
+
normal: frame.normal,
|
|
17
|
+
region: {
|
|
18
|
+
kind: 'polygon',
|
|
19
|
+
points: face.vertexIds.map((id) => {
|
|
20
|
+
const p = vertices.get(id);
|
|
21
|
+
return [p[0], p[2]];
|
|
22
|
+
}),
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
});
|
|
27
|
+
topSurfaces.set(host.topology, surfaces);
|
|
28
|
+
return surfaces;
|
|
29
|
+
}
|
|
30
|
+
export const blockSurfaceProvider = {
|
|
31
|
+
childFrame: 'host-local',
|
|
32
|
+
surfaces: (host) => blockTopSurfaces(host),
|
|
33
|
+
resolveHit(rawHost, hit) {
|
|
34
|
+
if (hit.normalWorldY < 0.75 || !hit.point.every(Number.isFinite))
|
|
35
|
+
return null;
|
|
36
|
+
const host = rawHost;
|
|
37
|
+
const candidates = blockTopSurfaces(host).filter((surface) => surfaceRegionContainsPoint(surface.region, [hit.point[0], hit.point[2]]));
|
|
38
|
+
let nearest = null;
|
|
39
|
+
let distance = Infinity;
|
|
40
|
+
for (const surface of candidates) {
|
|
41
|
+
const face = getBlockFaceFrame(host.topology, surface.id);
|
|
42
|
+
const y = face.origin[1] -
|
|
43
|
+
(face.normal[0] * (hit.point[0] - face.origin[0]) +
|
|
44
|
+
face.normal[2] * (hit.point[2] - face.origin[2])) /
|
|
45
|
+
face.normal[1];
|
|
46
|
+
const delta = Math.abs(y - hit.point[1]);
|
|
47
|
+
if (delta < distance) {
|
|
48
|
+
distance = delta;
|
|
49
|
+
nearest = { ...surface, position: [0, y, 0] };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return nearest;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AnyNodeId, BlockTopology, SceneApi } from '@pascal-app/core';
|
|
2
2
|
import { type SelectionAffordanceProps } from '@pascal-app/editor';
|
|
3
3
|
import { type Dispatch, type MutableRefObject, type SetStateAction } from 'react';
|
|
4
4
|
import type { Camera, Object3D } from 'three';
|
|
@@ -22,7 +22,7 @@ export type UseBlockFaceOperationOptions = {
|
|
|
22
22
|
nodeId: AnyNodeId;
|
|
23
23
|
ownsEditSession: () => boolean;
|
|
24
24
|
playSfx: (action: BlockSfxAction) => void;
|
|
25
|
-
sceneApi: Pick<SceneApi, 'markDirty'>;
|
|
25
|
+
sceneApi: Pick<SceneApi, 'get' | 'markDirty'>;
|
|
26
26
|
selectedIds: string[];
|
|
27
27
|
selection: BlockSelection;
|
|
28
28
|
setActiveFaceOperation: StateSetter<BlockModalFaceOperation | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-block-face-operation.d.ts","sourceRoot":"","sources":["../../src/block/use-block-face-operation.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"use-block-face-operation.d.ts","sourceRoot":"","sources":["../../src/block/use-block-face-operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC1E,OAAO,EAGL,KAAK,wBAAwB,EAI9B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,cAAc,EAGpB,MAAM,OAAO,CAAA;AACd,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC7C,OAAO,EAAE,OAAO,EAAW,MAAM,OAAO,CAAA;AACxC,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAA;AAEnB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAG7B,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EACL,KAAK,sBAAsB,EAK5B,MAAM,mBAAmB,CAAA;AAO1B,KAAK,WAAW,CAAC,CAAC,IAAI,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;AAEjD,MAAM,MAAM,4BAA4B,GAAG;IACzC,cAAc,EAAE,wBAAwB,CAAC,gBAAgB,CAAC,CAAC,gBAAgB,CAAC,CAAA;IAC5E,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,gBAAgB,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;IAChD,MAAM,EAAE,iBAAiB,CAAA;IACzB,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,MAAM,EAAE,CAAC,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAA;IACtF,eAAe,EAAE,aAAa,CAAA;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,oBAAoB,EAAE,gBAAgB,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IACtD,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAA;IAC5B,MAAM,EAAE,SAAS,CAAA;IACjB,eAAe,EAAE,MAAM,OAAO,CAAA;IAC9B,OAAO,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IACzC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,GAAG,WAAW,CAAC,CAAA;IAC7C,WAAW,EAAE,MAAM,EAAE,CAAA;IACrB,SAAS,EAAE,cAAc,CAAA;IACzB,sBAAsB,EAAE,WAAW,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAA;IACnE,QAAQ,EAAE,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACpC,oBAAoB,EAAE,WAAW,CAAC,gBAAgB,CAAC,CAAA;IACnD,qBAAqB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC1C,oBAAoB,EAAE,WAAW,CAAC,sBAAsB,CAAC,CAAA;IACzD,kBAAkB,EAAE,WAAW,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IACrD,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC7C,MAAM,EAAE,QAAQ,CAAA;CACjB,CAAA;AAED,wBAAgB,qBAAqB,CAAC,EACpC,cAAc,EACd,MAAM,EACN,SAAS,EACT,MAAM,EACN,YAAY,EACZ,MAAM,EACN,eAAe,EACf,MAAM,EACN,oBAAoB,EACpB,IAAI,EACJ,MAAM,EACN,eAAe,EACf,OAAO,EACP,QAAQ,EACR,WAAW,EACX,SAAS,EACT,sBAAsB,EACtB,QAAQ,EACR,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,MAAM,GACP,EAAE,4BAA4B,eAMf,uBAAuB,aA8OtC"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { useLiveNodeOverrides, } from '@pascal-app/core';
|
|
2
1
|
import { isGridSnapActive, meshEditScope, swallowNextClick, useEditor, useInteractionScope, } from '@pascal-app/editor';
|
|
3
|
-
import { useCallback } from 'react';
|
|
2
|
+
import { useCallback, useMemo, } from 'react';
|
|
4
3
|
import { Vector2, Vector3 } from 'three';
|
|
5
4
|
import { applyBlockCommand, blockFaceNormal, } from './commands';
|
|
5
|
+
import { BLOCK_SUPPORT_REFUSAL, createBlockTopologyPreview } from './hosted-edit';
|
|
6
6
|
import { blockFaceOperationCommand, blockFaceOperationValueFromPointer, } from './modal-face-operation';
|
|
7
7
|
import { beginBlockModalSession } from './modal-session';
|
|
8
8
|
import { blockPointerDistanceForAxis, blockTransformAxisFromKey, blockTransformNumericInputFromKey, blockTransformNumericValue, } from './modal-transform';
|
|
9
9
|
import { blockLocalPointToClient, blockSelectionCentroid, blockTopologyClientExtent, } from './selection-geometry';
|
|
10
10
|
export function useBlockFaceOperation({ beginInputDrag, camera, cancelRef, canvas, closeToolbar, commit, displayTopology, extent, lastPointerClientRef, mode, nodeId, ownsEditSession, playSfx, sceneApi, selectedIds, selection, setActiveFaceOperation, setError, setFaceOperationAxis, setFaceOperationValue, setModalFeedbackMode, setPreviewTopology, setTransformNumericInput, target, }) {
|
|
11
|
+
const topologyPreview = useMemo(() => createBlockTopologyPreview(nodeId, sceneApi), [nodeId, sceneApi]);
|
|
11
12
|
return useCallback((operation) => {
|
|
12
13
|
if (!ownsEditSession() || mode !== 'face' || selectedIds.length === 0 || cancelRef.current) {
|
|
13
14
|
return false;
|
|
@@ -48,7 +49,8 @@ export function useBlockFaceOperation({ beginInputDrag, camera, cancelRef, canva
|
|
|
48
49
|
const baseTopology = displayTopology;
|
|
49
50
|
let latestTopology = null;
|
|
50
51
|
let latestSelection = null;
|
|
51
|
-
let
|
|
52
|
+
let latestCommand = null;
|
|
53
|
+
let supportRefused = false;
|
|
52
54
|
let typedInput = '';
|
|
53
55
|
let lastClientX = startPointer.x;
|
|
54
56
|
let lastClientY = startPointer.y;
|
|
@@ -77,9 +79,11 @@ export function useBlockFaceOperation({ beginInputDrag, camera, cancelRef, canva
|
|
|
77
79
|
if (Math.abs(value) <= 1e-6) {
|
|
78
80
|
latestTopology = null;
|
|
79
81
|
latestSelection = null;
|
|
80
|
-
|
|
82
|
+
latestCommand = null;
|
|
83
|
+
supportRefused = false;
|
|
84
|
+
setError(null);
|
|
81
85
|
setPreviewTopology(null);
|
|
82
|
-
|
|
86
|
+
topologyPreview.clear();
|
|
83
87
|
sceneApi.markDirty(nodeId);
|
|
84
88
|
return;
|
|
85
89
|
}
|
|
@@ -90,21 +94,29 @@ export function useBlockFaceOperation({ beginInputDrag, camera, cancelRef, canva
|
|
|
90
94
|
else if (!snapping) {
|
|
91
95
|
lastSnapValue = null;
|
|
92
96
|
}
|
|
93
|
-
const
|
|
97
|
+
const command = blockFaceOperationCommand(operation, faceIds, value, extrudeAxis);
|
|
98
|
+
const result = applyBlockCommand(baseTopology, command);
|
|
94
99
|
if (!result.ok) {
|
|
100
|
+
// Command errors keep the last accepted preview; only lost support holds the modal open.
|
|
101
|
+
supportRefused = false;
|
|
95
102
|
setError(result.error);
|
|
96
103
|
return;
|
|
97
104
|
}
|
|
105
|
+
if (!topologyPreview.set(result.topology)) {
|
|
106
|
+
supportRefused = true;
|
|
107
|
+
setError(BLOCK_SUPPORT_REFUSAL);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
98
110
|
latestTopology = result.topology;
|
|
99
111
|
latestSelection = result.selection;
|
|
100
|
-
|
|
112
|
+
latestCommand = command;
|
|
113
|
+
supportRefused = false;
|
|
101
114
|
setPreviewTopology(result.topology);
|
|
102
|
-
useLiveNodeOverrides.getState().set(nodeId, { topology: result.topology });
|
|
103
115
|
sceneApi.markDirty(nodeId);
|
|
104
116
|
setError(null);
|
|
105
117
|
};
|
|
106
118
|
const complete = (commitOperation) => {
|
|
107
|
-
|
|
119
|
+
topologyPreview.clear();
|
|
108
120
|
sceneApi.markDirty(nodeId);
|
|
109
121
|
setPreviewTopology(null);
|
|
110
122
|
setActiveFaceOperation(null);
|
|
@@ -112,8 +124,8 @@ export function useBlockFaceOperation({ beginInputDrag, camera, cancelRef, canva
|
|
|
112
124
|
setFaceOperationValue('');
|
|
113
125
|
setTransformNumericInput('');
|
|
114
126
|
setModalFeedbackMode('free');
|
|
115
|
-
if (commitOperation && latestTopology && latestSelection &&
|
|
116
|
-
commit(baseTopology,
|
|
127
|
+
if (commitOperation && latestTopology && latestSelection && latestCommand) {
|
|
128
|
+
commit(baseTopology, latestCommand, operation === 'extrude' ? 'Extrude' : 'Inset');
|
|
117
129
|
playSfx('operation-commit');
|
|
118
130
|
}
|
|
119
131
|
else if (!commitOperation) {
|
|
@@ -180,6 +192,7 @@ export function useBlockFaceOperation({ beginInputDrag, camera, cancelRef, canva
|
|
|
180
192
|
setModalFeedbackMode('free');
|
|
181
193
|
setError(null);
|
|
182
194
|
beginBlockModalSession({
|
|
195
|
+
canCommit: () => !supportRefused,
|
|
183
196
|
beginInputDrag,
|
|
184
197
|
cancelRef,
|
|
185
198
|
cursor: operation === 'extrude' ? 'ns-resize' : 'nwse-resize',
|
|
@@ -190,6 +203,7 @@ export function useBlockFaceOperation({ beginInputDrag, camera, cancelRef, canva
|
|
|
190
203
|
});
|
|
191
204
|
return true;
|
|
192
205
|
}, [
|
|
206
|
+
topologyPreview,
|
|
193
207
|
beginInputDrag,
|
|
194
208
|
camera,
|
|
195
209
|
cancelRef,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/box-vent/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AA4ItC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,
|
|
1
|
+
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/box-vent/definition.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AA4ItC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,CA4DhE,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/building/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAe,MAAM,kBAAkB,CAAA;AAKjE,eAAO,MAAM,gBAAgB,GAAI,UAAU;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/building/renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAe,MAAM,kBAAkB,CAAA;AAKjE,eAAO,MAAM,gBAAgB,GAAI,UAAU;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,gCAmBhE,CAAA;AAED,eAAe,gBAAgB,CAAA"}
|
|
@@ -7,6 +7,6 @@ export const BuildingRenderer = ({ node }) => {
|
|
|
7
7
|
const ref = useRef(null);
|
|
8
8
|
useRegistry(node.id, node.type, ref);
|
|
9
9
|
const handlers = useNodeEvents(node, 'building');
|
|
10
|
-
return (_jsx("group", { position: node.position, ref: ref, rotation: [node.rotation[0], node.rotation[1], node.rotation[2]], ...handlers, children: (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) }));
|
|
10
|
+
return (_jsx("group", { position: node.position, ref: ref, rotation: [node.rotation[0], node.rotation[1], node.rotation[2]], visible: node.visible !== false, ...handlers, children: (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) }));
|
|
11
11
|
};
|
|
12
12
|
export default BuildingRenderer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/cabinet/definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EAET,WAAW,IAAI,eAAe,EAG9B,oBAAoB,EAMpB,cAAc,EACd,QAAQ,EACT,MAAM,kBAAkB,CAAA;AAyDzB,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAwKzD,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,eAAe,EACrB,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,GAC3C,oBAAoB,EAAE,CAcxB;AAiKD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAA;IAC1B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,GAC1C,mBAAmB,CAWrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,SAAS,mBAAmB,EAAE,EAC1C,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,QAgD9B;AAED,oEAAoE;AACpE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAexE;AAgvDD,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,
|
|
1
|
+
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/cabinet/definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,SAAS,EAET,WAAW,IAAI,eAAe,EAG9B,oBAAoB,EAMpB,cAAc,EACd,QAAQ,EACT,MAAM,kBAAkB,CAAA;AAyDzB,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAwKzD,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,eAAe,EACrB,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,GAC3C,oBAAoB,EAAE,CAcxB;AAiKD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAA;IAC1B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,GAC1C,mBAAmB,CAWrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,SAAS,mBAAmB,EAAE,EAC1C,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,QAgD9B;AAED,oEAAoE;AACpE,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAexE;AAgvDD,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,OAAO,WAAW,CA+MhE,CAAA;AAED,eAAO,MAAM,uBAAuB,EAAE,cAAc,CAAC,OAAO,iBAAiB,CA0K5E,CAAA"}
|
|
@@ -22,7 +22,7 @@ import { cabinetSceneAction } from './scene-action';
|
|
|
22
22
|
import { CabinetModuleNode, CabinetNode } from './schema';
|
|
23
23
|
import { cabinetSlots } from './slots';
|
|
24
24
|
import { backAnchoredModuleZ, isHoodCompartmentType, minCabinetCarcassHeightForStack, stackForCabinet, } from './stack';
|
|
25
|
-
import { cabinetSurfaceProvider } from './surfaces';
|
|
25
|
+
import { cabinetModuleSurfaceProvider, cabinetSurfaceProvider } from './surfaces';
|
|
26
26
|
import { cabinetFloorplanAffectedIds, cabinetTreeChildIds, cabinetTreeHidden, cabinetTreeLabel, } from './tree-structure';
|
|
27
27
|
import { findClosestCabinetWallInPlan, resolveCabinetModuleWallSnapLocal, resolveCabinetRunWallSnap, resolveCabinetWallFaceOffset, } from './wall-snap';
|
|
28
28
|
function isCabinetDuplicableNode(node) {
|
|
@@ -1801,6 +1801,7 @@ export const cabinetDefinition = {
|
|
|
1801
1801
|
// material / materialPreset left undefined — paint mode writes slot refs.
|
|
1802
1802
|
}),
|
|
1803
1803
|
capabilities: {
|
|
1804
|
+
surfacePlacement: 'floor-only',
|
|
1804
1805
|
selectable: { hitVolume: 'bbox' },
|
|
1805
1806
|
movable: {
|
|
1806
1807
|
axes: ['x', 'z'],
|
|
@@ -1995,6 +1996,7 @@ export const cabinetModuleDefinition = {
|
|
|
1995
1996
|
}),
|
|
1996
1997
|
capabilities: {
|
|
1997
1998
|
selectable: { hitVolume: 'bbox' },
|
|
1999
|
+
surfaces: { hosting: cabinetModuleSurfaceProvider },
|
|
1998
2000
|
movable: {
|
|
1999
2001
|
axes: ['x', 'z'],
|
|
2000
2002
|
directDrag: true,
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export declare function cabinetHostedChildUpdates(before: Nodes, after: Nodes): Update[] | null;
|
|
5
|
-
export declare function planCabinetHostedEdit(sceneApi: SceneApi, apply: (staged: SceneApi) => void): Update[] | null;
|
|
1
|
+
import type { AnyNode, AnyNodeId, HandleDescriptor, SceneApi } from '@pascal-app/core';
|
|
2
|
+
export declare function cabinetHostedChildUpdates(before: Record<AnyNodeId, AnyNode>, after: Record<AnyNodeId, AnyNode>): import("../shared/hosted-resize").HostedUpdate[] | null;
|
|
3
|
+
export declare function planCabinetHostedEdit(scene: SceneApi, apply: (staged: SceneApi) => void): import("../shared/hosted-resize").HostedUpdate[] | null;
|
|
6
4
|
export declare function withCabinetHostedChildren<N extends AnyNode>(descriptor: HandleDescriptor<N>): HandleDescriptor<N>;
|
|
7
|
-
export {};
|
|
8
5
|
//# sourceMappingURL=hosted-resize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hosted-resize.d.ts","sourceRoot":"","sources":["../../src/cabinet/hosted-resize.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"hosted-resize.d.ts","sourceRoot":"","sources":["../../src/cabinet/hosted-resize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAQtF,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,EAClC,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,2DAGlC;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,QAAQ,KAAK,IAAI,2DAEvF;AAED,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,OAAO,EACzD,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAC9B,gBAAgB,CAAC,CAAC,CAAC,CAErB"}
|