Merge remote-tracking branch 'origin/main-dev' into main-demo

This commit is contained in:
2025-07-22 10:23:14 +05:30
19 changed files with 772 additions and 653 deletions

View File

@@ -44,9 +44,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
const loader = new GLTFLoader(); const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader(); const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/");
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
);
loader.setDRACOLoader(dracoLoader); loader.setDRACOLoader(dracoLoader);
useEffect(() => { useEffect(() => {

View File

@@ -1,20 +1,51 @@
import { Box3, BoxGeometry, EdgesGeometry, Vector3 } from "three"; import { Line } from "@react-three/drei";
import { Box3, Vector3 } from "three";
import { useMemo } from "react";
export const AssetBoundingBox = ({ name, boundingBox, color, lineWidth }: { name: string; boundingBox: Box3 | null; color: string; lineWidth: number; }) => {
const { points, size, center } = useMemo(() => {
if (!boundingBox) { return { points: [], center: new Vector3(), size: new Vector3(), }; }
const min = boundingBox.min;
const max = boundingBox.max;
const center = boundingBox.getCenter(new Vector3());
const size = boundingBox.getSize(new Vector3());
const edges: Array<[number, number, number]> = [
[min.x, min.y, min.z], [max.x, min.y, min.z],
[max.x, min.y, min.z], [max.x, max.y, min.z],
[max.x, max.y, min.z], [min.x, max.y, min.z],
[min.x, max.y, min.z], [min.x, min.y, min.z],
[min.x, min.y, max.z], [max.x, min.y, max.z],
[max.x, min.y, max.z], [max.x, max.y, max.z],
[max.x, max.y, max.z], [min.x, max.y, max.z],
[min.x, max.y, max.z], [min.x, min.y, max.z],
[min.x, min.y, min.z], [min.x, min.y, max.z],
[max.x, min.y, min.z], [max.x, min.y, max.z],
[max.x, max.y, min.z], [max.x, max.y, max.z],
[min.x, max.y, min.z], [min.x, max.y, max.z],
];
return { points: edges, center, size };
}, [boundingBox]);
export const AssetBoundingBox = ({ boundingBox }: { boundingBox: Box3 | null }) => {
if (!boundingBox) return null; if (!boundingBox) return null;
const size = boundingBox.getSize(new Vector3());
const center = boundingBox.getCenter(new Vector3());
const boxGeometry = new BoxGeometry(size.x, size.y, size.z);
const edges = new EdgesGeometry(boxGeometry);
return ( return (
<group name='Asset FallBack'> <group name={name}>
<lineSegments position={center}> <Line
<bufferGeometry attach="geometry" {...edges} /> segments
<lineBasicMaterial depthWrite={false} attach="material" color="gray" linewidth={1} /> depthWrite={false}
</lineSegments> points={points}
color={color}
lineWidth={lineWidth}
/>
<mesh visible={false} position={center}>
<boxGeometry args={[size.x, size.y, size.z]} />
</mesh>
</group> </group>
); );
}; };

View File

@@ -4,7 +4,7 @@ import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber'; import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store'; import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedAssets, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
import { AssetBoundingBox } from '../../functions/assetBoundingBox'; import { AssetBoundingBox } from '../../functions/assetBoundingBox';
import { CameraControls } from '@react-three/drei'; import { CameraControls } from '@react-three/drei';
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore'; import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
@@ -22,6 +22,7 @@ import { getAssetIksApi } from '../../../../../services/simulation/ik/getAssetIK
function Model({ asset }: { readonly asset: Asset }) { function Model({ asset }: { readonly asset: Asset }) {
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
const savedTheme: string = localStorage.getItem("theme") || "light";
const { camera, controls, gl } = useThree(); const { camera, controls, gl } = useThree();
const { activeTool } = useActiveTool(); const { activeTool } = useActiveTool();
const { toolMode } = useToolMode(); const { toolMode } = useToolMode();
@@ -51,6 +52,7 @@ function Model({ asset }: { readonly asset: Asset }) {
const [isRendered, setIsRendered] = useState(false); const [isRendered, setIsRendered] = useState(false);
const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null); const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null); const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
const [isSelected, setIsSelected] = useState(false);
const groupRef = useRef<THREE.Group>(null); const groupRef = useRef<THREE.Group>(null);
const mixerRef = useRef<THREE.AnimationMixer>(); const mixerRef = useRef<THREE.AnimationMixer>();
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({}); const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
@@ -62,6 +64,7 @@ function Model({ asset }: { readonly asset: Asset }) {
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const { projectId } = useParams(); const { projectId } = useParams();
const { selectedAssets } = useSelectedAssets();
const updateBackend = ( const updateBackend = (
productName: string, productName: string,
@@ -456,6 +459,18 @@ function Model({ asset }: { readonly asset: Asset }) {
}, [gl]) }, [gl])
useEffect(() => {
if (selectedAssets.length > 0) {
if (selectedAssets.some((selectedAsset: THREE.Object3D) => selectedAsset.userData.modelUuid === asset.modelUuid)) {
setIsSelected(true);
} else {
setIsSelected(false);
}
} else {
setIsSelected(false);
}
}, [selectedAssets])
return ( return (
<group <group
key={asset.modelUuid} key={asset.modelUuid}
@@ -496,11 +511,16 @@ function Model({ asset }: { readonly asset: Asset }) {
}} }}
> >
{gltfScene && ( {gltfScene && (
isRendered ? ( <>
<primitive object={gltfScene} /> {isRendered ? (
) : ( <primitive object={gltfScene} />
<AssetBoundingBox boundingBox={boundingBox} /> ) : (
) <AssetBoundingBox name='Asset Fallback' boundingBox={boundingBox} color='gray' lineWidth={1} />
)}
{isSelected &&
<AssetBoundingBox name='Asset BBox' boundingBox={boundingBox} color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} />
}
</>
)} )}
</group> </group>
); );

View File

@@ -242,7 +242,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
visible={wallAsset.isVisible} visible={wallAsset.isVisible}
userData={wallAsset} userData={wallAsset}
> >
<Subtraction position={[center.x, center.y, center.z]} scale={[size.x, size.y, wall.wallThickness + 0.05]}> <Subtraction position={[center.x, center.y, 0]} scale={[size.x, size.y, wall.wallThickness + 0.05]}>
<Geometry> <Geometry>
<Base geometry={new THREE.BoxGeometry()} /> <Base geometry={new THREE.BoxGeometry()} />
</Geometry> </Geometry>
@@ -265,7 +265,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
e.stopPropagation(); e.stopPropagation();
let currentObject = e.object as THREE.Object3D; let currentObject = e.object as THREE.Object3D;
while (currentObject) { while (currentObject) {
if (currentObject.name === "Scene") { if (currentObject.userData.wallUuid) {
break; break;
} }
currentObject = currentObject.parent as THREE.Object3D; currentObject = currentObject.parent as THREE.Object3D;
@@ -286,7 +286,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
e.stopPropagation(); e.stopPropagation();
let currentObject = e.object as THREE.Object3D; let currentObject = e.object as THREE.Object3D;
while (currentObject) { while (currentObject) {
if (currentObject.name === "Scene") { if (currentObject.userData.wallUuid) {
break; break;
} }
currentObject = currentObject.parent as THREE.Object3D; currentObject = currentObject.parent as THREE.Object3D;

View File

@@ -83,7 +83,7 @@ function MoveControls2D({
if (keyCombination === "G") { if (keyCombination === "G") {
if (selectedPoints.length > 0) { if (selectedPoints.length > 0) {
moveAssets(); movePoints();
} }
} }
@@ -164,7 +164,7 @@ function MoveControls2D({
return new THREE.Vector3().subVectors(pointPosition, hitPoint); return new THREE.Vector3().subVectors(pointPosition, hitPoint);
}, []); }, []);
const moveAssets = useCallback(() => { const movePoints = useCallback(() => {
if (selectedPoints.length === 0) return; if (selectedPoints.length === 0) return;
const states: Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }> = {}; const states: Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }> = {};

View File

@@ -1,5 +1,5 @@
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useMemo } 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 { SkeletonUtils } from "three-stdlib"; import { SkeletonUtils } from "three-stdlib";
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store"; import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
@@ -10,20 +10,18 @@ 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 { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; // import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
const CopyPasteControls3D = ({ const CopyPasteControls3D = ({
copiedObjects, copiedObjects,
setCopiedObjects, setCopiedObjects,
pastedObjects, pastedObjects,
setpastedObjects, setpastedObjects,
selectionGroup,
setDuplicatedObjects, setDuplicatedObjects,
movedObjects, movedObjects,
setMovedObjects, setMovedObjects,
rotatedObjects, rotatedObjects,
setRotatedObjects, setRotatedObjects,
boundingBoxRef
}: any) => { }: any) => {
const { camera, controls, gl, scene, pointer, raycaster } = useThree(); const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const { toggleView } = useToggleView(); const { toggleView } = useToggleView();
@@ -33,31 +31,67 @@ const CopyPasteControls3D = ({
const { assetStore, eventStore } = useSceneContext(); const { assetStore, eventStore } = useSceneContext();
const { addEvent } = eventStore(); const { addEvent } = eventStore();
const { projectId } = useParams(); const { projectId } = useParams();
const { assets, addAsset } = assetStore(); const { assets, addAsset, setPosition, updateAsset, removeAsset, getAssetById } = assetStore();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const [isPasting, setIsPasting] = useState(false);
const [relativePositions, setRelativePositions] = useState<THREE.Vector3[]>([]);
const [centerOffset, setCenterOffset] = useState<THREE.Vector3 | null>(null);
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({
left: false,
right: false,
});
const calculateRelativePositions = useCallback((objects: THREE.Object3D[]) => {
if (objects.length === 0) return { center: new THREE.Vector3(), relatives: [] };
const box = new THREE.Box3();
objects.forEach(obj => box.expandByObject(obj));
const center = new THREE.Vector3();
box.getCenter(center);
const relatives = objects.map(obj => {
const relativePos = new THREE.Vector3().subVectors(obj.position, center);
return relativePos;
});
return { center, relatives };
}, []);
useEffect(() => { useEffect(() => {
if (!camera || !scene || toggleView) return; if (!camera || !scene || toggleView) return;
const canvasElement = gl.domElement; const canvasElement = gl.domElement;
canvasElement.tabIndex = 0; canvasElement.tabIndex = 0;
let isMoving = false; let isPointerMoving = false;
const onPointerDown = () => { const onPointerDown = (event: PointerEvent) => {
isMoving = false; isPointerMoving = false;
if (event.button === 0) mouseButtonsDown.current.left = true;
if (event.button === 2) mouseButtonsDown.current.right = true;
}; };
const onPointerMove = () => { const onPointerMove = () => {
isMoving = true; isPointerMoving = true;
}; };
const onPointerUp = (event: PointerEvent) => { const onPointerUp = (event: PointerEvent) => {
if (!isMoving && pastedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { if (event.button === 0) mouseButtonsDown.current.left = false;
if (event.button === 2) mouseButtonsDown.current.right = false;
if (!isPointerMoving && pastedObjects.length > 0 && event.button === 0) {
event.preventDefault(); event.preventDefault();
addPastedObjects(); addPastedObjects();
} }
if (!isPointerMoving && pastedObjects.length > 0 && event.button === 2) {
event.preventDefault();
clearSelection();
pastedObjects.forEach((obj: THREE.Object3D) => {
removeAsset(obj.userData.modelUuid);
});
}
}; };
const onKeyDown = (event: KeyboardEvent) => { const onKeyDown = (event: KeyboardEvent) => {
@@ -69,6 +103,13 @@ const CopyPasteControls3D = ({
if (keyCombination === "Ctrl+V" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { if (keyCombination === "Ctrl+V" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
pasteCopiedObjects(); pasteCopiedObjects();
} }
if (keyCombination === "ESCAPE" && pastedObjects.length > 0) {
event.preventDefault();
clearSelection();
pastedObjects.forEach((obj: THREE.Object3D) => {
removeAsset(obj.userData.modelUuid);
});
}
}; };
if (!toggleView) { if (!toggleView) {
@@ -84,27 +125,22 @@ const CopyPasteControls3D = ({
canvasElement.removeEventListener("pointerup", onPointerUp); canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown); canvasElement.removeEventListener("keydown", onKeyDown);
}; };
}, [assets, camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, rotatedObjects]); }, [assets, camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, rotatedObjects]);
useFrame(() => { useFrame(() => {
if (pastedObjects.length > 0) { if (!isPasting || pastedObjects.length === 0) return;
const intersectionPoint = new THREE.Vector3(); if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) return;
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint); const intersectionPoint = new THREE.Vector3();
if (point) { raycaster.setFromCamera(pointer, camera);
const position = new THREE.Vector3(); const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (boundingBoxRef.current) {
boundingBoxRef.current?.getWorldPosition(position) if (hit && centerOffset) {
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); pastedObjects.forEach((pastedObject: THREE.Object3D, index: number) => {
} else { const newPos = new THREE.Vector3().addVectors(hit, relativePositions[index]);
const box = new THREE.Box3(); setPosition(pastedObject.userData.modelUuid, [newPos.x, 0, newPos.z]);
pastedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone())); });
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z));
}
}
} }
}); });
@@ -122,31 +158,41 @@ const CopyPasteControls3D = ({
const pasteCopiedObjects = () => { const pasteCopiedObjects = () => {
if (copiedObjects.length > 0 && pastedObjects.length === 0) { if (copiedObjects.length > 0 && pastedObjects.length === 0) {
const newClones = copiedObjects.map((obj: THREE.Object3D) => { const { center, relatives } = calculateRelativePositions(copiedObjects);
const clone = obj.clone(); setRelativePositions(relatives);
clone.position.copy(obj.position);
const newPastedObjects = copiedObjects.map((obj: any) => {
const clone = SkeletonUtils.clone(obj);
clone.userData.modelUuid = THREE.MathUtils.generateUUID();
return clone; return clone;
}); });
selectionGroup.current.add(...newClones);
setpastedObjects([...newClones]);
setSelectedAssets([...newClones]);
const intersectionPoint = new THREE.Vector3(); setpastedObjects(newPastedObjects);
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint); const intersectionPoint = new THREE.Vector3();
const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) { if (hit) {
const position = new THREE.Vector3(); const offset = new THREE.Vector3().subVectors(center, hit);
if (boundingBoxRef.current) { setCenterOffset(offset);
boundingBoxRef.current?.getWorldPosition(position) setIsPasting(true);
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
} else { newPastedObjects.forEach((obj: THREE.Object3D, index: number) => {
const box = new THREE.Box3(); const initialPos = new THREE.Vector3().addVectors(hit, relatives[index]);
newClones.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone())); const asset: Asset = {
const center = new THREE.Vector3(); modelUuid: obj.userData.modelUuid,
box.getCenter(center); modelName: obj.userData.modelName,
selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z)); assetId: obj.userData.assetId,
} position: initialPos.toArray(),
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
isLocked: false,
isVisible: true,
isCollidable: false,
opacity: 0.5,
};
addAsset(asset);
});
} }
} }
}; };
@@ -154,33 +200,33 @@ const CopyPasteControls3D = ({
const addPastedObjects = () => { const addPastedObjects = () => {
if (pastedObjects.length === 0) return; if (pastedObjects.length === 0) return;
pastedObjects.forEach(async (obj: THREE.Object3D) => { pastedObjects.forEach(async (pastedAsset: THREE.Object3D) => {
if (obj) { if (pastedAsset) {
const worldPosition = new THREE.Vector3(); const assetUuid = pastedAsset.userData.modelUuid;
obj.getWorldPosition(worldPosition); const asset = getAssetById(assetUuid);
obj.position.copy(worldPosition); if (!asset) return;
const newFloorItem: Types.FloorItemType = { const newFloorItem: Types.FloorItemType = {
modelUuid: THREE.MathUtils.generateUUID(), modelUuid: pastedAsset.userData.modelUuid,
modelName: obj.userData.modelName, modelName: pastedAsset.userData.modelName,
assetId: obj.userData.assetId, assetId: pastedAsset.userData.assetId,
position: [worldPosition.x, worldPosition.y, worldPosition.z], position: asset.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, rotation: { x: asset.rotation[0], y: asset.rotation[1], z: asset.rotation[2] },
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
}; };
let updatedEventData = null; let updatedEventData = null;
if (obj.userData.eventData) { if (pastedAsset.userData.eventData) {
updatedEventData = JSON.parse(JSON.stringify(obj.userData.eventData)); updatedEventData = JSON.parse(JSON.stringify(pastedAsset.userData.eventData));
updatedEventData.modelUuid = newFloorItem.modelUuid; updatedEventData.modelUuid = newFloorItem.modelUuid;
const eventData: any = { const eventData: any = {
type: obj.userData.eventData.type, type: pastedAsset.userData.eventData.type,
}; };
if (obj.userData.eventData.type === "Conveyor") { if (pastedAsset.userData.eventData.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = { const ConveyorEvent: ConveyorEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -212,7 +258,7 @@ const CopyPasteControls3D = ({
rotation: point.rotation rotation: point.rotation
})); }));
} else if (obj.userData.eventData.type === "Vehicle") { } else if (pastedAsset.userData.eventData.type === "Vehicle") {
const vehicleEvent: VehicleEventSchema = { const vehicleEvent: VehicleEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -250,7 +296,7 @@ const CopyPasteControls3D = ({
rotation: vehicleEvent.point.rotation rotation: vehicleEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "ArmBot") { } else if (pastedAsset.userData.eventData.type === "ArmBot") {
const roboticArmEvent: RoboticArmEventSchema = { const roboticArmEvent: RoboticArmEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -284,7 +330,7 @@ const CopyPasteControls3D = ({
rotation: roboticArmEvent.point.rotation rotation: roboticArmEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "StaticMachine") { } else if (pastedAsset.userData.eventData.type === "StaticMachine") {
const machineEvent: MachineEventSchema = { const machineEvent: MachineEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -312,7 +358,7 @@ const CopyPasteControls3D = ({
position: machineEvent.point.position, position: machineEvent.point.position,
rotation: machineEvent.point.rotation rotation: machineEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "Storage") { } else if (pastedAsset.userData.eventData.type === "Storage") {
const storageEvent: StorageEventSchema = { const storageEvent: StorageEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -339,7 +385,7 @@ const CopyPasteControls3D = ({
position: storageEvent.point.position, position: storageEvent.point.position,
rotation: storageEvent.point.rotation rotation: storageEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "Human") { } else if (pastedAsset.userData.eventData.type === "Human") {
const humanEvent: HumanEventSchema = { const humanEvent: HumanEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -374,20 +420,6 @@ const CopyPasteControls3D = ({
} }
newFloorItem.eventData = eventData; newFloorItem.eventData = eventData;
//REST
// await setAssetsApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = { const data = {
organization, organization,
@@ -395,7 +427,7 @@ const CopyPasteControls3D = ({
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId, assetId: newFloorItem.assetId,
position: newFloorItem.position, position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, rotation: { x: pastedAsset.rotation.x, y: pastedAsset.rotation.y, z: pastedAsset.rotation.z },
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
socketId: socket.id, socketId: socket.id,
@@ -405,15 +437,13 @@ const CopyPasteControls3D = ({
projectId projectId
}; };
// console.log('data: ', data); //REST
socket.emit("v1:model-asset:add", data);
obj.userData = { // await setAssetsApi(data);
name: newFloorItem.modelName,
modelId: newFloorItem.assetId, //SOCKET
modelUuid: newFloorItem.modelUuid,
eventData: JSON.parse(JSON.stringify(eventData)) socket.emit("v1:model-asset:add", data);
};
const asset: Asset = { const asset: Asset = {
modelUuid: data.modelUuid, modelUuid: data.modelUuid,
@@ -426,33 +456,17 @@ const CopyPasteControls3D = ({
isVisible: data.isVisible, isVisible: data.isVisible,
opacity: 1, opacity: 1,
eventData: data.eventData eventData: data.eventData
} };
addAsset(asset);
updateAsset(asset.modelUuid, asset);
} else { } else {
//REST
// await setAssetsApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = { const data = {
organization, organization,
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId, assetId: newFloorItem.assetId,
position: newFloorItem.position, position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, rotation: { x: pastedAsset.rotation.x, y: pastedAsset.rotation.y, z: pastedAsset.rotation.z },
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
socketId: socket.id, socketId: socket.id,
@@ -461,7 +475,6 @@ const CopyPasteControls3D = ({
userId userId
}; };
// console.log('data: ', data);
socket.emit("v1:model-asset:add", data); socket.emit("v1:model-asset:add", data);
const asset: Asset = { const asset: Asset = {
@@ -474,9 +487,9 @@ const CopyPasteControls3D = ({
isCollidable: false, isCollidable: false,
isVisible: data.isVisible, isVisible: data.isVisible,
opacity: 1, opacity: 1,
} };
addAsset(asset); updateAsset(asset.modelUuid, asset);
} }
} }
}); });
@@ -486,15 +499,14 @@ const CopyPasteControls3D = ({
}; };
const clearSelection = () => { const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setMovedObjects([]); setMovedObjects([]);
setpastedObjects([]); setpastedObjects([]);
setDuplicatedObjects([]); setDuplicatedObjects([]);
setRotatedObjects([]); setRotatedObjects([]);
setSelectedAssets([]); setSelectedAssets([]);
} setIsPasting(false);
setCenterOffset(null);
};
return null; return null;
}; };

View File

@@ -1,5 +1,5 @@
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useMemo } 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 { SkeletonUtils } from "three-stdlib"; import { SkeletonUtils } from "three-stdlib";
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store"; import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
@@ -10,18 +10,16 @@ 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 { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi'; // import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
const DuplicationControls3D = ({ const DuplicationControls3D = ({
duplicatedObjects, duplicatedObjects,
setDuplicatedObjects, setDuplicatedObjects,
setpastedObjects, setpastedObjects,
selectionGroup,
movedObjects, movedObjects,
setMovedObjects, setMovedObjects,
rotatedObjects, rotatedObjects,
setRotatedObjects, setRotatedObjects,
boundingBoxRef
}: any) => { }: any) => {
const { camera, controls, gl, scene, pointer, raycaster } = useThree(); const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const { toggleView } = useToggleView(); const { toggleView } = useToggleView();
@@ -31,39 +29,71 @@ const DuplicationControls3D = ({
const { assetStore, eventStore } = useSceneContext(); const { assetStore, eventStore } = useSceneContext();
const { addEvent } = eventStore(); const { addEvent } = eventStore();
const { projectId } = useParams(); const { projectId } = useParams();
const { assets, addAsset } = assetStore(); const { assets, addAsset, setPosition, updateAsset, removeAsset, getAssetById } = assetStore();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
const [isDuplicating, setIsDuplicating] = useState(false);
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({
left: false,
right: false,
});
const calculateDragOffset = useCallback((point: THREE.Vector3, hitPoint: THREE.Vector3) => {
const pointPosition = new THREE.Vector3().copy(point);
return new THREE.Vector3().subVectors(pointPosition, hitPoint);
}, []);
useEffect(() => { useEffect(() => {
if (!camera || !scene || toggleView) return; if (!camera || !scene || toggleView) return;
const canvasElement = gl.domElement; const canvasElement = gl.domElement;
canvasElement.tabIndex = 0; canvasElement.tabIndex = 0;
let isMoving = false; let isPointerMoving = false;
const onPointerDown = () => { const onPointerDown = (event: PointerEvent) => {
isMoving = false; isPointerMoving = false;
if (event.button === 0) mouseButtonsDown.current.left = true;
if (event.button === 2) mouseButtonsDown.current.right = true;
}; };
const onPointerMove = () => { const onPointerMove = () => {
isMoving = true; isPointerMoving = true;
}; };
const onPointerUp = (event: PointerEvent) => { const onPointerUp = (event: PointerEvent) => {
if (!isMoving && duplicatedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { if (event.button === 0) mouseButtonsDown.current.left = false;
if (event.button === 2) mouseButtonsDown.current.right = false;
if (!isPointerMoving && duplicatedObjects.length > 0 && event.button === 0) {
event.preventDefault(); event.preventDefault();
addDuplicatedAssets(); addDuplicatedAssets();
} }
if (!isPointerMoving && duplicatedObjects.length > 0 && event.button === 2) {
event.preventDefault();
clearSelection();
duplicatedObjects.forEach((obj: THREE.Object3D) => {
removeAsset(obj.userData.modelUuid);
});
}
}; };
const onKeyDown = (event: KeyboardEvent) => { const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event); const keyCombination = detectModifierKeys(event);
if (keyCombination === "Ctrl+D" && selectedAssets.length > 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { if (keyCombination === "Ctrl+D" && movedObjects.length === 0 && rotatedObjects.length === 0) {
duplicateSelection(); duplicateSelection();
} }
if (keyCombination === "ESCAPE" && duplicatedObjects.length > 0) {
event.preventDefault();
clearSelection();
duplicatedObjects.forEach((obj: THREE.Object3D) => {
removeAsset(obj.userData.modelUuid);
});
}
}; };
if (!toggleView) { if (!toggleView) {
@@ -79,82 +109,125 @@ const DuplicationControls3D = ({
canvasElement.removeEventListener("pointerup", onPointerUp); canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown); canvasElement.removeEventListener("keydown", onKeyDown);
}; };
}, [assets, camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, rotatedObjects]); }, [assets, camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, rotatedObjects]);
useFrame(() => { useFrame(() => {
if (duplicatedObjects.length > 0) { if (!isDuplicating || duplicatedObjects.length === 0) return;
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera); const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint); raycaster.setFromCamera(pointer, camera);
if (point) { const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
const position = new THREE.Vector3();
if (boundingBoxRef.current) { if (hit) {
boundingBoxRef.current?.getWorldPosition(position) if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) {
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z)); const assetUuid = duplicatedObjects[0].userData.modelUuid;
} else { const asset = getAssetById(assetUuid);
const box = new THREE.Box3(); if (!asset) return;
duplicatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone())); if (duplicatedObjects[0]) {
const center = new THREE.Vector3(); const newOffset = calculateDragOffset(new THREE.Vector3(...asset.position), intersectionPoint);
box.getCenter(center); setDragOffset(newOffset);
selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z));
} }
return;
}
if (dragOffset) {
const adjustedHit = new THREE.Vector3().addVectors(intersectionPoint, dragOffset);
duplicatedObjects.forEach((duplicatedObject: THREE.Object3D) => {
if (duplicatedObject.userData.modelUuid) {
const initialPosition = initialPositions[duplicatedObject.userData.modelUuid];
if (initialPosition) {
const relativeOffset = new THREE.Vector3().subVectors(
initialPosition,
initialPositions[duplicatedObjects[0].userData.modelUuid]
);
const newPosition = new THREE.Vector3().addVectors(adjustedHit, relativeOffset);
const positionArray: [number, number, number] = [newPosition.x, newPosition.y, newPosition.z];
setPosition(duplicatedObject.userData.modelUuid, positionArray);
}
}
});
} }
} }
}); });
const duplicateSelection = () => { const duplicateSelection = useCallback(() => {
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) { if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
const newClones = selectedAssets.map((asset: any) => { const positions: Record<string, THREE.Vector3> = {};
const clone = SkeletonUtils.clone(asset);
clone.position.copy(asset.position); const newDuplicatedObjects = selectedAssets.map((obj: any) => {
const clone = SkeletonUtils.clone(obj);
clone.userData.modelUuid = THREE.MathUtils.generateUUID();
positions[clone.userData.modelUuid] = new THREE.Vector3().copy(obj.position);
return clone; return clone;
}); });
selectionGroup.current.add(...newClones); setDuplicatedObjects(newDuplicatedObjects);
setDuplicatedObjects(newClones); setInitialPositions(positions);
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint); const intersectionPoint = new THREE.Vector3();
const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) { if (hit) {
const position = new THREE.Vector3(); const offset = calculateDragOffset(selectedAssets[0].position, hit);
boundingBoxRef.current?.getWorldPosition(position) setDragOffset(offset);
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
} }
setIsDuplicating(true);
newDuplicatedObjects.forEach((obj: THREE.Object3D) => {
const asset: Asset = {
modelUuid: obj.userData.modelUuid,
modelName: obj.userData.modelName,
assetId: obj.userData.assetId,
position: [obj.position.x, 0, obj.position.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
isLocked: false,
isVisible: true,
isCollidable: false,
opacity: 0.5,
};
addAsset(asset);
});
} }
}; }, [selectedAssets, duplicatedObjects]);
const addDuplicatedAssets = () => { const addDuplicatedAssets = () => {
if (duplicatedObjects.length === 0) return; if (duplicatedObjects.length === 0) return;
duplicatedObjects.forEach(async (obj: THREE.Object3D) => { duplicatedObjects.forEach(async (duplicatedAsset: THREE.Object3D) => {
if (obj) { if (duplicatedAsset) {
const worldPosition = new THREE.Vector3(); const assetUuid = duplicatedAsset.userData.modelUuid;
obj.getWorldPosition(worldPosition); const asset = getAssetById(assetUuid);
obj.position.copy(worldPosition); if (!asset) return;
const newFloorItem: Types.FloorItemType = { const newFloorItem: Types.FloorItemType = {
modelUuid: THREE.MathUtils.generateUUID(), modelUuid: duplicatedAsset.userData.modelUuid,
modelName: obj.userData.modelName, modelName: duplicatedAsset.userData.modelName,
assetId: obj.userData.assetId, assetId: duplicatedAsset.userData.assetId,
position: [worldPosition.x, worldPosition.y, worldPosition.z], position: asset.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, }, rotation: { x: asset.rotation[0], y: asset.rotation[1], z: asset.rotation[2] },
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
}; };
let updatedEventData = null; let updatedEventData = null;
if (obj.userData.eventData) {
updatedEventData = JSON.parse(JSON.stringify(obj.userData.eventData)); if (duplicatedAsset.userData.eventData) {
updatedEventData = JSON.parse(JSON.stringify(duplicatedAsset.userData.eventData));
updatedEventData.modelUuid = newFloorItem.modelUuid; updatedEventData.modelUuid = newFloorItem.modelUuid;
const eventData: any = { const eventData: any = {
type: obj.userData.eventData.type, type: duplicatedAsset.userData.eventData.type,
}; };
if (obj.userData.eventData.type === "Conveyor") { if (duplicatedAsset.userData.eventData.type === "Conveyor") {
const ConveyorEvent: ConveyorEventSchema = { const ConveyorEvent: ConveyorEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -186,7 +259,7 @@ const DuplicationControls3D = ({
rotation: point.rotation rotation: point.rotation
})); }));
} else if (obj.userData.eventData.type === "Vehicle") { } else if (duplicatedAsset.userData.eventData.type === "Vehicle") {
const vehicleEvent: VehicleEventSchema = { const vehicleEvent: VehicleEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -224,7 +297,7 @@ const DuplicationControls3D = ({
rotation: vehicleEvent.point.rotation rotation: vehicleEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "ArmBot") { } else if (duplicatedAsset.userData.eventData.type === "ArmBot") {
const roboticArmEvent: RoboticArmEventSchema = { const roboticArmEvent: RoboticArmEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -258,7 +331,7 @@ const DuplicationControls3D = ({
rotation: roboticArmEvent.point.rotation rotation: roboticArmEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "StaticMachine") { } else if (duplicatedAsset.userData.eventData.type === "StaticMachine") {
const machineEvent: MachineEventSchema = { const machineEvent: MachineEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -286,7 +359,7 @@ const DuplicationControls3D = ({
position: machineEvent.point.position, position: machineEvent.point.position,
rotation: machineEvent.point.rotation rotation: machineEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "Storage") { } else if (duplicatedAsset.userData.eventData.type === "Storage") {
const storageEvent: StorageEventSchema = { const storageEvent: StorageEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -313,7 +386,7 @@ const DuplicationControls3D = ({
position: storageEvent.point.position, position: storageEvent.point.position,
rotation: storageEvent.point.rotation rotation: storageEvent.point.rotation
}; };
} else if (obj.userData.eventData.type === "Human") { } else if (duplicatedAsset.userData.eventData.type === "Human") {
const humanEvent: HumanEventSchema = { const humanEvent: HumanEventSchema = {
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
@@ -349,38 +422,28 @@ const DuplicationControls3D = ({
newFloorItem.eventData = eventData; newFloorItem.eventData = eventData;
//REST
// await setAssetsApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = { const data = {
organization, organization,
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId, assetId: newFloorItem.assetId,
position: newFloorItem.position, position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, rotation: { x: duplicatedAsset.rotation.x, y: duplicatedAsset.rotation.y, z: duplicatedAsset.rotation.z },
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
socketId: socket.id, socketId: socket.id,
eventData: eventData, eventData: eventData,
versionId: selectedVersion?.versionId || '', versionId: selectedVersion?.versionId || '',
projectId, userId,
userId projectId
}; };
// console.log('data: ', data); //REST
// await setAssetsApi(data);
//SOCKET
socket.emit("v1:model-asset:add", data); socket.emit("v1:model-asset:add", data);
const asset: Asset = { const asset: Asset = {
@@ -394,43 +457,25 @@ const DuplicationControls3D = ({
isVisible: data.isVisible, isVisible: data.isVisible,
opacity: 1, opacity: 1,
eventData: data.eventData eventData: data.eventData
} };
addAsset(asset);
updateAsset(asset.modelUuid, asset);
} else { } else {
//REST
// await setAssetsApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = { const data = {
organization, organization,
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId, assetId: newFloorItem.assetId,
position: newFloorItem.position, position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, rotation: { x: duplicatedAsset.rotation.x, y: duplicatedAsset.rotation.y, z: duplicatedAsset.rotation.z },
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
socketId: socket.id, socketId: socket.id,
versionId: selectedVersion?.versionId || '', versionId: selectedVersion?.versionId || '',
userId, projectId,
projectId userId
}; };
// console.log('data: ', data);/
socket.emit("v1:model-asset:add", data); socket.emit("v1:model-asset:add", data);
const asset: Asset = { const asset: Asset = {
@@ -443,27 +488,26 @@ const DuplicationControls3D = ({
isCollidable: false, isCollidable: false,
isVisible: data.isVisible, isVisible: data.isVisible,
opacity: 1, opacity: 1,
} };
addAsset(asset); updateAsset(asset.modelUuid, asset);
} }
} }
}); });
echo.success("Object duplicated!"); echo.success("Object duplicated!");
clearSelection(); clearSelection();
} };
const clearSelection = () => { const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setMovedObjects([]); setMovedObjects([]);
setpastedObjects([]); setpastedObjects([]);
setDuplicatedObjects([]); setDuplicatedObjects([]);
setRotatedObjects([]); setRotatedObjects([]);
setSelectedAssets([]); setSelectedAssets([]);
} setIsDuplicating(false);
setDragOffset(null);
};
return null; return null;
}; };

View File

@@ -1,5 +1,5 @@
import * as THREE from "three"; import * as THREE from "three";
import { 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 { useSelectedAssets, useSocketStore, useToggleView, } from "../../../../../store/builder/store"; import { useSelectedAssets, useSocketStore, useToggleView, } from "../../../../../store/builder/store";
import * as Types from "../../../../../types/world/worldTypes"; import * as Types from "../../../../../types/world/worldTypes";
@@ -22,7 +22,6 @@ function MoveControls3D({
setpastedObjects, setpastedObjects,
duplicatedObjects, duplicatedObjects,
setDuplicatedObjects, setDuplicatedObjects,
selectionGroup,
rotatedObjects, rotatedObjects,
setRotatedObjects, setRotatedObjects,
boundingBoxRef, boundingBoxRef,
@@ -39,11 +38,19 @@ function MoveControls3D({
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const { projectId } = useParams(); const { projectId } = useParams();
const { assetStore, eventStore, productStore } = useSceneContext(); const { assetStore, eventStore, productStore } = useSceneContext();
const { updateAsset } = assetStore(); const { updateAsset, setPosition, getAssetById } = assetStore();
const AssetGroup = useRef<THREE.Group | undefined>(undefined);
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
const [initialStates, setInitialStates] = useState<Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }>>({});
const [isMoving, setIsMoving] = useState(false);
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({
left: false,
right: false,
});
const updateBackend = ( const updateBackend = (
productName: string, productName: string,
productUuid: string, productUuid: string,
@@ -65,22 +72,10 @@ function MoveControls3D({
const canvasElement = gl.domElement; const canvasElement = gl.domElement;
canvasElement.tabIndex = 0; canvasElement.tabIndex = 0;
const itemsGroup: any = scene.getObjectByName("Asset Group"); let isPointerMoving = false;
AssetGroup.current = itemsGroup;
if (!AssetGroup.current) {
console.error("Asset Group not found in the scene.");
return;
}
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => { const onPointerMove = () => {
isMoving = true; isPointerMoving = true;
}; };
const onKeyUp = (event: KeyboardEvent) => { const onKeyUp = (event: KeyboardEvent) => {
@@ -91,21 +86,25 @@ function MoveControls3D({
} }
}; };
const onPointerDown = (event: PointerEvent) => {
isPointerMoving = false;
if (event.button === 0) mouseButtonsDown.current.left = true;
if (event.button === 2) mouseButtonsDown.current.right = true;
};
const onPointerUp = (event: PointerEvent) => { const onPointerUp = (event: PointerEvent) => {
if (!isMoving && movedObjects.length > 0 && event.button === 0) { if (event.button === 0) mouseButtonsDown.current.left = false;
if (event.button === 2) mouseButtonsDown.current.right = false;
if (!isPointerMoving && movedObjects.length > 0 && event.button === 0) {
event.preventDefault(); event.preventDefault();
placeMovedAssets(); placeMovedAssets();
} }
if (!isMoving && movedObjects.length > 0 && event.button === 2) { if (!isPointerMoving && movedObjects.length > 0 && event.button === 2) {
event.preventDefault(); event.preventDefault();
resetToInitialPositions();
clearSelection(); clearSelection();
movedObjects.forEach((asset: any) => {
if (AssetGroup.current) {
AssetGroup.current.attach(asset);
}
});
setMovedObjects([]); setMovedObjects([]);
} }
setKeyEvent(""); setKeyEvent("");
@@ -117,10 +116,12 @@ function MoveControls3D({
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0)
return; return;
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") { if (keyCombination !== keyEvent) {
setKeyEvent(keyCombination); if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
} else { setKeyEvent(keyCombination);
setKeyEvent(""); } else {
setKeyEvent("");
}
} }
if (keyCombination === "G") { if (keyCombination === "G") {
@@ -131,14 +132,8 @@ function MoveControls3D({
if (keyCombination === "ESCAPE") { if (keyCombination === "ESCAPE") {
event.preventDefault(); event.preventDefault();
resetToInitialPositions();
clearSelection(); clearSelection();
movedObjects.forEach((asset: any) => {
if (AssetGroup.current) {
AssetGroup.current.attach(asset);
}
});
setMovedObjects([]); setMovedObjects([]);
} }
}; };
@@ -158,118 +153,161 @@ function MoveControls3D({
canvasElement.removeEventListener("keydown", onKeyDown); canvasElement.removeEventListener("keydown", onKeyDown);
canvasElement?.removeEventListener("keyup", onKeyUp); canvasElement?.removeEventListener("keyup", onKeyUp);
}; };
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent,]); }, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent]);
let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25; const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
const pointPosition = new THREE.Vector3().copy(point.position);
return new THREE.Vector3().subVectors(pointPosition, hitPoint);
}, []);
const resetToInitialPositions = useCallback(() => {
setTimeout(() => {
movedObjects.forEach((movedObject: THREE.Object3D) => {
if (movedObject.userData.modelUuid && initialStates[movedObject.uuid]) {
const initialState = initialStates[movedObject.uuid];
const positionArray: [number, number, number] = [
initialState.position.x,
initialState.position.y,
initialState.position.z
];
updateAsset(movedObject.userData.modelUuid, {
position: positionArray,
rotation: [
initialState.rotation?.x || 0,
initialState.rotation?.y || 0,
initialState.rotation?.z || 0
],
});
movedObject.position.copy(initialState.position);
if (initialState.rotation) {
movedObject.rotation.copy(initialState.rotation);
}
}
});
}, 0)
}, [movedObjects, initialStates, updateAsset]);
useFrame(() => { useFrame(() => {
if (movedObjects.length > 0) { if (!isMoving || movedObjects.length === 0) return;
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
let point = raycaster.ray.intersectPlane(plane, intersectionPoint);
const floorsGroup = scene.getObjectByName("Floors-Group") as Types.Group | null;
const floorChildren = floorsGroup?.children ?? [];
const floorIntersections = raycaster.intersectObjects([...floorChildren], true);
const intersectedFloor = floorIntersections.find((intersect) => intersect.object.name.includes("Floor"));
if (intersectedFloor && selectedAssets.length === 1) { const intersectionPoint = new THREE.Vector3();
if (intersectedFloor.object.userData.floorUuid) { raycaster.setFromCamera(pointer, camera);
point = new THREE.Vector3(intersectedFloor.point.x, intersectedFloor.object.userData.floorDepth, intersectedFloor.point.z); const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (hit) {
if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) {
if (movedObjects[0]) {
const newOffset = calculateDragOffset(movedObjects[0], intersectionPoint);
setDragOffset(newOffset);
} }
return;
} }
if (point) { if (dragOffset) {
let targetX = point.x; const rawBasePosition = new THREE.Vector3().addVectors(intersectionPoint, dragOffset);
let targetY = point.y;
let targetZ = point.z;
if (keyEvent === "Ctrl") { let moveSpeed = keyEvent.includes("Shift") ? 0.05 : 1;
targetX = snapControls(targetX, "Ctrl");
targetZ = snapControls(targetZ, "Ctrl"); const initialBasePosition = initialPositions[movedObjects[0].uuid];
const positionDifference = new THREE.Vector3().subVectors(rawBasePosition, initialBasePosition);
const adjustedDifference = positionDifference.multiplyScalar(moveSpeed);
const baseNewPosition = new THREE.Vector3().addVectors(initialBasePosition, adjustedDifference);
if (keyEvent.includes("Ctrl")) {
baseNewPosition.x = snapControls(baseNewPosition.x, "Ctrl");
baseNewPosition.z = snapControls(baseNewPosition.z, "Ctrl");
} }
// else if (keyEvent === "Ctrl+Shift") { movedObjects.forEach((movedAsset: THREE.Object3D) => {
// targetX = snapControls(targetX, "Ctrl+Shift"); if (movedAsset.userData.modelUuid) {
// targetZ = snapControls(targetZ, "Ctrl+Shift"); const initialPosition = initialPositions[movedAsset.userData.modelUuid];
// } else if (keyEvent === "Shift") {
// targetX = snapControls(targetX, "Shift"); if (initialPosition) {
// targetZ = snapControls(targetZ, "Shift"); const relativeOffset = new THREE.Vector3().subVectors(
// } else { initialPosition,
// } initialPositions[movedObjects[0].uuid]
);
const newPosition = new THREE.Vector3().addVectors(baseNewPosition, relativeOffset);
const positionArray: [number, number, number] = [newPosition.x, newPosition.y, newPosition.z];
setPosition(movedAsset.userData.modelUuid, positionArray);
}
}
});
const position = new THREE.Vector3(); const position = new THREE.Vector3();
if (boundingBoxRef.current) { if (boundingBoxRef.current) {
boundingBoxRef.current.getWorldPosition(position); boundingBoxRef.current.getWorldPosition(position);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (position.x - selectionGroup.current.position.x),
targetY - (position.y - selectionGroup.current.position.y),
targetZ - (position.z - selectionGroup.current.position.z)
),
moveSpeed
);
} else { } else {
const box = new THREE.Box3(); const box = new THREE.Box3();
movedObjects.forEach((obj: THREE.Object3D) => movedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
box.expandByObject(obj)
);
const center = new THREE.Vector3(); const center = new THREE.Vector3();
box.getCenter(center); box.getCenter(center);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (center.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (center.z - selectionGroup.current.position.z)
),
moveSpeed
);
} }
} }
} }
}); });
const moveAssets = () => { const moveAssets = () => {
setMovedObjects(selectedAssets); const states: Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }> = {};
selectedAssets.forEach((asset: any) => { const positions: Record<string, THREE.Vector3> = {};
selectionGroup.current.attach(asset);
selectedAssets.forEach((asset: THREE.Object3D) => {
states[asset.uuid] = {
position: new THREE.Vector3().copy(asset.position),
rotation: asset.rotation ? new THREE.Euler().copy(asset.rotation) : undefined
};
positions[asset.uuid] = new THREE.Vector3().copy(asset.position);
}); });
setInitialStates(states);
setInitialPositions(positions);
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (hit && selectedAssets[0]) {
const offset = calculateDragOffset(selectedAssets[0], hit);
setDragOffset(offset);
}
setMovedObjects(selectedAssets);
setIsMoving(true);
}; };
const placeMovedAssets = () => { const placeMovedAssets = () => {
if (movedObjects.length === 0) return; if (movedObjects.length === 0) return;
movedObjects.forEach(async (obj: THREE.Object3D) => { movedObjects.forEach(async (movedAsset: THREE.Object3D) => {
if (obj && AssetGroup.current) { if (movedAsset) {
let worldPosition = new THREE.Vector3(); const assetUuid = movedAsset.userData.modelUuid;
obj.getWorldPosition(worldPosition); const asset = getAssetById(assetUuid);
if (!asset) return;
if (worldPosition.y < 0) {
worldPosition.y = 0;
}
selectionGroup.current.remove(obj);
obj.position.copy(worldPosition);
const newFloorItem: Types.FloorItemType = { const newFloorItem: Types.FloorItemType = {
modelUuid: obj.userData.modelUuid, modelUuid: movedAsset.userData.modelUuid,
modelName: obj.userData.modelName, modelName: movedAsset.userData.modelName,
assetId: obj.userData.assetId, assetId: movedAsset.userData.assetId,
position: [worldPosition.x, worldPosition.y, worldPosition.z], position: asset.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, rotation: { x: movedAsset.rotation.x, y: movedAsset.rotation.y, z: movedAsset.rotation.z },
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
}; };
if (obj.userData.eventData) { if (movedAsset.userData.eventData) {
const eventData = eventStore.getState().getEventByModelUuid(obj.userData.modelUuid); const eventData = eventStore.getState().getEventByModelUuid(movedAsset.userData.modelUuid);
const productData = productStore.getState().getEventByModelUuid(selectedProduct.productUuid, obj.userData.modelUuid); const productData = productStore.getState().getEventByModelUuid(selectedProduct.productUuid, movedAsset.userData.modelUuid);
if (eventData) { if (eventData) {
eventStore.getState().updateEvent(obj.userData.modelUuid, { eventStore.getState().updateEvent(movedAsset.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z], position: asset.position,
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], rotation: [movedAsset.rotation.x, movedAsset.rotation.y, movedAsset.rotation.z],
}); });
} }
@@ -278,10 +316,10 @@ function MoveControls3D({
.getState() .getState()
.updateEvent( .updateEvent(
selectedProduct.productUuid, selectedProduct.productUuid,
obj.userData.modelUuid, movedAsset.userData.modelUuid,
{ {
position: [worldPosition.x, worldPosition.y, worldPosition.z], position: asset.position,
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], rotation: [movedAsset.rotation.x, movedAsset.rotation.y, movedAsset.rotation.z],
} }
); );
@@ -298,33 +336,18 @@ function MoveControls3D({
} }
} }
updateAsset(obj.userData.modelUuid, { updateAsset(movedAsset.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z], position: asset.position,
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], rotation: [movedAsset.rotation.x, movedAsset.rotation.y, movedAsset.rotation.z],
}); });
//REST
// await setAssetsApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = { const data = {
organization, organization,
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName, modelName: newFloorItem.modelName,
assetId: newFloorItem.assetId, assetId: newFloorItem.assetId,
position: newFloorItem.position, position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, rotation: { x: movedAsset.rotation.x, y: movedAsset.rotation.y, z: movedAsset.rotation.z },
isLocked: false, isLocked: false,
isVisible: true, isVisible: true,
socketId: socket.id, socketId: socket.id,
@@ -333,22 +356,22 @@ function MoveControls3D({
userId userId
}; };
// console.log('data: ', data); //REST
socket.emit("v1:model-asset:add", data);
AssetGroup.current.add(obj); // await setAssetsApi(data);
//SOCKET
socket.emit("v1:model-asset:add", data);
} }
}); });
echo.success("Object moved!"); echo.success("Object moved!");
setIsMoving(false);
clearSelection(); clearSelection();
}; };
const clearSelection = () => { const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]); setpastedObjects([]);
setDuplicatedObjects([]); setDuplicatedObjects([]);
setMovedObjects([]); setMovedObjects([]);

View File

@@ -1,5 +1,5 @@
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useMemo, useRef } 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 { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store"; import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
import * as Types from "../../../../../types/world/worldTypes"; import * as Types from "../../../../../types/world/worldTypes";
@@ -20,11 +20,9 @@ function RotateControls3D({
pastedObjects, pastedObjects,
setpastedObjects, setpastedObjects,
duplicatedObjects, duplicatedObjects,
setDuplicatedObjects, setDuplicatedObjects
selectionGroup
}: any) { }: any) {
const { camera, gl, scene, pointer, raycaster } = useThree();
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 { toggleView } = useToggleView();
@@ -36,26 +34,33 @@ function RotateControls3D({
const { projectId } = useParams(); const { projectId } = useParams();
const { assetStore, eventStore, productStore } = useSceneContext(); const { assetStore, eventStore, productStore } = useSceneContext();
const { updateAsset } = assetStore(); const { updateAsset } = assetStore();
const AssetGroup = useRef<THREE.Group | undefined>(undefined);
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const updateBackend = ( 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 updateBackend = useCallback((
productName: string, productName: string,
productUuid: string, productUuid: string,
projectId: string, projectId: string,
eventData: EventsSchema eventData: EventsSchema
) => { ) => {
upsertProductOrEventApi({ upsertProductOrEventApi({
productName: productName, productName,
productUuid: productUuid, productUuid,
projectId: projectId, projectId,
eventDatas: eventData, eventDatas: eventData,
versionId: selectedVersion?.versionId || '', versionId: selectedVersion?.versionId || '',
}) });
} }, [selectedVersion]);
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
useEffect(() => { useEffect(() => {
if (!camera || !scene || toggleView) return; if (!camera || !scene || toggleView) return;
@@ -63,45 +68,37 @@ function RotateControls3D({
const canvasElement = gl.domElement; const canvasElement = gl.domElement;
canvasElement.tabIndex = 0; canvasElement.tabIndex = 0;
const itemsGroup: any = scene.getObjectByName("Asset Group"); let isPointerMoving = false;
AssetGroup.current = itemsGroup;
if (!AssetGroup.current) {
console.error("Asset Group not found in the scene.");
return;
}
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => { const onPointerMove = () => {
isMoving = true; isPointerMoving = true;
};
const onPointerDown = (event: PointerEvent) => {
isPointerMoving = false;
if (event.button === 0) mouseButtonsDown.current.left = true;
if (event.button === 2) mouseButtonsDown.current.right = true;
}; };
const onPointerUp = (event: PointerEvent) => { const onPointerUp = (event: PointerEvent) => {
if (!isMoving && rotatedObjects.length > 0 && event.button === 0) { if (event.button === 0) mouseButtonsDown.current.left = false;
if (event.button === 2) mouseButtonsDown.current.right = false;
if (!isPointerMoving && rotatedObjects.length > 0 && event.button === 0) {
event.preventDefault(); event.preventDefault();
placeRotatedAssets(); placeRotatedAssets();
} }
if (!isMoving && rotatedObjects.length > 0 && event.button === 2) { if (!isPointerMoving && rotatedObjects.length > 0 && event.button === 2) {
event.preventDefault(); event.preventDefault();
resetToInitialRotations();
clearSelection(); clearSelection();
rotatedObjects.forEach((asset: any) => {
if (AssetGroup.current) {
AssetGroup.current.attach(asset);
}
});
setRotatedObjects([]); setRotatedObjects([]);
} }
}; };
const onKeyDown = (event: KeyboardEvent) => { const onKeyDown = (event: KeyboardEvent) => {
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return; if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return;
if (event.key.toLowerCase() === "r") { if (event.key.toLowerCase() === "r") {
if (selectedAssets.length > 0) { if (selectedAssets.length > 0) {
rotateAssets(); rotateAssets();
@@ -109,15 +106,8 @@ function RotateControls3D({
} }
if (event.key.toLowerCase() === "escape") { if (event.key.toLowerCase() === "escape") {
event.preventDefault(); event.preventDefault();
resetToInitialRotations();
clearSelection(); clearSelection();
rotatedObjects.forEach((asset: any) => {
if (AssetGroup.current) {
AssetGroup.current.attach(asset);
}
});
setRotatedObjects([]); setRotatedObjects([]);
} }
}; };
@@ -135,41 +125,85 @@ function RotateControls3D({
canvasElement.removeEventListener("pointerup", onPointerUp); canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown); canvasElement.removeEventListener("keydown", onKeyDown);
}; };
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]); }, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects]);
const resetToInitialRotations = useCallback(() => {
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,];
updateAsset(obj.userData.modelUuid, {
rotation: rotationArray,
position: positionArray,
});
obj.rotation.copy(initialRotation);
obj.position.copy(initialPosition);
}
}
});
}, [rotatedObjects, initialRotations, initialPositions, updateAsset]);
useFrame(() => { useFrame(() => {
if (rotatedObjects.length > 0) { if (!isRotating || rotatedObjects.length === 0) return;
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point && prevPointerPosition.current) { const intersectionPoint = new THREE.Vector3();
const box = new THREE.Box3(); raycaster.setFromCamera(pointer, camera);
rotatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj)); const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
const center = new THREE.Vector3();
box.getCenter(center);
const delta = new THREE.Vector3().subVectors(point, center);
const prevPointerPosition3D = new THREE.Vector3(prevPointerPosition.current.x, 0, prevPointerPosition.current.y);
const angle = Math.atan2(delta.z, delta.x) - Math.atan2(prevPointerPosition3D.z - center.z, prevPointerPosition3D.x - center.x);
selectionGroup.current.rotation.y += -angle;
selectionGroup.current.position.sub(center);
selectionGroup.current.position.applyAxisAngle(new THREE.Vector3(0, 1, 0), -angle);
selectionGroup.current.position.add(center);
if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) {
if (point) {
prevPointerPosition.current = new THREE.Vector2(point.x, point.z); prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
} }
return;
}
if (point && 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;
rotatedObjects.forEach((obj: THREE.Object3D) => {
if (obj.userData.modelUuid) {
const relativePos = new THREE.Vector3().subVectors(obj.position, center);
relativePos.applyAxisAngle(new THREE.Vector3(0, 1, 0), angleDelta);
obj.position.copy(center).add(relativePos);
obj.rotation.y += angleDelta;
}
});
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
} }
}); });
const rotateAssets = () => { const rotateAssets = useCallback(() => {
const rotations: Record<string, THREE.Euler> = {};
const positions: Record<string, THREE.Vector3> = {};
const box = new THREE.Box3(); const box = new THREE.Box3();
selectedAssets.forEach((asset: any) => box.expandByObject(asset)); selectedAssets.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
const center = new THREE.Vector3(); const center = new THREE.Vector3();
box.getCenter(center); box.getCenter(center);
rotationCenter.current = center;
selectedAssets.forEach((obj: THREE.Object3D) => {
rotations[obj.uuid] = new THREE.Euler().copy(obj.rotation);
positions[obj.uuid] = new THREE.Vector3().copy(obj.position);
});
setInitialRotations(rotations);
setInitialPositions(positions);
const intersectionPoint = new THREE.Vector3(); const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera); raycaster.setFromCamera(pointer, camera);
@@ -179,56 +213,54 @@ function RotateControls3D({
prevPointerPosition.current = new THREE.Vector2(point.x, point.z); prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
} }
selectedAssets.forEach((asset: any) => {
selectionGroup.current.attach(asset);
});
setRotatedObjects(selectedAssets); setRotatedObjects(selectedAssets);
}; setIsRotating(true);
}, [selectedAssets, camera, pointer, raycaster, plane]);
const placeRotatedAssets = () => { const placeRotatedAssets = useCallback(() => {
if (rotatedObjects.length === 0) return; if (rotatedObjects.length === 0) return;
rotatedObjects.forEach(async (obj: THREE.Object3D) => { rotatedObjects.forEach((obj: THREE.Object3D) => {
if (obj && AssetGroup.current) { if (obj && obj.userData.modelUuid) {
const worldPosition = new THREE.Vector3(); const rotationArray: [number, number, number] = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
const worldQuaternion = new THREE.Quaternion();
obj.getWorldPosition(worldPosition); const positionArray: [number, number, number] = [obj.position.x, obj.position.y, obj.position.z];
obj.getWorldQuaternion(worldQuaternion);
selectionGroup.current.remove(obj);
obj.position.copy(worldPosition);
obj.quaternion.copy(worldQuaternion);
const newFloorItem: Types.FloorItemType = { const newFloorItem: Types.FloorItemType = {
modelUuid: obj.userData.modelUuid, modelUuid: obj.userData.modelUuid,
modelName: obj.userData.modelName, modelName: obj.userData.modelName,
assetId: obj.userData.assetId, assetId: obj.userData.assetId,
position: [worldPosition.x, worldPosition.y, worldPosition.z], position: positionArray,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z }, rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false, isLocked: false,
isVisible: true isVisible: true,
}; };
if (obj.userData.eventData) { if (obj.userData.eventData) {
const eventData = eventStore.getState().getEventByModelUuid(obj.userData.modelUuid); const eventData = eventStore.getState().getEventByModelUuid(obj.userData.modelUuid);
const productData = productStore.getState().getEventByModelUuid(selectedProductStore.getState().selectedProduct.productUuid, obj.userData.modelUuid); const productData = productStore.getState().getEventByModelUuid(
selectedProduct.productUuid,
obj.userData.modelUuid
);
if (eventData) { if (eventData) {
eventStore.getState().updateEvent(obj.userData.modelUuid, { eventStore.getState().updateEvent(obj.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z], position: positionArray,
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], rotation: rotationArray,
}) });
} }
if (productData) {
const event = productStore.getState().updateEvent(selectedProductStore.getState().selectedProduct.productUuid, obj.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
})
if (event && organization) { if (productData) {
const event = productStore.getState().updateEvent(
selectedProduct.productUuid,
obj.userData.modelUuid,
{
position: positionArray,
rotation: rotationArray,
}
);
if (event) {
updateBackend( updateBackend(
selectedProduct.productName, selectedProduct.productName,
selectedProduct.productUuid, selectedProduct.productUuid,
@@ -242,25 +274,10 @@ function RotateControls3D({
} }
updateAsset(obj.userData.modelUuid, { updateAsset(obj.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z], position: positionArray,
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z], rotation: rotationArray,
}); });
//REST
// await setAssetsApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = { const data = {
organization, organization,
modelUuid: newFloorItem.modelUuid, modelUuid: newFloorItem.modelUuid,
@@ -276,29 +293,29 @@ function RotateControls3D({
userId userId
}; };
// console.log('data: ', data); //REST
socket.emit("v1:model-asset:add", data);
AssetGroup.current.add(obj); // await setAssetsApi(data);
//SOCKET
socket.emit("v1:model-asset:add", data);
} }
}); });
echo.success("Object rotated!");
setIsRotating(false);
clearSelection(); clearSelection();
} }, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId]);
const clearSelection = () => { const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]); setpastedObjects([]);
setDuplicatedObjects([]); setDuplicatedObjects([]);
setMovedObjects([]); setMovedObjects([]);
setRotatedObjects([]); setRotatedObjects([]);
setSelectedAssets([]); setSelectedAssets([]);
} };
return null; return null;
} }
export default RotateControls3D export default RotateControls3D;

View File

@@ -1,9 +1,8 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import * as THREE from "three"; import * as THREE from "three";
import { useFrame, useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import { SelectionHelper } from "../selectionHelper"; import { SelectionHelper } from "../selectionHelper";
import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox"; import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox";
import * as Types from "../../../../../types/world/worldTypes";
import useModuleStore from "../../../../../store/useModuleStore"; import useModuleStore from "../../../../../store/useModuleStore";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
@@ -13,7 +12,6 @@ import { useVersionContext } from "../../../../builder/version/versionContext";
import { useProductContext } from "../../../../simulation/products/productContext"; import { useProductContext } from "../../../../simulation/products/productContext";
import { useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../../store/builder/store"; import { useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../../store/builder/store";
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi"; import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
import BoundingBox from "./boundingBoxHelper3D";
import DuplicationControls3D from "./duplicationControls3D"; import DuplicationControls3D from "./duplicationControls3D";
import CopyPasteControls3D from "./copyPasteControls3D"; import CopyPasteControls3D from "./copyPasteControls3D";
import MoveControls3D from "./moveControls3D"; import MoveControls3D from "./moveControls3D";
@@ -23,7 +21,6 @@ import RotateControls3D from "./rotateControls3D";
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 selectionGroup = useRef() as Types.RefGroup;
const { toggleView } = useToggleView(); const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const [movedObjects, setMovedObjects] = useState<THREE.Object3D[]>([]); const [movedObjects, setMovedObjects] = useState<THREE.Object3D[]>([]);
@@ -229,12 +226,6 @@ const SelectionControls3D: React.FC = () => {
} }
}, [activeModule, toolMode, toggleView]); }, [activeModule, toolMode, toggleView]);
useFrame(() => {
if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
selectionGroup.current.position.set(0, 0, 0);
}
});
const selectAssets = useCallback(() => { const selectAssets = useCallback(() => {
selectionBox.endPoint.set(pointer.x, pointer.y, 0); selectionBox.endPoint.set(pointer.x, pointer.y, 0);
if (controls) (controls as any).enabled = true; if (controls) (controls as any).enabled = true;
@@ -267,9 +258,6 @@ const SelectionControls3D: React.FC = () => {
}, [selectionBox, pointer, controls, selectedAssets, setSelectedAssets]); }, [selectionBox, pointer, controls, selectedAssets, setSelectedAssets]);
const clearSelection = () => { const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]); setpastedObjects([]);
setDuplicatedObjects([]); setDuplicatedObjects([]);
setSelectedAssets([]); setSelectedAssets([]);
@@ -345,19 +333,13 @@ const SelectionControls3D: React.FC = () => {
return ( return (
<> <>
<group name="SelectionGroup"> <MoveControls3D movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
<group ref={selectionGroup} name="selectionAssetGroup">
<BoundingBox boundingBoxRef={boundingBoxRef} isPerAsset />
</group>
</group>
<MoveControls3D movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} selectionGroup={selectionGroup} boundingBoxRef={boundingBoxRef} /> <RotateControls3D rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} />
<RotateControls3D rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} selectionGroup={selectionGroup} /> <DuplicationControls3D duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
<DuplicationControls3D duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} /> <CopyPasteControls3D copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
<CopyPasteControls3D copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
</> </>
); );
}; };

