Merge remote-tracking branch 'origin/main-dev' into main-demo
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import * as THREE from "three";
|
||||
|
||||
export function handleAssetRotationSnap({
|
||||
object,
|
||||
pointerDeltaX,
|
||||
keyEvent,
|
||||
snapBaseRef,
|
||||
prevRotationRef,
|
||||
wasShiftHeldRef
|
||||
}: {
|
||||
object: THREE.Object3D;
|
||||
pointerDeltaX: 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 deltaAngle = pointerDeltaX * speedFactor;
|
||||
|
||||
// Track if modifier changed
|
||||
const modifierChanged = isShiftHeld !== wasShiftHeldRef.current;
|
||||
if (modifierChanged) {
|
||||
wasShiftHeldRef.current = isShiftHeld;
|
||||
if (isCtrlHeld) snapBaseRef.current = null;
|
||||
}
|
||||
|
||||
if (isCtrlHeld) {
|
||||
const snapDeg = isShiftHeld ? CTRL_SHIFT_SNAP_DEG : CTRL_SNAP_DEG;
|
||||
const snapRad = THREE.MathUtils.degToRad(snapDeg);
|
||||
|
||||
// Get current Y rotation from object's quaternion
|
||||
const euler = new THREE.Euler().setFromQuaternion(object.quaternion, 'YXZ');
|
||||
let currentAngle = euler.y;
|
||||
|
||||
// Initialize snap base on first frame
|
||||
if (snapBaseRef.current === null) {
|
||||
snapBaseRef.current = currentAngle;
|
||||
prevRotationRef.current = currentAngle;
|
||||
}
|
||||
|
||||
// Accumulate the total rotation from the base
|
||||
const totalRotation = snapBaseRef.current + deltaAngle;
|
||||
|
||||
// Snap to nearest increment
|
||||
const snappedAngle = Math.round(totalRotation / snapRad) * snapRad;
|
||||
|
||||
// Calculate the delta needed to reach the snapped angle from current rotation
|
||||
let targetDelta = snappedAngle - currentAngle;
|
||||
|
||||
// Handle wrapping around 360 degrees
|
||||
if (Math.abs(targetDelta) > Math.PI) {
|
||||
targetDelta = targetDelta - Math.sign(targetDelta) * 2 * Math.PI;
|
||||
}
|
||||
|
||||
// Update snap base for next frame
|
||||
snapBaseRef.current = totalRotation;
|
||||
|
||||
return targetDelta;
|
||||
} else {
|
||||
// Reset snapping when Ctrl is not held
|
||||
snapBaseRef.current = null;
|
||||
prevRotationRef.current = null;
|
||||
return deltaAngle;
|
||||
}
|
||||
}
|
||||
@@ -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,6 +39,7 @@ 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);
|
||||
@@ -45,6 +47,9 @@ function RotateControls3D({
|
||||
const rotationCenter = useRef<THREE.Vector3 | null>(null);
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
||||
const { contextAction, setContextAction } = useContextActionStore()
|
||||
const snapBaseRef = useRef<number | null>(null);
|
||||
const prevRotationRef = useRef<number | null>(null);
|
||||
const wasShiftHeldRef = useRef(false);
|
||||
|
||||
const updateBackend = useCallback((
|
||||
productName: string,
|
||||
@@ -103,6 +108,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") {
|
||||
@@ -110,6 +117,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();
|
||||
@@ -118,11 +134,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 () => {
|
||||
@@ -130,68 +159,72 @@ 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) => {
|
||||
const uuid = obj.uuid;
|
||||
if (obj.userData.modelUuid) {
|
||||
const initialRotation = initialRotations[uuid];
|
||||
const initialPosition = initialPositions[uuid];
|
||||
setTimeout(() => {
|
||||
rotatedObjects.forEach((obj: THREE.Object3D) => {
|
||||
const uuid = obj.uuid;
|
||||
if (obj.userData.modelUuid) {
|
||||
const initialRotation = initialRotations[uuid];
|
||||
const initialPosition = initialPositions[uuid];
|
||||
|
||||
if (initialRotation && initialPosition) {
|
||||
const rotationArray: [number, number, number] = [initialRotation.x, initialRotation.y, initialRotation.z,];
|
||||
const positionArray: [number, number, number] = [initialPosition.x, initialPosition.y, initialPosition.z,];
|
||||
if (initialRotation && initialPosition) {
|
||||
const rotationArray: [number, number, number] = [initialRotation.x, initialRotation.y, initialRotation.z,];
|
||||
const positionArray: [number, number, number] = [initialPosition.x, initialPosition.y, initialPosition.z,];
|
||||
|
||||
updateAsset(obj.userData.modelUuid, {
|
||||
rotation: rotationArray,
|
||||
position: positionArray,
|
||||
});
|
||||
updateAsset(obj.userData.modelUuid, {
|
||||
rotation: rotationArray,
|
||||
position: positionArray,
|
||||
});
|
||||
|
||||
obj.rotation.copy(initialRotation);
|
||||
obj.position.copy(initialPosition);
|
||||
obj.rotation.copy(initialRotation);
|
||||
obj.position.copy(initialPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 100)
|
||||
}, [rotatedObjects, initialRotations, initialPositions, updateAsset]);
|
||||
|
||||
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 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 rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
|
||||
const deltaX = currentPointer.x - prevPointerPosition.current.x;
|
||||
|
||||
rotatedObjects.forEach((obj: THREE.Object3D) => {
|
||||
if (obj.userData.modelUuid) {
|
||||
const angleDelta = handleAssetRotationSnap({
|
||||
object: obj,
|
||||
pointerDeltaX: deltaX,
|
||||
keyEvent,
|
||||
snapBaseRef,
|
||||
prevRotationRef,
|
||||
wasShiftHeldRef
|
||||
});
|
||||
|
||||
const relativePosition = new THREE.Vector3().subVectors(obj.position, center);
|
||||
const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
|
||||
relativePosition.applyMatrix4(rotationMatrix);
|
||||
obj.position.copy(center).add(relativePosition);
|
||||
obj.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), angleDelta);
|
||||
|
||||
const rotationQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), angleDelta);
|
||||
obj.quaternion.multiply(rotationQuat);
|
||||
obj.rotation.setFromQuaternion(obj.quaternion);
|
||||
}
|
||||
});
|
||||
|
||||
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
|
||||
prevPointerPosition.current = currentPointer.clone();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -213,17 +246,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