rotational snap added
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import * as THREE from "three";
|
||||
|
||||
function normalizeDegrees(deg: number): number {
|
||||
// Wrap into [-180, 180]
|
||||
deg = (deg + 180) % 360;
|
||||
if (deg < 0) deg += 360;
|
||||
return deg - 180;
|
||||
}
|
||||
|
||||
export function handleAssetRotationSnap({
|
||||
currentRotation,
|
||||
rotationDelta,
|
||||
keyEvent,
|
||||
snapBaseRef,
|
||||
prevRotationRef,
|
||||
wasShiftHeldRef
|
||||
}: {
|
||||
currentRotation: THREE.Euler;
|
||||
rotationDelta: number;
|
||||
keyEvent: string;
|
||||
snapBaseRef: React.MutableRefObject<number | null>;
|
||||
prevRotationRef: React.MutableRefObject<number | null>;
|
||||
wasShiftHeldRef: React.MutableRefObject<boolean>;
|
||||
}): number {
|
||||
const SHIFT_SPEED = 0.5; // Fine rotation speed
|
||||
const NORMAL_SPEED = 5; // Normal rotation speed
|
||||
const CTRL_SNAP_DEG = 15; // 15 degrees
|
||||
const CTRL_SHIFT_SNAP_DEG = 5; // 5 degrees
|
||||
|
||||
const isShiftHeld = keyEvent.includes("Shift");
|
||||
const isCtrlHeld = keyEvent.includes("Ctrl");
|
||||
|
||||
const speedFactor = isShiftHeld ? SHIFT_SPEED : NORMAL_SPEED;
|
||||
let newRotationDeltaDeg = THREE.MathUtils.radToDeg(rotationDelta) * speedFactor;
|
||||
|
||||
const modifierChanged = isShiftHeld !== wasShiftHeldRef.current;
|
||||
if (modifierChanged) {
|
||||
wasShiftHeldRef.current = isShiftHeld;
|
||||
}
|
||||
|
||||
if (isCtrlHeld) {
|
||||
const snapDeg = isShiftHeld ? CTRL_SHIFT_SNAP_DEG : CTRL_SNAP_DEG;
|
||||
|
||||
// Store base rotation in degrees when Ctrl first pressed
|
||||
if (snapBaseRef.current === null) {
|
||||
snapBaseRef.current = normalizeDegrees(THREE.MathUtils.radToDeg(currentRotation.y));
|
||||
} else {
|
||||
snapBaseRef.current = normalizeDegrees(snapBaseRef.current + newRotationDeltaDeg);
|
||||
}
|
||||
|
||||
// Snap to nearest increment
|
||||
const snappedDeg = Math.round(snapBaseRef.current / snapDeg) * snapDeg;
|
||||
|
||||
// Normalize so it never goes beyond [-180, 180]
|
||||
const normalizedSnappedDeg = normalizeDegrees(snappedDeg);
|
||||
|
||||
// Convert back to radians for returning delta
|
||||
newRotationDeltaDeg = normalizedSnappedDeg - THREE.MathUtils.radToDeg(currentRotation.y);
|
||||
|
||||
} else {
|
||||
snapBaseRef.current = null;
|
||||
}
|
||||
|
||||
prevRotationRef.current = currentRotation.y;
|
||||
|
||||
return THREE.MathUtils.degToRad(newRotationDeltaDeg);
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import { useProductContext } from "../../../../simulation/products/productContex
|
||||
import { getUserData } from "../../../../../functions/getUserData";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { handleAssetRotationSnap } from "./functions/handleAssetRotationSnap";
|
||||
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
|
||||
@@ -23,7 +25,6 @@ function RotateControls3D({
|
||||
setDuplicatedObjects
|
||||
}: any) {
|
||||
const { camera, gl, scene, pointer, raycaster } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
|
||||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
@@ -38,15 +39,16 @@ function RotateControls3D({
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
|
||||
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
|
||||
const [initialRotations, setInitialRotations] = useState<Record<string, THREE.Euler>>({});
|
||||
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
|
||||
const [isRotating, setIsRotating] = useState(false);
|
||||
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
|
||||
const rotationCenter = useRef<THREE.Vector3 | null>(null);
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({
|
||||
left: false,
|
||||
right: false,
|
||||
});
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
||||
const snapBaseRef = useRef<number | null>(null);
|
||||
const prevRotationRef = useRef<number | null>(null);
|
||||
const wasShiftHeldRef = useRef(false);
|
||||
|
||||
const updateBackend = useCallback((
|
||||
productName: string,
|
||||
@@ -98,6 +100,8 @@ function RotateControls3D({
|
||||
};
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
const keyCombination = detectModifierKeys(event);
|
||||
|
||||
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return;
|
||||
|
||||
if (event.key.toLowerCase() === "r") {
|
||||
@@ -105,6 +109,15 @@ function RotateControls3D({
|
||||
rotateAssets();
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCombination !== keyEvent) {
|
||||
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||
setKeyEvent(keyCombination);
|
||||
} else {
|
||||
setKeyEvent("");
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key.toLowerCase() === "escape") {
|
||||
event.preventDefault();
|
||||
resetToInitialRotations();
|
||||
@@ -113,11 +126,24 @@ function RotateControls3D({
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
const keyCombination = detectModifierKeys(event);
|
||||
|
||||
if (keyCombination === "") {
|
||||
setKeyEvent("");
|
||||
} else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||
setKeyEvent(keyCombination);
|
||||
}
|
||||
const currentPointer = new THREE.Vector2(pointer.x, pointer.y);
|
||||
prevPointerPosition.current = currentPointer.clone();
|
||||
};
|
||||
|
||||
if (!toggleView) {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
canvasElement.addEventListener("keydown", onKeyDown);
|
||||
canvasElement?.addEventListener("keyup", onKeyUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
@@ -125,8 +151,9 @@ function RotateControls3D({
|
||||
canvasElement.removeEventListener("pointermove", onPointerMove);
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
}, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects]);
|
||||
}, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent]);
|
||||
|
||||
const resetToInitialRotations = useCallback(() => {
|
||||
rotatedObjects.forEach((obj: THREE.Object3D) => {
|
||||
@@ -154,26 +181,27 @@ function RotateControls3D({
|
||||
useFrame(() => {
|
||||
if (!isRotating || rotatedObjects.length === 0) return;
|
||||
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
const currentPointer = new THREE.Vector2(pointer.x, pointer.y);
|
||||
|
||||
if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) {
|
||||
if (point) {
|
||||
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
|
||||
}
|
||||
prevPointerPosition.current = currentPointer.clone();
|
||||
return;
|
||||
}
|
||||
|
||||
if (point && prevPointerPosition.current && rotationCenter.current) {
|
||||
if (prevPointerPosition.current && rotationCenter.current) {
|
||||
const center = rotationCenter.current;
|
||||
const deltaX = currentPointer.x - prevPointerPosition.current.x;
|
||||
|
||||
const currentAngle = Math.atan2(point.z - center.z, point.x - center.x);
|
||||
const prevAngle = Math.atan2(
|
||||
prevPointerPosition.current.y - center.z,
|
||||
prevPointerPosition.current.x - center.x
|
||||
);
|
||||
const angleDelta = prevAngle - currentAngle;
|
||||
const rawAngleDelta = deltaX;
|
||||
|
||||
const angleDelta = handleAssetRotationSnap({
|
||||
currentRotation: rotatedObjects[0].rotation,
|
||||
rotationDelta: rawAngleDelta,
|
||||
keyEvent,
|
||||
snapBaseRef,
|
||||
prevRotationRef,
|
||||
wasShiftHeldRef
|
||||
});
|
||||
|
||||
const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
|
||||
|
||||
@@ -186,7 +214,7 @@ function RotateControls3D({
|
||||
}
|
||||
});
|
||||
|
||||
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
|
||||
prevPointerPosition.current = currentPointer.clone();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -208,17 +236,13 @@ function RotateControls3D({
|
||||
setInitialRotations(rotations);
|
||||
setInitialPositions(positions);
|
||||
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
|
||||
if (point) {
|
||||
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
|
||||
}
|
||||
prevPointerPosition.current = new THREE.Vector2(pointer.x, pointer.y);
|
||||
|
||||
setRotatedObjects(selectedAssets);
|
||||
setIsRotating(true);
|
||||
}, [selectedAssets, camera, pointer, raycaster, plane]);
|
||||
}, [selectedAssets, camera, pointer, raycaster]);
|
||||
|
||||
const placeRotatedAssets = useCallback(() => {
|
||||
if (rotatedObjects.length === 0) return;
|
||||
|
||||
@@ -23,7 +23,6 @@ export function useHumanEventManager() {
|
||||
}, [isReset, isPlaying]);
|
||||
|
||||
const addHumanToMonitor = (humanId: string, callback: () => void, actionUuid: string) => {
|
||||
console.log('humanId: ', humanId);
|
||||
const human = getHumanById(humanId);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, actionUuid);
|
||||
if (!human || !action || (action.actionType !== 'assembly' && action.actionType !== 'worker' && action.actionType !== 'operator') || !humanEventManagerRef.current) return;
|
||||
|
||||
Reference in New Issue
Block a user