From 594445ac2017127a2d1922b514a707bf9627cbae Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Thu, 14 Aug 2025 14:43:05 +0530 Subject: [PATCH] move controls snapping and slow movement added --- .../selection3D/moveControls3D.tsx | 46 +++++----- app/src/utils/handleSnap.ts | 87 +++++++++++++++---- 2 files changed, 90 insertions(+), 43 deletions(-) diff --git a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx index 865deea..d578bc1 100644 --- a/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx +++ b/app/src/modules/scene/controls/selectionControls/selection3D/moveControls3D.tsx @@ -5,7 +5,7 @@ import { useSelectedAssets, useSocketStore, useToggleView, } from "../../../../. import * as Types from "../../../../../types/world/worldTypes"; import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys"; import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi"; -import { snapControls } from "../../../../../utils/handleSnap"; +import { getSnappedBasePosition } from "../../../../../utils/handleSnap"; import DistanceFindingControls from "./distanceFindingControls"; import { useParams } from "react-router-dom"; import { useProductContext } from "../../../../simulation/products/productContext"; @@ -49,6 +49,10 @@ function MoveControls3D({ const [initialStates, setInitialStates] = useState>({}); const [isMoving, setIsMoving] = useState(false); const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, }); + // Add a ref to track fine-move base + const fineMoveBaseRef = useRef(null); + const lastPointerPositionRef = useRef(null); + const wasShiftHeldRef = useRef(false); const updateBackend = ( productName: string, @@ -78,10 +82,21 @@ function MoveControls3D({ }; const onKeyUp = (event: KeyboardEvent) => { - const isModifierKey = (!event.shiftKey && !event.ctrlKey); + const keyCombination = detectModifierKeys(event); - if (isModifierKey && keyEvent !== "") { + if (keyCombination === "") { setKeyEvent(""); + } else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") { + setKeyEvent(keyCombination); + } + if (movedObjects[0]) { + const intersectionPoint = new THREE.Vector3(); + raycaster.setFromCamera(pointer, camera); + const hit = raycaster.ray.intersectPlane(plane, intersectionPoint); + if (hit) { + const newOffset = calculateDragOffset(movedObjects[0], intersectionPoint); + setDragOffset(newOffset); + } } }; @@ -231,30 +246,9 @@ function MoveControls3D({ } if (dragOffset) { - let rawBasePosition = new THREE.Vector3().addVectors(intersectionPoint, dragOffset); + const rawBasePosition = new THREE.Vector3().addVectors(intersectionPoint, dragOffset); - if (axisConstraint) { - const currentBasePosition = movedObjects[0].position.clone(); - if (axisConstraint === 'x') { - rawBasePosition = new THREE.Vector3(rawBasePosition.x, currentBasePosition.y, currentBasePosition.z); - } else if (axisConstraint === 'z') { - rawBasePosition = new THREE.Vector3(currentBasePosition.x, currentBasePosition.y, rawBasePosition.z); - } - } - - let moveDistance = keyEvent.includes("Shift") ? 0.05 : 1; - - const initialBasePosition = initialPositions[movedObjects[0].uuid]; - const positionDifference = new THREE.Vector3().subVectors(rawBasePosition, initialBasePosition); - - let adjustedDifference = positionDifference.multiplyScalar(moveDistance); - - const baseNewPosition = new THREE.Vector3().addVectors(initialBasePosition, adjustedDifference); - - if (keyEvent.includes("Ctrl")) { - baseNewPosition.x = snapControls(baseNewPosition.x, keyEvent); - baseNewPosition.z = snapControls(baseNewPosition.z, keyEvent); - } + const baseNewPosition = getSnappedBasePosition({ rawBasePosition, intersectionPoint, movedObjects, axisConstraint, keyEvent, fineMoveBaseRef, lastPointerPositionRef, wasShiftHeldRef }); movedObjects.forEach((movedAsset: THREE.Object3D) => { if (movedAsset.userData.modelUuid) { diff --git a/app/src/utils/handleSnap.ts b/app/src/utils/handleSnap.ts index bd6a74d..d96bf14 100644 --- a/app/src/utils/handleSnap.ts +++ b/app/src/utils/handleSnap.ts @@ -1,22 +1,75 @@ -export function snapControls(value: number, event: string): number { - const CTRL_DISTANCE = 1; // Snap to whole numbers when Ctrl is pressed - const SHIFT_DISTANCE = 0.01; // Snap to half-step increments when Shift is pressed - const CTRL_SHIFT_DISTANCE = 0.1; // Snap to fine increments when both Ctrl and Shift are pressed +import * as THREE from "three"; - switch (event) { - case "Ctrl": - return Math.round(value / CTRL_DISTANCE) * CTRL_DISTANCE; +export function getSnappedBasePosition({ + rawBasePosition, + intersectionPoint, + movedObjects, + axisConstraint, + keyEvent, + fineMoveBaseRef, + lastPointerPositionRef, + wasShiftHeldRef +}: { + rawBasePosition: THREE.Vector3; + intersectionPoint: THREE.Vector3; + movedObjects: THREE.Object3D[]; + axisConstraint: "x" | "z" | null; + keyEvent: string; + fineMoveBaseRef: React.MutableRefObject; + lastPointerPositionRef: React.MutableRefObject; + wasShiftHeldRef: React.MutableRefObject; +}): THREE.Vector3 { + const CTRL_DISTANCE = 0.5; + const SHIFT_DISTANCE = 0.05; + const CTRL_SHIFT_DISTANCE = 0.05; - case "Shift": - return Math.round(value / SHIFT_DISTANCE) * SHIFT_DISTANCE; + const isShiftHeld = keyEvent.includes("Shift"); - case "Ctrl+Shift": - const base = Math.floor(value / CTRL_DISTANCE) * CTRL_DISTANCE; - const offset = - Math.round((value - base) / CTRL_SHIFT_DISTANCE) * CTRL_SHIFT_DISTANCE; - return base + offset; + // Handle Shift toggle state + if (isShiftHeld !== wasShiftHeldRef.current) { + if (isShiftHeld) { + fineMoveBaseRef.current = movedObjects[0].position.clone(); + lastPointerPositionRef.current = intersectionPoint.clone(); + } else { + fineMoveBaseRef.current = null; + } + wasShiftHeldRef.current = isShiftHeld; + } - default: - return value; // No snapping if no modifier key is pressed - } + // Start from raw + let baseNewPosition = rawBasePosition.clone(); + + // Apply snapping / fine move + if (keyEvent === "Ctrl") { + baseNewPosition.set( + Math.round(baseNewPosition.x / CTRL_DISTANCE) * CTRL_DISTANCE, + baseNewPosition.y, + Math.round(baseNewPosition.z / CTRL_DISTANCE) * CTRL_DISTANCE + ); + } else if (keyEvent === "Ctrl+Shift") { + if (isShiftHeld && fineMoveBaseRef.current && lastPointerPositionRef.current) { + const pointerDelta = new THREE.Vector3().subVectors(intersectionPoint, lastPointerPositionRef.current); + baseNewPosition = fineMoveBaseRef.current.clone().add(pointerDelta.multiplyScalar(SHIFT_DISTANCE)); + } + baseNewPosition.set( + Math.round(baseNewPosition.x / CTRL_SHIFT_DISTANCE) * CTRL_SHIFT_DISTANCE, + baseNewPosition.y, + Math.round(baseNewPosition.z / CTRL_SHIFT_DISTANCE) * CTRL_SHIFT_DISTANCE + ); + } else if (isShiftHeld && fineMoveBaseRef.current && lastPointerPositionRef.current) { + const pointerDelta = new THREE.Vector3().subVectors(intersectionPoint, lastPointerPositionRef.current); + baseNewPosition = fineMoveBaseRef.current.clone().add(pointerDelta.multiplyScalar(SHIFT_DISTANCE)); + } + + // Apply axis constraint last + if (axisConstraint) { + const currentBasePosition = movedObjects[0].position.clone(); + if (axisConstraint === 'x') { + baseNewPosition.set(baseNewPosition.x, currentBasePosition.y, currentBasePosition.z); + } else if (axisConstraint === 'z') { + baseNewPosition.set(currentBasePosition.x, currentBasePosition.y, baseNewPosition.z); + } + } + + return baseNewPosition; }