View File

@@ -37,7 +37,7 @@ export function useConveyorActions() {
switch (action.actionType) { switch (action.actionType) {
case 'default': case 'default':
handleDefaultAction(action); handleDefaultAction(action, materialId);
break; break;
case 'spawn': case 'spawn':
handleSpawnAction(action); handleSpawnAction(action);

View File

@@ -8,7 +8,7 @@ export function useWorkerHandler() {
const { getModelUuidByActionUuid } = productStore(); const { getModelUuidByActionUuid } = productStore();
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore(); const { selectedProduct } = selectedProductStore();
const { incrementHumanLoad, incrementLoadCount, addCurrentMaterial, addCurrentAction } = humanStore(); const { incrementHumanLoad, addCurrentMaterial, addCurrentAction } = humanStore();
const workerLogStatus = (materialUuid: string, status: string) => { const workerLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`); echo.info(`${materialUuid}, ${status}`);
@@ -24,7 +24,6 @@ export function useWorkerHandler() {
if (!modelUuid) return; if (!modelUuid) return;
incrementHumanLoad(modelUuid, 1); incrementHumanLoad(modelUuid, 1);
incrementLoadCount(modelUuid, 1);
addCurrentAction(modelUuid, action.actionUuid); addCurrentAction(modelUuid, action.actionUuid);
addCurrentMaterial(modelUuid, material.materialType, material.materialId); addCurrentMaterial(modelUuid, material.materialType, material.materialId);

View File

@@ -11,7 +11,7 @@ export function useRetrieveHandler() {
const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = productStore(); const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = productStore();
const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = storageUnitStore(); const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = storageUnitStore();
const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = vehicleStore(); const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = vehicleStore();
const { getHumanById, incrementHumanLoad, incrementLoadCount, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore(); const { getHumanById, incrementHumanLoad, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore();
const { getAssetById, setCurrentAnimation } = assetStore(); const { getAssetById, setCurrentAnimation } = assetStore();
const { selectedProduct } = selectedProductStore(); const { selectedProduct } = selectedProductStore();
const { getArmBotById, addCurrentAction } = armBotStore(); const { getArmBotById, addCurrentAction } = armBotStore();
@@ -316,7 +316,6 @@ export function useRetrieveHandler() {
removeLastMaterial(storageUnit.modelUuid); removeLastMaterial(storageUnit.modelUuid);
updateCurrentLoad(storageUnit.modelUuid, -1); updateCurrentLoad(storageUnit.modelUuid, -1);
incrementHumanLoad(human.modelUuid, 1); incrementHumanLoad(human.modelUuid, 1);
incrementLoadCount(human.modelUuid, 1);
addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId); addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId);
retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`); retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`);
} }

View File

@@ -6,16 +6,21 @@ import { useProductContext } from '../../products/productContext';
export function useHumanEventManager() { export function useHumanEventManager() {
const { humanStore, productStore, assetStore } = useSceneContext(); const { humanStore, productStore, assetStore } = useSceneContext();
const { getHumanById, clearLoadCount, setCurrentPhase } = humanStore(); const { getHumanById, setCurrentPhase } = humanStore();
const { getAssetById } = assetStore(); const { getAssetById } = assetStore();
const { getActionByUuid } = productStore(); const { getActionByUuid } = productStore();
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore(); const { selectedProduct } = selectedProductStore();
const callbacksRef = useRef<Map<string, (() => void)[]>>(new Map()); const stateRef = useRef({
const actionQueueRef = useRef<Map<string, { actionType: "worker" | "assembly", actionUuid: string }[]>>(new Map()); humanStates: new Map<string, {
const isCooldownRef = useRef<Map<string, boolean>>(new Map()); callbacks: (() => void)[],
const isMonitoringRef = useRef(false); actionQueue: { actionType: "worker" | "assembly", actionUuid: string, actionName: string }[],
isCooldown: boolean
}>(),
callbackCounts: new Map<string, Map<string, number>>(),
isMonitoring: false
});
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { isPaused } = usePauseButtonStore(); const { isPaused } = usePauseButtonStore();
@@ -23,10 +28,9 @@ export function useHumanEventManager() {
useEffect(() => { useEffect(() => {
if (isReset) { if (isReset) {
callbacksRef.current.clear(); stateRef.current.humanStates.clear();
actionQueueRef.current.clear(); stateRef.current.callbackCounts.clear();
isCooldownRef.current.clear(); stateRef.current.isMonitoring = false;
isMonitoringRef.current = false;
} }
}, [isReset]); }, [isReset]);
@@ -38,34 +42,64 @@ export function useHumanEventManager() {
const actionType = action.actionType; const actionType = action.actionType;
if (actionType !== "worker" && actionType !== "assembly") return; if (actionType !== "worker" && actionType !== "assembly") return;
if (!callbacksRef.current.has(humanId)) { if (!stateRef.current.callbackCounts.has(humanId)) {
callbacksRef.current.set(humanId, []); stateRef.current.callbackCounts.set(humanId, new Map());
actionQueueRef.current.set(humanId, []);
} }
callbacksRef.current.get(humanId)!.push(callback); const actionCounts = stateRef.current.callbackCounts.get(humanId)!;
actionQueueRef.current.get(humanId)!.push({ actionType, actionUuid }); if (!actionCounts.has(actionUuid)) {
actionCounts.set(actionUuid, 0);
}
isMonitoringRef.current = true; const currentCount = actionCounts.get(actionUuid)!;
if (actionType === 'worker' && currentCount >= action.loadCount) {
return;
}
if (!stateRef.current.humanStates.has(humanId)) {
stateRef.current.humanStates.set(humanId, {
callbacks: [],
actionQueue: [],
isCooldown: false
});
}
const humanState = stateRef.current.humanStates.get(humanId)!;
humanState.callbacks.push(callback);
humanState.actionQueue.push({ actionType, actionUuid, actionName: action.actionName });
stateRef.current.isMonitoring = true;
}; };
const removeHumanFromMonitor = (humanId: string) => { const removeHumanFromMonitor = (humanId: string) => {
callbacksRef.current.delete(humanId); // stateRef.current.humanStates.delete(humanId);
actionQueueRef.current.delete(humanId);
isCooldownRef.current.delete(humanId);
if (callbacksRef.current.size === 0) { if (stateRef.current.humanStates.size === 0) {
isMonitoringRef.current = false; stateRef.current.isMonitoring = false;
} }
}; };
const getCallbackCount = (humanId: string, actionUuid: string) => {
if (!stateRef.current.callbackCounts.has(humanId)) return 0;
return stateRef.current.callbackCounts.get(humanId)!.get(actionUuid) || 0;
};
const incrementCallbackCount = (humanId: string, actionUuid: string) => {
if (!stateRef.current.callbackCounts.has(humanId)) {
stateRef.current.callbackCounts.set(humanId, new Map());
}
const actionCounts = stateRef.current.callbackCounts.get(humanId)!;
const currentCount = actionCounts.get(actionUuid) || 0;
actionCounts.set(actionUuid, currentCount + 1);
};
useFrame(() => { useFrame(() => {
if (!isMonitoringRef.current || !isPlaying || isPaused) return; if (!stateRef.current.isMonitoring || !isPlaying || isPaused) return;
callbacksRef.current.forEach((queue, humanId) => { stateRef.current.humanStates.forEach((humanState, humanId) => {
if (queue.length === 0 || isCooldownRef.current.get(humanId)) return; if (humanState.callbacks.length === 0 || humanState.isCooldown) return;
const actionQueue = actionQueueRef.current.get(humanId); const actionQueue = humanState.actionQueue;
if (!actionQueue || actionQueue.length === 0) return; if (!actionQueue || actionQueue.length === 0) return;
const { actionType: expectedActionType, actionUuid } = actionQueue[0]; const { actionType: expectedActionType, actionUuid } = actionQueue[0];
@@ -75,11 +109,22 @@ export function useHumanEventManager() {
if (!humanAsset || !human || !action || action.actionType !== expectedActionType) return; if (!humanAsset || !human || !action || action.actionType !== expectedActionType) return;
let conditionMet = false; const currentCount = getCallbackCount(humanId, actionUuid);
const currentAction = getActionByUuid(selectedProduct.productUuid, human.currentAction?.actionUuid || '') as HumanAction | undefined; const currentAction = getActionByUuid(selectedProduct.productUuid, human.currentAction?.actionUuid || '') as HumanAction | undefined;
let conditionMet = false;
if (expectedActionType === "worker") { if (expectedActionType === "worker") {
if (currentAction && currentAction.actionType === 'worker' && currentCount >= currentAction.loadCount) {
humanState.callbacks.shift();
actionQueue.shift();
if (humanState.callbacks.length === 0) {
removeHumanFromMonitor(humanId);
}
return;
}
if (currentAction && currentAction.actionType === 'worker') { if (currentAction && currentAction.actionType === 'worker') {
conditionMet = ( conditionMet = (
!human.isActive && !human.isActive &&
@@ -88,15 +133,6 @@ export function useHumanEventManager() {
human.currentLoad < currentAction.loadCapacity human.currentLoad < currentAction.loadCapacity
); );
if (human.totalLoadCount >= currentAction.loadCount && actionUuid === human.currentAction?.actionUuid) {
queue.shift();
actionQueue.shift();
if (queue.length === 0) {
removeHumanFromMonitor(humanId);
}
return;
}
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) { if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
setCurrentPhase(human.modelUuid, 'init'); setCurrentPhase(human.modelUuid, 'init');
} }
@@ -104,8 +140,7 @@ export function useHumanEventManager() {
conditionMet = ( conditionMet = (
!human.isActive && !human.isActive &&
human.state === "idle" && human.state === "idle" &&
humanAsset.animationState?.current === 'idle' && humanAsset.animationState?.current === 'idle'
human.currentLoad < action.loadCapacity
); );
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) { if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
setCurrentPhase(human.modelUuid, 'init'); setCurrentPhase(human.modelUuid, 'init');
@@ -127,41 +162,31 @@ export function useHumanEventManager() {
conditionMet = ( conditionMet = (
!human.isActive && !human.isActive &&
human.state === "idle" && human.state === "idle" &&
humanAsset.animationState?.current === 'idle' && humanAsset.animationState?.current === 'idle'
human.currentLoad < action.loadCapacity
) )
} }
if (conditionMet) {
clearLoadCount(human.modelUuid);
}
} }
if (conditionMet) { if (conditionMet) {
const callback = queue.shift(); const callback = humanState.callbacks.shift();
actionQueue.shift(); actionQueue.shift();
if (callback) callback(); if (callback) {
callback();
incrementCallbackCount(humanId, actionUuid);
}
if (queue.length === 0) { if (humanState.callbacks.length === 0) {
removeHumanFromMonitor(humanId); removeHumanFromMonitor(humanId);
} else { } else {
isCooldownRef.current.set(humanId, true); humanState.isCooldown = true;
setTimeout(() => { setTimeout(() => {
isCooldownRef.current.set(humanId, false); humanState.isCooldown = false;
}, 1000); }, 1000);
} }
} }
}); });
}, 0); });
useEffect(() => {
return () => {
callbacksRef.current.clear();
actionQueueRef.current.clear();
isCooldownRef.current.clear();
isMonitoringRef.current = false;
};
}, []);
return { return {
addHumanToMonitor, addHumanToMonitor,

View File

@@ -478,8 +478,10 @@ export function useTriggerHandler() {
if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
// Vehicle to Transfer // Vehicle to Transfer
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const conveyor = getConveyorById(toEvent.modelUuid);
const material = getMaterialById(materialId); const material = getMaterialById(materialId);
if (material) {
if (material && conveyor) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
setPreviousLocation(material.materialId, { setPreviousLocation(material.materialId, {
@@ -494,23 +496,29 @@ export function useTriggerHandler() {
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
}); });
setIsPaused(materialId, false);
setIsVisible(materialId, true); setIsVisible(materialId, true);
if (action && if (action &&
action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid && action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid &&
action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid
) { ) {
setNextLocation(material.materialId, { setNextLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid, modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '',
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid, pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || '',
}); });
handleAction(action, materialId); addConveyorToMonitor(conveyor.modelUuid, () => {
}
setIsPaused(materialId, false);
handleAction(action, materialId);
})
}
} }
} }
} else if (toEvent?.type === 'vehicle') { } else if (toEvent?.type === 'vehicle') {
// Vehicle to Vehicle // Vehicle to Vehicle
@@ -539,7 +547,6 @@ export function useTriggerHandler() {
setNextLocation(material.materialId, null); setNextLocation(material.materialId, null);
if (action && armBot) { if (action && armBot) {
if (armBot.isActive === false && armBot.state === 'idle') { if (armBot.isActive === false && armBot.state === 'idle') {
@@ -1439,13 +1446,11 @@ export function useTriggerHandler() {
const material = getMaterialById(materialId); const material = getMaterialById(materialId);
if (material) { if (material) {
setIsPaused(material.materialId, false); setIsPaused(material.materialId, true);
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid); const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const vehicle = getVehicleById(trigger.triggeredAsset?.triggeredModel.modelUuid); const vehicle = getVehicleById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setNextLocation(material.materialId, null);
if (action) { if (action) {
if (vehicle) { if (vehicle) {
@@ -1466,16 +1471,15 @@ export function useTriggerHandler() {
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid, actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
}); });
setNextLocation(material.materialId, null);
// Handle current action from vehicle // Handle current action from vehicle
handleAction(action, materialId); handleAction(action, materialId);
} else { } else {
setIsPaused(materialId, true);
addVehicleToMonitor(vehicle.modelUuid, addVehicleToMonitor(vehicle.modelUuid,
() => { () => {
setIsPaused(materialId, false);
setIsVisible(materialId, false); setIsVisible(materialId, false);
setPreviousLocation(material.materialId, { setPreviousLocation(material.materialId, {
@@ -1493,6 +1497,7 @@ export function useTriggerHandler() {
setNextLocation(material.materialId, null); setNextLocation(material.materialId, null);
// Handle current action from vehicle // Handle current action from vehicle
console.log('action: ', action);
handleAction(action, materialId); handleAction(action, materialId);
} }
) )

View File

@@ -325,7 +325,6 @@ function DraggableLineSegment({
}; };
const onPointerMove = (e: ThreeEvent<PointerEvent>) => { const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
console.log('isAnyDragging: ', isAnyDragging);
if (isAnyDragging !== "line" || activeTool !== 'pen') return; if (isAnyDragging !== "line" || activeTool !== 'pen') return;
const intersect = new THREE.Vector3(); const intersect = new THREE.Vector3();

View File

@@ -19,7 +19,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore, assetStore } = useSceneContext(); const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore, assetStore } = useSceneContext();
const { removeMaterial, setEndTime, setIsVisible } = materialStore(); const { removeMaterial, setEndTime, setIsVisible } = materialStore();
const { getStorageUnitById } = storageUnitStore(); const { getStorageUnitById } = storageUnitStore();
const { getHumanById, addCurrentAction, addCurrentMaterial, incrementHumanLoad , incrementLoadCount } = humanStore(); const { getHumanById, addCurrentAction, addCurrentMaterial, incrementHumanLoad } = humanStore();
const { getArmBotById } = armBotStore(); const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore(); const { getConveyorById } = conveyorStore();
const { triggerPointActions } = useTriggerHandler(); const { triggerPointActions } = useTriggerHandler();
@@ -302,7 +302,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
setIsVisible(material.materialId, false); setIsVisible(material.materialId, false);
addCurrentMaterial(humanId, material.materialType, material.materialId); addCurrentMaterial(humanId, material.materialType, material.materialId);
incrementHumanLoad(humanId, 1); incrementHumanLoad(humanId, 1);
incrementLoadCount(humanId, 1);
} }
return; return;
} }

View File

@@ -27,11 +27,6 @@ interface HumansStore {
incrementHumanLoad: (modelUuid: string, incrementBy: number) => void; incrementHumanLoad: (modelUuid: string, incrementBy: number) => void;
decrementHumanLoad: (modelUuid: string, decrementBy: number) => void; decrementHumanLoad: (modelUuid: string, decrementBy: number) => void;
incrementLoadCount: (modelUuid: string, incrementBy: number) => void;
decrementLoadCount: (modelUuid: string, decrementBy: number) => void;
clearLoadCount: (modelUuid: string) => void;
addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void; addCurrentMaterial: (modelUuid: string, materialType: string, materialId: string) => void;
setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string }[]) => void; setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string }[]) => void;
removeLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined; removeLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined;
@@ -65,7 +60,6 @@ export const createHumanStore = () => {
isScheduled: false, isScheduled: false,
idleTime: 0, idleTime: 0,
activeTime: 0, activeTime: 0,
totalLoadCount: 0,
currentLoad: 0, currentLoad: 0,
currentMaterials: [], currentMaterials: [],
distanceTraveled: 0 distanceTraveled: 0
@@ -182,33 +176,6 @@ export const createHumanStore = () => {
}); });
}, },
incrementLoadCount: (modelUuid, incrementBy) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.totalLoadCount += incrementBy;
}
});
},
decrementLoadCount: (modelUuid, decrementBy) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.totalLoadCount -= decrementBy;
}
});
},
clearLoadCount: (modelUuid) => {
set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human) {
human.totalLoadCount = 0;
}
});
},
addCurrentMaterial: (modelUuid, materialType, materialId) => { addCurrentMaterial: (modelUuid, materialType, materialId) => {
set((state) => { set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid); const human = state.humans.find(h => h.modelUuid === modelUuid);

View File

@@ -252,7 +252,6 @@ interface HumanStatus extends HumanEventSchema {
isScheduled: boolean; isScheduled: boolean;
idleTime: number; idleTime: number;
activeTime: number; activeTime: number;
totalLoadCount: number;
currentLoad: number; currentLoad: number;
currentMaterials: { materialType: string; materialId: string; }[]; currentMaterials: { materialType: string; materialId: string; }[];
distanceTraveled: number; distanceTraveled: number;