added move and rotate tool using tranformControls
This commit is contained in:
@@ -32,9 +32,9 @@ const AssetProperties: React.FC = () => {
|
|||||||
setUserData([]);
|
setUserData([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUserDataChange = (id: number, newValue: string) => {};
|
const handleUserDataChange = (id: number, newValue: string) => { };
|
||||||
|
|
||||||
const handleRemoveUserData = (id: number) => {};
|
const handleRemoveUserData = (id: number) => { };
|
||||||
|
|
||||||
const handleAnimationClick = (animation: string) => {
|
const handleAnimationClick = (animation: string) => {
|
||||||
if (selectedFloorItem) {
|
if (selectedFloorItem) {
|
||||||
@@ -57,14 +57,14 @@ const AssetProperties: React.FC = () => {
|
|||||||
<section>
|
<section>
|
||||||
{objectPosition && (
|
{objectPosition && (
|
||||||
<PositionInput
|
<PositionInput
|
||||||
onChange={() => {}}
|
onChange={() => { }}
|
||||||
value1={parseFloat(objectPosition.x.toFixed(5))}
|
value1={parseFloat(objectPosition.x.toFixed(5))}
|
||||||
value2={parseFloat(objectPosition.z.toFixed(5))}
|
value2={parseFloat(objectPosition.z.toFixed(5))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{objectRotation && (
|
{objectRotation && (
|
||||||
<RotationInput
|
<RotationInput
|
||||||
onChange={() => {}}
|
onChange={() => { }}
|
||||||
value={parseFloat(objectRotation.y.toFixed(5))}
|
value={parseFloat(objectRotation.y.toFixed(5))}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -107,7 +107,7 @@ const AssetProperties: React.FC = () => {
|
|||||||
if (asset.modelUuid !== selectedFloorItem.uuid || !asset.animations)
|
if (asset.modelUuid !== selectedFloorItem.uuid || !asset.animations)
|
||||||
return (
|
return (
|
||||||
i === 0 && (
|
i === 0 && (
|
||||||
<div className="no-animation">
|
<div className="no-animation" key={i}>
|
||||||
Looks like there are no preset animations yet. Stay tuned for
|
Looks like there are no preset animations yet. Stay tuned for
|
||||||
future additions!
|
future additions!
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -146,6 +146,12 @@ const Tools: React.FC = () => {
|
|||||||
case "draw-floor":
|
case "draw-floor":
|
||||||
is2D && setToolMode("Floor");
|
is2D && setToolMode("Floor");
|
||||||
break;
|
break;
|
||||||
|
case "move":
|
||||||
|
if (!is2D) setToolMode("Move-Asset");
|
||||||
|
break;
|
||||||
|
case "rotate":
|
||||||
|
if (!is2D) setToolMode("Rotate-Asset");
|
||||||
|
break;
|
||||||
case "measure":
|
case "measure":
|
||||||
setToolMode("MeasurementScale");
|
setToolMode("MeasurementScale");
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function useModelEventHandlers({
|
|||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const { eventStore, productStore, assetStore, undoRedo3DStore } = useSceneContext();
|
const { eventStore, productStore, assetStore, undoRedo3DStore } = useSceneContext();
|
||||||
const { push3D } = undoRedo3DStore();
|
const { push3D } = undoRedo3DStore();
|
||||||
const { getAssetById, removeAsset } = assetStore();
|
const { removeAsset } = assetStore();
|
||||||
const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
|
const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
|
||||||
const { resourceManagementId, setResourceManagementId } = useResourceManagementId();
|
const { resourceManagementId, setResourceManagementId } = useResourceManagementId();
|
||||||
const { removeEvent, getEventByModelUuid } = eventStore();
|
const { removeEvent, getEventByModelUuid } = eventStore();
|
||||||
@@ -77,11 +77,12 @@ export function useModelEventHandlers({
|
|||||||
}
|
}
|
||||||
|
|
||||||
}, [zoneAssetId])
|
}, [zoneAssetId])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!resourceManagementId) return
|
if (!resourceManagementId) return
|
||||||
if (resourceManagementId === asset.modelUuid) {
|
if (resourceManagementId === asset.modelUuid) {
|
||||||
|
|
||||||
|
|
||||||
handleDblClick(asset);
|
handleDblClick(asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,14 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { CameraControls, Html, ScreenSpace } from "@react-three/drei";
|
import { CameraControls, Html, ScreenSpace } from "@react-three/drei";
|
||||||
import {
|
import { useContextActionStore, useRenameModeStore, useSelectedAssets, useToggleView, useToolMode, } from "../../../../store/builder/store";
|
||||||
useContextActionStore,
|
|
||||||
useRenameModeStore,
|
|
||||||
useSelectedAssets,
|
|
||||||
} from "../../../../store/builder/store";
|
|
||||||
import ContextMenu from "../../../../components/ui/menu/contextMenu";
|
import ContextMenu from "../../../../components/ui/menu/contextMenu";
|
||||||
|
import useModuleStore from "../../../../store/useModuleStore";
|
||||||
|
|
||||||
function ContextControls() {
|
function ContextControls() {
|
||||||
const { gl, controls } = useThree();
|
const { gl, controls } = useThree();
|
||||||
const [canRender, setCanRender] = useState(false);
|
const [canRender, setCanRender] = useState(false);
|
||||||
const [visibility, setVisibility] = useState({
|
const [visibility, setVisibility] = useState({
|
||||||
rename: true,
|
|
||||||
focus: true,
|
|
||||||
flipX: true,
|
|
||||||
flipZ: true,
|
|
||||||
move: true,
|
|
||||||
rotate: true,
|
|
||||||
duplicate: true,
|
|
||||||
copy: true,
|
|
||||||
paste: true,
|
|
||||||
modifier: false,
|
|
||||||
group: false,
|
|
||||||
array: false,
|
|
||||||
delete: true,
|
|
||||||
});
|
|
||||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
|
||||||
const { selectedAssets } = useSelectedAssets();
|
|
||||||
const { setContextAction } = useContextActionStore();
|
|
||||||
const { setIsRenameMode } = useRenameModeStore();
|
|
||||||
const rightDrag = useRef(false);
|
|
||||||
const isRightMouseDown = useRef(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selectedAssets.length === 1) {
|
|
||||||
setVisibility({
|
|
||||||
rename: true,
|
rename: true,
|
||||||
focus: true,
|
focus: true,
|
||||||
flipX: true,
|
flipX: true,
|
||||||
@@ -49,195 +22,219 @@ function ContextControls() {
|
|||||||
group: false,
|
group: false,
|
||||||
array: false,
|
array: false,
|
||||||
delete: true,
|
delete: true,
|
||||||
});
|
});
|
||||||
} else if (selectedAssets.length > 1) {
|
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||||
setVisibility({
|
const { toggleView } = useToggleView();
|
||||||
rename: false,
|
const { activeModule } = useModuleStore();
|
||||||
focus: true,
|
const { toolMode } = useToolMode();
|
||||||
flipX: true,
|
const { selectedAssets } = useSelectedAssets();
|
||||||
flipZ: true,
|
const { setContextAction } = useContextActionStore();
|
||||||
move: true,
|
const { setIsRenameMode } = useRenameModeStore();
|
||||||
rotate: true,
|
const rightDrag = useRef(false);
|
||||||
duplicate: true,
|
const isRightMouseDown = useRef(false);
|
||||||
copy: true,
|
|
||||||
paste: true,
|
|
||||||
modifier: false,
|
|
||||||
group: true,
|
|
||||||
array: false,
|
|
||||||
delete: true,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setVisibility({
|
|
||||||
rename: false,
|
|
||||||
focus: false,
|
|
||||||
flipX: false,
|
|
||||||
flipZ: false,
|
|
||||||
move: false,
|
|
||||||
rotate: false,
|
|
||||||
duplicate: false,
|
|
||||||
copy: false,
|
|
||||||
paste: false,
|
|
||||||
modifier: false,
|
|
||||||
group: false,
|
|
||||||
array: false,
|
|
||||||
delete: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [selectedAssets]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvasElement = gl.domElement;
|
if (selectedAssets.length === 1) {
|
||||||
|
setVisibility({
|
||||||
const onPointerDown = (evt: any) => {
|
rename: true,
|
||||||
if (evt.button === 2) {
|
focus: true,
|
||||||
isRightMouseDown.current = true;
|
flipX: true,
|
||||||
rightDrag.current = false;
|
flipZ: true,
|
||||||
}
|
move: true,
|
||||||
};
|
rotate: true,
|
||||||
|
duplicate: true,
|
||||||
const onPointerMove = () => {
|
copy: true,
|
||||||
if (isRightMouseDown.current) {
|
paste: true,
|
||||||
rightDrag.current = true;
|
modifier: false,
|
||||||
}
|
group: false,
|
||||||
};
|
array: false,
|
||||||
|
delete: true,
|
||||||
const onPointerUp = (evt: any) => {
|
});
|
||||||
if (evt.button === 2) {
|
} else if (selectedAssets.length > 1) {
|
||||||
isRightMouseDown.current = false;
|
setVisibility({
|
||||||
}
|
rename: false,
|
||||||
};
|
focus: true,
|
||||||
|
flipX: true,
|
||||||
const handleContextClick = (event: MouseEvent) => {
|
flipZ: true,
|
||||||
event.preventDefault();
|
move: true,
|
||||||
if (rightDrag.current) return;
|
rotate: true,
|
||||||
if (selectedAssets.length > 0) {
|
duplicate: true,
|
||||||
setMenuPosition({
|
copy: true,
|
||||||
x: event.clientX - gl.domElement.width / 2,
|
paste: true,
|
||||||
y: event.clientY - gl.domElement.height / 2,
|
modifier: false,
|
||||||
});
|
group: true,
|
||||||
setCanRender(true);
|
array: false,
|
||||||
if (controls) {
|
delete: true,
|
||||||
(controls as CameraControls).enabled = false;
|
});
|
||||||
|
} else {
|
||||||
|
setVisibility({
|
||||||
|
rename: false,
|
||||||
|
focus: false,
|
||||||
|
flipX: false,
|
||||||
|
flipZ: false,
|
||||||
|
move: false,
|
||||||
|
rotate: false,
|
||||||
|
duplicate: false,
|
||||||
|
copy: false,
|
||||||
|
paste: false,
|
||||||
|
modifier: false,
|
||||||
|
group: false,
|
||||||
|
array: false,
|
||||||
|
delete: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
}, [selectedAssets]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canvasElement = gl.domElement;
|
||||||
|
|
||||||
|
const onPointerDown = (evt: any) => {
|
||||||
|
if (evt.button === 2) {
|
||||||
|
isRightMouseDown.current = true;
|
||||||
|
rightDrag.current = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPointerMove = () => {
|
||||||
|
if (isRightMouseDown.current) {
|
||||||
|
rightDrag.current = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPointerUp = (evt: any) => {
|
||||||
|
if (evt.button === 2) {
|
||||||
|
isRightMouseDown.current = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleContextClick = (event: MouseEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (rightDrag.current) return;
|
||||||
|
if (selectedAssets.length > 0) {
|
||||||
|
setMenuPosition({ x: event.clientX - gl.domElement.width / 2, y: event.clientY - gl.domElement.height / 2, });
|
||||||
|
setCanRender(true);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setCanRender(false);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (selectedAssets.length > 0 && !toggleView && activeModule === "builder" && toolMode === 'cursor') {
|
||||||
|
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||||
|
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||||
|
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||||
|
canvasElement.addEventListener("contextmenu", handleContextClick);
|
||||||
|
} else {
|
||||||
|
setCanRender(false);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = true;
|
||||||
|
}
|
||||||
|
setMenuPosition({ x: 0, y: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
canvasElement.removeEventListener("pointerdown", onPointerDown);
|
||||||
|
canvasElement.removeEventListener("pointermove", onPointerMove);
|
||||||
|
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||||
|
canvasElement.removeEventListener("contextmenu", handleContextClick);
|
||||||
|
};
|
||||||
|
}, [controls, gl, selectedAssets, toggleView, activeModule, toolMode]);
|
||||||
|
|
||||||
|
const handleAssetRename = () => {
|
||||||
setCanRender(false);
|
setCanRender(false);
|
||||||
if (controls) {
|
if (controls) {
|
||||||
(controls as CameraControls).enabled = true;
|
(controls as CameraControls).enabled = true;
|
||||||
}
|
}
|
||||||
}
|
setContextAction("renameAsset");
|
||||||
|
setIsRenameMode(true);
|
||||||
|
};
|
||||||
|
const handleAssetFocus = () => {
|
||||||
|
setCanRender(false);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = true;
|
||||||
|
}
|
||||||
|
setContextAction("focusAsset");
|
||||||
|
};
|
||||||
|
const handleAssetMove = () => {
|
||||||
|
setCanRender(false);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = true;
|
||||||
|
}
|
||||||
|
setContextAction("moveAsset");
|
||||||
|
};
|
||||||
|
const handleAssetRotate = () => {
|
||||||
|
setCanRender(false);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = true;
|
||||||
|
}
|
||||||
|
setContextAction("rotateAsset");
|
||||||
|
};
|
||||||
|
const handleAssetCopy = () => {
|
||||||
|
setCanRender(false);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = true;
|
||||||
|
}
|
||||||
|
setContextAction("copyAsset");
|
||||||
|
};
|
||||||
|
const handleAssetPaste = () => {
|
||||||
|
setCanRender(false);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = true;
|
||||||
|
}
|
||||||
|
setContextAction("pasteAsset");
|
||||||
|
};
|
||||||
|
const handleAssetDelete = () => {
|
||||||
|
setCanRender(false);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = true;
|
||||||
|
}
|
||||||
|
setContextAction("deleteAsset");
|
||||||
|
};
|
||||||
|
const handleAssetDuplicate = () => {
|
||||||
|
setCanRender(false);
|
||||||
|
if (controls) {
|
||||||
|
(controls as CameraControls).enabled = true;
|
||||||
|
}
|
||||||
|
setContextAction("duplicateAsset");
|
||||||
};
|
};
|
||||||
|
|
||||||
if (selectedAssets.length > 0) {
|
return (
|
||||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
<>
|
||||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
{canRender && (
|
||||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
<ScreenSpace depth={1}>
|
||||||
canvasElement.addEventListener("contextmenu", handleContextClick);
|
<Html
|
||||||
} else {
|
style={{
|
||||||
setCanRender(false);
|
position: "fixed",
|
||||||
if (controls) {
|
top: menuPosition.y,
|
||||||
(controls as CameraControls).enabled = true;
|
left: menuPosition.x,
|
||||||
}
|
zIndex: 1000,
|
||||||
setMenuPosition({ x: 0, y: 0 });
|
}}
|
||||||
}
|
>
|
||||||
|
<ContextMenu
|
||||||
return () => {
|
visibility={visibility}
|
||||||
canvasElement.removeEventListener("pointerdown", onPointerDown);
|
onRename={() => handleAssetRename()}
|
||||||
canvasElement.removeEventListener("pointermove", onPointerMove);
|
onFocus={() => handleAssetFocus()}
|
||||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
onFlipX={() => console.log("Flip to X")}
|
||||||
canvasElement.removeEventListener("contextmenu", handleContextClick);
|
onFlipZ={() => console.log("Flip to Z")}
|
||||||
};
|
onMove={() => handleAssetMove()}
|
||||||
}, [controls, gl, selectedAssets]);
|
onRotate={() => handleAssetRotate()}
|
||||||
|
onDuplicate={() => handleAssetDuplicate()}
|
||||||
const handleAssetRename = () => {
|
onCopy={() => handleAssetCopy()}
|
||||||
setCanRender(false);
|
onPaste={() => handleAssetPaste()}
|
||||||
if (controls) {
|
onGroup={() => console.log("Group")}
|
||||||
(controls as CameraControls).enabled = true;
|
onArray={() => console.log("Array")}
|
||||||
}
|
onDelete={() => handleAssetDelete()}
|
||||||
setContextAction("renameAsset");
|
/>
|
||||||
setIsRenameMode(true);
|
</Html>
|
||||||
};
|
</ScreenSpace>
|
||||||
const handleAssetFocus = () => {
|
)}
|
||||||
setCanRender(false);
|
</>
|
||||||
if (controls) {
|
);
|
||||||
(controls as CameraControls).enabled = true;
|
|
||||||
}
|
|
||||||
setContextAction("focusAsset");
|
|
||||||
};
|
|
||||||
const handleAssetMove = () => {
|
|
||||||
setCanRender(false);
|
|
||||||
if (controls) {
|
|
||||||
(controls as CameraControls).enabled = true;
|
|
||||||
}
|
|
||||||
setContextAction("moveAsset");
|
|
||||||
};
|
|
||||||
const handleAssetRotate = () => {
|
|
||||||
setCanRender(false);
|
|
||||||
if (controls) {
|
|
||||||
(controls as CameraControls).enabled = true;
|
|
||||||
}
|
|
||||||
setContextAction("rotateAsset");
|
|
||||||
};
|
|
||||||
const handleAssetCopy = () => {
|
|
||||||
setCanRender(false);
|
|
||||||
if (controls) {
|
|
||||||
(controls as CameraControls).enabled = true;
|
|
||||||
}
|
|
||||||
setContextAction("copyAsset");
|
|
||||||
};
|
|
||||||
const handleAssetPaste = () => {
|
|
||||||
setCanRender(false);
|
|
||||||
if (controls) {
|
|
||||||
(controls as CameraControls).enabled = true;
|
|
||||||
}
|
|
||||||
setContextAction("pasteAsset");
|
|
||||||
};
|
|
||||||
const handleAssetDelete = () => {
|
|
||||||
setCanRender(false);
|
|
||||||
if (controls) {
|
|
||||||
(controls as CameraControls).enabled = true;
|
|
||||||
}
|
|
||||||
setContextAction("deleteAsset");
|
|
||||||
};
|
|
||||||
const handleAssetDuplicate = () => {
|
|
||||||
setCanRender(false);
|
|
||||||
if (controls) {
|
|
||||||
(controls as CameraControls).enabled = true;
|
|
||||||
}
|
|
||||||
setContextAction("duplicateAsset");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{canRender && (
|
|
||||||
<ScreenSpace depth={1}>
|
|
||||||
<Html
|
|
||||||
style={{
|
|
||||||
position: "fixed",
|
|
||||||
top: menuPosition.y,
|
|
||||||
left: menuPosition.x,
|
|
||||||
zIndex: 1000,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ContextMenu
|
|
||||||
visibility={visibility}
|
|
||||||
onRename={() => handleAssetRename()}
|
|
||||||
onFocus={() => handleAssetFocus()}
|
|
||||||
onFlipX={() => console.log("Flip to X")}
|
|
||||||
onFlipZ={() => console.log("Flip to Z")}
|
|
||||||
onMove={() => handleAssetMove()}
|
|
||||||
onRotate={() => handleAssetRotate()}
|
|
||||||
onDuplicate={() => handleAssetDuplicate()}
|
|
||||||
onCopy={() => handleAssetCopy()}
|
|
||||||
onPaste={() => handleAssetPaste()}
|
|
||||||
onGroup={() => console.log("Group")}
|
|
||||||
onArray={() => console.log("Array")}
|
|
||||||
onDelete={() => handleAssetDelete()}
|
|
||||||
/>
|
|
||||||
</Html>
|
|
||||||
</ScreenSpace>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ContextControls;
|
export default ContextControls;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, } from "../../../../../store/builder/store";
|
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../../store/builder/store";
|
||||||
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";
|
||||||
@@ -12,6 +12,7 @@ import { useProductContext } from "../../../../simulation/products/productContex
|
|||||||
import { getUserData } from "../../../../../functions/getUserData";
|
import { getUserData } from "../../../../../functions/getUserData";
|
||||||
import { useSceneContext } from "../../../sceneContext";
|
import { useSceneContext } from "../../../sceneContext";
|
||||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||||
|
import useModuleStore from "../../../../../store/useModuleStore";
|
||||||
|
|
||||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||||
|
|
||||||
@@ -20,6 +21,8 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
|||||||
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 { toggleView } = useToggleView();
|
||||||
|
const { toolMode } = useToolMode();
|
||||||
|
const { activeModule } = useModuleStore();
|
||||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||||
const { selectedProductStore } = useProductContext();
|
const { selectedProductStore } = useProductContext();
|
||||||
const { selectedProduct } = selectedProductStore();
|
const { selectedProduct } = selectedProductStore();
|
||||||
@@ -162,7 +165,7 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!toggleView) {
|
if (!toggleView && toolMode === 'cursor') {
|
||||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||||
@@ -178,7 +181,14 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
|||||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]);
|
}, [camera, controls, scene, toggleView, toolMode, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
||||||
|
resetToInitialPositions();
|
||||||
|
setMovedObjects([]);
|
||||||
|
}
|
||||||
|
}, [activeModule, toolMode, toggleView]);
|
||||||
|
|
||||||
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
|
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
|
||||||
const pointPosition = new THREE.Vector3().copy(point.position);
|
const pointPosition = new THREE.Vector3().copy(point.position);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useFrame, useThree } from "@react-three/fiber";
|
import { useFrame, useThree } from "@react-three/fiber";
|
||||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, useToolMode } from "../../../../../store/builder/store";
|
||||||
import * as Types from "../../../../../types/world/worldTypes";
|
import * as Types from "../../../../../types/world/worldTypes";
|
||||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
@@ -11,6 +11,7 @@ import { useSceneContext } from "../../../sceneContext";
|
|||||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||||
import { handleAssetRotationSnap } from "./functions/handleAssetRotationSnap";
|
import { handleAssetRotationSnap } from "./functions/handleAssetRotationSnap";
|
||||||
|
import useModuleStore from "../../../../../store/useModuleStore";
|
||||||
|
|
||||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||||
|
|
||||||
@@ -18,6 +19,8 @@ function RotateControls3D() {
|
|||||||
const { camera, gl, scene, pointer, raycaster } = useThree();
|
const { camera, gl, scene, pointer, raycaster } = useThree();
|
||||||
|
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
|
const { toolMode } = useToolMode();
|
||||||
|
const { activeModule } = useModuleStore();
|
||||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||||
const { selectedProductStore } = useProductContext();
|
const { selectedProductStore } = useProductContext();
|
||||||
const { selectedProduct } = selectedProductStore();
|
const { selectedProduct } = selectedProductStore();
|
||||||
@@ -137,7 +140,7 @@ function RotateControls3D() {
|
|||||||
prevPointerPosition.current = currentPointer.clone();
|
prevPointerPosition.current = currentPointer.clone();
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!toggleView) {
|
if (!toggleView && toolMode === 'cursor') {
|
||||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||||
@@ -152,7 +155,14 @@ function RotateControls3D() {
|
|||||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||||
};
|
};
|
||||||
}, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations]);
|
}, [camera, scene, toggleView, toolMode, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
||||||
|
resetToInitialRotations();
|
||||||
|
setRotatedObjects([]);
|
||||||
|
}
|
||||||
|
}, [activeModule, toolMode, toggleView]);
|
||||||
|
|
||||||
const resetToInitialRotations = useCallback(() => {
|
const resetToInitialRotations = useCallback(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -385,7 +395,7 @@ function RotateControls3D() {
|
|||||||
|
|
||||||
setIsRotating(false);
|
setIsRotating(false);
|
||||||
clearSelection();
|
clearSelection();
|
||||||
}, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId]);
|
}, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId, initialPositions, initialRotations]);
|
||||||
|
|
||||||
const clearSelection = () => {
|
const clearSelection = () => {
|
||||||
setPastedObjects([]);
|
setPastedObjects([]);
|
||||||
|
|||||||
@@ -16,22 +16,23 @@ import DuplicationControls3D from "./duplicationControls3D";
|
|||||||
import CopyPasteControls3D from "./copyPasteControls3D";
|
import CopyPasteControls3D from "./copyPasteControls3D";
|
||||||
import MoveControls3D from "./moveControls3D";
|
import MoveControls3D from "./moveControls3D";
|
||||||
import RotateControls3D from "./rotateControls3D";
|
import RotateControls3D from "./rotateControls3D";
|
||||||
|
import TransformControls3D from "./transformControls3D";
|
||||||
|
|
||||||
// import { deleteFloorItem } from '../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi';
|
// import { deleteFloorItem } from '../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi';
|
||||||
|
|
||||||
const SelectionControls3D: React.FC = () => {
|
const SelectionControls3D: React.FC = () => {
|
||||||
const { camera, controls, gl, scene, raycaster, pointer } = useThree();
|
const { camera, controls, gl, scene, raycaster, pointer } = useThree();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
|
const { activeModule } = useModuleStore();
|
||||||
|
const { toolMode } = useToolMode();
|
||||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||||
const boundingBoxRef = useRef<THREE.Mesh>();
|
const boundingBoxRef = useRef<THREE.Mesh>();
|
||||||
const { activeModule } = useModuleStore();
|
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const { contextAction, setContextAction } = useContextActionStore()
|
const { contextAction, setContextAction } = useContextActionStore()
|
||||||
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
||||||
const { push3D } = undoRedo3DStore();
|
const { push3D } = undoRedo3DStore();
|
||||||
const { removeAsset, getAssetById, movedObjects, rotatedObjects, copiedObjects, pastedObjects, duplicatedObjects, setPastedObjects, setDuplicatedObjects } = assetStore();
|
const { removeAsset, getAssetById, movedObjects, rotatedObjects, copiedObjects, pastedObjects, duplicatedObjects, setPastedObjects, setDuplicatedObjects } = assetStore();
|
||||||
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
|
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
|
||||||
const { toolMode } = useToolMode();
|
|
||||||
const { selectedVersionStore } = useVersionContext();
|
const { selectedVersionStore } = useVersionContext();
|
||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
const { selectedProductStore } = useProductContext();
|
const { selectedProductStore } = useProductContext();
|
||||||
@@ -202,7 +203,7 @@ const SelectionControls3D: React.FC = () => {
|
|||||||
rightClickMoved.current = false;
|
rightClickMoved.current = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!toggleView && activeModule === "builder" && toolMode === 'cursor') {
|
if (!toggleView && activeModule === "builder" && (toolMode === 'cursor' || toolMode === 'Move-Asset' || toolMode === 'Rotate-Asset')) {
|
||||||
helper.enabled = true;
|
helper.enabled = true;
|
||||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||||
@@ -227,7 +228,7 @@ const SelectionControls3D: React.FC = () => {
|
|||||||
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode]);
|
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
if (activeModule !== "builder" || (toolMode !== 'cursor' && toolMode !== 'Move-Asset' && toolMode !== 'Rotate-Asset') || toggleView) {
|
||||||
clearSelection();
|
clearSelection();
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@@ -381,6 +382,8 @@ const SelectionControls3D: React.FC = () => {
|
|||||||
|
|
||||||
<CopyPasteControls3D />
|
<CopyPasteControls3D />
|
||||||
|
|
||||||
|
<TransformControls3D />
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,380 @@
|
|||||||
|
import * as THREE from 'three';
|
||||||
|
import { useRef, useEffect, useState, useCallback } from 'react';
|
||||||
|
import { useThree } from '@react-three/fiber';
|
||||||
|
import { TransformControls } from '@react-three/drei';
|
||||||
|
import { useSelectedAssets, useSocketStore, useToolMode } from '../../../../../store/builder/store';
|
||||||
|
import { useProductContext } from '../../../../simulation/products/productContext';
|
||||||
|
import { useVersionContext } from '../../../../builder/version/versionContext';
|
||||||
|
import { useSceneContext } from '../../../sceneContext';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { getUserData } from '../../../../../functions/getUserData';
|
||||||
|
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||||
|
import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
|
||||||
|
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||||
|
|
||||||
|
function TransformControls3D() {
|
||||||
|
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||||
|
const { toolMode } = useToolMode();
|
||||||
|
const { camera, scene, gl } = useThree();
|
||||||
|
const transformControlsRef = useRef<any>(null);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
|
||||||
|
const { socket } = useSocketStore();
|
||||||
|
const { selectedProductStore } = useProductContext();
|
||||||
|
const { selectedProduct } = selectedProductStore();
|
||||||
|
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
||||||
|
const { push3D, subscribeUndoRedo } = undoRedo3DStore();
|
||||||
|
const { updateAsset, getAssetById } = assetStore();
|
||||||
|
const { userId, organization } = getUserData();
|
||||||
|
const { selectedVersionStore } = useVersionContext();
|
||||||
|
const { selectedVersion } = selectedVersionStore();
|
||||||
|
const { projectId } = useParams();
|
||||||
|
const pivotPointRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||||
|
const initialPositionsRef = useRef<Map<string, THREE.Vector3>>(new Map());
|
||||||
|
const initialRotationsRef = useRef<Map<string, THREE.Euler>>(new Map());
|
||||||
|
const initialPivotPositionRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||||
|
const initialQuaternionsRef = useRef<Map<string, THREE.Quaternion>>(new Map());
|
||||||
|
const tempObjectRef = useRef<THREE.Object3D>(new THREE.Object3D());
|
||||||
|
|
||||||
|
const updateBackend = useCallback((
|
||||||
|
productName: string,
|
||||||
|
productUuid: string,
|
||||||
|
projectId: string,
|
||||||
|
eventData: any
|
||||||
|
) => {
|
||||||
|
upsertProductOrEventApi({
|
||||||
|
productName: productName,
|
||||||
|
productUuid: productUuid,
|
||||||
|
projectId: projectId,
|
||||||
|
eventDatas: eventData,
|
||||||
|
versionId: selectedVersion?.versionId || '',
|
||||||
|
});
|
||||||
|
}, [selectedVersion]);
|
||||||
|
|
||||||
|
const recalcGizmo = useCallback(() => {
|
||||||
|
if (selectedAssets.length === 0) return;
|
||||||
|
|
||||||
|
const bbox = new THREE.Box3();
|
||||||
|
selectedAssets.forEach(obj => bbox.expandByObject(obj));
|
||||||
|
bbox.getCenter(pivotPointRef.current);
|
||||||
|
initialPivotPositionRef.current.copy(pivotPointRef.current);
|
||||||
|
|
||||||
|
tempObjectRef.current.position.copy(pivotPointRef.current);
|
||||||
|
tempObjectRef.current.rotation.set(0, 0, 0);
|
||||||
|
tempObjectRef.current.scale.set(1, 1, 1);
|
||||||
|
|
||||||
|
initialPositionsRef.current.clear();
|
||||||
|
initialRotationsRef.current.clear();
|
||||||
|
initialQuaternionsRef.current.clear();
|
||||||
|
|
||||||
|
selectedAssets.forEach(obj => {
|
||||||
|
initialPositionsRef.current.set(obj.uuid, obj.position.clone());
|
||||||
|
initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
|
||||||
|
initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
|
||||||
|
});
|
||||||
|
}, [selectedAssets]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unsubscribe = subscribeUndoRedo(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
recalcGizmo();
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
return unsubscribe;
|
||||||
|
}, [subscribeUndoRedo, recalcGizmo]);
|
||||||
|
|
||||||
|
const handleTransformationComplete = useCallback(() => {
|
||||||
|
if (selectedAssets.length === 0) return;
|
||||||
|
|
||||||
|
const updatedAssets = [...selectedAssets];
|
||||||
|
setSelectedAssets(updatedAssets);
|
||||||
|
|
||||||
|
selectedAssets.forEach(obj => {
|
||||||
|
const asset = getAssetById(obj.uuid);
|
||||||
|
if (!asset) return;
|
||||||
|
|
||||||
|
updateAsset(asset.modelUuid, {
|
||||||
|
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||||
|
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (asset.eventData) {
|
||||||
|
const eventData = eventStore.getState().getEventByModelUuid(asset.modelUuid);
|
||||||
|
const productData = productStore.getState().getEventByModelUuid(
|
||||||
|
selectedProduct.productUuid,
|
||||||
|
asset.modelUuid
|
||||||
|
);
|
||||||
|
|
||||||
|
if (eventData) {
|
||||||
|
eventStore.getState().updateEvent(asset.modelUuid, {
|
||||||
|
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||||
|
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productData) {
|
||||||
|
const event = productStore
|
||||||
|
.getState()
|
||||||
|
.updateEvent(
|
||||||
|
selectedProduct.productUuid,
|
||||||
|
asset.modelUuid,
|
||||||
|
{
|
||||||
|
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||||
|
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (event) {
|
||||||
|
updateBackend(
|
||||||
|
selectedProduct.productName,
|
||||||
|
selectedProduct.productUuid,
|
||||||
|
projectId || '',
|
||||||
|
event
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//REST
|
||||||
|
|
||||||
|
// setAssetsApi(
|
||||||
|
// organization,
|
||||||
|
// asset.modelUuid,
|
||||||
|
// asset.modelName,
|
||||||
|
// [obj.position.x, 0, obj.position.z],
|
||||||
|
// { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||||
|
// asset.assetId,
|
||||||
|
// false,
|
||||||
|
// true,
|
||||||
|
// );
|
||||||
|
|
||||||
|
//SOCKET
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
organization,
|
||||||
|
modelUuid: asset.modelUuid,
|
||||||
|
modelName: asset.modelName,
|
||||||
|
assetId: asset.assetId,
|
||||||
|
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||||
|
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||||
|
isLocked: false,
|
||||||
|
isVisible: true,
|
||||||
|
socketId: socket.id,
|
||||||
|
versionId: selectedVersion?.versionId || '',
|
||||||
|
userId,
|
||||||
|
projectId
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.emit("v1:model-asset:add", data);
|
||||||
|
|
||||||
|
});
|
||||||
|
}, [selectedAssets, setSelectedAssets, getAssetById, updateAsset, eventStore, productStore, selectedProduct, updateBackend, projectId, organization, socket, selectedVersion, userId, push3D]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const temp = tempObjectRef.current;
|
||||||
|
scene.add(temp);
|
||||||
|
return () => {
|
||||||
|
scene.remove(temp);
|
||||||
|
};
|
||||||
|
}, [scene]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedAssets.length === 0) {
|
||||||
|
setVisible(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bbox = new THREE.Box3();
|
||||||
|
selectedAssets.forEach(obj => bbox.expandByObject(obj));
|
||||||
|
bbox.getCenter(pivotPointRef.current);
|
||||||
|
initialPivotPositionRef.current.copy(pivotPointRef.current);
|
||||||
|
|
||||||
|
tempObjectRef.current.position.copy(pivotPointRef.current);
|
||||||
|
tempObjectRef.current.rotation.set(0, 0, 0);
|
||||||
|
tempObjectRef.current.scale.set(1, 1, 1);
|
||||||
|
|
||||||
|
initialPositionsRef.current.clear();
|
||||||
|
initialRotationsRef.current.clear();
|
||||||
|
initialQuaternionsRef.current.clear();
|
||||||
|
selectedAssets.forEach(obj => {
|
||||||
|
initialPositionsRef.current.set(obj.uuid, obj.position.clone());
|
||||||
|
initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
|
||||||
|
initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
|
||||||
|
});
|
||||||
|
|
||||||
|
setVisible(true);
|
||||||
|
}, [selectedAssets]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const controls = transformControlsRef.current;
|
||||||
|
if (!controls || !visible) return;
|
||||||
|
|
||||||
|
controls.attach(tempObjectRef.current);
|
||||||
|
controls.setMode(toolMode === 'Move-Asset' ? 'translate' : 'rotate');
|
||||||
|
|
||||||
|
const onObjectChange = () => {
|
||||||
|
const temp = tempObjectRef.current;
|
||||||
|
if (!temp) return;
|
||||||
|
|
||||||
|
selectedAssets.forEach(obj => {
|
||||||
|
const initialPos = initialPositionsRef.current.get(obj.uuid);
|
||||||
|
const initialQuat = initialQuaternionsRef.current.get(obj.uuid);
|
||||||
|
if (!initialPos || !initialQuat) return;
|
||||||
|
|
||||||
|
if (controls.mode === 'translate') {
|
||||||
|
const delta = new THREE.Vector3().copy(temp.position).sub(initialPivotPositionRef.current);
|
||||||
|
obj.position.copy(initialPos).add(delta);
|
||||||
|
} else if (controls.mode === 'rotate') {
|
||||||
|
const deltaQuat = new THREE.Quaternion().setFromEuler(temp.rotation);
|
||||||
|
const relPos = initialPos.clone().sub(initialPivotPositionRef.current);
|
||||||
|
relPos.applyQuaternion(deltaQuat);
|
||||||
|
obj.position.copy(initialPivotPositionRef.current).add(relPos);
|
||||||
|
obj.quaternion.copy(initialQuat).multiply(deltaQuat);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseUp = () => {
|
||||||
|
const assetsToUpdate: AssetData[] = [];
|
||||||
|
const undoActions: UndoRedo3DAction[] = [];
|
||||||
|
|
||||||
|
selectedAssets.forEach((obj) => {
|
||||||
|
const asset = getAssetById(obj.uuid);
|
||||||
|
if (!asset) return;
|
||||||
|
|
||||||
|
const initialPos = initialPositionsRef.current.get(obj.uuid);
|
||||||
|
const initialRot = initialRotationsRef.current.get(obj.uuid);
|
||||||
|
if (!initialPos || !initialRot) return;
|
||||||
|
|
||||||
|
const assetData: Asset = {
|
||||||
|
...asset,
|
||||||
|
position: [initialPos.x, initialPos.y, initialPos.z],
|
||||||
|
rotation: [initialRot.x, initialRot.y, initialRot.z],
|
||||||
|
};
|
||||||
|
|
||||||
|
const newData: Asset = {
|
||||||
|
...asset,
|
||||||
|
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||||
|
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||||
|
};
|
||||||
|
|
||||||
|
assetsToUpdate.push({
|
||||||
|
type: "Asset",
|
||||||
|
assetData,
|
||||||
|
newData,
|
||||||
|
timeStap: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
|
||||||
|
updateAsset(asset.modelUuid, {
|
||||||
|
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||||
|
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (assetsToUpdate.length > 0) {
|
||||||
|
if (assetsToUpdate.length === 1) {
|
||||||
|
undoActions.push({
|
||||||
|
module: "builder",
|
||||||
|
actionType: "Asset-Update",
|
||||||
|
asset: assetsToUpdate[0],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
undoActions.push({
|
||||||
|
module: "builder",
|
||||||
|
actionType: "Assets-Update",
|
||||||
|
assets: assetsToUpdate,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
push3D({
|
||||||
|
type: "Scene",
|
||||||
|
actions: undoActions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedAssets.forEach(obj => {
|
||||||
|
initialPositionsRef.current.set(obj.uuid, obj.position.clone());
|
||||||
|
initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
|
||||||
|
initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (selectedAssets.length > 0) {
|
||||||
|
const bbox = new THREE.Box3();
|
||||||
|
selectedAssets.forEach(obj => bbox.expandByObject(obj));
|
||||||
|
bbox.getCenter(pivotPointRef.current);
|
||||||
|
initialPivotPositionRef.current.copy(pivotPointRef.current);
|
||||||
|
|
||||||
|
tempObjectRef.current.position.copy(pivotPointRef.current);
|
||||||
|
tempObjectRef.current.rotation.set(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
recalcGizmo();
|
||||||
|
handleTransformationComplete();
|
||||||
|
};
|
||||||
|
|
||||||
|
controls.addEventListener('objectChange', onObjectChange);
|
||||||
|
controls.addEventListener('mouseUp', onMouseUp);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
controls.removeEventListener('objectChange', onObjectChange);
|
||||||
|
controls.removeEventListener('mouseUp', onMouseUp);
|
||||||
|
controls.detach();
|
||||||
|
};
|
||||||
|
}, [selectedAssets, toolMode, visible, handleTransformationComplete]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canvasElement = gl.domElement;
|
||||||
|
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
const keyCombination = detectModifierKeys(event);
|
||||||
|
|
||||||
|
if (keyCombination !== keyEvent) {
|
||||||
|
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||||
|
setKeyEvent(keyCombination);
|
||||||
|
} else {
|
||||||
|
setKeyEvent("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKeyUp = (event: KeyboardEvent) => {
|
||||||
|
const keyCombination = detectModifierKeys(event);
|
||||||
|
|
||||||
|
if (keyCombination === "") {
|
||||||
|
setKeyEvent("");
|
||||||
|
} else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||||
|
setKeyEvent(keyCombination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
canvasElement.addEventListener('keydown', handleKeyDown);
|
||||||
|
canvasElement.addEventListener('keyup', onKeyUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
canvasElement.removeEventListener('keydown', handleKeyDown);
|
||||||
|
canvasElement.removeEventListener('keyup', onKeyUp);
|
||||||
|
}
|
||||||
|
}, [gl, visible, keyEvent]);
|
||||||
|
|
||||||
|
if (!visible || (toolMode !== 'Move-Asset' && toolMode !== 'Rotate-Asset')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TransformControls
|
||||||
|
space="world"
|
||||||
|
translationSnap={keyEvent === 'Ctrl' ? 0.5 : keyEvent === 'Ctrl+Shift' ? 0.1 : 0}
|
||||||
|
rotationSnap={keyEvent === 'Ctrl' ? THREE.MathUtils.degToRad(15) : keyEvent === 'Ctrl+Shift' ? THREE.MathUtils.degToRad(5) : 0}
|
||||||
|
ref={transformControlsRef}
|
||||||
|
camera={camera}
|
||||||
|
showX
|
||||||
|
showY
|
||||||
|
showZ
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TransformControls3D;
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
import { TransformControls } from "@react-three/drei";
|
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store";
|
|
||||||
import { useThree } from "@react-three/fiber";
|
|
||||||
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
|
||||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
|
||||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
|
||||||
// import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import { useThree } from "@react-three/fiber";
|
||||||
|
import { TransformControls } from "@react-three/drei";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useProductContext } from "../../../simulation/products/productContext";
|
import { useProductContext } from "../../../simulation/products/productContext";
|
||||||
import { getUserData } from "../../../../functions/getUserData";
|
import { getUserData } from "../../../../functions/getUserData";
|
||||||
import { useSceneContext } from "../../sceneContext";
|
import { useSceneContext } from "../../sceneContext";
|
||||||
import { useVersionContext } from "../../../builder/version/versionContext";
|
import { useVersionContext } from "../../../builder/version/versionContext";
|
||||||
|
import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store";
|
||||||
|
|
||||||
|
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||||
|
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||||
|
// import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||||
|
|
||||||
export default function TransformControl() {
|
export default function TransformControl() {
|
||||||
const state = useThree();
|
const state = useThree();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ function UndoRedo3DControls() {
|
|||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// console.log(undoStack, redoStack);
|
console.log(undoStack, redoStack);
|
||||||
}, [undoStack, redoStack]);
|
}, [undoStack, redoStack]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ type UndoRedo3DStore = {
|
|||||||
|
|
||||||
peekUndo3D: () => UndoRedo3DTypes | undefined;
|
peekUndo3D: () => UndoRedo3DTypes | undefined;
|
||||||
peekRedo3D: () => UndoRedo3DTypes | undefined;
|
peekRedo3D: () => UndoRedo3DTypes | undefined;
|
||||||
|
|
||||||
|
subscribeUndoRedo: (cb: (action: { type: "undo" | "redo"; entry: UndoRedo3DTypes }) => void) => () => void;
|
||||||
|
_listeners: ((action: { type: "undo" | "redo"; entry: UndoRedo3DTypes }) => void)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createUndoRedo3DStore = () => {
|
export const createUndoRedo3DStore = () => {
|
||||||
@@ -20,15 +23,14 @@ export const createUndoRedo3DStore = () => {
|
|||||||
immer((set, get) => ({
|
immer((set, get) => ({
|
||||||
undoStack: [],
|
undoStack: [],
|
||||||
redoStack: [],
|
redoStack: [],
|
||||||
|
_listeners: [],
|
||||||
|
|
||||||
push3D: (entry) => {
|
push3D: (entry) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.undoStack.push(entry);
|
state.undoStack.push(entry);
|
||||||
|
|
||||||
if (state.undoStack.length > undoRedoConfig.undoRedoCount) {
|
if (state.undoStack.length > undoRedoConfig.undoRedoCount) {
|
||||||
state.undoStack.shift();
|
state.undoStack.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
state.redoStack = [];
|
state.redoStack = [];
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -41,6 +43,9 @@ export const createUndoRedo3DStore = () => {
|
|||||||
state.redoStack.unshift(lastAction);
|
state.redoStack.unshift(lastAction);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (lastAction) {
|
||||||
|
get()._listeners.forEach((cb) => cb({ type: "undo", entry: lastAction! }));
|
||||||
|
}
|
||||||
return lastAction;
|
return lastAction;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -52,6 +57,9 @@ export const createUndoRedo3DStore = () => {
|
|||||||
state.undoStack.push(redoAction);
|
state.undoStack.push(redoAction);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (redoAction) {
|
||||||
|
get()._listeners.forEach((cb) => cb({ type: "redo", entry: redoAction! }));
|
||||||
|
}
|
||||||
return redoAction;
|
return redoAction;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -71,8 +79,19 @@ export const createUndoRedo3DStore = () => {
|
|||||||
const stack = get().redoStack;
|
const stack = get().redoStack;
|
||||||
return stack.length > 0 ? stack[0] : undefined;
|
return stack.length > 0 ? stack[0] : undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
subscribeUndoRedo: (cb) => {
|
||||||
|
set((state) => {
|
||||||
|
state._listeners.push(cb);
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
set((state) => {
|
||||||
|
state._listeners = state._listeners.filter((l) => l !== cb);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export type UndoRedo3DStoreType = ReturnType<typeof createUndoRedo3DStore>;
|
export type UndoRedo3DStoreType = ReturnType<typeof createUndoRedo3DStore>;
|
||||||
Reference in New Issue
Block a user