move controls snapping and slow movement added
This commit is contained in:
@@ -5,7 +5,7 @@ import { useSelectedAssets, useSocketStore, useToggleView, } from "../../../../.
|
|||||||
import * as Types from "../../../../../types/world/worldTypes";
|
import * as Types from "../../../../../types/world/worldTypes";
|
||||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||||
import { snapControls } from "../../../../../utils/handleSnap";
|
import { getSnappedBasePosition } from "../../../../../utils/handleSnap";
|
||||||
import DistanceFindingControls from "./distanceFindingControls";
|
import DistanceFindingControls from "./distanceFindingControls";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useProductContext } from "../../../../simulation/products/productContext";
|
import { useProductContext } from "../../../../simulation/products/productContext";
|
||||||
@@ -49,6 +49,10 @@ function MoveControls3D({
|
|||||||
const [initialStates, setInitialStates] = useState<Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }>>({});
|
const [initialStates, setInitialStates] = useState<Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }>>({});
|
||||||
const [isMoving, setIsMoving] = useState(false);
|
const [isMoving, setIsMoving] = useState(false);
|
||||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
||||||
|
// Add a ref to track fine-move base
|
||||||
|
const fineMoveBaseRef = useRef<THREE.Vector3 | null>(null);
|
||||||
|
const lastPointerPositionRef = useRef<THREE.Vector3 | null>(null);
|
||||||
|
const wasShiftHeldRef = useRef(false);
|
||||||
|
|
||||||
const updateBackend = (
|
const updateBackend = (
|
||||||
productName: string,
|
productName: string,
|
||||||
@@ -78,10 +82,21 @@ function MoveControls3D({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onKeyUp = (event: KeyboardEvent) => {
|
const onKeyUp = (event: KeyboardEvent) => {
|
||||||
const isModifierKey = (!event.shiftKey && !event.ctrlKey);
|
const keyCombination = detectModifierKeys(event);
|
||||||
|
|
||||||
if (isModifierKey && keyEvent !== "") {
|
if (keyCombination === "") {
|
||||||
setKeyEvent("");
|
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) {
|
if (dragOffset) {
|
||||||
let rawBasePosition = new THREE.Vector3().addVectors(intersectionPoint, dragOffset);
|
const rawBasePosition = new THREE.Vector3().addVectors(intersectionPoint, dragOffset);
|
||||||
|
|
||||||
if (axisConstraint) {
|
const baseNewPosition = getSnappedBasePosition({ rawBasePosition, intersectionPoint, movedObjects, axisConstraint, keyEvent, fineMoveBaseRef, lastPointerPositionRef, wasShiftHeldRef });
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
movedObjects.forEach((movedAsset: THREE.Object3D) => {
|
movedObjects.forEach((movedAsset: THREE.Object3D) => {
|
||||||
if (movedAsset.userData.modelUuid) {
|
if (movedAsset.userData.modelUuid) {
|
||||||
|
|||||||
@@ -1,22 +1,75 @@
|
|||||||
export function snapControls(value: number, event: string): number {
|
import * as THREE from "three";
|
||||||
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
|
|
||||||
|
|
||||||
switch (event) {
|
export function getSnappedBasePosition({
|
||||||
case "Ctrl":
|
rawBasePosition,
|
||||||
return Math.round(value / CTRL_DISTANCE) * CTRL_DISTANCE;
|
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<THREE.Vector3 | null>;
|
||||||
|
lastPointerPositionRef: React.MutableRefObject<THREE.Vector3 | null>;
|
||||||
|
wasShiftHeldRef: React.MutableRefObject<boolean>;
|
||||||
|
}): THREE.Vector3 {
|
||||||
|
const CTRL_DISTANCE = 0.5;
|
||||||
|
const SHIFT_DISTANCE = 0.05;
|
||||||
|
const CTRL_SHIFT_DISTANCE = 0.05;
|
||||||
|
|
||||||
case "Shift":
|
const isShiftHeld = keyEvent.includes("Shift");
|
||||||
return Math.round(value / SHIFT_DISTANCE) * SHIFT_DISTANCE;
|
|
||||||
|
|
||||||
case "Ctrl+Shift":
|
// Handle Shift toggle state
|
||||||
const base = Math.floor(value / CTRL_DISTANCE) * CTRL_DISTANCE;
|
if (isShiftHeld !== wasShiftHeldRef.current) {
|
||||||
const offset =
|
if (isShiftHeld) {
|
||||||
Math.round((value - base) / CTRL_SHIFT_DISTANCE) * CTRL_SHIFT_DISTANCE;
|
fineMoveBaseRef.current = movedObjects[0].position.clone();
|
||||||
return base + offset;
|
lastPointerPositionRef.current = intersectionPoint.clone();
|
||||||
|
} else {
|
||||||
|
fineMoveBaseRef.current = null;
|
||||||
|
}
|
||||||
|
wasShiftHeldRef.current = isShiftHeld;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
// Start from raw
|
||||||
return value; // No snapping if no modifier key is pressed
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user