Refactor MoveControls and TransformControls for improved asset manipulation

- Cleaned up MoveControls component by removing unused variables and optimizing event handling.
- Enhanced asset movement logic with better key event detection and state management.
- Removed deprecated transform mode state from store.
- Updated worldTypes to remove unnecessary positionY property.
- Introduced TransformControls component for handling object transformations (translate/rotate) with proper state management and backend updates.
- Implemented event handling for mouse actions and keyboard shortcuts to toggle transformation modes.
This commit is contained in:
Jerald-Golden-B 2025-05-13 12:58:04 +05:30
parent b1569e64ed
commit ecab03c5f0
16 changed files with 1401 additions and 1278 deletions

View File

@ -234,6 +234,7 @@ const Assets: React.FC = () => {
alt={asset.filename}
className="asset-image"
onPointerDown={() => {
if (asset.category !== 'Feneration') {
setSelectedItem({
name: asset.filename,
id: asset.AssetID,
@ -242,6 +243,7 @@ const Assets: React.FC = () => {
? undefined
: asset.type,
});
}
}}
/>
<div className="asset-name">

View File

@ -29,7 +29,6 @@ import {
useSocketStore,
useToggleView,
useToolMode,
useTransformMode,
useActiveSubTool,
} from "../../store/store";
import useToggleStore from "../../store/useUIToggleStore";
@ -61,7 +60,6 @@ const Tools: React.FC = () => {
const { setAddAction } = useAddAction();
const { setSelectedWallItem } = useSelectedWallItem();
const { setTransformMode } = useTransformMode();
const { setDeletePointOrLine } = useDeletePointOrLine();
const { setToolMode } = useToolMode();
const { activeTool, setActiveTool } = useActiveTool();
@ -126,7 +124,6 @@ const Tools: React.FC = () => {
setToolMode(null);
setDeleteTool(false);
setAddAction(null);
setTransformMode(null);
setDeletePointOrLine(false);
setRefTextUpdate((prevUpdate) => prevUpdate - 1);
@ -134,20 +131,6 @@ const Tools: React.FC = () => {
case "cursor":
if (toggleView) {
setToolMode('move');
} else {
setTransformMode("translate");
}
break;
case "Rotate":
if (!toggleView) {
setTransformMode("rotate");
}
break;
case "Scale":
if (!toggleView) {
setTransformMode("scale");
}
break;

View File

@ -134,7 +134,6 @@ export default function Builder() {
scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0],
positionY: () => 0,
type: "Fixed-Move",
},
door: {
@ -142,7 +141,6 @@ export default function Builder() {
scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0],
positionY: () => 0,
type: "Fixed-Move",
},
window: {
@ -150,7 +148,6 @@ export default function Builder() {
scale: [0.75, 0.75, 0.75],
csgscale: [5, 3, 0.5],
csgposition: [0, 1.5, 0],
positionY: (intersectionPoint) => intersectionPoint.point.y,
type: "Free-Move",
},
};

View File

@ -26,7 +26,6 @@ export default function addFloorToScene(
if (materialCache.has(materialKey)) {
material = materialCache.get(materialKey) as THREE.Material;
// } else {
} else {
const floorTexture = textureLoader.load(savedTheme === "dark" ? texturePathDark : texturePath);
// const floorTexture = textureLoader.load(texturePath);

View File

@ -35,7 +35,7 @@ async function AddWallItems(
});
const config = AssetConfigurations[selected];
let positionY = typeof config.positionY === 'function' ? config.positionY(intersectionPoint) : config.positionY;
let positionY = config.type === 'Fixed-Move' ? 0 : intersectionPoint.point.y;
if (positionY === 0) {
positionY = Math.floor(intersectionPoint.point.y / CONSTANTS.wallConfig.height) * CONSTANTS.wallConfig.height;
}

View File

@ -11,7 +11,6 @@ import {
useSelectedItem,
useSocketStore,
useToggleView,
useTransformMode,
} from "../../../store/store";
import { useEffect } from "react";
import * as THREE from "three";
@ -59,7 +58,6 @@ const FloorItemsGroup = ({
const { camMode } = useCamMode();
const { deleteTool } = useDeleteTool();
const { setDeletableFloorItem } = useDeletableFloorItem();
const { transformMode } = useTransformMode();
const { setSelectedFloorItem } = useSelectedFloorItem();
const { activeTool } = useActiveTool();
const { selectedItem, setSelectedItem } = useSelectedItem();
@ -257,9 +255,8 @@ const FloorItemsGroup = ({
socket
);
}
const Mode = transformMode;
if (Mode !== null || activeTool === "cursor") {
if (activeTool === "cursor") {
if (!itemsGroup.current) return;
let intersects = raycaster.intersectObjects(
itemsGroup.current.children,
@ -296,9 +293,8 @@ const FloorItemsGroup = ({
isLeftMouseDown = false;
if (drag) return;
const Mode = transformMode;
if (Mode !== null || activeTool === "cursor") {
if (activeTool === "cursor") {
if (!itemsGroup.current) return;
let intersects = raycaster.intersectObjects(
itemsGroup.current.children,
@ -412,16 +408,7 @@ const FloorItemsGroup = ({
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [
deleteTool,
transformMode,
controls,
selectedItem,
state.camera,
state.pointer,
activeTool,
activeModule,
]);
}, [deleteTool, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]);
useFrame(() => {
if (controls)

View File

@ -3,7 +3,6 @@ import {
useDeleteTool,
useSelectedWallItem,
useToggleView,
useTransformMode,
useWallItems,
useWalls,
} from "../../../store/store";
@ -27,22 +26,8 @@ const WallsAndWallItems = ({
const { wallItems } = useWallItems();
const { toggleView } = useToggleView();
const { deleteTool } = useDeleteTool();
const { transformMode } = useTransformMode();
const { setSelectedWallItem } = useSelectedWallItem();
useEffect(() => {
if (transformMode === null) {
if (!deleteTool) {
handleMeshMissed(
currentWallItem,
setSelectedWallItem,
setSelectedItemsIndex
);
setSelectedWallItem(null);
setSelectedItemsIndex(null);
}
}
}, [transformMode]);
return (
<mesh
ref={CSGGroup as any}
@ -51,7 +36,7 @@ const WallsAndWallItems = ({
receiveShadow
visible={!toggleView}
onClick={(event) => {
if (!deleteTool && transformMode !== null) {
if (!deleteTool) {
handleMeshDown(
event,
currentWallItem,

View File

@ -10,6 +10,7 @@ import updateCamPosition from "../camera/updateCameraPosition";
import CamMode from "../camera/camMode";
import SwitchView from "../camera/switchView";
import SelectionControls from "./selectionControls/selectionControls";
import TransformControl from "./transformControls/transformControls";
export default function Controls() {
const controlsRef = useRef<CameraControls>(null);
@ -138,6 +139,8 @@ export default function Controls() {
<SelectionControls />
<TransformControl />
</>
);
}

View File

@ -22,8 +22,6 @@ function MoveControls({
movedObjects,
setMovedObjects,
itemsGroupRef,
copiedObjects,
setCopiedObjects,
pastedObjects,
setpastedObjects,
duplicatedObjects,
@ -34,10 +32,7 @@ function MoveControls({
boundingBoxRef,
}: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(
() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
[]
);
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
@ -45,9 +40,7 @@ function MoveControls({
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
const [keyEvent, setKeyEvent] = useState<
"Ctrl" | "Shift" | "Ctrl+Shift" | ""
>("");
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
@ -80,8 +73,8 @@ function MoveControls({
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) {
@ -115,19 +108,10 @@ function MoveControls({
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (
pastedObjects.length > 0 ||
duplicatedObjects.length > 0 ||
rotatedObjects.length > 0
)
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0)
return;
if (
keyCombination === "Ctrl" ||
keyCombination === "Ctrl+Shift" ||
keyCombination === "Shift"
) {
// update state here
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
setKeyEvent(keyCombination);
} else {
setKeyEvent("");
@ -174,20 +158,7 @@ function MoveControls({
canvasElement.removeEventListener("keydown", onKeyDown);
canvasElement?.removeEventListener("keyup", onKeyUp);
};
}, [
camera,
controls,
scene,
toggleView,
selectedAssets,
socket,
floorItems,
pastedObjects,
duplicatedObjects,
movedObjects,
rotatedObjects,
keyEvent,
]);
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]);
let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25;
@ -200,10 +171,12 @@ function MoveControls({
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");
@ -280,15 +253,8 @@ function MoveControls({
};
if (obj.userData.eventData) {
const eventData = useEventsStore
.getState()
.getEventByModelUuid(obj.userData.modelUuid);
const productData = useProductStore
.getState()
.getEventByModelUuid(
useSelectedProduct.getState().selectedProduct.productId,
obj.userData.modelUuid
);
const eventData = useEventsStore.getState().getEventByModelUuid(obj.userData.modelUuid);
const productData = useProductStore.getState().getEventByModelUuid(selectedProduct.productId, obj.userData.modelUuid);
if (eventData) {
useEventsStore.getState().updateEvent(obj.userData.modelUuid, {
@ -296,11 +262,12 @@ function MoveControls({
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
});
}
if (productData) {
const event = useProductStore
.getState()
.updateEvent(
useSelectedProduct.getState().selectedProduct.productId,
selectedProduct.productId,
obj.userData.modelUuid,
{
position: [worldPosition.x, worldPosition.y, worldPosition.z],

View File

@ -0,0 +1,206 @@
import { TransformControls } from "@react-three/drei";
import * as THREE from "three";
import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useFloorItems, useActiveTool, useSocketStore } from "../../../../store/store";
import { useThree } from "@react-three/fiber";
import * as Types from '../../../../types/world/worldTypes';
import { useEffect, useState } from "react";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
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 { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
export default function TransformControl() {
const state = useThree();
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { setObjectPosition } = useObjectPosition();
const { setObjectRotation } = useObjectRotation();
const { setFloorItems } = useFloorItems();
const { activeTool } = useActiveTool();
const { socket } = useSocketStore();
const { selectedProduct } = useSelectedProduct();
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData,
});
};
function handleObjectChange() {
if (selectedFloorItem) {
setObjectPosition(selectedFloorItem.position);
setObjectRotation({
x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z),
});
}
}
function handleMouseUp() {
if (selectedFloorItem) {
setObjectPosition(selectedFloorItem.position);
setObjectRotation({
x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z),
});
}
setFloorItems((prevItems: Types.FloorItems) => {
if (!prevItems) {
return
}
let updatedItem: any = null;
const updatedItems = prevItems.map((item) => {
if (item.modelUuid === selectedFloorItem?.uuid) {
updatedItem = {
...item,
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z] as [number, number, number],
rotation: { x: selectedFloorItem.rotation.x, y: selectedFloorItem.rotation.y, z: selectedFloorItem.rotation.z },
};
return updatedItem;
}
return item;
});
if (updatedItem && selectedFloorItem) {
if (updatedItem.eventData) {
const eventData = useEventsStore.getState().getEventByModelUuid(updatedItem.modelUuid);
const productData = useProductStore.getState().getEventByModelUuid(selectedProduct.productId, updatedItem.modelUuid);
if (eventData) {
useEventsStore.getState().updateEvent(updatedItem.modelUuid, {
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z],
rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z],
});
}
if (productData) {
const event = useProductStore
.getState()
.updateEvent(
selectedProduct.productId,
updatedItem.modelUuid,
{
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z],
rotation: [selectedFloorItem.rotation.x, selectedFloorItem.rotation.y, selectedFloorItem.rotation.z],
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
updatedItem.eventData = eventData;
}
}
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), updatedItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
// REST
// setFloorItemApi(
// organization,
// updatedItem.modelUuid,
// updatedItem.modelName,
// updatedItem.modelfileid,
// [selectedFloorItem.position.x, 0, selectedFloorItem.position.z,],
// { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z },
// false,
// true,
// );
// SOCKET
const data = {
organization: organization,
modelUuid: updatedItem.modelUuid,
modelName: updatedItem.modelName,
modelfileID: updatedItem.modelfileID,
position: [selectedFloorItem.position.x, 0, selectedFloorItem.position.z],
rotation: { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id
}
socket.emit("v2:model-asset:add", data);
}
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
}
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
const keyCombination = detectModifierKeys(e);
if (!selectedFloorItem) return;
if (keyCombination === "G") {
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
}
if (keyCombination === "R") {
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
}
};
if (selectedFloorItem) {
window.addEventListener("keydown", handleKeyDown);
} else {
setTransformMode(null);
}
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, [selectedFloorItem]);
useEffect(() => {
if (activeTool === "delete") {
if (state.controls) {
const target = (state.controls as any).getTarget(new THREE.Vector3());
(state.controls as any).setTarget(target.x, 0, target.z, true);
}
setSelectedFloorItem(null);
setObjectPosition({ x: undefined, y: undefined, z: undefined });
setObjectRotation({ x: undefined, y: undefined, z: undefined });
}
}, [activeTool]);
return (
<>
{(selectedFloorItem && transformMode) &&
<TransformControls
showX={transformMode === "translate"}
showY={transformMode === "rotate"}
showZ={transformMode === "translate"}
object={selectedFloorItem}
mode={transformMode}
onObjectChange={handleObjectChange}
onMouseUp={handleMouseUp}
/>
}
</>
);
}

View File

@ -144,11 +144,6 @@ export const useMovePoint = create<any>((set: any) => ({
setMovePoint: (x: any) => set(() => ({ movePoint: x })),
}));
export const useTransformMode = create<any>((set: any) => ({
transformMode: null,
setTransformMode: (x: any) => set(() => ({ transformMode: x })),
}));
export const useDeletePointOrLine = create<any>((set: any) => ({
deletePointOrLine: false,
setDeletePointOrLine: (x: any) => set(() => ({ deletePointOrLine: x })),

View File

@ -225,7 +225,6 @@ interface AssetConfiguration {
scale?: [number, number, number];
csgscale?: [number, number, number];
csgposition?: [number, number, number];
positionY?: (intersectionPoint: { point: THREE.Vector3 }) => number;
type?: "Fixed-Move" | "Free-Move";
}