From ef2baacf558cd822e6a53dc3fe88b5fcdc3bd06e Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Fri, 2 May 2025 14:04:52 +0530 Subject: [PATCH] added snap function for ctrl and ctrl+shift key --- .../selectionControls/moveControls.tsx | 47 ++++++++--- app/src/utils/handleSnap.ts | 79 +++++-------------- 2 files changed, 56 insertions(+), 70 deletions(-) diff --git a/app/src/modules/scene/controls/selectionControls/moveControls.tsx b/app/src/modules/scene/controls/selectionControls/moveControls.tsx index cc9ce50..f29396d 100644 --- a/app/src/modules/scene/controls/selectionControls/moveControls.tsx +++ b/app/src/modules/scene/controls/selectionControls/moveControls.tsx @@ -1,7 +1,7 @@ import * as THREE from "three"; -import { useEffect, useMemo, useRef } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; -import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store"; +import { useFloorItems, useSelectedAssets, useSocketStore, useStartSimulation, useToggleView } from "../../../../store/store"; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { toast } from "react-toastify"; import * as Types from "../../../../types/world/worldTypes"; @@ -10,6 +10,7 @@ import { useEventsStore } from "../../../../store/simulation/useEventsStore"; import { useProductStore } from "../../../../store/simulation/useProductStore"; import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; +import { snapControls } from "../../../../utils/handleSnap"; function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) { const { camera, controls, gl, scene, pointer, raycaster } = useThree(); @@ -21,6 +22,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore(); const itemsData = useRef([]); + const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("") const email = localStorage.getItem('email') const organization = (email!.split("@")[1]).split(".")[0]; @@ -54,6 +56,15 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje const onPointerMove = () => { isMoving = true; }; + const onKeyUp = (event: KeyboardEvent) => { + // When any modifier is released, reset snap + const isModifierKey = + event.key === "Control" || event.key === "Shift"; + + if (isModifierKey) { + setKeyEvent(""); + } + }; const onPointerUp = (event: PointerEvent) => { if (!isMoving && movedObjects.length > 0 && event.button === 0) { @@ -75,18 +86,28 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje setMovedObjects([]); itemsData.current = []; } + setKeyEvent("") }; const onKeyDown = (event: KeyboardEvent) => { const keyCombination = detectModifierKeys(event); if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) return; + + if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") { + // update state here + setKeyEvent(keyCombination) + } else { + setKeyEvent("") + } + if (keyCombination === "G") { if (selectedAssets.length > 0) { moveAssets(); itemsData.current = floorItems.filter((item: { modelUuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)); } } + if (keyCombination === "ESCAPE") { event.preventDefault(); @@ -109,6 +130,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje canvasElement.addEventListener("pointermove", onPointerMove); canvasElement.addEventListener("pointerup", onPointerUp); canvasElement.addEventListener("keydown", onKeyDown); + canvasElement?.addEventListener("keyup", onKeyUp); } return () => { @@ -116,12 +138,11 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje canvasElement.removeEventListener("pointermove", onPointerMove); canvasElement.removeEventListener("pointerup", onPointerUp); canvasElement.removeEventListener("keydown", onKeyDown); + canvasElement?.removeEventListener("keyup", onKeyUp); }; - }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects]); + }, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent]); - const gridSize = 0.25; - const moveSpeed = 0.25; - const isGridSnap = false; + let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25; useFrame(() => { if (movedObjects.length > 0) { @@ -132,10 +153,17 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje if (point) { let targetX = point.x; let targetZ = point.z; + if (keyEvent === "Ctrl") { + targetX = snapControls(targetX, "Ctrl"); + targetZ = snapControls(targetZ, "Ctrl"); + } else if (keyEvent === "Ctrl+Shift") { + targetX = snapControls(targetX, "Ctrl+Shift"); + targetZ = snapControls(targetZ, "Ctrl+Shift"); + } else if (keyEvent === "Shift") { + targetX = snapControls(targetX, "Shift"); + targetZ = snapControls(targetZ, "Shift"); + } else { - if (isGridSnap) { - targetX = Math.round(point.x / gridSize) * gridSize; - targetZ = Math.round(point.z / gridSize) * gridSize; } const position = new THREE.Vector3(); @@ -278,6 +306,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje setMovedObjects([]); setRotatedObjects([]); setSelectedAssets([]); + setKeyEvent("") } return null; diff --git a/app/src/utils/handleSnap.ts b/app/src/utils/handleSnap.ts index 35a0f96..bd6a74d 100644 --- a/app/src/utils/handleSnap.ts +++ b/app/src/utils/handleSnap.ts @@ -1,65 +1,22 @@ -import { useEffect } from "react"; -import { detectModifierKeys } from "./shortcutkeys/detectModifierKeys"; +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 -// Define the props expected by the component -type SnapControlsProps = { - x_point: number; // X-coordinate value to snap - z_point: number; // Z-coordinate value to snap -}; + switch (event) { + case "Ctrl": + return Math.round(value / CTRL_DISTANCE) * CTRL_DISTANCE; -const SnapControls: React.FC = ({ x_point, z_point }) => { - useEffect(() => { - // Handler function for keypress events - const handleKeyPress = (event: KeyboardEvent) => { - // Detect which modifier keys (Ctrl, Shift, etc.) are currently pressed - const keyCombination = detectModifierKeys(event); + case "Shift": + return Math.round(value / SHIFT_DISTANCE) * SHIFT_DISTANCE; - // Define the snapping distances - const CTRL_DISTANCE = 1; // Coarse snapping when Ctrl is pressed - const SHIFT_DISTANCE = 0.01; // Fine snapping when Shift is pressed + 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; - // Prevent default behavior to avoid unintended side effects - event.preventDefault(); - - // Create new coordinates to apply snapping to - let newX = x_point; - let newZ = z_point; - - // Check for modifier key combinations and apply appropriate snapping logic - if (keyCombination) { - if (keyCombination === "Ctrl") { - // Snap to nearest integer unit - newX = Math.round(x_point / CTRL_DISTANCE) * CTRL_DISTANCE; - newZ = Math.round(z_point / CTRL_DISTANCE) * CTRL_DISTANCE; - return { newX, newZ }; - } - - if (keyCombination === "Ctrl+Shift") { - // Snap to nearest small unit (0.01) - newX = Math.round(x_point / SHIFT_DISTANCE) * SHIFT_DISTANCE; - newZ = Math.round(z_point / SHIFT_DISTANCE) * SHIFT_DISTANCE; - return { newX, newZ }; - } - - if (keyCombination === "Shift") { - // Incorrect snapping logic — rounding the point and multiplying by small value - newX = Math.round(x_point) * SHIFT_DISTANCE; - newZ = Math.round(z_point) * SHIFT_DISTANCE; - return { newX, newZ }; - } - } - }; - - // Attach keydown event listener when the component mounts - window.addEventListener("keydown", handleKeyPress); - - // Clean up the event listener when the component unmounts - return () => { - window.removeEventListener("keydown", handleKeyPress); - }; - }, []); // Empty dependency array means this effect runs once on mount - - return null; // This component doesn’t render anything -}; - -export default SnapControls; + default: + return value; // No snapping if no modifier key is pressed + } +}