Merge branch 'main-dev' into dev-physics
This commit is contained in:
@@ -216,6 +216,7 @@ const GlobalProperties: React.FC = () => {
|
||||
// setRenderDistance(parseInt(e.target.value));
|
||||
// }
|
||||
// }
|
||||
|
||||
return (
|
||||
<div className="global-properties-container">
|
||||
<section>
|
||||
@@ -239,12 +240,12 @@ const GlobalProperties: React.FC = () => {
|
||||
label="Wall Visibility"
|
||||
onClick={changeWallVisibility}
|
||||
/>
|
||||
{/* <InputToggle
|
||||
<InputToggle
|
||||
value={shadows}
|
||||
inputKey="3"
|
||||
label="Shadows Visibility"
|
||||
onClick={shadowVisibility}
|
||||
/> */}
|
||||
/>
|
||||
<LabeledButton
|
||||
label="Reset Camera"
|
||||
onClick={toggleResetCamera}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import InputRange from "../../../../../ui/inputs/InputRange";
|
||||
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
|
||||
import SwapAction from "./SwapAction";
|
||||
|
||||
interface AssemblyActionProps {
|
||||
@@ -10,6 +11,15 @@ interface AssemblyActionProps {
|
||||
disabled?: boolean,
|
||||
onChange: (value: number) => void;
|
||||
};
|
||||
assemblyCount: {
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
defaultValue: string,
|
||||
disabled: false,
|
||||
onChange: (value: number) => void;
|
||||
}
|
||||
swapOptions: string[];
|
||||
swapDefaultOption: string;
|
||||
onSwapSelect: (value: string) => void;
|
||||
@@ -18,6 +28,7 @@ interface AssemblyActionProps {
|
||||
|
||||
const AssemblyAction: React.FC<AssemblyActionProps> = ({
|
||||
processTime,
|
||||
assemblyCount,
|
||||
swapOptions,
|
||||
swapDefaultOption,
|
||||
onSwapSelect,
|
||||
@@ -34,6 +45,21 @@ const AssemblyAction: React.FC<AssemblyActionProps> = ({
|
||||
onClick={() => { }}
|
||||
onChange={processTime.onChange}
|
||||
/>
|
||||
|
||||
{assemblyCount && (
|
||||
<InputWithDropDown
|
||||
label="Assembly Count"
|
||||
value={assemblyCount.value.toString()}
|
||||
min={assemblyCount.min}
|
||||
max={assemblyCount.max}
|
||||
disabled={assemblyCount.disabled}
|
||||
defaultValue={assemblyCount.defaultValue}
|
||||
step={assemblyCount.step}
|
||||
activeOption="unit"
|
||||
onClick={() => { }}
|
||||
onChange={(value) => assemblyCount.onChange(parseInt(value))}
|
||||
/>
|
||||
)}
|
||||
<SwapAction
|
||||
options={swapOptions}
|
||||
defaultOption={swapDefaultOption}
|
||||
|
||||
@@ -11,7 +11,7 @@ interface WorkerActionProps {
|
||||
disabled?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
};
|
||||
loadCount?: {
|
||||
loadCount: {
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
|
||||
@@ -20,6 +20,7 @@ function HumanMechanics() {
|
||||
const [activeOption, setActiveOption] = useState<"worker" | "assembly">("worker");
|
||||
const [speed, setSpeed] = useState("0.5");
|
||||
const [loadCount, setLoadCount] = useState(0);
|
||||
const [assemblyCount, setAssemblyCount] = useState(0);
|
||||
const [loadCapacity, setLoadCapacity] = useState("1");
|
||||
const [processTime, setProcessTime] = useState(10);
|
||||
const [swappedMaterial, setSwappedMaterial] = useState("Default material");
|
||||
@@ -56,6 +57,7 @@ function HumanMechanics() {
|
||||
setLoadCapacity(firstAction.loadCapacity.toString());
|
||||
setActiveOption(firstAction.actionType);
|
||||
setLoadCount(firstAction.loadCount || 0);
|
||||
setAssemblyCount(firstAction.assemblyCount || 0);
|
||||
setProcessTime(firstAction.processTime || 10);
|
||||
setSwappedMaterial(firstAction.swapMaterial || "Default material");
|
||||
}
|
||||
@@ -84,6 +86,7 @@ function HumanMechanics() {
|
||||
setActiveOption(newCurrentAction.actionType);
|
||||
setLoadCapacity(newCurrentAction.loadCapacity.toString());
|
||||
setLoadCount(newCurrentAction.loadCount || 0);
|
||||
setAssemblyCount(newCurrentAction.assemblyCount || 0);
|
||||
|
||||
if (newCurrentAction.actionType === 'assembly') {
|
||||
setProcessTime(newCurrentAction.processTime || 10);
|
||||
@@ -200,6 +203,28 @@ function HumanMechanics() {
|
||||
setLoadCount(value);
|
||||
};
|
||||
|
||||
const handleAssemblyCountChange = (value: number) => {
|
||||
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
||||
|
||||
const updatedAction = { ...currentAction, assemblyCount: value };
|
||||
const updatedActions = selectedPointData.actions.map(action => action.actionUuid === updatedAction.actionUuid ? updatedAction : action);
|
||||
const updatedPoint = { ...selectedPointData, actions: updatedActions };
|
||||
|
||||
const event = updateAction(
|
||||
selectedProduct.productUuid,
|
||||
selectedAction.actionId,
|
||||
updatedAction
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
|
||||
}
|
||||
|
||||
setCurrentAction(updatedAction);
|
||||
setSelectedPointData(updatedPoint);
|
||||
setAssemblyCount(value);
|
||||
};
|
||||
|
||||
const handleProcessTimeChange = (value: number) => {
|
||||
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
|
||||
|
||||
@@ -281,6 +306,7 @@ function HumanMechanics() {
|
||||
actionName: `Action ${selectedPointData.actions.length + 1}`,
|
||||
actionType: "worker",
|
||||
loadCount: 1,
|
||||
assemblyCount: 1,
|
||||
loadCapacity: 1,
|
||||
processTime: 10,
|
||||
triggers: [],
|
||||
@@ -304,15 +330,15 @@ function HumanMechanics() {
|
||||
setSelectedAction(newAction.actionUuid, newAction.actionName);
|
||||
};
|
||||
|
||||
const handleDeleteAction = () => {
|
||||
if (!selectedPointData || !selectedAction.actionId) return;
|
||||
const handleDeleteAction = (actionUuid: string) => {
|
||||
if (!selectedPointData || !actionUuid) return;
|
||||
|
||||
const updatedActions = selectedPointData.actions.filter(action => action.actionUuid !== selectedAction.actionId);
|
||||
const updatedActions = selectedPointData.actions.filter(action => action.actionUuid !== actionUuid);
|
||||
const updatedPoint = { ...selectedPointData, actions: updatedActions };
|
||||
|
||||
const event = removeAction(
|
||||
selectedProduct.productUuid,
|
||||
selectedAction.actionId
|
||||
actionUuid
|
||||
);
|
||||
|
||||
if (event) {
|
||||
@@ -389,7 +415,7 @@ function HumanMechanics() {
|
||||
}}
|
||||
loadCount={{
|
||||
value: loadCount,
|
||||
min: 0,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
defaultValue: "1",
|
||||
@@ -407,6 +433,15 @@ function HumanMechanics() {
|
||||
max: 60,
|
||||
onChange: handleProcessTimeChange,
|
||||
}}
|
||||
assemblyCount={{
|
||||
value: assemblyCount,
|
||||
min: 1,
|
||||
max: 20,
|
||||
step: 1,
|
||||
defaultValue: "1",
|
||||
disabled: false,
|
||||
onChange: handleAssemblyCountChange,
|
||||
}}
|
||||
swapOptions={["Default material", "Material 1", "Material 2", "Material 3"]}
|
||||
swapDefaultOption={swappedMaterial}
|
||||
onSwapSelect={handleMaterialChange}
|
||||
|
||||
@@ -16,12 +16,7 @@ import { getUserData } from "../../../functions/getUserData";
|
||||
import { useSceneContext } from "../../scene/sceneContext";
|
||||
import { useVersionContext } from "../version/versionContext";
|
||||
|
||||
const gltfLoaderWorker = new Worker(
|
||||
new URL(
|
||||
"../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js",
|
||||
import.meta.url
|
||||
)
|
||||
);
|
||||
const gltfLoaderWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", import.meta.url));
|
||||
|
||||
function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
||||
const { activeModule } = useModuleStore();
|
||||
@@ -44,9 +39,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
|
||||
dracoLoader.setDecoderPath(
|
||||
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
|
||||
);
|
||||
dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/");
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -267,6 +260,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
||||
actionName: "Action 1",
|
||||
actionType: "worker",
|
||||
loadCount: 1,
|
||||
assemblyCount: 1,
|
||||
loadCapacity: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
|
||||
@@ -384,6 +384,7 @@ async function handleModelLoad(
|
||||
actionName: "Action 1",
|
||||
actionType: "worker",
|
||||
loadCount: 1,
|
||||
assemblyCount: 1,
|
||||
loadCapacity: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
|
||||
@@ -1,26 +1,71 @@
|
||||
import { Box3, BoxGeometry, EdgesGeometry, Vector3 } from "three";
|
||||
import { RigidBody } from "@react-three/rapier";
|
||||
import { Box3, Vector3, Quaternion } from "three";
|
||||
import { useMemo } from "react";
|
||||
import { Cylinder } from "@react-three/drei";
|
||||
|
||||
export const AssetBoundingBox = ({ boundingBox }: { boundingBox: Box3 }) => {
|
||||
export const AssetBoundingBox = ({ name, boundingBox, color, lineWidth, }: { name: string; boundingBox: Box3 | null; color: string; lineWidth: number; }) => {
|
||||
const { edgeCylinders, center, size } = useMemo(() => {
|
||||
if (!boundingBox) return { edgeCylinders: [], center: new Vector3(), size: new Vector3() };
|
||||
|
||||
const size = boundingBox.getSize(new Vector3());
|
||||
const center = boundingBox.getCenter(new Vector3());
|
||||
const min = boundingBox.min;
|
||||
const max = boundingBox.max;
|
||||
const center = boundingBox.getCenter(new Vector3());
|
||||
const size = boundingBox.getSize(new Vector3());
|
||||
|
||||
const boxGeometry = useMemo(() => new BoxGeometry(size.x, size.y, size.z), [size]);
|
||||
const edges = useMemo(() => new EdgesGeometry(boxGeometry), [boxGeometry]);
|
||||
const corners = [
|
||||
new Vector3(min.x, min.y, min.z),
|
||||
new Vector3(max.x, min.y, min.z),
|
||||
new Vector3(max.x, max.y, min.z),
|
||||
new Vector3(min.x, max.y, min.z),
|
||||
new Vector3(min.x, min.y, max.z),
|
||||
new Vector3(max.x, min.y, max.z),
|
||||
new Vector3(max.x, max.y, max.z),
|
||||
new Vector3(min.x, max.y, max.z),
|
||||
];
|
||||
|
||||
const edgeIndices: [number, number][] = [
|
||||
[0, 1], [1, 2], [2, 3], [3, 0],
|
||||
[4, 5], [5, 6], [6, 7], [7, 4],
|
||||
[0, 4], [1, 5], [2, 6], [3, 7],
|
||||
];
|
||||
|
||||
const radius = 0.005 * lineWidth;
|
||||
|
||||
const edgeCylinders = edgeIndices.map(([startIdx, endIdx], i) => {
|
||||
const start = corners[startIdx];
|
||||
const end = corners[endIdx];
|
||||
const direction = new Vector3().subVectors(end, start);
|
||||
const length = direction.length();
|
||||
const midPoint = new Vector3().addVectors(start, end).multiplyScalar(0.5);
|
||||
const quaternion = new Quaternion().setFromUnitVectors(
|
||||
new Vector3(0, 1, 0),
|
||||
direction.clone().normalize()
|
||||
);
|
||||
|
||||
return {
|
||||
key: `edge-cylinder-${i}`,
|
||||
position: midPoint,
|
||||
rotation: quaternion,
|
||||
length,
|
||||
radius,
|
||||
};
|
||||
});
|
||||
|
||||
return { edgeCylinders, center, size };
|
||||
}, [boundingBox, lineWidth]);
|
||||
|
||||
if (!boundingBox) return null;
|
||||
|
||||
return (
|
||||
<RigidBody includeInvisible type="fixed" colliders="cuboid" position={center.toArray()} rotation={[0, 0, 0]}>
|
||||
<lineSegments>
|
||||
<bufferGeometry attach="geometry" {...edges} />
|
||||
<lineBasicMaterial depthWrite={false} attach="material" color="gray" linewidth={1} />
|
||||
</lineSegments>
|
||||
<group name={name}>
|
||||
{edgeCylinders.map(({ key, position, rotation, length, radius }) => (
|
||||
<Cylinder key={key} args={[radius, radius, length, 6]} position={position} quaternion={rotation} >
|
||||
<meshBasicMaterial color={color} depthWrite={false} />
|
||||
</Cylinder>
|
||||
))}
|
||||
|
||||
<mesh visible={false}>
|
||||
<mesh visible={false} position={center}>
|
||||
<boxGeometry args={[size.x, size.y, size.z]} />
|
||||
<meshStandardMaterial transparent opacity={0} />
|
||||
</mesh>
|
||||
</RigidBody>
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { useEffect, useRef, useCallback, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
||||
import useModuleStore from '../../../../../../store/useModuleStore';
|
||||
import { usePauseButtonStore, useAnimationPlaySpeed } from '../../../../../../store/usePlayButtonStore';
|
||||
|
||||
interface ModelAnimatorProps {
|
||||
asset: Asset;
|
||||
gltfScene: THREE.Object3D;
|
||||
}
|
||||
|
||||
export function ModelAnimator({
|
||||
asset,
|
||||
gltfScene,
|
||||
}: ModelAnimatorProps) {
|
||||
const mixerRef = useRef<THREE.AnimationMixer>();
|
||||
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
|
||||
const [previousAnimation, setPreviousAnimation] = useState<string | null>(null);
|
||||
|
||||
const blendFactor = useRef(0);
|
||||
const blendDuration = 0.5;
|
||||
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { assetStore } = useSceneContext();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { setAnimations, setAnimationComplete } = assetStore();
|
||||
|
||||
const handleAnimationComplete = useCallback(() => {
|
||||
if (asset.animationState) {
|
||||
setAnimationComplete(asset.modelUuid, true);
|
||||
}
|
||||
}, [asset.animationState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!gltfScene || !gltfScene.animations || gltfScene.animations.length === 0) return;
|
||||
|
||||
mixerRef.current = new THREE.AnimationMixer(gltfScene);
|
||||
|
||||
gltfScene.animations.forEach((clip: THREE.AnimationClip) => {
|
||||
const action = mixerRef.current!.clipAction(clip);
|
||||
actions.current[clip.name] = action;
|
||||
});
|
||||
|
||||
const animationNames = gltfScene.animations.map(clip => clip.name);
|
||||
setAnimations(asset.modelUuid, animationNames);
|
||||
|
||||
return () => {
|
||||
mixerRef.current?.stopAllAction();
|
||||
mixerRef.current = undefined;
|
||||
};
|
||||
}, [gltfScene]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!asset.animationState || !mixerRef.current) return;
|
||||
|
||||
const { current, loopAnimation, isPlaying } = asset.animationState;
|
||||
const currentAction = actions.current[current];
|
||||
const previousAction = previousAnimation ? actions.current[previousAnimation] : null;
|
||||
|
||||
if (isPlaying && currentAction && !isPaused) {
|
||||
blendFactor.current = 0;
|
||||
|
||||
currentAction.reset();
|
||||
currentAction.setLoop(loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce, loopAnimation ? Infinity : 1);
|
||||
currentAction.clampWhenFinished = true;
|
||||
|
||||
if (previousAction && previousAction !== currentAction) {
|
||||
previousAction.crossFadeTo(currentAction, blendDuration, false);
|
||||
}
|
||||
|
||||
currentAction.play();
|
||||
mixerRef.current.addEventListener('finished', handleAnimationComplete);
|
||||
setPreviousAnimation(current);
|
||||
} else {
|
||||
Object.values(actions.current).forEach((action) => action.stop());
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (mixerRef.current) {
|
||||
mixerRef.current.removeEventListener('finished', handleAnimationComplete);
|
||||
}
|
||||
};
|
||||
}, [asset.animationState?.current, asset.animationState?.isCompleted, asset.animationState?.isPlaying, isPaused, activeModule]);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (mixerRef.current) {
|
||||
mixerRef.current.update(delta * (activeModule === 'simulation' ? speed : 1));
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
import * as THREE from 'three';
|
||||
import { CameraControls } from '@react-three/drei';
|
||||
import { ThreeEvent } from '@react-three/fiber';
|
||||
import { useCallback } from 'react';
|
||||
import { ProductStoreType } from '../../../../../../store/simulation/useProductStore';
|
||||
import { EventStoreType } from '../../../../../../store/simulation/useEventsStore';
|
||||
import { Socket } from 'socket.io-client';
|
||||
|
||||
import { useActiveTool, useToolMode } from '../../../../../../store/builder/store';
|
||||
import useModuleStore, { useSubModuleStore } from '../../../../../../store/useModuleStore';
|
||||
import { useSocketStore } from '../../../../../../store/builder/store';
|
||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../../../simulation/products/productContext';
|
||||
import { useVersionContext } from '../../../../version/versionContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { getUserData } from '../../../../../../functions/getUserData';
|
||||
|
||||
// import { deleteFloorItem } from '../../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi';
|
||||
|
||||
export function useModelEventHandlers({
|
||||
controls,
|
||||
boundingBox,
|
||||
groupRef,
|
||||
toggleView,
|
||||
deletableFloorItem,
|
||||
setDeletableFloorItem,
|
||||
setSelectedFloorItem,
|
||||
gl,
|
||||
setTop,
|
||||
setLeft,
|
||||
getIsEventInProduct,
|
||||
getEventByModelUuid,
|
||||
setSelectedAsset,
|
||||
clearSelectedAsset,
|
||||
removeAsset,
|
||||
updateBackend,
|
||||
leftDrag,
|
||||
rightDrag
|
||||
}: {
|
||||
controls: CameraControls | any,
|
||||
boundingBox: THREE.Box3 | null,
|
||||
groupRef: React.RefObject<THREE.Group>,
|
||||
toggleView: boolean,
|
||||
deletableFloorItem: THREE.Object3D | null,
|
||||
setDeletableFloorItem: (item: THREE.Object3D | null) => void,
|
||||
setSelectedFloorItem: (item: THREE.Object3D | null) => void,
|
||||
gl: THREE.WebGLRenderer,
|
||||
setTop: (top: number) => void,
|
||||
setLeft: (left: number) => void,
|
||||
getIsEventInProduct: (productUuid: string, modelUuid: string) => boolean,
|
||||
getEventByModelUuid: (modelUuid: string) => EventsSchema | undefined,
|
||||
setSelectedAsset: (EventData: EventsSchema) => void,
|
||||
clearSelectedAsset: () => void,
|
||||
removeAsset: (modelUuid: string) => void,
|
||||
updateBackend: (productName: string, productUuid: string, projectId: string, event: EventsSchema) => void,
|
||||
leftDrag: React.MutableRefObject<boolean>,
|
||||
rightDrag: React.MutableRefObject<boolean>
|
||||
}) {
|
||||
|
||||
const { activeTool } = useActiveTool();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { subModule } = useSubModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const { eventStore, productStore } = useSceneContext();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { userId, organization } = getUserData();
|
||||
|
||||
const handleDblClick = (asset: Asset) => {
|
||||
if (asset && activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') {
|
||||
const size = boundingBox.getSize(new THREE.Vector3());
|
||||
const center = boundingBox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
groupRef.current.localToWorld(front);
|
||||
front.sub(groupRef.current.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center.clone().addScaledVector(front, distance);
|
||||
|
||||
(controls as CameraControls).setPosition(newPosition.x, newPosition.y, newPosition.z, true);
|
||||
(controls as CameraControls).setTarget(center.x, center.y, center.z, true);
|
||||
(controls as CameraControls).fitToBox(groupRef.current, true, {
|
||||
cover: true,
|
||||
paddingTop: 5,
|
||||
paddingLeft: 5,
|
||||
paddingBottom: 5,
|
||||
paddingRight: 5,
|
||||
});
|
||||
|
||||
setSelectedFloorItem(groupRef.current);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = async (evt: ThreeEvent<MouseEvent>, asset: Asset) => {
|
||||
if (leftDrag.current || toggleView) return;
|
||||
if (activeTool === 'delete' && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
|
||||
|
||||
//REST
|
||||
|
||||
// const response = await deleteFloorItem(organization, asset.modelUuid, asset.modelName);
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modelUuid: asset.modelUuid,
|
||||
modelName: asset.modelName,
|
||||
socketId: socket.id,
|
||||
userId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
projectId
|
||||
}
|
||||
|
||||
const response = socket.emit('v1:model-asset:delete', data)
|
||||
|
||||
eventStore.getState().removeEvent(asset.modelUuid);
|
||||
const updatedEvents = productStore.getState().deleteEvent(asset.modelUuid);
|
||||
|
||||
updatedEvents.forEach((event) => {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
})
|
||||
|
||||
if (response) {
|
||||
|
||||
removeAsset(asset.modelUuid);
|
||||
|
||||
echo.success("Model Removed!");
|
||||
}
|
||||
|
||||
} else if (activeModule === 'simulation' && subModule === "simulations" && activeTool === 'pen') {
|
||||
if (asset.eventData && asset.eventData.type === 'Conveyor') {
|
||||
const intersectedPoint = evt.point;
|
||||
const localPosition = groupRef.current?.worldToLocal(intersectedPoint.clone());
|
||||
if (localPosition) {
|
||||
const conveyorPoint: ConveyorPointSchema = {
|
||||
uuid: THREE.MathUtils.generateUUID(),
|
||||
position: [localPosition?.x, localPosition?.y, localPosition?.z],
|
||||
rotation: [0, 0, 0],
|
||||
action: {
|
||||
actionUuid: THREE.MathUtils.generateUUID(),
|
||||
actionName: `Action 1`,
|
||||
actionType: 'default',
|
||||
material: 'Default Material',
|
||||
delay: 0,
|
||||
spawnInterval: 5,
|
||||
spawnCount: 1,
|
||||
triggers: []
|
||||
}
|
||||
}
|
||||
|
||||
const event = productStore.getState().addPoint(selectedProduct.productUuid, asset.modelUuid, conveyorPoint);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerOver = useCallback((asset: Asset) => {
|
||||
if (activeTool === "delete" && activeModule === 'builder') {
|
||||
if (deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
|
||||
return;
|
||||
} else {
|
||||
setDeletableFloorItem(groupRef.current);
|
||||
}
|
||||
}
|
||||
}, [activeTool, activeModule, deletableFloorItem]);
|
||||
|
||||
const handlePointerOut = useCallback((evt: ThreeEvent<MouseEvent>, asset: Asset) => {
|
||||
if (evt.intersections.length === 0 && activeTool === "delete" && deletableFloorItem && deletableFloorItem.uuid === asset.modelUuid) {
|
||||
setDeletableFloorItem(null);
|
||||
}
|
||||
}, [activeTool, deletableFloorItem]);
|
||||
|
||||
const handleContextMenu = (asset: Asset, evt: ThreeEvent<MouseEvent>) => {
|
||||
if (rightDrag.current || toggleView) return;
|
||||
if (activeTool === "cursor" && subModule === 'simulations') {
|
||||
if (asset.modelUuid) {
|
||||
const canvasElement = gl.domElement;
|
||||
const isInProduct = getIsEventInProduct(selectedProduct.productUuid, asset.modelUuid);
|
||||
if (isInProduct) {
|
||||
const event = getEventByModelUuid(asset.modelUuid);
|
||||
if (event) {
|
||||
setSelectedAsset(event);
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = evt.clientX - canvasRect.left;
|
||||
const relativeY = evt.clientY - canvasRect.top;
|
||||
setTop(relativeY);
|
||||
setLeft(relativeX);
|
||||
} else {
|
||||
clearSelectedAsset();
|
||||
}
|
||||
} else {
|
||||
const event = getEventByModelUuid(asset.modelUuid);
|
||||
if (event) {
|
||||
setSelectedAsset(event)
|
||||
const canvasRect = canvasElement.getBoundingClientRect();
|
||||
const relativeX = evt.clientX - canvasRect.left;
|
||||
const relativeY = evt.clientY - canvasRect.top;
|
||||
setTop(relativeY);
|
||||
setLeft(relativeX);
|
||||
} else {
|
||||
clearSelectedAsset()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
clearSelectedAsset()
|
||||
}
|
||||
} else {
|
||||
clearSelectedAsset()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
handleDblClick,
|
||||
handleClick,
|
||||
handlePointerOver,
|
||||
handlePointerOut,
|
||||
handleContextMenu
|
||||
};
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import { retrieveGLTF, storeGLTF } from '../../../../../utils/indexDB/idbUtils';
|
||||
import { RapierRigidBody, RigidBody } from '@react-three/rapier';
|
||||
import { GLTF, GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
|
||||
import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
|
||||
import { ThreeEvent, useThree } from '@react-three/fiber';
|
||||
import { useActiveTool, useDeletableFloorItem, useSelectedAssets, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
|
||||
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
|
||||
import { CameraControls } from '@react-three/drei';
|
||||
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
|
||||
@@ -17,23 +17,23 @@ import { useParams } from 'react-router-dom';
|
||||
import { getUserData } from '../../../../../functions/getUserData';
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useVersionContext } from '../../../version/versionContext';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||
import { getAssetIksApi } from '../../../../../services/simulation/ik/getAssetIKs';
|
||||
import ConveyorCollider from './conveyorCollider';
|
||||
|
||||
function Model({ asset }: { readonly asset: Asset }) {
|
||||
import { ModelAnimator } from './animator/modelAnimator';
|
||||
|
||||
function Model({ asset, isRendered }: { readonly asset: Asset, isRendered: boolean }) {
|
||||
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
const { camera, controls, gl, scene } = useThree();
|
||||
const { controls, gl, scene } = useThree();
|
||||
const savedTheme: string = localStorage.getItem("theme") || "light";
|
||||
const { activeTool } = useActiveTool();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { subModule } = useSubModuleStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { assetStore, eventStore, productStore } = useSceneContext();
|
||||
const { removeAsset, setAnimations, resetAnimation, setAnimationComplete } = assetStore();
|
||||
const { removeAsset, resetAnimation } = assetStore();
|
||||
const { setTop } = useTopData();
|
||||
const { setLeft } = useLeftData();
|
||||
const { getIsEventInProduct, addPoint } = productStore();
|
||||
@@ -44,28 +44,22 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
const { socket } = useSocketStore();
|
||||
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
|
||||
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { limitDistance } = useLimitDistance();
|
||||
const { renderDistance } = useRenderDistance();
|
||||
const leftDrag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
const rightDrag = useRef(false);
|
||||
const isRightMouseDown = useRef(false);
|
||||
const [isRendered, setIsRendered] = useState(false);
|
||||
const [gltfScene, setGltfScene] = useState<GLTF["scene"] | null>(null);
|
||||
const [boundingBox, setBoundingBox] = useState<THREE.Box3 | null>(null);
|
||||
const [conveyorPlaneSize, setConveyorPlaneSize] = useState<[number, number] | null>(null);
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const rigidBodyRef = useRef<RapierRigidBody>(null);
|
||||
const mixerRef = useRef<THREE.AnimationMixer>();
|
||||
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
|
||||
const [previousAnimation, setPreviousAnimation] = useState<string | null>(null);
|
||||
const [isSelected, setIsSelected] = useState(false);
|
||||
const [ikData, setIkData] = useState<any>();
|
||||
const blendFactor = useRef(0);
|
||||
const blendDuration = 0.5;
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectId } = useParams();
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
@@ -93,6 +87,17 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
}
|
||||
}, [asset.modelUuid, ikData])
|
||||
|
||||
useEffect(() => {
|
||||
if (gltfScene) {
|
||||
gltfScene.traverse((child: any) => {
|
||||
if (child.isMesh) {
|
||||
child.castShadow = true;
|
||||
child.receiveShadow = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [gltfScene]);
|
||||
|
||||
useEffect(() => {
|
||||
setDeletableFloorItem(null);
|
||||
if (selectedFloorItem === null || selectedFloorItem.userData.modelUuid !== asset.modelUuid) {
|
||||
@@ -123,16 +128,6 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
clone.animations = cachedModel.animations || [];
|
||||
setGltfScene(clone);
|
||||
calculateBoundingBox(clone);
|
||||
if (cachedModel.animations && clone.animations.length > 0) {
|
||||
const animationName = clone.animations.map((clip: any) => clip.name);
|
||||
setAnimations(asset.modelUuid, animationName)
|
||||
mixerRef.current = new THREE.AnimationMixer(clone);
|
||||
|
||||
clone.animations.forEach((animation: any) => {
|
||||
const action = mixerRef.current!.clipAction(animation);
|
||||
actions.current[animation.name] = action;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -197,21 +192,6 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
|
||||
}, []);
|
||||
|
||||
useFrame(() => {
|
||||
const assetPosition = new THREE.Vector3(...asset.position);
|
||||
if (limitDistance) {
|
||||
if (!isRendered && assetPosition.distanceTo(camera.position) <= renderDistance) {
|
||||
setIsRendered(true);
|
||||
} else if (isRendered && assetPosition.distanceTo(camera.position) > renderDistance) {
|
||||
setIsRendered(false);
|
||||
}
|
||||
} else {
|
||||
if (!isRendered) {
|
||||
setIsRendered(true);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const handleDblClick = (asset: Asset) => {
|
||||
if (asset) {
|
||||
if (activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') {
|
||||
@@ -377,50 +357,6 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleAnimationComplete = useCallback(() => {
|
||||
if (asset.animationState) {
|
||||
setAnimationComplete(asset.modelUuid, true);
|
||||
}
|
||||
}, [asset.animationState]);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (mixerRef.current) {
|
||||
mixerRef.current.update(delta * (activeModule === 'simulation' ? speed : 1));
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!asset.animationState || !mixerRef.current) return;
|
||||
|
||||
const { current, loopAnimation, isPlaying } = asset.animationState;
|
||||
const currentAction = actions.current[current];
|
||||
const previousAction = previousAnimation ? actions.current[previousAnimation] : null;
|
||||
|
||||
if (isPlaying && currentAction && activeModule === 'simulation' && !isPaused) {
|
||||
blendFactor.current = 0;
|
||||
|
||||
currentAction.reset();
|
||||
currentAction.setLoop(loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce, loopAnimation ? Infinity : 1);
|
||||
currentAction.clampWhenFinished = true;
|
||||
|
||||
if (previousAction && previousAction !== currentAction) {
|
||||
previousAction.crossFadeTo(currentAction, blendDuration, false);
|
||||
}
|
||||
|
||||
currentAction.play();
|
||||
mixerRef.current.addEventListener('finished', handleAnimationComplete);
|
||||
setPreviousAnimation(current);
|
||||
} else {
|
||||
Object.values(actions.current).forEach((action) => action.stop());
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (mixerRef.current) {
|
||||
mixerRef.current.removeEventListener('finished', handleAnimationComplete);
|
||||
}
|
||||
};
|
||||
}, [asset.animationState?.current, asset.animationState?.isCompleted, asset.animationState?.isPlaying, isPaused, activeModule]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
@@ -465,6 +401,18 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
|
||||
}, [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 (
|
||||
<group
|
||||
key={asset.modelUuid}
|
||||
@@ -475,6 +423,8 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
rotation={asset.rotation}
|
||||
visible={asset.isVisible}
|
||||
userData={{ ...asset, iks: ikData, rigidBodyRef: rigidBodyRef.current, boundingBox: boundingBox }}
|
||||
castShadow
|
||||
receiveShadow
|
||||
onDoubleClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (!toggleView) {
|
||||
@@ -508,6 +458,7 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
<>
|
||||
{isRendered ? (
|
||||
<>
|
||||
|
||||
<RigidBody
|
||||
type="fixed"
|
||||
colliders='cuboid'
|
||||
@@ -516,20 +467,23 @@ function Model({ asset }: { readonly asset: Asset }) {
|
||||
<primitive object={gltfScene} />
|
||||
</ RigidBody>
|
||||
|
||||
<ModelAnimator asset={asset} gltfScene={gltfScene} />
|
||||
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{boundingBox &&
|
||||
<AssetBoundingBox boundingBox={boundingBox} />
|
||||
}
|
||||
</>
|
||||
<AssetBoundingBox name='Asset Fallback' boundingBox={boundingBox} color='gray' lineWidth={2.5} />
|
||||
)}
|
||||
|
||||
<ConveyorCollider boundingBox={boundingBox}
|
||||
asset={asset}
|
||||
modelName={asset.modelName}
|
||||
scene={scene}
|
||||
conveyorPlaneSize={conveyorPlaneSize}
|
||||
/>
|
||||
|
||||
{isSelected &&
|
||||
<AssetBoundingBox name='Asset BBox' boundingBox={boundingBox} color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} />
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</group>
|
||||
|
||||
@@ -1,17 +1,45 @@
|
||||
import Model from './model/model';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useThree, useFrame } from "@react-three/fiber";
|
||||
import { Vector3 } from "three";
|
||||
import { CameraControls } from '@react-three/drei';
|
||||
import { Vector3 } from 'three';
|
||||
import { useSelectedFloorItem } from '../../../../store/builder/store';
|
||||
import { useLimitDistance, useRenderDistance, useSelectedFloorItem } from '../../../../store/builder/store';
|
||||
import { useSelectedAsset } from '../../../../store/simulation/useSimulationStore';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
|
||||
import Model from './model/model';
|
||||
|
||||
const distanceWorker = new Worker(new URL("../../../../services/factoryBuilder/webWorkers/distanceWorker.js", import.meta.url));
|
||||
|
||||
function Models() {
|
||||
const { controls } = useThree();
|
||||
const { controls, camera } = useThree();
|
||||
const { assetStore } = useSceneContext();
|
||||
const { assets } = assetStore();
|
||||
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedAsset, clearSelectedAsset } = useSelectedAsset();
|
||||
const { limitDistance } = useLimitDistance();
|
||||
const { renderDistance } = useRenderDistance();
|
||||
|
||||
const [renderMap, setRenderMap] = useState<Record<string, boolean>>({});
|
||||
|
||||
const cameraPos = useRef(new Vector3());
|
||||
|
||||
useEffect(() => {
|
||||
distanceWorker.onmessage = (e) => {
|
||||
const { shouldRender, modelUuid } = e.data;
|
||||
setRenderMap((prev) => {
|
||||
if (prev[modelUuid] === shouldRender) return prev;
|
||||
return { ...prev, [modelUuid]: shouldRender };
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
useFrame(() => {
|
||||
camera.getWorldPosition(cameraPos.current);
|
||||
for (const asset of assets) {
|
||||
const isRendered = renderMap[asset.modelUuid] ?? false;
|
||||
distanceWorker.postMessage({ modelUuid: asset.modelUuid, assetPosition: { x: asset.position[0], y: asset.position[1], z: asset.position[2], }, cameraPosition: cameraPos.current, limitDistance, renderDistance, isRendered, });
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group
|
||||
@@ -28,11 +56,11 @@ function Models() {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{assets.map((asset) =>
|
||||
<Model key={asset.modelUuid} asset={asset} />
|
||||
)}
|
||||
{assets.map((asset) => (
|
||||
<Model key={asset.modelUuid} asset={asset} isRendered={renderMap[asset.modelUuid] ?? false} />
|
||||
))}
|
||||
</group>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default Models
|
||||
export default Models;
|
||||
@@ -19,8 +19,9 @@ function FloorCreator() {
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { floorStore } = useSceneContext();
|
||||
const { addFloor, getFloorPointById, getFloorByPoints } = floorStore();
|
||||
const { floorStore, undoRedo2DStore } = useSceneContext();
|
||||
const { addFloor, getFloorPointById } = floorStore();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
@@ -103,6 +104,21 @@ function FloorCreator() {
|
||||
};
|
||||
|
||||
addFloor(floor);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
point: {
|
||||
type: 'Floor',
|
||||
lineData: floor,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
@@ -142,6 +158,21 @@ function FloorCreator() {
|
||||
};
|
||||
|
||||
addFloor(floor);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
point: {
|
||||
type: 'Floor',
|
||||
lineData: floor,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
@@ -191,6 +222,21 @@ function FloorCreator() {
|
||||
};
|
||||
|
||||
addFloor(floor);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
point: {
|
||||
type: 'Floor',
|
||||
lineData: floor,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
@@ -243,7 +289,7 @@ function FloorCreator() {
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, getFloorPointById, getFloorByPoints, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]);
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addFloor, getFloorPointById, floorDepth, isBeveled, bevelStrength, sideMaterial, topMaterial, snappedPosition, snappedPoint]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -30,10 +30,11 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
const [isDeletable, setIsDeletable] = useState(false);
|
||||
const { socket } = useSocketStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { wallStore, floorStore, zoneStore } = useSceneContext();
|
||||
const { wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const { removeWallByPoints, setPosition: setWallPosition, getWallsByPointId } = wallStore();
|
||||
const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId } = floorStore();
|
||||
const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId } = zoneStore();
|
||||
const { removeFloorByPoints, setPosition: setFloorPosition, getFloorsByPointId, getFloorsByPoints } = floorStore();
|
||||
const { removeZoneByPoints, setPosition: setZonePosition, getZonesByPointId, getZonesByPoints } = zoneStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
@@ -117,10 +118,26 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:delete', data);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Delete',
|
||||
point: {
|
||||
type: 'Wall',
|
||||
lineData: removedWall,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
setHoveredLine(null);
|
||||
}
|
||||
if (points[0].pointType === 'Floor' && points[1].pointType === 'Floor') {
|
||||
const Floors = getFloorsByPoints(points);
|
||||
const { removedFloors, updatedFloors } = removeFloorByPoints(points);
|
||||
if (removedFloors.length > 0) {
|
||||
removedFloors.forEach(floor => {
|
||||
@@ -143,6 +160,22 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
socket.emit('v1:model-Floor:delete', data);
|
||||
}
|
||||
});
|
||||
|
||||
const removedFloorsData = removedFloors.map((floor) => ({
|
||||
type: "Floor" as const,
|
||||
lineData: floor,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Delete',
|
||||
points: removedFloorsData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
if (updatedFloors.length > 0) {
|
||||
updatedFloors.forEach(floor => {
|
||||
@@ -165,11 +198,29 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
});
|
||||
|
||||
const updatedFloorsData = updatedFloors.map((floor) => ({
|
||||
type: "Floor" as const,
|
||||
lineData: Floors.find(f => f.floorUuid === floor.floorUuid) || floor,
|
||||
newData: floor,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Update',
|
||||
points: updatedFloorsData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
setHoveredLine(null);
|
||||
}
|
||||
if (points[0].pointType === 'Zone' && points[1].pointType === 'Zone') {
|
||||
const Zones = getZonesByPoints(points);
|
||||
const { removedZones, updatedZones } = removeZoneByPoints(points);
|
||||
if (removedZones.length > 0) {
|
||||
removedZones.forEach(zone => {
|
||||
@@ -192,6 +243,22 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
socket.emit('v1:zone:delete', data);
|
||||
}
|
||||
});
|
||||
|
||||
const removedZonesData = removedZones.map((zone) => ({
|
||||
type: "Zone" as const,
|
||||
lineData: zone,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Delete',
|
||||
points: removedZonesData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
if (updatedZones.length > 0) {
|
||||
updatedZones.forEach(zone => {
|
||||
@@ -214,7 +281,26 @@ function Line({ points }: Readonly<LineProps>) {
|
||||
socket.emit('v1:zone:add', data);
|
||||
}
|
||||
});
|
||||
|
||||
const updatedZonesData = updatedZones.map((zone) => ({
|
||||
type: "Zone" as const,
|
||||
lineData: Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone,
|
||||
newData: zone,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Update',
|
||||
points: updatedZonesData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
setHoveredLine(null);
|
||||
}
|
||||
handleCanvasCursors('default');
|
||||
}
|
||||
|
||||
@@ -32,13 +32,14 @@ function Point({ point }: { readonly point: Point }) {
|
||||
const [dragOffset, setDragOffset] = useState<THREE.Vector3 | null>(null);
|
||||
const { socket } = useSocketStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { aisleStore, wallStore, floorStore, zoneStore } = useSceneContext();
|
||||
const { aisleStore, wallStore, floorStore, zoneStore, undoRedo2DStore } = useSceneContext();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const { setPosition: setAislePosition, removePoint: removeAislePoint, getAislesByPointId } = aisleStore();
|
||||
const { setPosition: setWallPosition, removePoint: removeWallPoint, getWallsByPointId } = wallStore();
|
||||
const { setPosition: setFloorPosition, removePoint: removeFloorPoint, getFloorsByPointId } = floorStore();
|
||||
const { setPosition: setZonePosition, removePoint: removeZonePoint, getZonesByPointId } = zoneStore();
|
||||
const { snapAislePoint, snapAisleAngle, snapWallPoint, snapWallAngle, snapFloorPoint, snapFloorAngle, snapZonePoint, snapZoneAngle } = usePointSnapping({ uuid: point.pointUuid, pointType: point.pointType, position: point.position });
|
||||
const { hoveredPoint,hoveredLine, setHoveredPoint } = useBuilderStore();
|
||||
const { hoveredPoint, hoveredLine, setHoveredPoint } = useBuilderStore();
|
||||
const { selectedPoints } = useSelectedPoints();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
@@ -47,6 +48,13 @@ function Point({ point }: { readonly point: Point }) {
|
||||
const boxScale: [number, number, number] = Constants.pointConfig.boxScale;
|
||||
const colors = getColor(point);
|
||||
|
||||
const [initialPositions, setInitialPositions] = useState<{
|
||||
aisles?: Aisle[],
|
||||
walls?: Wall[],
|
||||
floors?: Floor[],
|
||||
zones?: Zone[]
|
||||
}>({});
|
||||
|
||||
useEffect(() => {
|
||||
handleCanvasCursors('default');
|
||||
}, [toolMode])
|
||||
@@ -152,6 +160,20 @@ function Point({ point }: { readonly point: Point }) {
|
||||
const currentPosition = new THREE.Vector3(...point.position);
|
||||
const offset = new THREE.Vector3().subVectors(currentPosition, hit);
|
||||
setDragOffset(offset);
|
||||
|
||||
if (point.pointType === 'Aisle') {
|
||||
const aisles = getAislesByPointId(point.pointUuid);
|
||||
setInitialPositions({ aisles });
|
||||
} else if (point.pointType === 'Wall') {
|
||||
const walls = getWallsByPointId(point.pointUuid);
|
||||
setInitialPositions({ walls });
|
||||
} else if (point.pointType === 'Floor') {
|
||||
const floors = getFloorsByPointId(point.pointUuid);
|
||||
setInitialPositions({ floors });
|
||||
} else if (point.pointType === 'Zone') {
|
||||
const zones = getZonesByPointId(point.pointUuid);
|
||||
setInitialPositions({ zones });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -159,6 +181,7 @@ function Point({ point }: { readonly point: Point }) {
|
||||
handleCanvasCursors('default');
|
||||
setDragOffset(null);
|
||||
if (toolMode !== 'move') return;
|
||||
|
||||
if (point.pointType === 'Aisle') {
|
||||
const updatedAisles = getAislesByPointId(point.pointUuid);
|
||||
if (updatedAisles.length > 0 && projectId) {
|
||||
@@ -180,6 +203,23 @@ function Point({ point }: { readonly point: Point }) {
|
||||
type: updatedAisle.type
|
||||
})
|
||||
})
|
||||
|
||||
if (initialPositions.aisles && initialPositions.aisles.length > 0) {
|
||||
const updatedPoints = initialPositions.aisles.map((aisle) => ({
|
||||
type: "Aisle" as const,
|
||||
lineData: aisle,
|
||||
newData: updatedAisles.find(a => a.aisleUuid === aisle.aisleUuid),
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [{
|
||||
actionType: 'Lines-Update',
|
||||
points: updatedPoints,
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (point.pointType === 'Wall') {
|
||||
const updatedWalls = getWallsByPointId(point.pointUuid);
|
||||
@@ -203,6 +243,23 @@ function Point({ point }: { readonly point: Point }) {
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
});
|
||||
}
|
||||
|
||||
if (initialPositions.walls && initialPositions.walls.length > 0) {
|
||||
const updatedPoints = initialPositions.walls.map((wall) => ({
|
||||
type: "Wall" as const,
|
||||
lineData: wall,
|
||||
newData: updatedWalls.find(w => w.wallUuid === wall.wallUuid),
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [{
|
||||
actionType: 'Lines-Update',
|
||||
points: updatedPoints,
|
||||
}]
|
||||
});
|
||||
}
|
||||
} else if (point.pointType === 'Floor') {
|
||||
const updatedFloors = getFloorsByPointId(point.pointUuid);
|
||||
if (updatedFloors && updatedFloors.length > 0 && projectId) {
|
||||
@@ -225,6 +282,23 @@ function Point({ point }: { readonly point: Point }) {
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
});
|
||||
}
|
||||
|
||||
if (initialPositions.floors && initialPositions.floors.length > 0) {
|
||||
const updatedPoints = initialPositions.floors.map((floor) => ({
|
||||
type: "Floor" as const,
|
||||
lineData: floor,
|
||||
newData: updatedFloors.find(f => f.floorUuid === floor.floorUuid),
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [{
|
||||
actionType: 'Lines-Update',
|
||||
points: updatedPoints,
|
||||
}]
|
||||
});
|
||||
}
|
||||
} else if (point.pointType === 'Zone') {
|
||||
const updatedZones = getZonesByPointId(point.pointUuid);
|
||||
if (updatedZones && updatedZones.length > 0 && projectId) {
|
||||
@@ -247,13 +321,33 @@ function Point({ point }: { readonly point: Point }) {
|
||||
socket.emit('v1:zone:add', data);
|
||||
});
|
||||
}
|
||||
|
||||
if (initialPositions.zones && initialPositions.zones.length > 0) {
|
||||
const updatedPoints = initialPositions.zones.map((zone) => ({
|
||||
type: "Zone" as const,
|
||||
lineData: zone,
|
||||
newData: updatedZones.find(z => z.zoneUuid === zone.zoneUuid),
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [{
|
||||
actionType: 'Lines-Update',
|
||||
points: updatedPoints,
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setInitialPositions({});
|
||||
}
|
||||
|
||||
const handlePointClick = (point: Point) => {
|
||||
if (toolMode === '2D-Delete') {
|
||||
if (point.pointType === 'Aisle') {
|
||||
const removedAisles = removeAislePoint(point.pointUuid);
|
||||
setHoveredPoint(null);
|
||||
if (removedAisles.length > 0) {
|
||||
removedAisles.forEach(aisle => {
|
||||
if (projectId) {
|
||||
@@ -273,9 +367,25 @@ function Point({ point }: { readonly point: Point }) {
|
||||
}
|
||||
|
||||
socket.emit('v1:model-aisle:delete', data);
|
||||
|
||||
}
|
||||
});
|
||||
setHoveredPoint(null);
|
||||
|
||||
const removedAislesData = removedAisles.map((aisle) => ({
|
||||
type: "Aisle" as const,
|
||||
lineData: aisle,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Delete',
|
||||
points: removedAislesData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
if (point.pointType === 'Wall') {
|
||||
@@ -302,9 +412,26 @@ function Point({ point }: { readonly point: Point }) {
|
||||
socket.emit('v1:model-Wall:delete', data);
|
||||
}
|
||||
});
|
||||
|
||||
const removedWallsData = removedWalls.map((wall) => ({
|
||||
type: "Wall" as const,
|
||||
lineData: wall,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Delete',
|
||||
points: removedWallsData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
if (point.pointType === 'Floor') {
|
||||
const Floors = getFloorsByPointId(point.pointUuid);
|
||||
const { removedFloors, updatedFloors } = removeFloorPoint(point.pointUuid);
|
||||
setHoveredPoint(null);
|
||||
if (removedFloors.length > 0) {
|
||||
@@ -328,6 +455,22 @@ function Point({ point }: { readonly point: Point }) {
|
||||
socket.emit('v1:model-Floor:delete', data);
|
||||
}
|
||||
});
|
||||
|
||||
const removedFloorsData = removedFloors.map((floor) => ({
|
||||
type: "Floor" as const,
|
||||
lineData: floor,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Delete',
|
||||
points: removedFloorsData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
if (updatedFloors.length > 0) {
|
||||
updatedFloors.forEach(floor => {
|
||||
@@ -350,9 +493,27 @@ function Point({ point }: { readonly point: Point }) {
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
});
|
||||
|
||||
const updatedFloorsData = updatedFloors.map((floor) => ({
|
||||
type: "Floor" as const,
|
||||
lineData: Floors.find(f => f.floorUuid === floor.floorUuid) || floor,
|
||||
newData: floor,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Update',
|
||||
points: updatedFloorsData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
if (point.pointType === 'Zone') {
|
||||
const Zones = getZonesByPointId(point.pointUuid);
|
||||
const { removedZones, updatedZones } = removeZonePoint(point.pointUuid);
|
||||
setHoveredPoint(null);
|
||||
if (removedZones.length > 0) {
|
||||
@@ -376,6 +537,22 @@ function Point({ point }: { readonly point: Point }) {
|
||||
socket.emit('v1:zone:delete', data);
|
||||
}
|
||||
});
|
||||
|
||||
const removedZonesData = removedZones.map((zone) => ({
|
||||
type: "Zone" as const,
|
||||
lineData: zone,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Delete',
|
||||
points: removedZonesData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
if (updatedZones.length > 0) {
|
||||
updatedZones.forEach(zone => {
|
||||
@@ -398,6 +575,23 @@ function Point({ point }: { readonly point: Point }) {
|
||||
socket.emit('v1:zone:add', data);
|
||||
}
|
||||
});
|
||||
|
||||
const updatedZonesData = updatedZones.map((zone) => ({
|
||||
type: "Zone" as const,
|
||||
lineData: Zones.find(z => z.zoneUuid === zone.zoneUuid) || zone,
|
||||
newData: zone,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}));
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Update',
|
||||
points: updatedZonesData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
handleCanvasCursors('default');
|
||||
@@ -422,7 +616,6 @@ function Point({ point }: { readonly point: Point }) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{!isSelected ?
|
||||
@@ -453,7 +646,7 @@ function Point({ point }: { readonly point: Point }) {
|
||||
onPointerOut={() => {
|
||||
if (hoveredPoint) {
|
||||
setHoveredPoint(null);
|
||||
if(!hoveredLine){
|
||||
if (!hoveredLine) {
|
||||
handleCanvasCursors('default');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useToggleView, useWallVisibility } from '../../../../../store/builder/s
|
||||
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
|
||||
import * as Constants from '../../../../../types/world/worldConstants';
|
||||
|
||||
import DecalInstance from '../../../Decal/decalInstance';
|
||||
// import DecalInstance from '../../../Decal/decalInstance';
|
||||
|
||||
import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
|
||||
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
|
||||
@@ -63,10 +63,10 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
|
||||
const materials = useMemo(() => {
|
||||
return [
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible }), // Left
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible }), // Right
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible }), // Top
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible }), // Bottom
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible, clipShadows: true }), // Left
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible, clipShadows: true }), // Right
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible, clipShadows: true }), // Top
|
||||
new THREE.MeshStandardMaterial({ color: Constants.wallConfig.defaultColor, side: THREE.DoubleSide, visible: visible, clipShadows: true }), // Bottom
|
||||
new THREE.MeshStandardMaterial({
|
||||
color: Constants.wallConfig.defaultColor,
|
||||
side: THREE.DoubleSide,
|
||||
@@ -99,11 +99,15 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
|
||||
return (
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
name={`Wall-${wall.wallUuid}`}
|
||||
key={wall.wallUuid}
|
||||
userData={wall}
|
||||
>
|
||||
<Base
|
||||
castShadow
|
||||
receiveShadow
|
||||
ref={meshRef}
|
||||
geometry={geometry}
|
||||
position={[centerX, centerY, centerZ]}
|
||||
|
||||
@@ -132,6 +132,7 @@ function Floor({ room }: { room: Point[] }) {
|
||||
<mesh name="Wall-Floor" rotation={[Math.PI / 2, 0, 0]}>
|
||||
<Extrude
|
||||
receiveShadow
|
||||
castShadow
|
||||
name="Wall-Floor"
|
||||
args={[shape, { depth: Constants.floorConfig.height, bevelEnabled: false }]}
|
||||
position={[0, 0, 0]}
|
||||
|
||||
@@ -22,8 +22,9 @@ function WallCreator() {
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { wallStore } = useSceneContext();
|
||||
const { wallStore, undoRedo2DStore } = useSceneContext();
|
||||
const { addWall, getWallPointById, removeWall, getWallByPoints } = wallStore();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
@@ -91,6 +92,7 @@ function WallCreator() {
|
||||
const closestPoint = new THREE.Vector3().lerpVectors(point1Vec, point2Vec, t);
|
||||
|
||||
removeWall(wall.wallUuid);
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
@@ -142,6 +144,7 @@ function WallCreator() {
|
||||
wallHeight: wallHeight,
|
||||
decals: []
|
||||
}
|
||||
|
||||
addWall(wall2);
|
||||
|
||||
// API
|
||||
@@ -171,8 +174,36 @@ function WallCreator() {
|
||||
wallHeight: wallHeight,
|
||||
decals: []
|
||||
}
|
||||
|
||||
addWall(wall3);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Create',
|
||||
points: [
|
||||
{
|
||||
type: 'Wall',
|
||||
lineData: wall3,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}, {
|
||||
type: 'Wall',
|
||||
lineData: wall2,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
]
|
||||
}, {
|
||||
actionType: 'Line-Delete',
|
||||
point: {
|
||||
type: 'Wall',
|
||||
lineData: wall,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
// API
|
||||
|
||||
// if (projectId) {
|
||||
@@ -202,7 +233,8 @@ function WallCreator() {
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
decals: []
|
||||
};
|
||||
}
|
||||
|
||||
addWall(wall1);
|
||||
|
||||
// API
|
||||
@@ -232,6 +264,7 @@ function WallCreator() {
|
||||
wallHeight: wallHeight,
|
||||
decals: []
|
||||
}
|
||||
|
||||
addWall(wall2);
|
||||
|
||||
// API
|
||||
@@ -261,8 +294,40 @@ function WallCreator() {
|
||||
wallHeight: wallHeight,
|
||||
decals: []
|
||||
}
|
||||
|
||||
addWall(wall3);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Lines-Create',
|
||||
points: [
|
||||
{
|
||||
type: 'Wall',
|
||||
lineData: wall3,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}, {
|
||||
type: 'Wall',
|
||||
lineData: wall1,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}, {
|
||||
type: 'Wall',
|
||||
lineData: wall2,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
]
|
||||
}, {
|
||||
actionType: 'Line-Delete',
|
||||
point: {
|
||||
type: 'Wall',
|
||||
lineData: wall,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
// API
|
||||
|
||||
// if (projectId) {
|
||||
@@ -328,9 +393,24 @@ function WallCreator() {
|
||||
wallThickness: wallThickness,
|
||||
wallHeight: wallHeight,
|
||||
decals: []
|
||||
};
|
||||
}
|
||||
|
||||
addWall(wall);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
point: {
|
||||
type: 'Wall',
|
||||
lineData: wall,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
// API
|
||||
|
||||
// if (projectId) {
|
||||
|
||||
@@ -242,7 +242,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
||||
visible={wallAsset.isVisible}
|
||||
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>
|
||||
<Base geometry={new THREE.BoxGeometry()} />
|
||||
</Geometry>
|
||||
@@ -265,7 +265,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
||||
e.stopPropagation();
|
||||
let currentObject = e.object as THREE.Object3D;
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
if (currentObject.userData.wallUuid) {
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as THREE.Object3D;
|
||||
@@ -286,7 +286,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
||||
e.stopPropagation();
|
||||
let currentObject = e.object as THREE.Object3D;
|
||||
while (currentObject) {
|
||||
if (currentObject.name === "Scene") {
|
||||
if (currentObject.userData.wallUuid) {
|
||||
break;
|
||||
}
|
||||
currentObject = currentObject.parent as THREE.Object3D;
|
||||
|
||||
@@ -19,8 +19,9 @@ function ZoneCreator() {
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeLayer } = useActiveLayer();
|
||||
const { socket } = useSocketStore();
|
||||
const { zoneStore } = useSceneContext();
|
||||
const { zones, addZone, getZonePointById, getZoneByPoints } = zoneStore();
|
||||
const { zoneStore, undoRedo2DStore } = useSceneContext();
|
||||
const { addZone, getZonePointById } = zoneStore();
|
||||
const { push2D } = undoRedo2DStore();
|
||||
const drag = useRef(false);
|
||||
const isLeftMouseDown = useRef(false);
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
@@ -102,6 +103,21 @@ function ZoneCreator() {
|
||||
};
|
||||
|
||||
addZone(zone);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
point: {
|
||||
type: 'Zone',
|
||||
lineData: zone,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
@@ -139,6 +155,21 @@ function ZoneCreator() {
|
||||
};
|
||||
|
||||
addZone(zone);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
point: {
|
||||
type: 'Zone',
|
||||
lineData: zone,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
@@ -186,6 +217,21 @@ function ZoneCreator() {
|
||||
};
|
||||
|
||||
addZone(zone);
|
||||
|
||||
push2D({
|
||||
type: 'Draw',
|
||||
actions: [
|
||||
{
|
||||
actionType: 'Line-Create',
|
||||
point: {
|
||||
type: 'Zone',
|
||||
lineData: zone,
|
||||
timeStamp: new Date().toISOString(),
|
||||
}
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
if (projectId) {
|
||||
|
||||
// API
|
||||
@@ -238,7 +284,7 @@ function ZoneCreator() {
|
||||
canvasElement.removeEventListener("click", onMouseClick);
|
||||
canvasElement.removeEventListener("contextmenu", onContext);
|
||||
};
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, getZoneByPoints, zoneColor, zoneHeight, snappedPosition, snappedPoint]);
|
||||
}, [gl, camera, scene, raycaster, pointer, plane, toggleView, toolMode, activeLayer, socket, tempPoints, isCreating, addZone, getZonePointById, zoneColor, zoneHeight, snappedPosition, snappedPoint]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -14,6 +14,7 @@ import TransformControl from "./transformControls/transformControls";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D";
|
||||
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
|
||||
|
||||
export default function Controls() {
|
||||
const controlsRef = useRef<CameraControls>(null);
|
||||
@@ -142,6 +143,8 @@ export default function Controls() {
|
||||
|
||||
<SelectionControls2D />
|
||||
|
||||
<UndoRedo2DControls />
|
||||
|
||||
<TransformControl />
|
||||
|
||||
</>
|
||||
|
||||
@@ -83,7 +83,7 @@ function MoveControls2D({
|
||||
|
||||
if (keyCombination === "G") {
|
||||
if (selectedPoints.length > 0) {
|
||||
moveAssets();
|
||||
movePoints();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ function MoveControls2D({
|
||||
return new THREE.Vector3().subVectors(pointPosition, hitPoint);
|
||||
}, []);
|
||||
|
||||
const moveAssets = useCallback(() => {
|
||||
const movePoints = (() => {
|
||||
if (selectedPoints.length === 0) return;
|
||||
|
||||
const states: Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }> = {};
|
||||
@@ -192,9 +192,9 @@ function MoveControls2D({
|
||||
|
||||
setMovedObjects(selectedPoints);
|
||||
setIsMoving(true);
|
||||
}, [selectedPoints, camera, pointer, plane, raycaster, calculateDragOffset]);
|
||||
});
|
||||
|
||||
const resetToInitialPositions = useCallback(() => {
|
||||
const resetToInitialPositions = () => {
|
||||
setTimeout(() => {
|
||||
movedObjects.forEach((movedPoint: THREE.Object3D) => {
|
||||
if (movedPoint.userData.pointUuid && initialStates[movedPoint.uuid]) {
|
||||
@@ -218,7 +218,7 @@ function MoveControls2D({
|
||||
}
|
||||
});
|
||||
}, 0)
|
||||
}, [movedObjects, initialStates, setAislePosition, setWallPosition, setFloorPosition, setZonePosition]);
|
||||
};
|
||||
|
||||
const placeMovedAssets = () => {
|
||||
if (movedObjects.length === 0) return;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { SkeletonUtils } from "three-stdlib";
|
||||
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
@@ -10,20 +10,18 @@ import { getUserData } from "../../../../../functions/getUserData";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||
|
||||
const CopyPasteControls3D = ({
|
||||
copiedObjects,
|
||||
setCopiedObjects,
|
||||
pastedObjects,
|
||||
setpastedObjects,
|
||||
selectionGroup,
|
||||
setDuplicatedObjects,
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
boundingBoxRef
|
||||
}: any) => {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
const { toggleView } = useToggleView();
|
||||
@@ -33,31 +31,67 @@ const CopyPasteControls3D = ({
|
||||
const { assetStore, eventStore } = useSceneContext();
|
||||
const { addEvent } = eventStore();
|
||||
const { projectId } = useParams();
|
||||
const { assets, addAsset } = assetStore();
|
||||
const { assets, addAsset, setPosition, updateAsset, removeAsset, getAssetById } = assetStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
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(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
const canvasElement = gl.domElement;
|
||||
canvasElement.tabIndex = 0;
|
||||
|
||||
let isMoving = false;
|
||||
let isPointerMoving = false;
|
||||
|
||||
const onPointerDown = () => {
|
||||
isMoving = false;
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
isPointerMoving = false;
|
||||
if (event.button === 0) mouseButtonsDown.current.left = true;
|
||||
if (event.button === 2) mouseButtonsDown.current.right = true;
|
||||
};
|
||||
|
||||
const onPointerMove = () => {
|
||||
isMoving = true;
|
||||
isPointerMoving = true;
|
||||
};
|
||||
|
||||
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();
|
||||
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) => {
|
||||
@@ -69,6 +103,13 @@ const CopyPasteControls3D = ({
|
||||
if (keyCombination === "Ctrl+V" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
|
||||
pasteCopiedObjects();
|
||||
}
|
||||
if (keyCombination === "ESCAPE" && pastedObjects.length > 0) {
|
||||
event.preventDefault();
|
||||
clearSelection();
|
||||
pastedObjects.forEach((obj: THREE.Object3D) => {
|
||||
removeAsset(obj.userData.modelUuid);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (!toggleView) {
|
||||
@@ -84,27 +125,23 @@ const CopyPasteControls3D = ({
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
|
||||
}, [assets, camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, rotatedObjects]);
|
||||
|
||||
useFrame(() => {
|
||||
if (pastedObjects.length > 0) {
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
if (point) {
|
||||
const position = new THREE.Vector3();
|
||||
if (boundingBoxRef.current) {
|
||||
boundingBoxRef.current?.getWorldPosition(position)
|
||||
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 {
|
||||
const box = new THREE.Box3();
|
||||
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));
|
||||
}
|
||||
}
|
||||
if (!isPasting || pastedObjects.length === 0) return;
|
||||
if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) return;
|
||||
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
|
||||
if (hit && centerOffset) {
|
||||
pastedObjects.forEach((pastedObject: THREE.Object3D, index: number) => {
|
||||
const model = scene.getObjectByProperty("uuid", pastedObject.userData.modelUuid);
|
||||
if (!model) return;
|
||||
const newPos = new THREE.Vector3().addVectors(hit, relativePositions[index]);
|
||||
model.position.set(newPos.x, 0, newPos.z);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -127,31 +164,41 @@ const CopyPasteControls3D = ({
|
||||
|
||||
const pasteCopiedObjects = () => {
|
||||
if (copiedObjects.length > 0 && pastedObjects.length === 0) {
|
||||
const newClones = copiedObjects.map((obj: THREE.Object3D) => {
|
||||
const clone = obj.clone();
|
||||
clone.position.copy(obj.position);
|
||||
const { center, relatives } = calculateRelativePositions(copiedObjects);
|
||||
setRelativePositions(relatives);
|
||||
|
||||
const newPastedObjects = copiedObjects.map((obj: any) => {
|
||||
const clone = SkeletonUtils.clone(obj);
|
||||
clone.userData.modelUuid = THREE.MathUtils.generateUUID();
|
||||
return clone;
|
||||
});
|
||||
selectionGroup.current.add(...newClones);
|
||||
setpastedObjects([...newClones]);
|
||||
setSelectedAssets([...newClones]);
|
||||
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
setpastedObjects(newPastedObjects);
|
||||
|
||||
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) {
|
||||
const position = new THREE.Vector3();
|
||||
if (boundingBoxRef.current) {
|
||||
boundingBoxRef.current?.getWorldPosition(position)
|
||||
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 {
|
||||
const box = new THREE.Box3();
|
||||
newClones.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));
|
||||
}
|
||||
if (hit) {
|
||||
const offset = new THREE.Vector3().subVectors(center, hit);
|
||||
setCenterOffset(offset);
|
||||
setIsPasting(true);
|
||||
|
||||
newPastedObjects.forEach((obj: THREE.Object3D, index: number) => {
|
||||
const initialPos = new THREE.Vector3().addVectors(hit, relatives[index]);
|
||||
const asset: Asset = {
|
||||
modelUuid: obj.userData.modelUuid,
|
||||
modelName: obj.userData.modelName,
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -159,33 +206,35 @@ const CopyPasteControls3D = ({
|
||||
const addPastedObjects = () => {
|
||||
if (pastedObjects.length === 0) return;
|
||||
|
||||
pastedObjects.forEach(async (obj: THREE.Object3D) => {
|
||||
if (obj) {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
obj.getWorldPosition(worldPosition);
|
||||
obj.position.copy(worldPosition);
|
||||
pastedObjects.forEach(async (pastedAsset: THREE.Object3D) => {
|
||||
if (pastedAsset) {
|
||||
const assetUuid = pastedAsset.userData.modelUuid;
|
||||
const asset = getAssetById(assetUuid);
|
||||
const model = scene.getObjectByProperty("uuid", pastedAsset.userData.modelUuid);
|
||||
if (!asset || !model) return;
|
||||
const position = new THREE.Vector3().copy(model.position);
|
||||
|
||||
const newFloorItem: Types.FloorItemType = {
|
||||
modelUuid: THREE.MathUtils.generateUUID(),
|
||||
modelName: obj.userData.modelName,
|
||||
assetId: obj.userData.assetId,
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
|
||||
modelUuid: pastedAsset.userData.modelUuid,
|
||||
modelName: pastedAsset.userData.modelName,
|
||||
assetId: pastedAsset.userData.assetId,
|
||||
position: [position.x, position.y, position.z],
|
||||
rotation: { x: asset.rotation[0], y: asset.rotation[1], z: asset.rotation[2] },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
};
|
||||
|
||||
let updatedEventData = null;
|
||||
|
||||
if (obj.userData.eventData) {
|
||||
updatedEventData = JSON.parse(JSON.stringify(obj.userData.eventData));
|
||||
if (pastedAsset.userData.eventData) {
|
||||
updatedEventData = JSON.parse(JSON.stringify(pastedAsset.userData.eventData));
|
||||
updatedEventData.modelUuid = newFloorItem.modelUuid;
|
||||
|
||||
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 = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -217,7 +266,7 @@ const CopyPasteControls3D = ({
|
||||
rotation: point.rotation
|
||||
}));
|
||||
|
||||
} else if (obj.userData.eventData.type === "Vehicle") {
|
||||
} else if (pastedAsset.userData.eventData.type === "Vehicle") {
|
||||
const vehicleEvent: VehicleEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -255,7 +304,7 @@ const CopyPasteControls3D = ({
|
||||
rotation: vehicleEvent.point.rotation
|
||||
};
|
||||
|
||||
} else if (obj.userData.eventData.type === "ArmBot") {
|
||||
} else if (pastedAsset.userData.eventData.type === "ArmBot") {
|
||||
const roboticArmEvent: RoboticArmEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -289,7 +338,7 @@ const CopyPasteControls3D = ({
|
||||
rotation: roboticArmEvent.point.rotation
|
||||
};
|
||||
|
||||
} else if (obj.userData.eventData.type === "StaticMachine") {
|
||||
} else if (pastedAsset.userData.eventData.type === "StaticMachine") {
|
||||
const machineEvent: MachineEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -317,7 +366,7 @@ const CopyPasteControls3D = ({
|
||||
position: machineEvent.point.position,
|
||||
rotation: machineEvent.point.rotation
|
||||
};
|
||||
} else if (obj.userData.eventData.type === "Storage") {
|
||||
} else if (pastedAsset.userData.eventData.type === "Storage") {
|
||||
const storageEvent: StorageEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -344,7 +393,7 @@ const CopyPasteControls3D = ({
|
||||
position: storageEvent.point.position,
|
||||
rotation: storageEvent.point.rotation
|
||||
};
|
||||
} else if (obj.userData.eventData.type === "Human") {
|
||||
} else if (pastedAsset.userData.eventData.type === "Human") {
|
||||
const humanEvent: HumanEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -363,6 +412,7 @@ const CopyPasteControls3D = ({
|
||||
actionName: "Action 1",
|
||||
actionType: "worker",
|
||||
loadCapacity: 1,
|
||||
assemblyCount: 1,
|
||||
loadCount: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
@@ -379,28 +429,14 @@ const CopyPasteControls3D = ({
|
||||
}
|
||||
|
||||
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 = {
|
||||
organization,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
assetId: newFloorItem.assetId,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
position: [position.x, 0, position.z],
|
||||
rotation: { x: pastedAsset.rotation.x, y: pastedAsset.rotation.y, z: pastedAsset.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
@@ -410,54 +446,36 @@ const CopyPasteControls3D = ({
|
||||
projectId
|
||||
};
|
||||
|
||||
// console.log('data: ', data);
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
//REST
|
||||
|
||||
obj.userData = {
|
||||
name: newFloorItem.modelName,
|
||||
modelId: newFloorItem.assetId,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
eventData: JSON.parse(JSON.stringify(eventData))
|
||||
};
|
||||
// await setAssetsApi(data);
|
||||
|
||||
//SOCKET
|
||||
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
|
||||
const asset: Asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
modelName: data.modelName,
|
||||
assetId: data.assetId,
|
||||
position: data.position,
|
||||
position: [position.x, 0, position.z],
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
|
||||
isLocked: data.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: data.isVisible,
|
||||
opacity: 1,
|
||||
eventData: data.eventData
|
||||
}
|
||||
|
||||
addAsset(asset);
|
||||
};
|
||||
|
||||
updateAsset(asset.modelUuid, asset);
|
||||
} 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 = {
|
||||
organization,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
assetId: newFloorItem.assetId,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
position: [position.x, 0, position.z],
|
||||
rotation: { x: pastedAsset.rotation.x, y: pastedAsset.rotation.y, z: pastedAsset.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
@@ -466,22 +484,21 @@ const CopyPasteControls3D = ({
|
||||
userId
|
||||
};
|
||||
|
||||
// console.log('data: ', data);
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
|
||||
const asset: Asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
modelName: data.modelName,
|
||||
assetId: data.assetId,
|
||||
position: data.position,
|
||||
position: [position.x, 0, position.z],
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
|
||||
isLocked: data.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: data.isVisible,
|
||||
opacity: 1,
|
||||
}
|
||||
};
|
||||
|
||||
addAsset(asset);
|
||||
updateAsset(asset.modelUuid, asset);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -491,15 +508,14 @@ const CopyPasteControls3D = ({
|
||||
};
|
||||
|
||||
const clearSelection = () => {
|
||||
selectionGroup.current.children = [];
|
||||
selectionGroup.current.position.set(0, 0, 0);
|
||||
selectionGroup.current.rotation.set(0, 0, 0);
|
||||
setMovedObjects([]);
|
||||
setpastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
}
|
||||
setIsPasting(false);
|
||||
setCenterOffset(null);
|
||||
};
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { SkeletonUtils } from "three-stdlib";
|
||||
import { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
@@ -10,18 +10,16 @@ import { getUserData } from "../../../../../functions/getUserData";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
// import { setAssetsApi } from "../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||
|
||||
const DuplicationControls3D = ({
|
||||
duplicatedObjects,
|
||||
setDuplicatedObjects,
|
||||
setpastedObjects,
|
||||
selectionGroup,
|
||||
movedObjects,
|
||||
setMovedObjects,
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
boundingBoxRef
|
||||
}: any) => {
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
const { toggleView } = useToggleView();
|
||||
@@ -31,39 +29,71 @@ const DuplicationControls3D = ({
|
||||
const { assetStore, eventStore } = useSceneContext();
|
||||
const { addEvent } = eventStore();
|
||||
const { projectId } = useParams();
|
||||
const { assets, addAsset } = assetStore();
|
||||
const { assets, addAsset, updateAsset, removeAsset, getAssetById } = assetStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
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.Object3D, hitPoint: THREE.Vector3) => {
|
||||
const pointPosition = new THREE.Vector3().copy(point.position);
|
||||
return new THREE.Vector3().subVectors(pointPosition, hitPoint);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
const canvasElement = gl.domElement;
|
||||
canvasElement.tabIndex = 0;
|
||||
|
||||
let isMoving = false;
|
||||
let isPointerMoving = false;
|
||||
|
||||
const onPointerDown = () => {
|
||||
isMoving = false;
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
isPointerMoving = false;
|
||||
if (event.button === 0) mouseButtonsDown.current.left = true;
|
||||
if (event.button === 2) mouseButtonsDown.current.right = true;
|
||||
};
|
||||
|
||||
const onPointerMove = () => {
|
||||
isMoving = true;
|
||||
isPointerMoving = true;
|
||||
};
|
||||
|
||||
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();
|
||||
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 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();
|
||||
}
|
||||
if (keyCombination === "ESCAPE" && duplicatedObjects.length > 0) {
|
||||
event.preventDefault();
|
||||
clearSelection();
|
||||
duplicatedObjects.forEach((obj: THREE.Object3D) => {
|
||||
removeAsset(obj.userData.modelUuid);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (!toggleView) {
|
||||
@@ -79,87 +109,133 @@ const DuplicationControls3D = ({
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
};
|
||||
|
||||
}, [assets, camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, rotatedObjects]);
|
||||
|
||||
useFrame(() => {
|
||||
if (duplicatedObjects.length > 0) {
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
if (point) {
|
||||
const position = new THREE.Vector3();
|
||||
if (boundingBoxRef.current) {
|
||||
boundingBoxRef.current?.getWorldPosition(position)
|
||||
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 {
|
||||
const box = new THREE.Box3();
|
||||
duplicatedObjects.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));
|
||||
if (!isDuplicating || duplicatedObjects.length === 0) return;
|
||||
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const hit = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
|
||||
if (hit) {
|
||||
if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) {
|
||||
const model = scene.getObjectByProperty("uuid", duplicatedObjects[0].userData.modelUuid);
|
||||
if (model) {
|
||||
const newOffset = calculateDragOffset(model, intersectionPoint);
|
||||
setDragOffset(newOffset);
|
||||
}
|
||||
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 model = scene.getObjectByProperty("uuid", duplicatedObject.userData.modelUuid);
|
||||
|
||||
const newPosition = new THREE.Vector3().addVectors(adjustedHit, relativeOffset);
|
||||
const positionArray: [number, number, number] = [newPosition.x, newPosition.y, newPosition.z];
|
||||
|
||||
if (model) {
|
||||
model.position.set(...positionArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const duplicateSelection = () => {
|
||||
const duplicateSelection = useCallback(() => {
|
||||
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
|
||||
const newClones = selectedAssets.map((asset: any) => {
|
||||
if (asset.userData.rigidBodyRef) {
|
||||
let userData = { ...asset.userData };
|
||||
const positions: Record<string, THREE.Vector3> = {};
|
||||
|
||||
const newDuplicatedObjects = selectedAssets.map((obj: any) => {
|
||||
if (obj.userData.rigidBodyRef) {
|
||||
let userData = { ...obj.userData };
|
||||
delete userData.rigidBodyRef;
|
||||
asset.userData = userData;
|
||||
obj.userData = userData;
|
||||
}
|
||||
const clone = SkeletonUtils.clone(asset);
|
||||
clone.position.copy(asset.position);
|
||||
const clone = SkeletonUtils.clone(obj);
|
||||
clone.userData.modelUuid = THREE.MathUtils.generateUUID();
|
||||
positions[clone.userData.modelUuid] = new THREE.Vector3().copy(obj.position);
|
||||
return clone;
|
||||
});
|
||||
|
||||
selectionGroup.current.add(...newClones);
|
||||
setDuplicatedObjects(newClones);
|
||||
setDuplicatedObjects(newDuplicatedObjects);
|
||||
setInitialPositions(positions);
|
||||
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
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) {
|
||||
const position = new THREE.Vector3();
|
||||
boundingBoxRef.current?.getWorldPosition(position)
|
||||
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
|
||||
if (hit) {
|
||||
const offset = calculateDragOffset(selectedAssets[0], hit);
|
||||
setDragOffset(offset);
|
||||
}
|
||||
|
||||
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 = () => {
|
||||
if (duplicatedObjects.length === 0) return;
|
||||
|
||||
duplicatedObjects.forEach(async (obj: THREE.Object3D) => {
|
||||
if (obj) {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
obj.getWorldPosition(worldPosition);
|
||||
obj.position.copy(worldPosition);
|
||||
duplicatedObjects.forEach(async (duplicatedAsset: THREE.Object3D) => {
|
||||
if (duplicatedAsset) {
|
||||
const assetUuid = duplicatedAsset.userData.modelUuid;
|
||||
const asset = getAssetById(assetUuid);
|
||||
const model = scene.getObjectByProperty("uuid", duplicatedAsset.userData.modelUuid);
|
||||
if (!asset || !model) return;
|
||||
const position = new THREE.Vector3().copy(model.position);
|
||||
|
||||
const newFloorItem: Types.FloorItemType = {
|
||||
modelUuid: THREE.MathUtils.generateUUID(),
|
||||
modelName: obj.userData.modelName,
|
||||
assetId: obj.userData.assetId,
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
|
||||
modelUuid: duplicatedAsset.userData.modelUuid,
|
||||
modelName: duplicatedAsset.userData.modelName,
|
||||
assetId: duplicatedAsset.userData.assetId,
|
||||
position: [position.x, position.y, position.z],
|
||||
rotation: { x: asset.rotation[0], y: asset.rotation[1], z: asset.rotation[2] },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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 = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -191,7 +267,7 @@ const DuplicationControls3D = ({
|
||||
rotation: point.rotation
|
||||
}));
|
||||
|
||||
} else if (obj.userData.eventData.type === "Vehicle") {
|
||||
} else if (duplicatedAsset.userData.eventData.type === "Vehicle") {
|
||||
const vehicleEvent: VehicleEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -229,7 +305,7 @@ const DuplicationControls3D = ({
|
||||
rotation: vehicleEvent.point.rotation
|
||||
};
|
||||
|
||||
} else if (obj.userData.eventData.type === "ArmBot") {
|
||||
} else if (duplicatedAsset.userData.eventData.type === "ArmBot") {
|
||||
const roboticArmEvent: RoboticArmEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -263,7 +339,7 @@ const DuplicationControls3D = ({
|
||||
rotation: roboticArmEvent.point.rotation
|
||||
};
|
||||
|
||||
} else if (obj.userData.eventData.type === "StaticMachine") {
|
||||
} else if (duplicatedAsset.userData.eventData.type === "StaticMachine") {
|
||||
const machineEvent: MachineEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -291,7 +367,7 @@ const DuplicationControls3D = ({
|
||||
position: machineEvent.point.position,
|
||||
rotation: machineEvent.point.rotation
|
||||
};
|
||||
} else if (obj.userData.eventData.type === "Storage") {
|
||||
} else if (duplicatedAsset.userData.eventData.type === "Storage") {
|
||||
const storageEvent: StorageEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -318,7 +394,7 @@ const DuplicationControls3D = ({
|
||||
position: storageEvent.point.position,
|
||||
rotation: storageEvent.point.rotation
|
||||
};
|
||||
} else if (obj.userData.eventData.type === "Human") {
|
||||
} else if (duplicatedAsset.userData.eventData.type === "Human") {
|
||||
const humanEvent: HumanEventSchema = {
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
@@ -337,6 +413,7 @@ const DuplicationControls3D = ({
|
||||
actionName: "Action 1",
|
||||
actionType: "worker",
|
||||
loadCapacity: 1,
|
||||
assemblyCount: 1,
|
||||
loadCount: 1,
|
||||
processTime: 10,
|
||||
triggers: []
|
||||
@@ -354,121 +431,92 @@ const DuplicationControls3D = ({
|
||||
|
||||
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 = {
|
||||
organization,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
assetId: newFloorItem.assetId,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
position: [position.x, position.y, position.z],
|
||||
rotation: { x: duplicatedAsset.rotation.x, y: duplicatedAsset.rotation.y, z: duplicatedAsset.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
eventData: eventData,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
projectId,
|
||||
userId
|
||||
userId,
|
||||
projectId
|
||||
};
|
||||
|
||||
// console.log('data: ', data);
|
||||
//REST
|
||||
|
||||
// await setAssetsApi(data);
|
||||
|
||||
//SOCKET
|
||||
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
|
||||
const asset: Asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
modelName: data.modelName,
|
||||
assetId: data.assetId,
|
||||
position: data.position,
|
||||
position: [position.x, position.y, position.z],
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
|
||||
isLocked: data.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: data.isVisible,
|
||||
opacity: 1,
|
||||
eventData: data.eventData
|
||||
}
|
||||
|
||||
addAsset(asset);
|
||||
};
|
||||
|
||||
updateAsset(asset.modelUuid, asset);
|
||||
} 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 = {
|
||||
organization,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
assetId: newFloorItem.assetId,
|
||||
position: newFloorItem.position,
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
position: [position.x, position.y, position.z],
|
||||
rotation: { x: duplicatedAsset.rotation.x, y: duplicatedAsset.rotation.y, z: duplicatedAsset.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
projectId
|
||||
projectId,
|
||||
userId
|
||||
};
|
||||
|
||||
// console.log('data: ', data);/
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
|
||||
const asset: Asset = {
|
||||
modelUuid: data.modelUuid,
|
||||
modelName: data.modelName,
|
||||
assetId: data.assetId,
|
||||
position: data.position,
|
||||
position: [position.x, position.y, position.z],
|
||||
rotation: [data.rotation.x, data.rotation.y, data.rotation.z],
|
||||
isLocked: data.isLocked,
|
||||
isCollidable: false,
|
||||
isVisible: data.isVisible,
|
||||
opacity: 1,
|
||||
}
|
||||
};
|
||||
|
||||
addAsset(asset);
|
||||
updateAsset(asset.modelUuid, asset);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
echo.success("Object duplicated!");
|
||||
clearSelection();
|
||||
}
|
||||
};
|
||||
|
||||
const clearSelection = () => {
|
||||
selectionGroup.current.children = [];
|
||||
selectionGroup.current.position.set(0, 0, 0);
|
||||
selectionGroup.current.rotation.set(0, 0, 0);
|
||||
setMovedObjects([]);
|
||||
setpastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
}
|
||||
setIsDuplicating(false);
|
||||
setDragOffset(null);
|
||||
};
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { useSelectedAssets, useSocketStore, useToggleView, } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
@@ -22,7 +22,6 @@ function MoveControls3D({
|
||||
setpastedObjects,
|
||||
duplicatedObjects,
|
||||
setDuplicatedObjects,
|
||||
selectionGroup,
|
||||
rotatedObjects,
|
||||
setRotatedObjects,
|
||||
boundingBoxRef,
|
||||
@@ -39,11 +38,16 @@ function MoveControls3D({
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectId } = useParams();
|
||||
const { assetStore, eventStore, productStore } = useSceneContext();
|
||||
const { updateAsset } = assetStore();
|
||||
const AssetGroup = useRef<THREE.Group | undefined>(undefined);
|
||||
const { updateAsset, getAssetById } = assetStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
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 = (
|
||||
productName: string,
|
||||
productUuid: string,
|
||||
@@ -65,47 +69,39 @@ function MoveControls3D({
|
||||
const canvasElement = gl.domElement;
|
||||
canvasElement.tabIndex = 0;
|
||||
|
||||
const itemsGroup: any = scene.getObjectByName("Asset Group");
|
||||
AssetGroup.current = itemsGroup;
|
||||
|
||||
if (!AssetGroup.current) {
|
||||
console.error("Asset Group not found in the scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
let isMoving = false;
|
||||
|
||||
const onPointerDown = () => {
|
||||
isMoving = false;
|
||||
};
|
||||
let isPointerMoving = false;
|
||||
|
||||
const onPointerMove = () => {
|
||||
isMoving = true;
|
||||
isPointerMoving = true;
|
||||
};
|
||||
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
const isModifierKey = event.key === "Control" || event.key === "Shift";
|
||||
const isModifierKey = (!event.shiftKey && !event.ctrlKey);
|
||||
|
||||
if (isModifierKey) {
|
||||
if (isModifierKey && keyEvent !== "") {
|
||||
setKeyEvent("");
|
||||
}
|
||||
};
|
||||
|
||||
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) => {
|
||||
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();
|
||||
placeMovedAssets();
|
||||
}
|
||||
if (!isMoving && movedObjects.length > 0 && event.button === 2) {
|
||||
if (!isPointerMoving && movedObjects.length > 0 && event.button === 2) {
|
||||
event.preventDefault();
|
||||
|
||||
resetToInitialPositions();
|
||||
clearSelection();
|
||||
movedObjects.forEach((asset: any) => {
|
||||
if (AssetGroup.current) {
|
||||
AssetGroup.current.attach(asset);
|
||||
}
|
||||
});
|
||||
|
||||
setMovedObjects([]);
|
||||
}
|
||||
setKeyEvent("");
|
||||
@@ -117,10 +113,12 @@ function MoveControls3D({
|
||||
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0)
|
||||
return;
|
||||
|
||||
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||
setKeyEvent(keyCombination);
|
||||
} else {
|
||||
setKeyEvent("");
|
||||
if (keyCombination !== keyEvent) {
|
||||
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||
setKeyEvent(keyCombination);
|
||||
} else {
|
||||
setKeyEvent("");
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCombination === "G") {
|
||||
@@ -131,14 +129,8 @@ function MoveControls3D({
|
||||
|
||||
if (keyCombination === "ESCAPE") {
|
||||
event.preventDefault();
|
||||
|
||||
resetToInitialPositions();
|
||||
clearSelection();
|
||||
movedObjects.forEach((asset: any) => {
|
||||
if (AssetGroup.current) {
|
||||
AssetGroup.current.attach(asset);
|
||||
}
|
||||
});
|
||||
|
||||
setMovedObjects([]);
|
||||
}
|
||||
};
|
||||
@@ -158,118 +150,166 @@ function MoveControls3D({
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
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(() => {
|
||||
if (movedObjects.length > 0) {
|
||||
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 (!isMoving || movedObjects.length === 0) return;
|
||||
|
||||
if (intersectedFloor && selectedAssets.length === 1) {
|
||||
if (intersectedFloor.object.userData.floorUuid) {
|
||||
point = new THREE.Vector3(intersectedFloor.point.x, intersectedFloor.object.userData.floorDepth, intersectedFloor.point.z);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
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) {
|
||||
let targetX = point.x;
|
||||
let targetY = point.y;
|
||||
let targetZ = point.z;
|
||||
if (dragOffset) {
|
||||
const rawBasePosition = new THREE.Vector3().addVectors(intersectionPoint, dragOffset);
|
||||
|
||||
if (keyEvent === "Ctrl") {
|
||||
targetX = snapControls(targetX, "Ctrl");
|
||||
targetZ = snapControls(targetZ, "Ctrl");
|
||||
let moveDistance = keyEvent.includes("Shift") ? 0.05 : 1;
|
||||
|
||||
const initialBasePosition = initialPositions[movedObjects[0].uuid];
|
||||
const positionDifference = new THREE.Vector3().subVectors(rawBasePosition, initialBasePosition);
|
||||
|
||||
let adjustedDifference = positionDifference.multiplyScalar(moveDistance);
|
||||
|
||||
const baseNewPosition = new THREE.Vector3().addVectors(initialBasePosition, adjustedDifference);
|
||||
|
||||
if (keyEvent.includes("Ctrl")) {
|
||||
baseNewPosition.x = snapControls(baseNewPosition.x, keyEvent);
|
||||
baseNewPosition.z = snapControls(baseNewPosition.z, keyEvent);
|
||||
}
|
||||
|
||||
// else if (keyEvent === "Ctrl+Shift") {
|
||||
// targetX = snapControls(targetX, "Ctrl+Shift");
|
||||
// targetZ = snapControls(targetZ, "Ctrl+Shift");
|
||||
// } else if (keyEvent === "Shift") {
|
||||
// targetX = snapControls(targetX, "Shift");
|
||||
// targetZ = snapControls(targetZ, "Shift");
|
||||
// } else {
|
||||
// }
|
||||
movedObjects.forEach((movedAsset: THREE.Object3D) => {
|
||||
if (movedAsset.userData.modelUuid) {
|
||||
const initialPosition = initialPositions[movedAsset.userData.modelUuid];
|
||||
|
||||
if (initialPosition) {
|
||||
const relativeOffset = new THREE.Vector3().subVectors(
|
||||
initialPosition,
|
||||
initialPositions[movedObjects[0].uuid]
|
||||
);
|
||||
const model = scene.getObjectByProperty("uuid", movedAsset.userData.modelUuid);
|
||||
|
||||
const newPosition = new THREE.Vector3().addVectors(baseNewPosition, relativeOffset);
|
||||
const positionArray: [number, number, number] = [newPosition.x, newPosition.y, newPosition.z];
|
||||
|
||||
if (model) {
|
||||
model.position.set(...positionArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const position = new THREE.Vector3();
|
||||
|
||||
if (boundingBoxRef.current) {
|
||||
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 {
|
||||
const box = new THREE.Box3();
|
||||
movedObjects.forEach((obj: THREE.Object3D) =>
|
||||
box.expandByObject(obj)
|
||||
);
|
||||
movedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
|
||||
const center = new THREE.Vector3();
|
||||
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 = () => {
|
||||
setMovedObjects(selectedAssets);
|
||||
selectedAssets.forEach((asset: any) => {
|
||||
selectionGroup.current.attach(asset);
|
||||
const states: Record<string, { position: THREE.Vector3; rotation?: THREE.Euler; }> = {};
|
||||
const positions: Record<string, THREE.Vector3> = {};
|
||||
|
||||
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 = () => {
|
||||
if (movedObjects.length === 0) return;
|
||||
|
||||
movedObjects.forEach(async (obj: THREE.Object3D) => {
|
||||
if (obj && AssetGroup.current) {
|
||||
let worldPosition = new THREE.Vector3();
|
||||
obj.getWorldPosition(worldPosition);
|
||||
|
||||
if (worldPosition.y < 0) {
|
||||
worldPosition.y = 0;
|
||||
}
|
||||
|
||||
selectionGroup.current.remove(obj);
|
||||
obj.position.copy(worldPosition);
|
||||
movedObjects.forEach(async (movedAsset: THREE.Object3D) => {
|
||||
if (movedAsset) {
|
||||
const assetUuid = movedAsset.userData.modelUuid;
|
||||
const asset = getAssetById(assetUuid);
|
||||
const model = scene.getObjectByProperty("uuid", movedAsset.userData.modelUuid);
|
||||
if (!asset || !model) return;
|
||||
const position = new THREE.Vector3().copy(model.position);
|
||||
|
||||
const newFloorItem: Types.FloorItemType = {
|
||||
modelUuid: obj.userData.modelUuid,
|
||||
modelName: obj.userData.modelName,
|
||||
assetId: obj.userData.assetId,
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
modelUuid: movedAsset.userData.modelUuid,
|
||||
modelName: movedAsset.userData.modelName,
|
||||
assetId: movedAsset.userData.assetId,
|
||||
position: [position.x, position.y, position.z],
|
||||
rotation: { x: movedAsset.rotation.x, y: movedAsset.rotation.y, z: movedAsset.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
};
|
||||
|
||||
if (obj.userData.eventData) {
|
||||
const eventData = eventStore.getState().getEventByModelUuid(obj.userData.modelUuid);
|
||||
const productData = productStore.getState().getEventByModelUuid(selectedProduct.productUuid, obj.userData.modelUuid);
|
||||
if (movedAsset.userData.eventData) {
|
||||
const eventData = eventStore.getState().getEventByModelUuid(movedAsset.userData.modelUuid);
|
||||
const productData = productStore.getState().getEventByModelUuid(selectedProduct.productUuid, movedAsset.userData.modelUuid);
|
||||
|
||||
if (eventData) {
|
||||
eventStore.getState().updateEvent(obj.userData.modelUuid, {
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
eventStore.getState().updateEvent(movedAsset.userData.modelUuid, {
|
||||
position: [position.x, position.y, position.z],
|
||||
rotation: [movedAsset.rotation.x, movedAsset.rotation.y, movedAsset.rotation.z],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -278,10 +318,10 @@ function MoveControls3D({
|
||||
.getState()
|
||||
.updateEvent(
|
||||
selectedProduct.productUuid,
|
||||
obj.userData.modelUuid,
|
||||
movedAsset.userData.modelUuid,
|
||||
{
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
position: [position.x, position.y, position.z],
|
||||
rotation: [movedAsset.rotation.x, movedAsset.rotation.y, movedAsset.rotation.z],
|
||||
}
|
||||
);
|
||||
|
||||
@@ -298,33 +338,18 @@ function MoveControls3D({
|
||||
}
|
||||
}
|
||||
|
||||
updateAsset(obj.userData.modelUuid, {
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
updateAsset(movedAsset.userData.modelUuid, {
|
||||
position: asset.position,
|
||||
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 = {
|
||||
organization,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
modelName: newFloorItem.modelName,
|
||||
assetId: newFloorItem.assetId,
|
||||
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,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
@@ -333,22 +358,22 @@ function MoveControls3D({
|
||||
userId
|
||||
};
|
||||
|
||||
// console.log('data: ', data);
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
//REST
|
||||
|
||||
AssetGroup.current.add(obj);
|
||||
// await setAssetsApi(data);
|
||||
|
||||
//SOCKET
|
||||
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
}
|
||||
});
|
||||
|
||||
echo.success("Object moved!");
|
||||
|
||||
setIsMoving(false);
|
||||
clearSelection();
|
||||
};
|
||||
|
||||
const clearSelection = () => {
|
||||
selectionGroup.current.children = [];
|
||||
selectionGroup.current.position.set(0, 0, 0);
|
||||
selectionGroup.current.rotation.set(0, 0, 0);
|
||||
setpastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setMovedObjects([]);
|
||||
@@ -365,4 +390,4 @@ function MoveControls3D({
|
||||
);
|
||||
}
|
||||
|
||||
export default MoveControls3D;
|
||||
export default MoveControls3D;
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
@@ -20,11 +20,9 @@ function RotateControls3D({
|
||||
pastedObjects,
|
||||
setpastedObjects,
|
||||
duplicatedObjects,
|
||||
setDuplicatedObjects,
|
||||
selectionGroup
|
||||
setDuplicatedObjects
|
||||
}: any) {
|
||||
|
||||
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
|
||||
const { camera, gl, scene, pointer, raycaster } = useThree();
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
|
||||
const { toggleView } = useToggleView();
|
||||
@@ -36,26 +34,33 @@ function RotateControls3D({
|
||||
const { projectId } = useParams();
|
||||
const { assetStore, eventStore, productStore } = useSceneContext();
|
||||
const { updateAsset } = assetStore();
|
||||
const AssetGroup = useRef<THREE.Group | undefined>(undefined);
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
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,
|
||||
productUuid: string,
|
||||
projectId: string,
|
||||
eventData: EventsSchema
|
||||
) => {
|
||||
upsertProductOrEventApi({
|
||||
productName: productName,
|
||||
productUuid: productUuid,
|
||||
projectId: projectId,
|
||||
productName,
|
||||
productUuid,
|
||||
projectId,
|
||||
eventDatas: eventData,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
})
|
||||
}
|
||||
|
||||
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
|
||||
});
|
||||
}, [selectedVersion]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!camera || !scene || toggleView) return;
|
||||
@@ -63,45 +68,37 @@ function RotateControls3D({
|
||||
const canvasElement = gl.domElement;
|
||||
canvasElement.tabIndex = 0;
|
||||
|
||||
const itemsGroup: any = scene.getObjectByName("Asset Group");
|
||||
AssetGroup.current = itemsGroup;
|
||||
|
||||
if (!AssetGroup.current) {
|
||||
console.error("Asset Group not found in the scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
let isMoving = false;
|
||||
|
||||
const onPointerDown = () => {
|
||||
isMoving = false;
|
||||
};
|
||||
let isPointerMoving = false;
|
||||
|
||||
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) => {
|
||||
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();
|
||||
placeRotatedAssets();
|
||||
}
|
||||
if (!isMoving && rotatedObjects.length > 0 && event.button === 2) {
|
||||
if (!isPointerMoving && rotatedObjects.length > 0 && event.button === 2) {
|
||||
event.preventDefault();
|
||||
|
||||
resetToInitialRotations();
|
||||
clearSelection();
|
||||
rotatedObjects.forEach((asset: any) => {
|
||||
if (AssetGroup.current) {
|
||||
AssetGroup.current.attach(asset);
|
||||
}
|
||||
});
|
||||
|
||||
setRotatedObjects([]);
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return;
|
||||
|
||||
if (event.key.toLowerCase() === "r") {
|
||||
if (selectedAssets.length > 0) {
|
||||
rotateAssets();
|
||||
@@ -109,15 +106,8 @@ function RotateControls3D({
|
||||
}
|
||||
if (event.key.toLowerCase() === "escape") {
|
||||
event.preventDefault();
|
||||
|
||||
resetToInitialRotations();
|
||||
clearSelection();
|
||||
rotatedObjects.forEach((asset: any) => {
|
||||
if (AssetGroup.current) {
|
||||
AssetGroup.current.attach(asset);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
setRotatedObjects([]);
|
||||
}
|
||||
};
|
||||
@@ -135,41 +125,87 @@ function RotateControls3D({
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
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(() => {
|
||||
if (rotatedObjects.length > 0) {
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
if (!isRotating || rotatedObjects.length === 0) return;
|
||||
|
||||
if (point && prevPointerPosition.current) {
|
||||
const box = new THREE.Box3();
|
||||
rotatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
|
||||
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);
|
||||
const intersectionPoint = new THREE.Vector3();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
|
||||
|
||||
if (mouseButtonsDown.current.left || mouseButtonsDown.current.right) {
|
||||
if (point) {
|
||||
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;
|
||||
|
||||
const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
|
||||
|
||||
rotatedObjects.forEach((obj: THREE.Object3D) => {
|
||||
if (obj.userData.modelUuid) {
|
||||
const relativePosition = new THREE.Vector3().subVectors(obj.position, center);
|
||||
relativePosition.applyMatrix4(rotationMatrix);
|
||||
obj.position.copy(center).add(relativePosition);
|
||||
obj.rotateOnWorldAxis(new THREE.Vector3(0, 1, 0), 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();
|
||||
selectedAssets.forEach((asset: any) => box.expandByObject(asset));
|
||||
selectedAssets.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
|
||||
const center = new THREE.Vector3();
|
||||
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();
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
@@ -179,56 +215,54 @@ function RotateControls3D({
|
||||
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
|
||||
}
|
||||
|
||||
selectedAssets.forEach((asset: any) => {
|
||||
selectionGroup.current.attach(asset);
|
||||
});
|
||||
|
||||
setRotatedObjects(selectedAssets);
|
||||
};
|
||||
setIsRotating(true);
|
||||
}, [selectedAssets, camera, pointer, raycaster, plane]);
|
||||
|
||||
const placeRotatedAssets = () => {
|
||||
const placeRotatedAssets = useCallback(() => {
|
||||
if (rotatedObjects.length === 0) return;
|
||||
|
||||
rotatedObjects.forEach(async (obj: THREE.Object3D) => {
|
||||
if (obj && AssetGroup.current) {
|
||||
const worldPosition = new THREE.Vector3();
|
||||
const worldQuaternion = new THREE.Quaternion();
|
||||
rotatedObjects.forEach((obj: THREE.Object3D) => {
|
||||
if (obj && obj.userData.modelUuid) {
|
||||
const rotationArray: [number, number, number] = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
|
||||
|
||||
obj.getWorldPosition(worldPosition);
|
||||
obj.getWorldQuaternion(worldQuaternion);
|
||||
|
||||
selectionGroup.current.remove(obj);
|
||||
|
||||
obj.position.copy(worldPosition);
|
||||
obj.quaternion.copy(worldQuaternion);
|
||||
const positionArray: [number, number, number] = [obj.position.x, obj.position.y, obj.position.z];
|
||||
|
||||
const newFloorItem: Types.FloorItemType = {
|
||||
modelUuid: obj.userData.modelUuid,
|
||||
modelName: obj.userData.modelName,
|
||||
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 },
|
||||
isLocked: false,
|
||||
isVisible: true
|
||||
isVisible: true,
|
||||
};
|
||||
|
||||
if (obj.userData.eventData) {
|
||||
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) {
|
||||
eventStore.getState().updateEvent(obj.userData.modelUuid, {
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
})
|
||||
position: positionArray,
|
||||
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(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
@@ -242,25 +276,10 @@ function RotateControls3D({
|
||||
}
|
||||
|
||||
updateAsset(obj.userData.modelUuid, {
|
||||
position: [worldPosition.x, worldPosition.y, worldPosition.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
position: positionArray,
|
||||
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 = {
|
||||
organization,
|
||||
modelUuid: newFloorItem.modelUuid,
|
||||
@@ -276,29 +295,29 @@ function RotateControls3D({
|
||||
userId
|
||||
};
|
||||
|
||||
// console.log('data: ', data);
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
//REST
|
||||
|
||||
AssetGroup.current.add(obj);
|
||||
// await setAssetsApi(data);
|
||||
|
||||
//SOCKET
|
||||
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
}
|
||||
});
|
||||
echo.success("Object rotated!");
|
||||
|
||||
setIsRotating(false);
|
||||
clearSelection();
|
||||
}
|
||||
}, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId]);
|
||||
|
||||
const clearSelection = () => {
|
||||
selectionGroup.current.children = [];
|
||||
selectionGroup.current.position.set(0, 0, 0);
|
||||
selectionGroup.current.rotation.set(0, 0, 0);
|
||||
setpastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setMovedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
}
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default RotateControls3D
|
||||
export default RotateControls3D;
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import * as THREE from "three";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { SelectionHelper } from "../selectionHelper";
|
||||
import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
|
||||
import useModuleStore from "../../../../../store/useModuleStore";
|
||||
import { useParams } from "react-router-dom";
|
||||
@@ -13,7 +12,6 @@ import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import { useProductContext } from "../../../../simulation/products/productContext";
|
||||
import { useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../../store/builder/store";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import BoundingBox from "./boundingBoxHelper3D";
|
||||
import DuplicationControls3D from "./duplicationControls3D";
|
||||
import CopyPasteControls3D from "./copyPasteControls3D";
|
||||
import MoveControls3D from "./moveControls3D";
|
||||
@@ -23,7 +21,6 @@ import RotateControls3D from "./rotateControls3D";
|
||||
|
||||
const SelectionControls3D: React.FC = () => {
|
||||
const { camera, controls, gl, scene, raycaster, pointer } = useThree();
|
||||
const selectionGroup = useRef() as Types.RefGroup;
|
||||
const { toggleView } = useToggleView();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const [movedObjects, setMovedObjects] = useState<THREE.Object3D[]>([]);
|
||||
@@ -229,12 +226,6 @@ const SelectionControls3D: React.FC = () => {
|
||||
}
|
||||
}, [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(() => {
|
||||
selectionBox.endPoint.set(pointer.x, pointer.y, 0);
|
||||
if (controls) (controls as any).enabled = true;
|
||||
@@ -267,9 +258,6 @@ const SelectionControls3D: React.FC = () => {
|
||||
}, [selectionBox, pointer, controls, selectedAssets, setSelectedAssets]);
|
||||
|
||||
const clearSelection = () => {
|
||||
selectionGroup.current.children = [];
|
||||
selectionGroup.current.position.set(0, 0, 0);
|
||||
selectionGroup.current.rotation.set(0, 0, 0);
|
||||
setpastedObjects([]);
|
||||
setDuplicatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
@@ -345,19 +333,13 @@ const SelectionControls3D: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<group name="SelectionGroup">
|
||||
<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} boundingBoxRef={boundingBoxRef} />
|
||||
|
||||
<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} selectionGroup={selectionGroup} setDuplicatedObjects={setDuplicatedObjects} 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} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../../../functions/getUserData";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
import { useSocketStore } from "../../../../../store/builder/store";
|
||||
|
||||
// import { upsertWallApi } from "../../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi";
|
||||
|
||||
// import { upsertZoneApi } from "../../../../../services/factoryBuilder/zone/upsertZoneApi";
|
||||
// import { deleteZoneApi } from "../../../../../services/factoryBuilder/zone/deleteZoneApi";
|
||||
|
||||
// import { upsertFloorApi } from "../../../../../services/factoryBuilder/floor/upsertFloorApi";
|
||||
// import { deleteFloorApi } from "../../../../../services/factoryBuilder/floor/deleteFloorApi";
|
||||
|
||||
// import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi";
|
||||
// import { deleteAisleApi } from "../../../../../services/factoryBuilder/aisle/deleteAisleApi";
|
||||
|
||||
function useRedoHandler() {
|
||||
const { undoRedo2DStore, wallStore, floorStore, zoneStore, aisleStore } = useSceneContext();
|
||||
const { redo2D, peekRedo2D } = undoRedo2DStore();
|
||||
const { addWall, removeWall, updateWall } = wallStore();
|
||||
const { addFloor, removeFloor, updateFloor } = floorStore();
|
||||
const { addZone, removeZone, updateZone } = zoneStore();
|
||||
const { addAisle, removeAisle, updateAisle } = aisleStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectId } = useParams();
|
||||
const { socket } = useSocketStore();
|
||||
|
||||
const handleRedo = () => {
|
||||
const redoData = peekRedo2D();
|
||||
if (!redoData) return;
|
||||
|
||||
if (redoData.type === 'Draw') {
|
||||
const { actions } = redoData;
|
||||
|
||||
actions.forEach(action => {
|
||||
const { actionType } = action;
|
||||
|
||||
if ('point' in action) {
|
||||
const point = action.point;
|
||||
|
||||
if (actionType === 'Line-Create') {
|
||||
handleCreate(point);
|
||||
} else if (actionType === 'Line-Update') {
|
||||
handleUpdate(point);
|
||||
} else if (actionType === 'Line-Delete') {
|
||||
handleRemove(point);
|
||||
}
|
||||
|
||||
} else if ('points' in action) {
|
||||
const points = action.points;
|
||||
|
||||
if (actionType === 'Lines-Create') {
|
||||
points.forEach(handleCreate);
|
||||
} else if (actionType === 'Lines-Update') {
|
||||
points.forEach(handleUpdate);
|
||||
} else if (actionType === 'Lines-Delete') {
|
||||
points.forEach(handleRemove);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (redoData.type === 'UI') {
|
||||
// Handle UI actions if needed
|
||||
}
|
||||
|
||||
redo2D();
|
||||
};
|
||||
|
||||
const handleCreate = (point: UndoRedo2DDataTypeSchema) => {
|
||||
switch (point.type) {
|
||||
case 'Wall': createWallFromBackend(point.lineData); break;
|
||||
case 'Floor': createFloorFromBackend(point.lineData); break;
|
||||
case 'Zone': createZoneFromBackend(point.lineData); break;
|
||||
case 'Aisle': createAisleFromBackend(point.lineData); break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = (point: UndoRedo2DDataTypeSchema) => {
|
||||
switch (point.type) {
|
||||
case 'Wall': removeWallFromBackend(point.lineData.wallUuid); break;
|
||||
case 'Floor': removeFloorFromBackend(point.lineData.floorUuid); break;
|
||||
case 'Zone': removeZoneFromBackend(point.lineData.zoneUuid); break;
|
||||
case 'Aisle': removeAisleFromBackend(point.lineData.aisleUuid); break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdate = (point: UndoRedo2DDataTypeSchema) => {
|
||||
if (!point.newData) return;
|
||||
switch (point.type) {
|
||||
case 'Wall': updateWallFromBackend(point.newData.wallUuid, point.newData); break;
|
||||
case 'Floor': updateFloorFromBackend(point.newData.floorUuid, point.newData); break;
|
||||
case 'Zone': updateZoneFromBackend(point.newData.zoneUuid, point.newData); break;
|
||||
case 'Aisle': updateAisleFromBackend(point.newData.aisleUuid, point.newData); break;
|
||||
}
|
||||
};
|
||||
|
||||
const createWallFromBackend = (wallData: Wall) => {
|
||||
addWall(wallData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wallData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: wallData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeWallFromBackend = (wallUuid: string) => {
|
||||
removeWall(wallUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteWallApi(projectId, selectedVersion?.versionId || '', wallUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallUuid: wallUuid,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateWallFromBackend = (wallUuid: string, updatedData: Wall) => {
|
||||
updateWall(wallUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const createFloorFromBackend = (floorData: Floor) => {
|
||||
addFloor(floorData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floorData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: floorData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeFloorFromBackend = (floorUuid: string) => {
|
||||
removeFloor(floorUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteFloorApi(projectId, selectedVersion?.versionId || '', floorUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorUuid: floorUuid,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateFloorFromBackend = (floorUuid: string, updatedData: Floor) => {
|
||||
updateFloor(floorUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const createZoneFromBackend = (zoneData: Zone) => {
|
||||
addZone(zoneData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertZoneApi(projectId, selectedVersion?.versionId || '', zoneData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
zoneData: zoneData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:zone:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeZoneFromBackend = (zoneUuid: string) => {
|
||||
removeZone(zoneUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteZoneApi(projectId, selectedVersion?.versionId || '', zoneUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
zoneUuid,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:zone:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateZoneFromBackend = (zoneUuid: string, updatedData: Zone) => {
|
||||
updateZone(zoneUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
zoneData: updatedData,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:zone:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const createAisleFromBackend = (aisleData: Aisle) => {
|
||||
addAisle(aisleData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertAisleApi(projectId, selectedVersion?.versionId || '', aisleData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
aisleData,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:model-aisle:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeAisleFromBackend = (aisleUuid: string) => {
|
||||
removeAisle(aisleUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteAisleApi(projectId, selectedVersion?.versionId || '', aisleUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
aisleUuid,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:model-aisle:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateAisleFromBackend = (aisleUuid: string, updatedData: Aisle) => {
|
||||
updateAisle(aisleUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertAisleApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
aisleData: updatedData,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:model-aisle:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
return { handleRedo };
|
||||
}
|
||||
|
||||
export default useRedoHandler;
|
||||
@@ -0,0 +1,356 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../../../functions/getUserData";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
import { useSocketStore } from "../../../../../store/builder/store";
|
||||
|
||||
// import { upsertWallApi } from "../../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi";
|
||||
|
||||
// import { upsertZoneApi } from "../../../../../services/factoryBuilder/zone/upsertZoneApi";
|
||||
// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi";
|
||||
|
||||
// import { upsertFloorApi } from "../../../../../services/factoryBuilder/floor/upsertFloorApi";
|
||||
// import { deleteFloorApi } from "../../../../../services/factoryBuilder/floor/deleteFloorApi";
|
||||
|
||||
// import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi";
|
||||
// import { deleteAisleApi } from "../../../../../services/factoryBuilder/aisle/deleteAisleApi";
|
||||
|
||||
function useUndoHandler() {
|
||||
const { undoRedo2DStore, wallStore, floorStore, zoneStore, aisleStore } = useSceneContext();
|
||||
const { undo2D, peekUndo2D } = undoRedo2DStore();
|
||||
const { addWall, removeWall, updateWall } = wallStore();
|
||||
const { addFloor, removeFloor, updateFloor } = floorStore();
|
||||
const { addZone, removeZone, updateZone } = zoneStore();
|
||||
const { addAisle, removeAisle, updateAisle } = aisleStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectId } = useParams();
|
||||
const { socket } = useSocketStore();
|
||||
|
||||
const handleUndo = () => {
|
||||
const unDoData = peekUndo2D();
|
||||
if (!unDoData) return;
|
||||
|
||||
if (unDoData.type === 'Draw') {
|
||||
const { actions } = unDoData;
|
||||
|
||||
actions.forEach(action => {
|
||||
const { actionType } = action;
|
||||
|
||||
if ('point' in action) {
|
||||
const point = action.point;
|
||||
|
||||
if (actionType === 'Line-Create') {
|
||||
handleRemove(point);
|
||||
} else if (actionType === 'Line-Update') {
|
||||
handleUpdate(point);
|
||||
} else if (actionType === 'Line-Delete') {
|
||||
handleCreate(point);
|
||||
}
|
||||
|
||||
} else if ('points' in action) {
|
||||
const points = action.points;
|
||||
|
||||
if (actionType === 'Lines-Create') {
|
||||
points.forEach(handleRemove);
|
||||
} else if (actionType === 'Lines-Update') {
|
||||
points.forEach(handleUpdate);
|
||||
} else if (actionType === 'Lines-Delete') {
|
||||
points.forEach(handleCreate);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (unDoData.type === 'UI') {
|
||||
// Handle UI actions if needed
|
||||
}
|
||||
|
||||
undo2D();
|
||||
|
||||
};
|
||||
|
||||
const handleCreate = (point: UndoRedo2DDataTypeSchema) => {
|
||||
switch (point.type) {
|
||||
case 'Wall': createWallFromBackend(point.lineData); break;
|
||||
case 'Floor': createFloorFromBackend(point.lineData); break;
|
||||
case 'Zone': createZoneFromBackend(point.lineData); break;
|
||||
case 'Aisle': createAisleFromBackend(point.lineData); break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = (point: UndoRedo2DDataTypeSchema) => {
|
||||
switch (point.type) {
|
||||
case 'Wall': removeWallFromBackend(point.lineData.wallUuid); break;
|
||||
case 'Floor': removeFloorFromBackend(point.lineData.floorUuid); break;
|
||||
case 'Zone': removeZoneFromBackend(point.lineData.zoneUuid); break;
|
||||
case 'Aisle': removeAisleFromBackend(point.lineData.aisleUuid); break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdate = (point: UndoRedo2DDataTypeSchema) => {
|
||||
switch (point.type) {
|
||||
case 'Wall': updateWallFromBackend(point.lineData.wallUuid, point.lineData); break;
|
||||
case 'Floor': updateFloorFromBackend(point.lineData.floorUuid, point.lineData); break;
|
||||
case 'Zone': updateZoneFromBackend(point.lineData.zoneUuid, point.lineData); break;
|
||||
case 'Aisle': updateAisleFromBackend(point.lineData.aisleUuid, point.lineData); break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const createWallFromBackend = (wallData: Wall) => {
|
||||
addWall(wallData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wallData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: wallData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeWallFromBackend = (wallUuid: string) => {
|
||||
removeWall(wallUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteWallApi(projectId, selectedVersion?.versionId || '', wallUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallUuid: wallUuid,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateWallFromBackend = (wallUuid: string, updatedData: Wall) => {
|
||||
updateWall(wallUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const createFloorFromBackend = (floorData: Floor) => {
|
||||
addFloor(floorData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floorData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: floorData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeFloorFromBackend = (floorUuid: string) => {
|
||||
removeFloor(floorUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteFloorApi(projectId, selectedVersion?.versionId || '', floorUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorUuid: floorUuid,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateFloorFromBackend = (floorUuid: string, updatedData: Floor) => {
|
||||
updateFloor(floorUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const createZoneFromBackend = (zoneData: Zone) => {
|
||||
addZone(zoneData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertZoneApi(projectId, selectedVersion?.versionId || '', zoneData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
zoneData: zoneData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:zone:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeZoneFromBackend = (zoneUuid: string) => {
|
||||
removeZone(zoneUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteZoneApi(projectId, selectedVersion?.versionId || '', zoneUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
zoneUuid,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:zone:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateZoneFromBackend = (zoneUuid: string, updatedData: Zone) => {
|
||||
updateZone(zoneUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
zoneData: updatedData,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:zone:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const createAisleFromBackend = (aisleData: Aisle) => {
|
||||
addAisle(aisleData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertAisleApi(projectId, selectedVersion?.versionId || '', aisleData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
aisleData,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:model-aisle:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeAisleFromBackend = (aisleUuid: string) => {
|
||||
removeAisle(aisleUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteAisleApi(projectId, selectedVersion?.versionId || '', aisleUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
aisleUuid,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:model-aisle:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateAisleFromBackend = (aisleUuid: string, updatedData: Aisle) => {
|
||||
updateAisle(aisleUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertAisleApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
aisleData: updatedData,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:model-aisle:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
return { handleUndo };
|
||||
}
|
||||
|
||||
export default useUndoHandler;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useSceneContext } from '../../../sceneContext'
|
||||
import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
|
||||
import { useSocketStore, useToggleView } from '../../../../../store/builder/store';
|
||||
import { useVersionContext } from '../../../../builder/version/versionContext';
|
||||
|
||||
import useUndoHandler from '../handlers/useUndoHandler';
|
||||
import useRedoHandler from '../handlers/useRedoHandler';
|
||||
|
||||
function UndoRedo2DControls() {
|
||||
const { undoRedo2DStore } = useSceneContext();
|
||||
const { undoStack, redoStack } = undoRedo2DStore();
|
||||
const { toggleView } = useToggleView();
|
||||
const { handleUndo } = useUndoHandler();
|
||||
const { handleRedo } = useRedoHandler();
|
||||
const { socket } = useSocketStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
|
||||
useEffect(() => {
|
||||
console.log(undoStack, redoStack);
|
||||
}, [undoStack, redoStack]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
const keyCombination = detectModifierKeys(event);
|
||||
|
||||
if (keyCombination === 'Ctrl+Z') {
|
||||
handleUndo();
|
||||
}
|
||||
|
||||
if (keyCombination === 'Ctrl+Y') {
|
||||
handleRedo();
|
||||
}
|
||||
};
|
||||
|
||||
if (toggleView) {
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [toggleView, undoStack, redoStack, socket, selectedVersion]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default UndoRedo2DControls;
|
||||
@@ -9,21 +9,17 @@ import {
|
||||
useTileDistance,
|
||||
} from "../../../store/builder/store";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
const shadowWorker = new Worker(
|
||||
new URL(
|
||||
"../../../services/factoryBuilder/webWorkers/shadowWorker",
|
||||
import.meta.url
|
||||
)
|
||||
);
|
||||
|
||||
const shadowWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/shadowWorker", import.meta.url));
|
||||
|
||||
export default function Shadows() {
|
||||
const { shadows, setShadows } = useShadows();
|
||||
const { sunPosition, setSunPosition } = useSunPosition();
|
||||
const { shadows } = useShadows();
|
||||
const { sunPosition } = useSunPosition();
|
||||
const lightRef = useRef<THREE.DirectionalLight | null>(null);
|
||||
const targetRef = useRef<THREE.Object3D | null>(null);
|
||||
const { controls, gl } = useThree();
|
||||
const { elevation, setElevation } = useElevation();
|
||||
const { azimuth, setAzimuth } = useAzimuth();
|
||||
const { elevation } = useElevation();
|
||||
const { azimuth } = useAzimuth();
|
||||
const { planeValue } = useTileDistance();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, useContext, useMemo } from 'react';
|
||||
import { createContext, useContext, useMemo, useRef } from 'react';
|
||||
|
||||
import { createAssetStore, AssetStoreType } from '../../store/builder/useAssetStore';
|
||||
import { createWallAssetStore, WallAssetStoreType } from '../../store/builder/useWallAssetStore';
|
||||
@@ -7,6 +7,8 @@ import { createAisleStore, AisleStoreType } from '../../store/builder/useAisleSt
|
||||
import { createZoneStore, ZoneStoreType } from '../../store/builder/useZoneStore';
|
||||
import { createFloorStore, FloorStoreType } from '../../store/builder/useFloorStore';
|
||||
|
||||
import { createUndoRedo2DStore, UndoRedo2DStoreType } from '../../store/builder/useUndoRedo2DStore';
|
||||
|
||||
import { createEventStore, EventStoreType } from '../../store/simulation/useEventsStore';
|
||||
import { createProductStore, ProductStoreType } from '../../store/simulation/useProductStore';
|
||||
|
||||
@@ -27,6 +29,8 @@ type SceneContextValue = {
|
||||
zoneStore: ZoneStoreType,
|
||||
floorStore: FloorStoreType,
|
||||
|
||||
undoRedo2DStore: UndoRedo2DStoreType,
|
||||
|
||||
eventStore: EventStoreType,
|
||||
productStore: ProductStoreType,
|
||||
|
||||
@@ -38,6 +42,8 @@ type SceneContextValue = {
|
||||
storageUnitStore: StorageUnitStoreType;
|
||||
humanStore: HumanStoreType;
|
||||
|
||||
humanEventManagerRef: React.RefObject<HumanEventManagerState>;
|
||||
|
||||
clearStores: () => void;
|
||||
|
||||
layout: 'Main Layout' | 'Comparison Layout';
|
||||
@@ -60,6 +66,8 @@ export function SceneProvider({
|
||||
const zoneStore = useMemo(() => createZoneStore(), []);
|
||||
const floorStore = useMemo(() => createFloorStore(), []);
|
||||
|
||||
const undoRedo2DStore = useMemo(() => createUndoRedo2DStore(), []);
|
||||
|
||||
const eventStore = useMemo(() => createEventStore(), []);
|
||||
const productStore = useMemo(() => createProductStore(), []);
|
||||
|
||||
@@ -71,6 +79,8 @@ export function SceneProvider({
|
||||
const storageUnitStore = useMemo(() => createStorageUnitStore(), []);
|
||||
const humanStore = useMemo(() => createHumanStore(), []);
|
||||
|
||||
const humanEventManagerRef = useRef<HumanEventManagerState>({ humanStates: [] });
|
||||
|
||||
const clearStores = useMemo(() => () => {
|
||||
assetStore.getState().clearAssets();
|
||||
wallAssetStore.getState().clearWallAssets();
|
||||
@@ -78,6 +88,7 @@ export function SceneProvider({
|
||||
aisleStore.getState().clearAisles();
|
||||
zoneStore.getState().clearZones();
|
||||
floorStore.getState().clearFloors();
|
||||
undoRedo2DStore.getState().clearUndoRedo2D();
|
||||
eventStore.getState().clearEvents();
|
||||
productStore.getState().clearProducts();
|
||||
materialStore.getState().clearMaterials();
|
||||
@@ -87,7 +98,8 @@ export function SceneProvider({
|
||||
vehicleStore.getState().clearVehicles();
|
||||
storageUnitStore.getState().clearStorageUnits();
|
||||
humanStore.getState().clearHumans();
|
||||
}, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]);
|
||||
humanEventManagerRef.current.humanStates = [];
|
||||
}, [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, undoRedo2DStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore]);
|
||||
|
||||
const contextValue = useMemo(() => (
|
||||
{
|
||||
@@ -97,6 +109,7 @@ export function SceneProvider({
|
||||
aisleStore,
|
||||
zoneStore,
|
||||
floorStore,
|
||||
undoRedo2DStore,
|
||||
eventStore,
|
||||
productStore,
|
||||
materialStore,
|
||||
@@ -106,10 +119,11 @@ export function SceneProvider({
|
||||
vehicleStore,
|
||||
storageUnitStore,
|
||||
humanStore,
|
||||
humanEventManagerRef,
|
||||
clearStores,
|
||||
layout
|
||||
}
|
||||
), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]);
|
||||
), [assetStore, wallAssetStore, wallStore, aisleStore, zoneStore, floorStore, undoRedo2DStore, eventStore, productStore, materialStore, armBotStore, machineStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, clearStores, layout]);
|
||||
|
||||
return (
|
||||
<SceneContext.Provider value={contextValue}>
|
||||
|
||||
@@ -37,7 +37,7 @@ export function useConveyorActions() {
|
||||
|
||||
switch (action.actionType) {
|
||||
case 'default':
|
||||
handleDefaultAction(action);
|
||||
handleDefaultAction(action, materialId);
|
||||
break;
|
||||
case 'spawn':
|
||||
handleSpawnAction(action);
|
||||
|
||||
@@ -8,7 +8,7 @@ export function useWorkerHandler() {
|
||||
const { getModelUuidByActionUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { incrementHumanLoad, incrementLoadCount, addCurrentMaterial, addCurrentAction } = humanStore();
|
||||
const { incrementHumanLoad, addCurrentMaterial, addCurrentAction } = humanStore();
|
||||
|
||||
const workerLogStatus = (materialUuid: string, status: string) => {
|
||||
echo.info(`${materialUuid}, ${status}`);
|
||||
@@ -24,7 +24,6 @@ export function useWorkerHandler() {
|
||||
if (!modelUuid) return;
|
||||
|
||||
incrementHumanLoad(modelUuid, 1);
|
||||
incrementLoadCount(modelUuid, 1);
|
||||
addCurrentAction(modelUuid, action.actionUuid);
|
||||
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
|
||||
|
||||
|
||||
@@ -3,25 +3,30 @@ import { useFrame } from "@react-three/fiber";
|
||||
import { usePlayButtonStore, usePauseButtonStore, useResetButtonStore, useAnimationPlaySpeed } from "../../../../../store/usePlayButtonStore";
|
||||
import { useSceneContext } from "../../../../scene/sceneContext";
|
||||
import { useProductContext } from "../../../products/productContext";
|
||||
import { useHumanEventManager } from "../../../human/eventManager/useHumanEventManager";
|
||||
|
||||
export function useRetrieveHandler() {
|
||||
const { materialStore, armBotStore, vehicleStore, storageUnitStore, productStore, humanStore, assetStore } = useSceneContext();
|
||||
const { materialStore, armBotStore, machineStore, vehicleStore, storageUnitStore, productStore, humanStore, assetStore } = useSceneContext();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { addMaterial } = materialStore();
|
||||
const { getModelUuidByActionUuid, getPointUuidByActionUuid, getEventByModelUuid, getActionByUuid } = productStore();
|
||||
const { getStorageUnitById, getLastMaterial, updateCurrentLoad, removeLastMaterial } = storageUnitStore();
|
||||
const { getVehicleById, incrementVehicleLoad, addCurrentMaterial } = vehicleStore();
|
||||
const { getHumanById, incrementHumanLoad, incrementLoadCount, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore();
|
||||
const { getHumanById, incrementHumanLoad, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore();
|
||||
const { getAssetById, setCurrentAnimation } = assetStore();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { getArmBotById, addCurrentAction } = armBotStore();
|
||||
const { getMachineById } = machineStore();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
const { addHumanToMonitor } = useHumanEventManager();
|
||||
|
||||
const [activeRetrievals, setActiveRetrievals] = useState<Map<string, { action: StorageAction, isProcessing: boolean, lastCheckTime: number }>>(new Map());
|
||||
const retrievalTimeRef = useRef<Map<string, number>>(new Map());
|
||||
const retrievalCountRef = useRef<Map<string, number>>(new Map());
|
||||
const monitoredHumansRef = useRef<Set<string>>(new Set());
|
||||
|
||||
const [initialDelayComplete, setInitialDelayComplete] = useState(false);
|
||||
const delayTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
@@ -300,28 +305,120 @@ export function useRetrieveHandler() {
|
||||
const humanAsset = getAssetById(triggeredModel.modelUuid);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
if (human && !human.isScheduled && human.state === 'idle' && human.currentLoad < (action as HumanAction).loadCapacity) {
|
||||
if (humanAsset && humanAsset.animationState?.current === 'idle') {
|
||||
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
|
||||
} else if (humanAsset && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) {
|
||||
if (!action || action.actionType !== 'worker') return;
|
||||
|
||||
const currentCount = retrievalCountRef.current.get(actionUuid) ?? 0;
|
||||
|
||||
if (currentCount >= action.loadCount) {
|
||||
completedActions.push(actionUuid);
|
||||
hasChanges = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (human && !human.isScheduled && human.state === 'idle' && human.currentLoad < action.loadCapacity) {
|
||||
const triggeredModel = action.triggers[0]?.triggeredAsset?.triggeredModel?.modelUuid
|
||||
? getEventByModelUuid(selectedProduct.productUuid, action.triggers[0].triggeredAsset.triggeredModel.modelUuid)
|
||||
: null;
|
||||
if (triggeredModel?.type === 'vehicle') {
|
||||
const model = getVehicleById(triggeredModel.modelUuid);
|
||||
if (model && !model.isActive && model.state === 'idle' && model.isPicking && model.currentLoad < model.point.action.loadCapacity) {
|
||||
if (humanAsset?.animationState?.current === 'idle') {
|
||||
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
|
||||
} else if (humanAsset?.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) {
|
||||
const lastMaterial = getLastMaterial(storageUnit.modelUuid);
|
||||
if (lastMaterial) {
|
||||
const material = createNewMaterial(
|
||||
lastMaterial.materialId,
|
||||
lastMaterial.materialType,
|
||||
storageUnit.point.action
|
||||
);
|
||||
if (material) {
|
||||
removeLastMaterial(storageUnit.modelUuid);
|
||||
updateCurrentLoad(storageUnit.modelUuid, -1);
|
||||
incrementHumanLoad(human.modelUuid, 1);
|
||||
addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId);
|
||||
retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`);
|
||||
|
||||
retrievalCountRef.current.set(actionUuid, currentCount + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (triggeredModel?.type === 'roboticArm') {
|
||||
const armBot = getArmBotById(triggeredModel.modelUuid);
|
||||
if (armBot && !armBot.isActive && armBot.state === 'idle' && !armBot.currentAction) {
|
||||
if (humanAsset?.animationState?.current === 'idle') {
|
||||
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
|
||||
} else if (humanAsset?.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) {
|
||||
const lastMaterial = getLastMaterial(storageUnit.modelUuid);
|
||||
if (lastMaterial) {
|
||||
const material = createNewMaterial(
|
||||
lastMaterial.materialId,
|
||||
lastMaterial.materialType,
|
||||
storageUnit.point.action
|
||||
);
|
||||
if (material) {
|
||||
removeLastMaterial(storageUnit.modelUuid);
|
||||
updateCurrentLoad(storageUnit.modelUuid, -1);
|
||||
incrementHumanLoad(human.modelUuid, 1);
|
||||
addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId);
|
||||
retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`);
|
||||
|
||||
retrievalCountRef.current.set(actionUuid, currentCount + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (triggeredModel?.type === 'machine') {
|
||||
const machine = getMachineById(triggeredModel.modelUuid);
|
||||
if (machine && !machine.isActive && machine.state === 'idle' && !machine.currentAction) {
|
||||
if (!monitoredHumansRef.current.has(human.modelUuid)) {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
if (humanAsset?.animationState?.current === 'idle') {
|
||||
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
|
||||
}
|
||||
}, action.actionUuid);
|
||||
}
|
||||
monitoredHumansRef.current.add(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current === 'pickup' && humanAsset.animationState.isCompleted) {
|
||||
const lastMaterial = getLastMaterial(storageUnit.modelUuid);
|
||||
if (lastMaterial) {
|
||||
const material = createNewMaterial(
|
||||
lastMaterial.materialId,
|
||||
lastMaterial.materialType,
|
||||
storageUnit.point.action
|
||||
);
|
||||
if (material) {
|
||||
removeLastMaterial(storageUnit.modelUuid);
|
||||
updateCurrentLoad(storageUnit.modelUuid, -1);
|
||||
incrementHumanLoad(human.modelUuid, 1);
|
||||
addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId);
|
||||
retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`);
|
||||
retrievalCountRef.current.set(actionUuid, currentCount + 1);
|
||||
}
|
||||
}
|
||||
monitoredHumansRef.current.delete(human.modelUuid);
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (triggeredModel?.type === 'storageUnit') {
|
||||
const lastMaterial = getLastMaterial(storageUnit.modelUuid);
|
||||
if (lastMaterial) {
|
||||
if (action && human.currentLoad < (action as HumanAction).loadCapacity) {
|
||||
const material = createNewMaterial(
|
||||
lastMaterial.materialId,
|
||||
lastMaterial.materialType,
|
||||
storageUnit.point.action
|
||||
);
|
||||
if (material) {
|
||||
removeLastMaterial(storageUnit.modelUuid);
|
||||
updateCurrentLoad(storageUnit.modelUuid, -1);
|
||||
incrementHumanLoad(human.modelUuid, 1);
|
||||
incrementLoadCount(human.modelUuid, 1);
|
||||
addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId);
|
||||
retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`);
|
||||
}
|
||||
if (human.currentLoad + 1 < (action as HumanAction).loadCapacity) {
|
||||
}
|
||||
const material = createNewMaterial(
|
||||
lastMaterial.materialId,
|
||||
lastMaterial.materialType,
|
||||
storageUnit.point.action
|
||||
);
|
||||
if (material) {
|
||||
removeLastMaterial(storageUnit.modelUuid);
|
||||
updateCurrentLoad(storageUnit.modelUuid, -1);
|
||||
incrementHumanLoad(human.modelUuid, 1);
|
||||
addCurrentMaterialToHuman(human.modelUuid, material.materialType, material.materialId);
|
||||
retrieveLogStatus(material.materialName, `is picked by ${human.modelName}`);
|
||||
|
||||
retrievalCountRef.current.set(actionUuid, currentCount + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,11 +450,12 @@ export function useRetrieveHandler() {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isReset) {
|
||||
if (isReset || !isPlaying) {
|
||||
setActiveRetrievals(new Map());
|
||||
retrievalCountRef.current.clear();
|
||||
setInitialDelayComplete(false);
|
||||
}
|
||||
}, [isReset]);
|
||||
}, [isReset, isPlaying]);
|
||||
|
||||
return {
|
||||
handleRetrieve,
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useFrame } from '@react-three/fiber'
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
// import { findConveyorSubsequence } from '../../../simulator/functions/getConveyorSequencesInProduct';
|
||||
|
||||
function ConveyorInstance({ conveyor }: { readonly conveyor: ConveyorStatus }) {
|
||||
|
||||
const { materialStore, conveyorStore, productStore } = useSceneContext();
|
||||
const { getProductById } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { getMaterialsByCurrentModelUuid, materials } = materialStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
const { getMaterialsByCurrentModelUuid } = materialStore();
|
||||
const { setConveyorPaused } = conveyorStore();
|
||||
|
||||
useEffect(() => {
|
||||
useFrame(() => {
|
||||
const product = getProductById(selectedProduct.productUuid);
|
||||
if (!product) return;
|
||||
|
||||
const conveyorMaterials = getMaterialsByCurrentModelUuid(conveyor.modelUuid);
|
||||
if (conveyorMaterials && conveyorMaterials?.length > 0) {
|
||||
if (conveyorMaterials && conveyorMaterials.length > 0) {
|
||||
|
||||
const hasPausedMaterials = conveyorMaterials.some(material => material.isPaused);
|
||||
|
||||
@@ -52,7 +52,7 @@ function ConveyorInstance({ conveyor }: { readonly conveyor: ConveyorStatus }) {
|
||||
// }
|
||||
// });
|
||||
|
||||
}, [materials, conveyor.modelUuid, getMaterialsByCurrentModelUuid, setConveyorPaused, isReset, selectedProduct.productUuid, getProductById]);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('conveyor: ', conveyor);
|
||||
@@ -61,7 +61,7 @@ function ConveyorInstance({ conveyor }: { readonly conveyor: ConveyorStatus }) {
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
export default ConveyorInstance
|
||||
@@ -1,170 +1,142 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useFrame } from '@react-three/fiber';
|
||||
import { usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../store/usePlayButtonStore';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../products/productContext';
|
||||
|
||||
export function useHumanEventManager() {
|
||||
const { humanStore, productStore, assetStore } = useSceneContext();
|
||||
const { getHumanById, clearLoadCount, setCurrentPhase } = humanStore();
|
||||
const { humanStore, productStore, assetStore, humanEventManagerRef } = useSceneContext();
|
||||
const { getHumanById, setCurrentPhase, removeCurrentAction } = humanStore();
|
||||
const { getAssetById } = assetStore();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
|
||||
const callbacksRef = useRef<Map<string, (() => void)[]>>(new Map());
|
||||
const actionQueueRef = useRef<Map<string, { actionType: "worker" | "assembly", actionUuid: string }[]>>(new Map());
|
||||
const isCooldownRef = useRef<Map<string, boolean>>(new Map());
|
||||
const isMonitoringRef = useRef(false);
|
||||
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { isReset } = useResetButtonStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (isReset) {
|
||||
callbacksRef.current.clear();
|
||||
actionQueueRef.current.clear();
|
||||
isCooldownRef.current.clear();
|
||||
isMonitoringRef.current = false;
|
||||
if ((isReset || !isPlaying) && humanEventManagerRef.current) {
|
||||
humanEventManagerRef.current.humanStates = [];
|
||||
}
|
||||
}, [isReset]);
|
||||
}, [isReset, isPlaying]);
|
||||
|
||||
const addHumanToMonitor = (humanId: string, callback: () => void, actionUuid: string) => {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, actionUuid || '') as HumanAction | undefined;
|
||||
const human = getHumanById(humanId);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, actionUuid);
|
||||
if (!human || !action || (action.actionType !== 'assembly' && action.actionType !== 'worker') || !humanEventManagerRef.current) return;
|
||||
|
||||
if (!action) return;
|
||||
|
||||
const actionType = action.actionType;
|
||||
if (actionType !== "worker" && actionType !== "assembly") return;
|
||||
|
||||
if (!callbacksRef.current.has(humanId)) {
|
||||
callbacksRef.current.set(humanId, []);
|
||||
actionQueueRef.current.set(humanId, []);
|
||||
let state = humanEventManagerRef.current.humanStates.find(h => h.humanId === humanId);
|
||||
if (!state) {
|
||||
state = { humanId, actionQueue: [], isCooldown: false };
|
||||
humanEventManagerRef.current.humanStates.push(state);
|
||||
}
|
||||
|
||||
callbacksRef.current.get(humanId)!.push(callback);
|
||||
actionQueueRef.current.get(humanId)!.push({ actionType, actionUuid });
|
||||
const existingAction = state.actionQueue.find(a => a.actionUuid === actionUuid);
|
||||
if (existingAction) {
|
||||
const currentCount = existingAction.count ?? 0;
|
||||
if (existingAction.actionType === 'worker') {
|
||||
if (currentCount < existingAction.maxLoadCount) {
|
||||
existingAction.callback = callback;
|
||||
existingAction.isMonitored = true;
|
||||
existingAction.isCompleted = false;
|
||||
}
|
||||
} else if (existingAction.actionType === 'assembly') {
|
||||
if (currentCount < existingAction.maxAssemblyCount) {
|
||||
existingAction.callback = callback;
|
||||
existingAction.isMonitored = true;
|
||||
existingAction.isCompleted = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
isMonitoringRef.current = true;
|
||||
state.actionQueue.push({
|
||||
actionType: action.actionType,
|
||||
actionUuid,
|
||||
actionName: action.actionName,
|
||||
maxLoadCount: action.loadCount ?? 0,
|
||||
maxAssemblyCount: action.assemblyCount ?? 0,
|
||||
count: 0,
|
||||
isMonitored: true,
|
||||
isCompleted: false,
|
||||
callback
|
||||
});
|
||||
};
|
||||
|
||||
const removeHumanFromMonitor = (humanId: string) => {
|
||||
callbacksRef.current.delete(humanId);
|
||||
actionQueueRef.current.delete(humanId);
|
||||
isCooldownRef.current.delete(humanId);
|
||||
const removeHumanFromMonitor = (humanId: string, actionUuid: string) => {
|
||||
if (!humanEventManagerRef.current) return;
|
||||
const state = humanEventManagerRef.current.humanStates.find(h => h.humanId === humanId);
|
||||
if (!state) return;
|
||||
|
||||
if (callbacksRef.current.size === 0) {
|
||||
isMonitoringRef.current = false;
|
||||
const action = state.actionQueue.find(a => a.actionUuid === actionUuid);
|
||||
if (action) {
|
||||
action.callback = undefined;
|
||||
action.isMonitored = false;
|
||||
}
|
||||
};
|
||||
|
||||
useFrame(() => {
|
||||
if (!isMonitoringRef.current || !isPlaying || isPaused) return;
|
||||
if (!humanEventManagerRef.current || humanEventManagerRef.current.humanStates.length === 0 || !isPlaying || isPaused) return;
|
||||
|
||||
callbacksRef.current.forEach((queue, humanId) => {
|
||||
if (queue.length === 0 || isCooldownRef.current.get(humanId)) return;
|
||||
for (const humanState of humanEventManagerRef.current.humanStates) {
|
||||
if (humanState.isCooldown) continue;
|
||||
|
||||
const actionQueue = actionQueueRef.current.get(humanId);
|
||||
if (!actionQueue || actionQueue.length === 0) return;
|
||||
const { humanId, actionQueue } = humanState;
|
||||
if (!actionQueue || actionQueue.length === 0) continue;
|
||||
|
||||
const action = actionQueue.find(a => !a.isCompleted);
|
||||
if (!action || !action.isMonitored || !action.callback) continue;
|
||||
|
||||
const { actionType: expectedActionType, actionUuid } = actionQueue[0];
|
||||
const human = getHumanById(humanId);
|
||||
const humanAsset = getAssetById(humanId);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, actionUuid) as HumanAction | undefined;
|
||||
const currentAction = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '') as HumanAction | undefined;
|
||||
|
||||
if (!humanAsset || !human || !action || action.actionType !== expectedActionType) return;
|
||||
if (!human || !humanAsset || !currentAction) continue;
|
||||
if (human.isActive || human.state !== "idle" || humanAsset.animationState?.current !== 'idle') continue;
|
||||
|
||||
let conditionMet = false;
|
||||
|
||||
const currentAction = getActionByUuid(selectedProduct.productUuid, human.currentAction?.actionUuid || '') as HumanAction | undefined;
|
||||
|
||||
if (expectedActionType === "worker") {
|
||||
if (currentAction && currentAction.actionType === 'worker') {
|
||||
conditionMet = (
|
||||
!human.isActive &&
|
||||
human.state === "idle" &&
|
||||
humanAsset.animationState?.current === 'idle' &&
|
||||
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) {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
}
|
||||
} else {
|
||||
conditionMet = (
|
||||
!human.isActive &&
|
||||
human.state === "idle" &&
|
||||
humanAsset.animationState?.current === 'idle' &&
|
||||
human.currentLoad < action.loadCapacity
|
||||
);
|
||||
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
}
|
||||
if (currentAction.actionType === 'worker') {
|
||||
if (action.actionType === 'worker' && human.currentLoad < currentAction.loadCapacity) {
|
||||
conditionMet = true;
|
||||
} else if (action.actionType === 'assembly') {
|
||||
conditionMet = true;
|
||||
}
|
||||
|
||||
} else if (expectedActionType === "assembly") {
|
||||
if (currentAction && currentAction.actionType === 'worker') {
|
||||
conditionMet = (
|
||||
!human.isActive &&
|
||||
human.state === "idle" &&
|
||||
humanAsset.animationState?.current === 'idle' &&
|
||||
human.currentLoad < currentAction.loadCapacity
|
||||
);
|
||||
if (conditionMet && actionUuid !== human.currentAction?.actionUuid) {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
}
|
||||
} else {
|
||||
conditionMet = (
|
||||
!human.isActive &&
|
||||
human.state === "idle" &&
|
||||
humanAsset.animationState?.current === 'idle' &&
|
||||
human.currentLoad < action.loadCapacity
|
||||
)
|
||||
}
|
||||
if (conditionMet) {
|
||||
clearLoadCount(human.modelUuid);
|
||||
} else if (currentAction.actionType === 'assembly') {
|
||||
if (action.actionType === 'assembly') {
|
||||
conditionMet = true;
|
||||
} else if (action.actionType === 'worker' && human.currentLoad < currentAction.loadCapacity) {
|
||||
conditionMet = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (conditionMet) {
|
||||
const callback = queue.shift();
|
||||
actionQueue.shift();
|
||||
|
||||
if (callback) callback();
|
||||
|
||||
if (queue.length === 0) {
|
||||
removeHumanFromMonitor(humanId);
|
||||
} else {
|
||||
isCooldownRef.current.set(humanId, true);
|
||||
setTimeout(() => {
|
||||
isCooldownRef.current.set(humanId, false);
|
||||
}, 1000);
|
||||
if (action.actionUuid !== human.currentAction?.actionUuid) {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
removeCurrentAction(human.modelUuid);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
callbacksRef.current.clear();
|
||||
actionQueueRef.current.clear();
|
||||
isCooldownRef.current.clear();
|
||||
isMonitoringRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
action.callback();
|
||||
action.count = (action.count ?? 0) + 1;
|
||||
action.isMonitored = false;
|
||||
if ((action.actionType === 'worker' && action.count >= action.maxLoadCount) ||
|
||||
(action.actionType === 'assembly' && action.count >= action.maxAssemblyCount)) {
|
||||
action.isCompleted = true;
|
||||
}
|
||||
humanState.isCooldown = true;
|
||||
setTimeout(() => {
|
||||
humanState.isCooldown = false;
|
||||
}, 1000);
|
||||
|
||||
removeHumanFromMonitor(human.modelUuid, action.actionUuid);
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
|
||||
return {
|
||||
addHumanToMonitor,
|
||||
removeHumanFromMonitor,
|
||||
removeHumanFromMonitor
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import * as THREE from 'three';
|
||||
import { Line } from '@react-three/drei';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
|
||||
interface AssemblerAnimatorProps {
|
||||
path: [number, number, number][];
|
||||
handleCallBack: () => void;
|
||||
reset: () => void;
|
||||
human: HumanStatus;
|
||||
}
|
||||
|
||||
function AssemblerAnimator({ path, handleCallBack, human, reset }: Readonly<AssemblerAnimatorProps>) {
|
||||
const { humanStore, assetStore, productStore } = useSceneContext();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { getHumanById } = humanStore();
|
||||
const { setCurrentAnimation } = assetStore();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isReset, setReset } = useResetButtonStore();
|
||||
const progressRef = useRef<number>(0);
|
||||
const completedRef = useRef<boolean>(false);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.assemblyPoint?.rotation || [0, 0, 0]);
|
||||
const [restRotation, setRestingRotation] = useState<boolean>(true);
|
||||
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
|
||||
const { scene } = useThree();
|
||||
|
||||
useEffect(() => {
|
||||
if (!human.currentAction?.actionUuid) return;
|
||||
if (human.currentPhase === 'init-assembly' && path.length > 0) {
|
||||
setCurrentPath(path);
|
||||
setObjectRotation((action as HumanAction)?.assemblyPoint?.rotation ?? null);
|
||||
}
|
||||
}, [human.currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);
|
||||
|
||||
useEffect(() => {
|
||||
completedRef.current = false;
|
||||
}, [currentPath]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isReset || !isPlaying) {
|
||||
reset();
|
||||
setCurrentPath([]);
|
||||
completedRef.current = false;
|
||||
progressRef.current = 0;
|
||||
setReset(false);
|
||||
setRestingRotation(true);
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
const humanData = getHumanById(human.modelUuid);
|
||||
if (object && humanData) {
|
||||
object.position.set(humanData.position[0], humanData.position[1], humanData.position[2]);
|
||||
object.rotation.set(humanData.rotation[0], humanData.rotation[1], humanData.rotation[2]);
|
||||
}
|
||||
}
|
||||
}, [isReset, isPlaying]);
|
||||
|
||||
const lastTimeRef = useRef(performance.now());
|
||||
|
||||
useFrame(() => {
|
||||
const now = performance.now();
|
||||
const delta = (now - lastTimeRef.current) / 1000;
|
||||
lastTimeRef.current = now;
|
||||
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!object || currentPath.length < 2) return;
|
||||
if (isPaused || !isPlaying) return;
|
||||
|
||||
let totalDistance = 0;
|
||||
const distances = [];
|
||||
let accumulatedDistance = 0;
|
||||
let index = 0;
|
||||
const rotationSpeed = 1.5;
|
||||
|
||||
for (let i = 0; i < currentPath.length - 1; i++) {
|
||||
const start = new THREE.Vector3(...currentPath[i]);
|
||||
const end = new THREE.Vector3(...currentPath[i + 1]);
|
||||
const segmentDistance = start.distanceTo(end);
|
||||
distances.push(segmentDistance);
|
||||
totalDistance += segmentDistance;
|
||||
}
|
||||
|
||||
while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) {
|
||||
accumulatedDistance += distances[index];
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index < distances.length) {
|
||||
const start = new THREE.Vector3(...currentPath[index]);
|
||||
const end = new THREE.Vector3(...currentPath[index + 1]);
|
||||
const segmentDistance = distances[index];
|
||||
|
||||
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(
|
||||
new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0))
|
||||
);
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
targetQuaternion.multiply(y180);
|
||||
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
if (angle < 0.01) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * human.speed;
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
|
||||
const isAligned = angle < 0.01;
|
||||
|
||||
if (isAligned) {
|
||||
progressRef.current += delta * (speed * human.speed);
|
||||
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
|
||||
const position = start.clone().lerp(end, t);
|
||||
object.position.copy(position);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (progressRef.current >= totalDistance) {
|
||||
if (restRotation && objectRotation) {
|
||||
const targetEuler = new THREE.Euler(0, objectRotation[1], 0);
|
||||
const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
const targetQuaternion = baseQuaternion.multiply(y180);
|
||||
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
if (angle < 0.01) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
setRestingRotation(false);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * human.speed;
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (progressRef.current >= totalDistance) {
|
||||
setRestingRotation(true);
|
||||
progressRef.current = 0;
|
||||
setCurrentPath([]);
|
||||
handleCallBack();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{currentPath.length > 0 && (
|
||||
<group visible={false}>
|
||||
<Line points={currentPath} color="blue" lineWidth={3} />
|
||||
{currentPath.map((point, index) => (
|
||||
<mesh key={index} position={point}>
|
||||
<sphereGeometry args={[0.1, 16, 16]} />
|
||||
<meshStandardMaterial color="red" />
|
||||
</mesh>
|
||||
))}
|
||||
</group>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default AssemblerAnimator;
|
||||
@@ -50,7 +50,7 @@ const MaterialAnimator = ({ human }: { human: HumanStatus; }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasLoad && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup') && (
|
||||
{hasLoad && action && (action as HumanAction).actionType === 'worker' && human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup') && (
|
||||
<MaterialModel
|
||||
matRef={meshRef}
|
||||
materialId={human.currentMaterials[0].materialId || ''}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { RapierRigidBody } from '@react-three/rapier';
|
||||
import * as THREE from 'three';
|
||||
@@ -7,7 +7,7 @@ import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useRese
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
|
||||
interface HumanAnimatorProps {
|
||||
interface WorkerAnimatorProps {
|
||||
path: [number, number, number][];
|
||||
handleCallBack: () => void;
|
||||
reset: () => void;
|
||||
@@ -15,7 +15,7 @@ interface HumanAnimatorProps {
|
||||
human: HumanStatus;
|
||||
}
|
||||
|
||||
function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProcess }: Readonly<HumanAnimatorProps>) {
|
||||
function WorkerAnimator({ path, handleCallBack, human, reset, startUnloadingProcess }: Readonly<WorkerAnimatorProps>) {
|
||||
const { humanStore, assetStore, productStore } = useSceneContext();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
@@ -30,7 +30,7 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
const movingForward = useRef<boolean>(true);
|
||||
const completedRef = useRef<boolean>(false);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.pickUpPoint?.rotation || [0, 0, 0])
|
||||
const [objectRotation, setObjectRotation] = useState<[number, number, number] | null>((action as HumanAction)?.pickUpPoint?.rotation || [0, 0, 0]);
|
||||
const [restRotation, setRestingRotation] = useState<boolean>(true);
|
||||
const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]);
|
||||
const rigidBodyRef = useRef<RapierRigidBody>(null);
|
||||
@@ -41,15 +41,12 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
if (human.currentPhase === 'init-pickup' && path.length > 0) {
|
||||
setCurrentPath(path);
|
||||
setObjectRotation((action as HumanAction).pickUpPoint?.rotation ?? null)
|
||||
} else if (human.currentPhase === 'init-assembly' && path.length > 0) {
|
||||
setObjectRotation((action as HumanAction)?.assemblyPoint?.rotation ?? null)
|
||||
setCurrentPath(path);
|
||||
setObjectRotation((action as HumanAction).pickUpPoint?.rotation ?? null);
|
||||
} else if (human.currentPhase === 'pickup-drop' && path.length > 0) {
|
||||
setObjectRotation((action as HumanAction)?.dropPoint?.rotation ?? null)
|
||||
setObjectRotation((action as HumanAction)?.dropPoint?.rotation ?? null);
|
||||
setCurrentPath(path);
|
||||
} else if (human.currentPhase === 'drop-pickup' && path.length > 0) {
|
||||
setObjectRotation((action as HumanAction)?.pickUpPoint?.rotation ?? null)
|
||||
setObjectRotation((action as HumanAction)?.pickUpPoint?.rotation ?? null);
|
||||
setCurrentPath(path);
|
||||
}
|
||||
}, [human.currentPhase, path, objectRotation, selectedProduct, human.currentAction?.actionUuid]);
|
||||
@@ -74,7 +71,7 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
object.rotation.set(humanData.rotation[0], humanData.rotation[1], humanData.rotation[2]);
|
||||
}
|
||||
}
|
||||
}, [isReset, isPlaying])
|
||||
}, [isReset, isPlaying]);
|
||||
|
||||
const lastTimeRef = useRef(performance.now());
|
||||
|
||||
@@ -111,7 +108,9 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
const end = new THREE.Vector3(...currentPath[index + 1]);
|
||||
const segmentDistance = distances[index];
|
||||
|
||||
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0)));
|
||||
const targetQuaternion = new THREE.Quaternion().setFromRotationMatrix(
|
||||
new THREE.Matrix4().lookAt(start, end, new THREE.Vector3(0, 1, 0))
|
||||
);
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
targetQuaternion.multiply(y180);
|
||||
|
||||
@@ -120,13 +119,7 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * human.speed;
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
|
||||
if (angle < step) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
|
||||
const isAligned = angle < 0.01;
|
||||
@@ -136,13 +129,13 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
const t = (progressRef.current - accumulatedDistance) / segmentDistance;
|
||||
const position = start.clone().lerp(end, t);
|
||||
object.position.copy(position);
|
||||
if (human.currentMaterials.length > 0 && action?.actionType === 'worker' && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
}
|
||||
} else {
|
||||
if (human.currentMaterials.length > 0 && action?.actionType === 'worker' && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
if (human.currentMaterials.length > 0 && (human.currentPhase !== 'init-pickup' && human.currentPhase !== 'init-assembly' && human.currentPhase !== 'drop-pickup')) {
|
||||
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
@@ -153,7 +146,6 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
if (progressRef.current >= totalDistance) {
|
||||
if (restRotation && objectRotation) {
|
||||
const targetEuler = new THREE.Euler(0, objectRotation[1], 0);
|
||||
|
||||
const baseQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
||||
const y180 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
||||
const targetQuaternion = baseQuaternion.multiply(y180);
|
||||
@@ -164,21 +156,14 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
setRestingRotation(false);
|
||||
} else {
|
||||
const step = rotationSpeed * delta * speed * human.speed;
|
||||
const angle = object.quaternion.angleTo(targetQuaternion);
|
||||
|
||||
if (angle < step) {
|
||||
object.quaternion.copy(targetQuaternion);
|
||||
} else {
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
}
|
||||
|
||||
if (human.currentMaterials.length > 0) {
|
||||
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
|
||||
} else {
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
object.quaternion.rotateTowards(targetQuaternion, step);
|
||||
}
|
||||
|
||||
setCurrentAnimation(
|
||||
human.modelUuid,
|
||||
human.currentMaterials.length > 0 ? 'idle_with_box' : 'idle',
|
||||
true, true, true
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -198,7 +183,6 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
return (
|
||||
<>
|
||||
{currentPath.length > 0 && (
|
||||
// helper
|
||||
<group visible={false}>
|
||||
<Line points={currentPath} color="blue" lineWidth={3} />
|
||||
{currentPath.map((point, index) => (
|
||||
@@ -210,7 +194,7 @@ function HumanAnimator({ path, handleCallBack, human, reset, startUnloadingProce
|
||||
</group>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default HumanAnimator
|
||||
export default WorkerAnimator;
|
||||
@@ -0,0 +1,221 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { NavMeshQuery } from '@recast-navigation/core';
|
||||
import { useNavMesh } from '../../../../../../store/builder/store';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../../store/usePlayButtonStore';
|
||||
import { useTriggerHandler } from '../../../../triggers/triggerHandler/useTriggerHandler';
|
||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../../products/productContext';
|
||||
|
||||
import AssemblerAnimator from '../../animator/assemblerAnimator';
|
||||
|
||||
function AssemblerInstance({ human }: { human: HumanStatus }) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { scene } = useThree();
|
||||
const { assetStore, materialStore, humanStore, productStore } = useSceneContext();
|
||||
const { setMaterial } = materialStore();
|
||||
const { triggerPointActions } = useTriggerHandler();
|
||||
const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setHumanActive, setHumanState, decrementHumanLoad, removeLastMaterial, setCurrentPhase } = humanStore();
|
||||
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
const isPausedRef = useRef<boolean>(false);
|
||||
const isSpeedRef = useRef<number>(0);
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
const processStartTimeRef = useRef<number | null>(null);
|
||||
const processTimeRef = useRef<number>(0);
|
||||
const processAnimationIdRef = useRef<number | null>(null);
|
||||
const accumulatedPausedTimeRef = useRef<number>(0);
|
||||
const lastPauseTimeRef = useRef<number | null>(null);
|
||||
const hasLoggedHalfway = useRef(false);
|
||||
const hasLoggedCompleted = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
}, [isPaused]);
|
||||
|
||||
useEffect(() => {
|
||||
isSpeedRef.current = speed;
|
||||
}, [speed]);
|
||||
|
||||
const computePath = useCallback((start: [number, number, number], end: [number, number, number]) => {
|
||||
try {
|
||||
const navMeshQuery = new NavMeshQuery(navMesh);
|
||||
let startPoint = new THREE.Vector3(start[0], start[1], start[2]);
|
||||
let endPoint = new THREE.Vector3(end[0], end[1], end[2]);
|
||||
const { path: segmentPath } = navMeshQuery.computePath(startPoint, endPoint);
|
||||
if (
|
||||
segmentPath.length > 0 &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(endPoint.x) &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(endPoint.z)
|
||||
) {
|
||||
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
} else {
|
||||
console.log("There is no path here...Choose valid path")
|
||||
const { path: segmentPaths } = navMeshQuery.computePath(startPoint, startPoint);
|
||||
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
}
|
||||
} catch {
|
||||
console.error("Failed to compute path");
|
||||
return [];
|
||||
}
|
||||
}, [navMesh]);
|
||||
|
||||
function humanStatus(modelId: string, status: string) {
|
||||
// console.log(`${modelId} , ${status}`);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
resetAnimation(human.modelUuid);
|
||||
setPath([]);
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
processStartTimeRef.current = null;
|
||||
processTimeRef.current = 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (object && human) {
|
||||
object.position.set(human.position[0], human.position[1], human.position[2]);
|
||||
object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
if (!action || !(action as HumanAction).assemblyPoint || (action as HumanAction).actionType === 'worker') return;
|
||||
|
||||
if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
|
||||
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!humanMesh) return;
|
||||
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), (action as HumanAction)?.assemblyPoint?.position || [0, 0, 0]);
|
||||
setPath(toPickupPath);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'init-assembly');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'waiting') {
|
||||
if (human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current !== 'working_standing') {
|
||||
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, false);
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setCurrentPhase(human.modelUuid, 'assembling');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
|
||||
processStartTimeRef.current = performance.now();
|
||||
processTimeRef.current = (action as HumanAction).processTime || 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
|
||||
if (!processAnimationIdRef.current) {
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}
|
||||
}
|
||||
} else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
|
||||
if ((action as HumanAction).assemblyPoint && human.currentPhase === 'assembling') {
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
if (material) {
|
||||
triggerPointActions((action as HumanAction), material.materialId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
}, [human, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
||||
|
||||
const trackAssemblyProcess = useCallback(() => {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
const now = performance.now();
|
||||
|
||||
if (!processStartTimeRef.current || !(action as HumanAction).processTime || !action) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPausedRef.current) {
|
||||
if (!lastPauseTimeRef.current) {
|
||||
lastPauseTimeRef.current = now;
|
||||
}
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
return;
|
||||
} else if (lastPauseTimeRef.current) {
|
||||
accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
|
||||
lastPauseTimeRef.current = null;
|
||||
}
|
||||
|
||||
const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
|
||||
const totalProcessTimeMs = ((action as HumanAction).processTime || 1) * 1000;
|
||||
|
||||
if (elapsed >= totalProcessTimeMs / 2 && !hasLoggedHalfway.current) {
|
||||
hasLoggedHalfway.current = true;
|
||||
if (human.currentMaterials.length > 0) {
|
||||
setMaterial(human.currentMaterials[0].materialId, (action as HumanAction).swapMaterial || 'Default Material');
|
||||
}
|
||||
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
|
||||
}
|
||||
|
||||
if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
|
||||
hasLoggedCompleted.current = true;
|
||||
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, true);
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed assembly process.`);
|
||||
return;
|
||||
}
|
||||
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}, [human.modelUuid, human.currentMaterials]);
|
||||
|
||||
function handleCallBack() {
|
||||
if (human.currentPhase === 'init-assembly') {
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached assembly point, waiting for material');
|
||||
setPath([]);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<AssemblerAnimator
|
||||
path={path}
|
||||
handleCallBack={handleCallBack}
|
||||
human={human}
|
||||
reset={reset}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AssemblerInstance;
|
||||
@@ -0,0 +1,615 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { NavMeshQuery } from '@recast-navigation/core';
|
||||
import { useNavMesh } from '../../../../../../store/builder/store';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../../store/usePlayButtonStore';
|
||||
import { useTriggerHandler } from '../../../../triggers/triggerHandler/useTriggerHandler';
|
||||
import { useSceneContext } from '../../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../../products/productContext';
|
||||
|
||||
import WorkerAnimator from '../../animator/workerAnimator';
|
||||
|
||||
function WorkerInstance({ human }: { human: HumanStatus }) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { scene } = useThree();
|
||||
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
|
||||
const { removeMaterial, setEndTime, setIsVisible } = materialStore();
|
||||
const { getStorageUnitById } = storageUnitStore();
|
||||
const { getArmBotById } = armBotStore();
|
||||
const { getConveyorById } = conveyorStore();
|
||||
const { getVehicleById } = vehicleStore();
|
||||
const { getMachineById } = machineStore();
|
||||
const { triggerPointActions } = useTriggerHandler();
|
||||
const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
|
||||
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setHumanActive, setHumanState, clearCurrentMaterials, setHumanLoad, setHumanScheduled, decrementHumanLoad, removeLastMaterial, incrementIdleTime, incrementActiveTime, resetTime, setCurrentPhase } = humanStore();
|
||||
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
const pauseTimeRef = useRef<number | null>(null);
|
||||
const idleTimeRef = useRef<number>(0);
|
||||
const activeTimeRef = useRef<number>(0);
|
||||
const isPausedRef = useRef<boolean>(false);
|
||||
const isSpeedRef = useRef<number>(0);
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const previousTimeRef = useRef<number | null>(null);
|
||||
const animationFrameIdRef = useRef<number | null>(null);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
}, [isPaused]);
|
||||
|
||||
useEffect(() => {
|
||||
isSpeedRef.current = speed;
|
||||
}, [speed]);
|
||||
|
||||
const computePath = useCallback((start: [number, number, number], end: [number, number, number]) => {
|
||||
try {
|
||||
const navMeshQuery = new NavMeshQuery(navMesh);
|
||||
let startPoint = new THREE.Vector3(start[0], start[1], start[2]);
|
||||
let endPoint = new THREE.Vector3(end[0], end[1], end[2]);
|
||||
const { path: segmentPath } = navMeshQuery.computePath(startPoint, endPoint);
|
||||
if (
|
||||
segmentPath.length > 0 &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(endPoint.x) &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(endPoint.z)
|
||||
) {
|
||||
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
} else {
|
||||
console.log("There is no path here...Choose valid path")
|
||||
const { path: segmentPaths } = navMeshQuery.computePath(startPoint, startPoint);
|
||||
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
}
|
||||
} catch {
|
||||
console.error("Failed to compute path");
|
||||
return [];
|
||||
}
|
||||
}, [navMesh]);
|
||||
|
||||
function humanStatus(modelId: string, status: string) {
|
||||
// console.log(`${modelId} , ${status}`);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setHumanLoad(human.modelUuid, 0);
|
||||
resetAnimation(human.modelUuid);
|
||||
setPath([]);
|
||||
isPausedRef.current = false;
|
||||
pauseTimeRef.current = 0;
|
||||
resetTime(human.modelUuid)
|
||||
activeTimeRef.current = 0
|
||||
idleTimeRef.current = 0
|
||||
previousTimeRef.current = null
|
||||
if (animationFrameIdRef.current !== null) {
|
||||
cancelAnimationFrame(animationFrameIdRef.current)
|
||||
animationFrameIdRef.current = null
|
||||
}
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (object && human) {
|
||||
object.position.set(human.position[0], human.position[1], human.position[2]);
|
||||
object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
if (!action || action.actionType !== 'worker' || !action.pickUpPoint || !action.dropPoint) return;
|
||||
|
||||
if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
|
||||
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!humanMesh) return;
|
||||
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), action?.pickUpPoint?.position || [0, 0, 0]);
|
||||
|
||||
setPath(toPickupPath);
|
||||
setCurrentPhase(human.modelUuid, 'init-pickup');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from init, heading to pickup');
|
||||
return;
|
||||
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'picking') {
|
||||
if (humanAsset && human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && human.currentLoad > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
|
||||
if (action.pickUpPoint && action.dropPoint) {
|
||||
const toDrop = computePath(action.pickUpPoint.position || [0, 0, 0], action.dropPoint.position || [0, 0, 0]);
|
||||
setPath(toDrop);
|
||||
setCurrentPhase(human.modelUuid, 'pickup-drop');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
|
||||
}
|
||||
} else if (human.currentMaterials.length > 0 && human.currentLoad > 0 && humanAsset?.animationState?.current !== 'pickup') {
|
||||
if (human.currentMaterials[0]?.materialId) {
|
||||
setIsVisible(human.currentMaterials[0]?.materialId, false);
|
||||
}
|
||||
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
|
||||
}
|
||||
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'dropping' && human.currentLoad === 0) {
|
||||
if (action.pickUpPoint && action.dropPoint) {
|
||||
const dropToPickup = computePath(action.dropPoint.position || [0, 0, 0], action.pickUpPoint.position || [0, 0, 0]);
|
||||
setPath(dropToPickup);
|
||||
setCurrentPhase(human.modelUuid, 'drop-pickup');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
}, [human, human.currentAction, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
||||
|
||||
function handleCallBack() {
|
||||
if (human.currentPhase === 'init-pickup') {
|
||||
setCurrentPhase(human.modelUuid, 'picking');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached pickup point, waiting for material');
|
||||
setPath([]);
|
||||
} else if (human.currentPhase === 'pickup-drop') {
|
||||
setCurrentPhase(human.modelUuid, 'dropping');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
humanStatus(human.modelUuid, 'Reached drop point');
|
||||
setPath([]);
|
||||
} else if (human.currentPhase === 'drop-pickup') {
|
||||
setCurrentPhase(human.modelUuid, 'picking');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setPath([]);
|
||||
clearCurrentMaterials(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached pickup point again, cycle complete');
|
||||
}
|
||||
}
|
||||
|
||||
function startUnloadingProcess() {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
if ((action as HumanAction).triggers.length > 0) {
|
||||
const trigger = getTriggerByUuid(selectedProduct.productUuid, (action as HumanAction).triggers[0]?.triggerUuid);
|
||||
const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
|
||||
|
||||
if (trigger && model) {
|
||||
if (model.type === 'transfer') {
|
||||
if (action) {
|
||||
handleMaterialDropToConveyor(model);
|
||||
}
|
||||
} else if (model.type === 'machine') {
|
||||
if (action) {
|
||||
handleMaterialDropToMachine(model);
|
||||
}
|
||||
} else if (model.type === 'roboticArm') {
|
||||
if (action) {
|
||||
handleMaterialDropToArmBot(model);
|
||||
}
|
||||
} else if (model.type === 'storageUnit') {
|
||||
if (action) {
|
||||
handleMaterialDropToStorageUnit(model);
|
||||
}
|
||||
} else if (model.type === 'vehicle') {
|
||||
if (action) {
|
||||
handleMaterialDropToVehicle(model);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const droppedMaterial = human.currentLoad;
|
||||
handleMaterialDropByDefault(droppedMaterial);
|
||||
}
|
||||
} else {
|
||||
const droppedMaterial = human.currentLoad;
|
||||
handleMaterialDropByDefault(droppedMaterial);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(startUnloadingProcess);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (model && humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
if (model.point.action.actionType === 'store') {
|
||||
loopMaterialDropToStorage(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
model.point.action.storageCapacity,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToStorage(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
storageUnitId: string,
|
||||
storageMaxCapacity: number,
|
||||
action: HumanAction
|
||||
) {
|
||||
const storageUnit = getStorageUnitById(storageUnitId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!storageUnit || humanCurrentLoad <= 0 || storageUnit.currentLoad >= storageMaxCapacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextDrop = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToStorage(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
storageUnitId,
|
||||
storageMaxCapacity,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextDrop);
|
||||
}
|
||||
};
|
||||
waitForNextDrop();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
const conveyor = getConveyorById(model.modelUuid);
|
||||
if (conveyor) {
|
||||
loopMaterialDropToConveyor(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
conveyor.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToConveyor(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
conveyorId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const conveyor = getConveyorById(conveyorId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!conveyor || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextDrop = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToConveyor(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
conveyorId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextDrop);
|
||||
}
|
||||
};
|
||||
waitForNextDrop();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const armBot = getArmBotById(model.modelUuid);
|
||||
if (armBot && armBot.state === 'idle' && !armBot.isActive) {
|
||||
loopMaterialDropToArmBot(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToArmBot(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
armBotId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const armBot = getArmBotById(armBotId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!armBot || armBot.state !== 'idle' || armBot.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentArmBot = getArmBotById(armBotId);
|
||||
if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToArmBot(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
armBotId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToVehicle(model: VehicleEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const vehicle = getVehicleById(model.modelUuid);
|
||||
if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) {
|
||||
loopMaterialDropToVehicle(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToVehicle(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
vehicleId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const vehicle = getVehicleById(vehicleId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!vehicle || vehicle.state !== 'idle' || vehicle.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentVehicle = getVehicleById(vehicleId);
|
||||
if (currentVehicle && currentVehicle.state === 'idle' && !currentVehicle.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToVehicle(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
vehicleId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToMachine(model: MachineEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const machine = getMachineById(model.modelUuid);
|
||||
if (machine && machine.state === 'idle' && !machine.isActive) {
|
||||
loopMaterialDropToMachine(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToMachine(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
machineId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const machine = getMachineById(machineId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!machine || machine.state !== 'idle' || machine.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentMachine = getMachineById(machineId);
|
||||
if (currentMachine && currentMachine.state === 'idle' && !currentMachine.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToMachine(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
machineId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropByDefault(droppedMaterial: number) {
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
const remainingMaterials = droppedMaterial - 1;
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
|
||||
if (material) {
|
||||
setEndTime(material.materialId, performance.now());
|
||||
removeMaterial(material.materialId);
|
||||
}
|
||||
|
||||
if (remainingMaterials > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
requestAnimationFrame(() => handleMaterialDropByDefault(remainingMaterials));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<WorkerAnimator
|
||||
path={path}
|
||||
handleCallBack={handleCallBack}
|
||||
human={human}
|
||||
reset={reset}
|
||||
startUnloadingProcess={startUnloadingProcess}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkerInstance;
|
||||
@@ -1,36 +1,20 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { NavMeshQuery } from '@recast-navigation/core';
|
||||
import { useNavMesh } from '../../../../../store/builder/store';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
import { useProductContext } from '../../../products/productContext';
|
||||
|
||||
import HumanAnimator from '../animator/humanAnimator';
|
||||
import MaterialAnimator from '../animator/materialAnimator';
|
||||
import AssemblerInstance from './actions/assemberInstance';
|
||||
import WorkerInstance from './actions/workerInstance';
|
||||
|
||||
function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
const { scene } = useThree();
|
||||
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
|
||||
const { removeMaterial, setEndTime, setMaterial, setIsVisible } = materialStore();
|
||||
const { getStorageUnitById } = storageUnitStore();
|
||||
const { getArmBotById } = armBotStore();
|
||||
const { getConveyorById } = conveyorStore();
|
||||
const { getVehicleById } = vehicleStore();
|
||||
const { getMachineById } = machineStore();
|
||||
const { triggerPointActions } = useTriggerHandler();
|
||||
const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
|
||||
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
|
||||
const { humanStore, productStore } = useSceneContext();
|
||||
const { getActionByUuid } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setHumanActive, setHumanState, clearCurrentMaterials, setHumanLoad, setHumanScheduled, decrementHumanLoad, removeLastMaterial, incrementIdleTime, incrementActiveTime, resetTime, setCurrentPhase } = humanStore();
|
||||
const { incrementIdleTime, incrementActiveTime } = humanStore();
|
||||
|
||||
const [path, setPath] = useState<[number, number, number][]>([]);
|
||||
const pauseTimeRef = useRef<number | null>(null);
|
||||
const idleTimeRef = useRef<number>(0);
|
||||
const activeTimeRef = useRef<number>(0);
|
||||
const isPausedRef = useRef<boolean>(false);
|
||||
@@ -39,14 +23,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
const previousTimeRef = useRef<number | null>(null);
|
||||
const animationFrameIdRef = useRef<number | null>(null);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
const processStartTimeRef = useRef<number | null>(null);
|
||||
const processTimeRef = useRef<number>(0);
|
||||
const processAnimationIdRef = useRef<number | null>(null);
|
||||
const accumulatedPausedTimeRef = useRef<number>(0);
|
||||
const lastPauseTimeRef = useRef<number | null>(null);
|
||||
const hasLoggedHalfway = useRef(false);
|
||||
const hasLoggedCompleted = useRef(false);
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
@@ -56,259 +33,6 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
isSpeedRef.current = speed;
|
||||
}, [speed]);
|
||||
|
||||
const computePath = useCallback((start: [number, number, number], end: [number, number, number]) => {
|
||||
try {
|
||||
const navMeshQuery = new NavMeshQuery(navMesh);
|
||||
let startPoint = new THREE.Vector3(start[0], start[1], start[2]);
|
||||
let endPoint = new THREE.Vector3(end[0], end[1], end[2]);
|
||||
const { path: segmentPath } = navMeshQuery.computePath(startPoint, endPoint);
|
||||
if (
|
||||
segmentPath.length > 0 &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(endPoint.x) &&
|
||||
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(endPoint.z)
|
||||
) {
|
||||
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
} else {
|
||||
console.log("There is no path here...Choose valid path")
|
||||
const { path: segmentPaths } = navMeshQuery.computePath(startPoint, startPoint);
|
||||
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
||||
}
|
||||
} catch {
|
||||
console.error("Failed to compute path");
|
||||
return [];
|
||||
}
|
||||
}, [navMesh]);
|
||||
|
||||
function humanStatus(modelId: string, status: string) {
|
||||
// console.log(`${modelId} , ${status}`);
|
||||
}
|
||||
|
||||
function reset() {
|
||||
setCurrentPhase(human.modelUuid, 'init');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setHumanLoad(human.modelUuid, 0);
|
||||
resetAnimation(human.modelUuid);
|
||||
setPath([]);
|
||||
isPausedRef.current = false;
|
||||
pauseTimeRef.current = 0;
|
||||
resetTime(human.modelUuid)
|
||||
activeTimeRef.current = 0
|
||||
idleTimeRef.current = 0
|
||||
previousTimeRef.current = null
|
||||
if (animationFrameIdRef.current !== null) {
|
||||
cancelAnimationFrame(animationFrameIdRef.current)
|
||||
animationFrameIdRef.current = null
|
||||
}
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
processStartTimeRef.current = null;
|
||||
processTimeRef.current = 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (object && human) {
|
||||
object.position.set(human.position[0], human.position[1], human.position[2]);
|
||||
object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
if (!action || !(action as HumanAction).assemblyPoint || (action as HumanAction).actionType === 'worker') return;
|
||||
|
||||
if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
|
||||
|
||||
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!humanMesh) return;
|
||||
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), (action as HumanAction)?.assemblyPoint?.position || [0, 0, 0]);
|
||||
setPath(toPickupPath);
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'init-assembly');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'waiting') {
|
||||
|
||||
if (human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current !== 'working_standing') {
|
||||
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, false);
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setCurrentPhase(human.modelUuid, 'assembling');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
|
||||
processStartTimeRef.current = performance.now();
|
||||
processTimeRef.current = (action as HumanAction).processTime || 0;
|
||||
accumulatedPausedTimeRef.current = 0;
|
||||
lastPauseTimeRef.current = null;
|
||||
hasLoggedHalfway.current = false;
|
||||
hasLoggedCompleted.current = false;
|
||||
|
||||
if (!processAnimationIdRef.current) {
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}
|
||||
}
|
||||
} else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
|
||||
if ((action as HumanAction).assemblyPoint && human.currentPhase === 'assembling') {
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
||||
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
if (material) {
|
||||
triggerPointActions((action as HumanAction), material.materialId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [human, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
||||
|
||||
const trackAssemblyProcess = useCallback(() => {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
const now = performance.now();
|
||||
|
||||
if (!processStartTimeRef.current || !(action as HumanAction).processTime || !action) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPausedRef.current) {
|
||||
if (!lastPauseTimeRef.current) {
|
||||
lastPauseTimeRef.current = now;
|
||||
}
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
return;
|
||||
} else if (lastPauseTimeRef.current) {
|
||||
accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
|
||||
lastPauseTimeRef.current = null;
|
||||
}
|
||||
|
||||
const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
|
||||
const totalProcessTimeMs = ((action as HumanAction).processTime || 1) * 1000;
|
||||
|
||||
if (elapsed >= totalProcessTimeMs / 2 && !hasLoggedHalfway.current) {
|
||||
hasLoggedHalfway.current = true;
|
||||
if (human.currentMaterials.length > 0) {
|
||||
setMaterial(human.currentMaterials[0].materialId, (action as HumanAction).swapMaterial || 'Default Material');
|
||||
}
|
||||
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
|
||||
}
|
||||
|
||||
if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
|
||||
hasLoggedCompleted.current = true;
|
||||
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, true);
|
||||
if (processAnimationIdRef.current) {
|
||||
cancelAnimationFrame(processAnimationIdRef.current);
|
||||
processAnimationIdRef.current = null;
|
||||
}
|
||||
humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed assembly process.`);
|
||||
return;
|
||||
}
|
||||
|
||||
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
||||
}, [human.modelUuid, human.currentMaterials]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isPlaying) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
if (!action || action.actionType !== 'worker' || !action.pickUpPoint || !action.dropPoint) return;
|
||||
|
||||
if (!human.isActive && human.state === 'idle' && human.currentPhase === 'init') {
|
||||
|
||||
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
||||
if (!humanMesh) return;
|
||||
|
||||
const toPickupPath = computePath(humanMesh.position.toArray(), action?.pickUpPoint?.position || [0, 0, 0]);
|
||||
setPath(toPickupPath);
|
||||
setCurrentPhase(human.modelUuid, 'init-pickup');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from init, heading to pickup');
|
||||
return;
|
||||
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'picking') {
|
||||
if (humanAsset && human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
|
||||
if (action.pickUpPoint && action.dropPoint) {
|
||||
const toDrop = computePath(action.pickUpPoint.position || [0, 0, 0], action.dropPoint.position || [0, 0, 0]);
|
||||
setPath(toDrop);
|
||||
setCurrentPhase(human.modelUuid, 'pickup-drop');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
|
||||
}
|
||||
} else if (human.currentMaterials.length > 0 && humanAsset?.animationState?.current !== 'pickup') {
|
||||
if (human.currentMaterials[0]?.materialId) {
|
||||
setIsVisible(human.currentMaterials[0]?.materialId, false);
|
||||
}
|
||||
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
|
||||
}
|
||||
} else if (!human.isActive && human.state === 'idle' && human.currentPhase === 'dropping' && human.currentLoad === 0) {
|
||||
if (action.pickUpPoint && action.dropPoint) {
|
||||
const dropToPickup = computePath(action.dropPoint.position || [0, 0, 0], action.pickUpPoint.position || [0, 0, 0]);
|
||||
setPath(dropToPickup);
|
||||
setCurrentPhase(human.modelUuid, 'drop-pickup');
|
||||
setHumanState(human.modelUuid, 'running');
|
||||
setHumanActive(human.modelUuid, true);
|
||||
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [human, human.currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
||||
|
||||
function handleCallBack() {
|
||||
if (human.currentPhase === 'init-pickup') {
|
||||
setCurrentPhase(human.modelUuid, 'picking');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached pickup point, waiting for material');
|
||||
setPath([]);
|
||||
} if (human.currentPhase === 'init-assembly') {
|
||||
setCurrentPhase(human.modelUuid, 'waiting');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached assembly point, waiting for material');
|
||||
setPath([]);
|
||||
} else if (human.currentPhase === 'pickup-drop') {
|
||||
setCurrentPhase(human.modelUuid, 'dropping');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
humanStatus(human.modelUuid, 'Reached drop point');
|
||||
setPath([]);
|
||||
} else if (human.currentPhase === 'drop-pickup') {
|
||||
setCurrentPhase(human.modelUuid, 'picking');
|
||||
setHumanState(human.modelUuid, 'idle');
|
||||
setHumanActive(human.modelUuid, false);
|
||||
setHumanScheduled(human.modelUuid, false);
|
||||
setPath([]);
|
||||
clearCurrentMaterials(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
||||
humanStatus(human.modelUuid, 'Reached pickup point again, cycle complete');
|
||||
}
|
||||
}
|
||||
|
||||
function animate(currentTime: number) {
|
||||
if (previousTimeRef.current === null) {
|
||||
previousTimeRef.current = currentTime;
|
||||
@@ -353,438 +77,15 @@ function HumanInstance({ human }: { human: HumanStatus }) {
|
||||
};
|
||||
}, [human, isPlaying]);
|
||||
|
||||
function startUnloadingProcess() {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
if ((action as HumanAction).triggers.length > 0) {
|
||||
const trigger = getTriggerByUuid(selectedProduct.productUuid, (action as HumanAction).triggers[0]?.triggerUuid);
|
||||
const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
|
||||
|
||||
if (trigger && model) {
|
||||
if (model.type === 'transfer') {
|
||||
if (action) {
|
||||
handleMaterialDropToConveyor(model);
|
||||
}
|
||||
} else if (model.type === 'machine') {
|
||||
if (action) {
|
||||
handleMaterialDropToMachine(model);
|
||||
}
|
||||
} else if (model.type === 'roboticArm') {
|
||||
if (action) {
|
||||
handleMaterialDropToArmBot(model);
|
||||
}
|
||||
} else if (model.type === 'storageUnit') {
|
||||
if (action) {
|
||||
handleMaterialDropToStorageUnit(model);
|
||||
}
|
||||
} else if (model.type === 'vehicle') {
|
||||
if (action) {
|
||||
handleMaterialDropToVehicle(model);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const droppedMaterial = human.currentLoad;
|
||||
handleMaterialDropByDefault(droppedMaterial);
|
||||
}
|
||||
} else {
|
||||
const droppedMaterial = human.currentLoad;
|
||||
handleMaterialDropByDefault(droppedMaterial);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(startUnloadingProcess);
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (model && humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
if (model.point.action.actionType === 'store') {
|
||||
loopMaterialDropToStorage(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
model.point.action.storageCapacity,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToStorage(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
storageUnitId: string,
|
||||
storageMaxCapacity: number,
|
||||
action: HumanAction
|
||||
) {
|
||||
const storageUnit = getStorageUnitById(storageUnitId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!storageUnit || humanCurrentLoad <= 0 || storageUnit.currentLoad >= storageMaxCapacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextDrop = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToStorage(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
storageUnitId,
|
||||
storageMaxCapacity,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextDrop);
|
||||
}
|
||||
};
|
||||
waitForNextDrop();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
const conveyor = getConveyorById(model.modelUuid);
|
||||
if (conveyor) {
|
||||
loopMaterialDropToConveyor(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
conveyor.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToConveyor(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
conveyorId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const conveyor = getConveyorById(conveyorId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!conveyor || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextDrop = () => {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToConveyor(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
conveyorId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextDrop);
|
||||
}
|
||||
};
|
||||
waitForNextDrop();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const armBot = getArmBotById(model.modelUuid);
|
||||
if (armBot && armBot.state === 'idle' && !armBot.isActive) {
|
||||
loopMaterialDropToArmBot(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToArmBot(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
armBotId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const armBot = getArmBotById(armBotId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!armBot || armBot.state !== 'idle' || armBot.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentArmBot = getArmBotById(armBotId);
|
||||
if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToArmBot(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
armBotId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToVehicle(model: VehicleEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const vehicle = getVehicleById(model.modelUuid);
|
||||
if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) {
|
||||
loopMaterialDropToVehicle(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToVehicle(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
vehicleId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const vehicle = getVehicleById(vehicleId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!vehicle || vehicle.state !== 'idle' || vehicle.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentVehicle = getVehicleById(vehicleId);
|
||||
if (currentVehicle && currentVehicle.state === 'idle' && !currentVehicle.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToVehicle(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
vehicleId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropToMachine(model: MachineEventSchema) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
const checkAnimation = () => {
|
||||
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
||||
const machine = getMachineById(model.modelUuid);
|
||||
if (machine && machine.state === 'idle' && !machine.isActive) {
|
||||
loopMaterialDropToMachine(
|
||||
human.modelUuid,
|
||||
human.currentLoad,
|
||||
model.modelUuid,
|
||||
(action as HumanAction)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(checkAnimation);
|
||||
}
|
||||
};
|
||||
checkAnimation();
|
||||
}
|
||||
|
||||
function loopMaterialDropToMachine(
|
||||
humanId: string,
|
||||
humanCurrentLoad: number,
|
||||
machineId: string,
|
||||
action: HumanAction
|
||||
) {
|
||||
const machine = getMachineById(machineId);
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
|
||||
if (!machine || machine.state !== 'idle' || machine.isActive || humanCurrentLoad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
decrementHumanLoad(humanId, 1);
|
||||
humanCurrentLoad -= 1;
|
||||
|
||||
const material = removeLastMaterial(humanId);
|
||||
if (material) {
|
||||
triggerPointActions(action, material.materialId);
|
||||
}
|
||||
|
||||
if (humanCurrentLoad > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
const waitForNextTransfer = () => {
|
||||
const currentMachine = getMachineById(machineId);
|
||||
if (currentMachine && currentMachine.state === 'idle' && !currentMachine.isActive) {
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
loopMaterialDropToMachine(
|
||||
humanId,
|
||||
humanCurrentLoad,
|
||||
machineId,
|
||||
action
|
||||
);
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
} else {
|
||||
requestAnimationFrame(waitForNextTransfer);
|
||||
}
|
||||
};
|
||||
waitForNextTransfer();
|
||||
}
|
||||
}
|
||||
|
||||
function handleMaterialDropByDefault(droppedMaterial: number) {
|
||||
const humanAsset = getAssetById(human.modelUuid);
|
||||
if (humanAsset?.animationState?.current !== 'drop') {
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
}
|
||||
|
||||
if (humanAsset?.animationState?.isCompleted) {
|
||||
const remainingMaterials = droppedMaterial - 1;
|
||||
decrementHumanLoad(human.modelUuid, 1);
|
||||
const material = removeLastMaterial(human.modelUuid);
|
||||
|
||||
if (material) {
|
||||
setEndTime(material.materialId, performance.now());
|
||||
removeMaterial(material.materialId);
|
||||
}
|
||||
|
||||
if (remainingMaterials > 0) {
|
||||
resetAnimation(human.modelUuid);
|
||||
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
||||
|
||||
requestAnimationFrame(() => handleMaterialDropByDefault(remainingMaterials));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<HumanAnimator
|
||||
path={path}
|
||||
handleCallBack={handleCallBack}
|
||||
human={human}
|
||||
reset={reset}
|
||||
startUnloadingProcess={startUnloadingProcess}
|
||||
/>
|
||||
{action && action.actionType === 'worker' &&
|
||||
<WorkerInstance human={human} />
|
||||
}
|
||||
{action && action.actionType === 'assembly' &&
|
||||
<AssemblerInstance human={human} />
|
||||
}
|
||||
|
||||
<MaterialAnimator human={human} />
|
||||
</>
|
||||
|
||||
@@ -84,7 +84,6 @@ const MachineAnimator = ({ currentPhase, handleCallBack, processingTime, machine
|
||||
animationFrameId.current = null;
|
||||
handleCallBack();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ function MachineInstance({ machineDetail }: { readonly machineDetail: MachineSta
|
||||
const { triggerPointActions } = useTriggerHandler();
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
const { isPaused } = usePauseButtonStore();
|
||||
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused;
|
||||
}, [isPaused]);
|
||||
@@ -50,6 +51,7 @@ function MachineInstance({ machineDetail }: { readonly machineDetail: MachineSta
|
||||
function machineStatus(modelId: string, status: string) {
|
||||
// console.log(`${modelId} , ${status}`);
|
||||
}
|
||||
|
||||
function animate(currentTime: number) {
|
||||
if (previousTimeRef.current === null) {
|
||||
previousTimeRef.current = currentTime;
|
||||
@@ -114,7 +116,7 @@ function MachineInstance({ machineDetail }: { readonly machineDetail: MachineSta
|
||||
function handleCallBack() {
|
||||
if (currentPhase == "processing") {
|
||||
setMachineState(machineDetail.modelUuid, 'idle');
|
||||
setMachineActive(machineDetail.modelUuid, false);
|
||||
setMachineActive(machineDetail.modelUuid, true);
|
||||
setCurrentPhase("idle")
|
||||
isIncrememtable.current = true;
|
||||
machineStatus(machineDetail.modelUuid, "Machine has completed the processing")
|
||||
@@ -130,6 +132,7 @@ function MachineInstance({ machineDetail }: { readonly machineDetail: MachineSta
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<MachineAnimator processingTime={machineDetail.point.action.processTime} handleCallBack={handleCallBack} currentPhase={currentPhase} machineUuid={machineDetail.modelUuid} machineStatus={machineStatus} reset={reset} />
|
||||
|
||||
</>
|
||||
|
||||
@@ -23,7 +23,7 @@ export function useTriggerHandler() {
|
||||
const { addHumanToMonitor } = useHumanEventManager();
|
||||
const { getVehicleById } = vehicleStore();
|
||||
const { getHumanById, setHumanScheduled } = humanStore();
|
||||
const { getMachineById } = machineStore();
|
||||
const { getMachineById, setMachineActive } = machineStore();
|
||||
const { getStorageUnitById } = storageUnitStore();
|
||||
const { getMaterialById, setCurrentLocation, setNextLocation, setPreviousLocation, setIsPaused, setIsVisible, setEndTime } = materialStore();
|
||||
|
||||
@@ -325,48 +325,17 @@ export function useTriggerHandler() {
|
||||
if (model?.type === 'vehicle') {
|
||||
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
||||
if (human) {
|
||||
if (human.isActive === false && human.state === 'idle') {
|
||||
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
|
||||
if (vehicle) {
|
||||
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
|
||||
// Handle current action from vehicle
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (vehicle) {
|
||||
addVehicleToMonitor(vehicle.modelUuid, () => {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
|
||||
handleAction(action, materialId);
|
||||
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
|
||||
addVehicleToMonitor(vehicle.modelUuid, () => {
|
||||
handleAction(action, materialId);
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (vehicle) {
|
||||
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
|
||||
// Handle current action from vehicle
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
|
||||
addVehicleToMonitor(vehicle.modelUuid, () => {
|
||||
handleAction(action, materialId);
|
||||
})
|
||||
}
|
||||
}
|
||||
}, action.actionUuid)
|
||||
}, action.actionUuid)
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (model?.type === 'transfer') {
|
||||
@@ -374,61 +343,30 @@ export function useTriggerHandler() {
|
||||
if (human) {
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (conveyor) {
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
|
||||
addConveyorToMonitor(conveyor.modelUuid, () => {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
handleAction(action, materialId);
|
||||
}, action.actionUuid)
|
||||
}, [materialId])
|
||||
}
|
||||
}, action.actionUuid)
|
||||
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (conveyor) {
|
||||
addConveyorToMonitor(conveyor.modelUuid, () => {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
}, action.actionUuid)
|
||||
}, [materialId])
|
||||
}
|
||||
}
|
||||
} else if (model?.type === 'machine') {
|
||||
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
||||
if (human) {
|
||||
if (human.isActive === false && human.state === 'idle') {
|
||||
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid);
|
||||
if (machine) {
|
||||
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (machine) {
|
||||
addMachineToMonitor(machine.modelUuid, () => {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
|
||||
handleAction(action, materialId);
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
|
||||
addMachineToMonitor(machine.modelUuid, () => {
|
||||
handleAction(action, materialId);
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
const machine = getMachineById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (machine) {
|
||||
if (machine.isActive === false && machine.state === 'idle' && !machine.currentAction) {
|
||||
setIsPaused(materialId, true);
|
||||
handleAction(action, materialId);
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
|
||||
addMachineToMonitor(machine.modelUuid, () => {
|
||||
handleAction(action, materialId);
|
||||
})
|
||||
}
|
||||
}
|
||||
}, action.actionUuid);
|
||||
}, action.actionUuid);
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -465,7 +403,6 @@ export function useTriggerHandler() {
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
handleAction(action, materialId)
|
||||
}, action.actionUuid);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -478,8 +415,10 @@ export function useTriggerHandler() {
|
||||
if (toEvent?.type === 'transfer') {
|
||||
// Vehicle to Transfer
|
||||
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
|
||||
const conveyor = getConveyorById(toEvent.modelUuid);
|
||||
const material = getMaterialById(materialId);
|
||||
if (material) {
|
||||
|
||||
if (material && conveyor) {
|
||||
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
|
||||
|
||||
setPreviousLocation(material.materialId, {
|
||||
@@ -494,23 +433,29 @@ export function useTriggerHandler() {
|
||||
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
|
||||
});
|
||||
|
||||
setIsPaused(materialId, false);
|
||||
|
||||
setIsVisible(materialId, true);
|
||||
|
||||
if (action &&
|
||||
action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid &&
|
||||
action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid
|
||||
) {
|
||||
|
||||
setNextLocation(material.materialId, {
|
||||
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid,
|
||||
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid,
|
||||
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '',
|
||||
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || '',
|
||||
});
|
||||
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
addConveyorToMonitor(conveyor.modelUuid, () => {
|
||||
|
||||
setIsPaused(materialId, false);
|
||||
|
||||
handleAction(action, materialId);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (toEvent?.type === 'vehicle') {
|
||||
// Vehicle to Vehicle
|
||||
|
||||
@@ -539,7 +484,6 @@ export function useTriggerHandler() {
|
||||
|
||||
setNextLocation(material.materialId, null);
|
||||
|
||||
|
||||
if (action && armBot) {
|
||||
|
||||
if (armBot.isActive === false && armBot.state === 'idle') {
|
||||
@@ -686,21 +630,24 @@ export function useTriggerHandler() {
|
||||
const previousModel = getEventByModelUuid(selectedProduct.productUuid, material.previous?.modelUuid || '');
|
||||
if (previousModel) {
|
||||
if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) {
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId)
|
||||
} else {
|
||||
addConveyorToMonitor(conveyor.modelUuid,
|
||||
() => {
|
||||
handleAction(action, materialId)
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
handleAction(action, materialId)
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
// handleAction(action, materialId)
|
||||
}
|
||||
} else {
|
||||
handleAction(action, materialId)
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -716,21 +663,24 @@ export function useTriggerHandler() {
|
||||
const previousModel = getEventByModelUuid(selectedProduct.productUuid, material.previous?.modelUuid || '');
|
||||
if (previousModel) {
|
||||
if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) {
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId)
|
||||
} else {
|
||||
addConveyorToMonitor(conveyor.modelUuid,
|
||||
() => {
|
||||
handleAction(action, materialId)
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
handleAction(action, materialId)
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
// handleAction(action, materialId)
|
||||
}
|
||||
} else {
|
||||
handleAction(action, materialId)
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -770,10 +720,7 @@ export function useTriggerHandler() {
|
||||
setIsVisible(materialId, false);
|
||||
|
||||
if (action && human) {
|
||||
|
||||
if (human.isActive === false && human.state === 'idle') {
|
||||
|
||||
// Handle current action from arm bot
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (model?.type === 'transfer') {
|
||||
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
@@ -782,20 +729,20 @@ export function useTriggerHandler() {
|
||||
if (previousModel) {
|
||||
if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) {
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId)
|
||||
} else {
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
addConveyorToMonitor(conveyor.modelUuid,
|
||||
() => {
|
||||
handleAction(action, materialId)
|
||||
}
|
||||
)
|
||||
addConveyorToMonitor(conveyor.modelUuid, () => {
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId)
|
||||
}
|
||||
// handleAction(action, materialId)
|
||||
}
|
||||
} else if (model?.type === 'vehicle') {
|
||||
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
@@ -804,82 +751,26 @@ export function useTriggerHandler() {
|
||||
// Handle current action from vehicle
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId);
|
||||
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
|
||||
addVehicleToMonitor(vehicle.modelUuid,
|
||||
() => {
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
addVehicleToMonitor(vehicle.modelUuid, () => {
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId);
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
setMachineActive(trigger.triggerUuid, false);
|
||||
handleAction(action, materialId)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
|
||||
addHumanToMonitor(human.modelUuid, () => {
|
||||
const model = getEventByModelUuid(selectedProduct.productUuid, action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (model?.type === 'transfer') {
|
||||
const conveyor = getConveyorById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (conveyor) {
|
||||
const previousModel = getEventByModelUuid(selectedProduct.productUuid, material.previous?.modelUuid || '');
|
||||
if (previousModel) {
|
||||
if (previousModel.type === 'transfer' && previousModel.modelUuid === model.modelUuid) {
|
||||
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
handleAction(action, materialId)
|
||||
} else {
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
addConveyorToMonitor(conveyor.modelUuid,
|
||||
() => {
|
||||
handleAction(action, materialId)
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
handleAction(action, materialId)
|
||||
}
|
||||
}
|
||||
} else if (model?.type === 'vehicle') {
|
||||
const vehicle = getVehicleById(action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid || '');
|
||||
if (vehicle) {
|
||||
if (vehicle.isActive === false && vehicle.state === 'idle' && vehicle.isPicking && vehicle.currentLoad < vehicle.point.action.loadCapacity) {
|
||||
// Handle current action from vehicle
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
handleAction(action, materialId);
|
||||
|
||||
} else {
|
||||
|
||||
// Handle current action using Event Manager
|
||||
setIsPaused(materialId, true);
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
|
||||
addVehicleToMonitor(vehicle.modelUuid,
|
||||
() => {
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setHumanScheduled(human.modelUuid, true);
|
||||
handleAction(action, materialId)
|
||||
}
|
||||
}, action.actionUuid);
|
||||
}
|
||||
}, action.actionUuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1439,13 +1330,11 @@ export function useTriggerHandler() {
|
||||
const material = getMaterialById(materialId);
|
||||
if (material) {
|
||||
|
||||
setIsPaused(material.materialId, false);
|
||||
setIsPaused(material.materialId, true);
|
||||
|
||||
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
|
||||
const vehicle = getVehicleById(trigger.triggeredAsset?.triggeredModel.modelUuid);
|
||||
|
||||
setNextLocation(material.materialId, null);
|
||||
|
||||
if (action) {
|
||||
|
||||
if (vehicle) {
|
||||
@@ -1466,16 +1355,15 @@ export function useTriggerHandler() {
|
||||
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
|
||||
});
|
||||
|
||||
setNextLocation(material.materialId, null);
|
||||
|
||||
// Handle current action from vehicle
|
||||
handleAction(action, materialId);
|
||||
|
||||
} else {
|
||||
|
||||
setIsPaused(materialId, true);
|
||||
|
||||
addVehicleToMonitor(vehicle.modelUuid,
|
||||
() => {
|
||||
setIsPaused(materialId, false);
|
||||
setIsVisible(materialId, false);
|
||||
|
||||
setPreviousLocation(material.materialId, {
|
||||
@@ -1493,6 +1381,7 @@ export function useTriggerHandler() {
|
||||
setNextLocation(material.materialId, null);
|
||||
|
||||
// Handle current action from vehicle
|
||||
console.log('action: ', action);
|
||||
handleAction(action, materialId);
|
||||
}
|
||||
)
|
||||
|
||||
@@ -378,7 +378,6 @@ function DraggableLineSegment({
|
||||
};
|
||||
|
||||
const onPointerMove = (e: ThreeEvent<PointerEvent>) => {
|
||||
console.log('isAnyDragging: ', isAnyDragging);
|
||||
if (isAnyDragging !== "line" || activeTool !== 'pen') return;
|
||||
|
||||
const intersect = new THREE.Vector3();
|
||||
|
||||
@@ -19,7 +19,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
const { materialStore, armBotStore, conveyorStore, vehicleStore, storageUnitStore, humanStore, productStore, assetStore } = useSceneContext();
|
||||
const { removeMaterial, setEndTime, setIsVisible } = materialStore();
|
||||
const { getStorageUnitById } = storageUnitStore();
|
||||
const { getHumanById, addCurrentAction, addCurrentMaterial, incrementHumanLoad , incrementLoadCount } = humanStore();
|
||||
const { getHumanById, addCurrentAction, addCurrentMaterial, incrementHumanLoad } = humanStore();
|
||||
const { getArmBotById } = armBotStore();
|
||||
const { getConveyorById } = conveyorStore();
|
||||
const { triggerPointActions } = useTriggerHandler();
|
||||
@@ -302,7 +302,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
||||
setIsVisible(material.materialId, false);
|
||||
addCurrentMaterial(humanId, material.materialType, material.materialId);
|
||||
incrementHumanLoad(humanId, 1);
|
||||
incrementLoadCount(humanId, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import FollowPerson from "../components/templates/FollowPerson";
|
||||
import { useLogger } from "../components/ui/log/LoggerContext";
|
||||
import RenderOverlay from "../components/templates/Overlay";
|
||||
import LogList from "../components/ui/log/LogList";
|
||||
import { useToggleStore } from "../store/useUIToggleStore";
|
||||
import { getAllProjects } from "../services/dashboard/getAllProjects";
|
||||
import { viewProject } from "../services/dashboard/viewProject";
|
||||
import ComparisonSceneProvider from "../components/layout/scenes/ComparisonSceneProvider";
|
||||
@@ -31,11 +30,9 @@ import { handleCanvasCursors } from "../utils/mouseUtils/handleCanvasCursors";
|
||||
const Project: React.FC = () => {
|
||||
let navigate = useNavigate();
|
||||
const echo = useLogger();
|
||||
const { setToggleUI } = useToggleStore();
|
||||
const { setActiveModule } = useModuleStore();
|
||||
const { setUserName } = useUserName();
|
||||
const { setOrganization } = useOrganization();
|
||||
const { isVersionSaved } = useSaveVersion();
|
||||
const { projectId } = useParams();
|
||||
const { setProjectName } = useProjectName();
|
||||
const { userId, email, organization, userName } = getUserData();
|
||||
@@ -99,13 +96,6 @@ const Project: React.FC = () => {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [projectId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isVersionSaved) {
|
||||
setToggleUI(true, true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isVersionSaved]);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveModule("builder");
|
||||
if (email) {
|
||||
|
||||
21
app/src/services/factoryBuilder/webWorkers/distanceWorker.js
Normal file
21
app/src/services/factoryBuilder/webWorkers/distanceWorker.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import { Vector3 } from "three";
|
||||
|
||||
onmessage = function (e) {
|
||||
const { modelUuid, assetPosition, cameraPosition, limitDistance, renderDistance, isRendered, } = e.data;
|
||||
|
||||
const assetVec = new Vector3(assetPosition.x, assetPosition.y, assetPosition.z);
|
||||
const cameraVec = new Vector3(cameraPosition.x, cameraPosition.y, cameraPosition.z);
|
||||
const distance = assetVec.distanceTo(cameraVec);
|
||||
|
||||
if (limitDistance) {
|
||||
if (!isRendered && distance <= renderDistance) {
|
||||
postMessage({ shouldRender: true, modelUuid });
|
||||
} else if (isRendered && distance > renderDistance) {
|
||||
postMessage({ shouldRender: false, modelUuid });
|
||||
}
|
||||
} else {
|
||||
if (!isRendered) {
|
||||
postMessage({ shouldRender: true, modelUuid });
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,12 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
||||
import { retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
|
||||
|
||||
const loader = new GLTFLoader();
|
||||
const dracoLoader = new DRACOLoader();
|
||||
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
|
||||
onmessage = async (event) => {
|
||||
@@ -17,8 +10,8 @@ onmessage = async (event) => {
|
||||
);
|
||||
|
||||
for (const item of uniqueItems) {
|
||||
if(item.assetId === null || item.assetId === undefined) {
|
||||
continue; // Skip items without a valid assetId
|
||||
if (item.assetId === null || item.assetId === undefined) {
|
||||
continue;
|
||||
}
|
||||
const modelID = item.assetId;
|
||||
const indexedDBModel = await retrieveGLTF(modelID);
|
||||
@@ -37,5 +30,5 @@ onmessage = async (event) => {
|
||||
}
|
||||
}
|
||||
|
||||
postMessage({ message: 'done' })
|
||||
postMessage({ message: 'done' });
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ interface FloorStore {
|
||||
|
||||
getFloorById: (uuid: string) => Floor | undefined;
|
||||
getFloorsByPointId: (uuid: string) => Floor[] | [];
|
||||
getFloorByPoints: (points: Point[]) => Floor | undefined;
|
||||
getFloorsByPoints: (points: [Point, Point]) => Floor[] | [];
|
||||
getFloorPointById: (uuid: string) => Point | undefined;
|
||||
getConnectedPoints: (uuid: string) => Point[];
|
||||
}
|
||||
@@ -74,10 +74,13 @@ export const createFloorStore = () => {
|
||||
const updatedFloors: Floor[] = [];
|
||||
|
||||
set(state => {
|
||||
const newFloors: Floor[] = [];
|
||||
|
||||
for (const floor of state.floors) {
|
||||
const pointIndex = floor.points.findIndex(p => p.pointUuid === pointUuid);
|
||||
|
||||
if (pointIndex === -1) {
|
||||
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
|
||||
newFloors.push(floor);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -87,11 +90,13 @@ export const createFloorStore = () => {
|
||||
removedFloors.push(JSON.parse(JSON.stringify(floor)));
|
||||
continue;
|
||||
}
|
||||
floor.points = remainingPoints;
|
||||
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
|
||||
|
||||
const updatedFloor = { ...floor, points: remainingPoints };
|
||||
updatedFloors.push(JSON.parse(JSON.stringify(updatedFloor)));
|
||||
newFloors.push(updatedFloor);
|
||||
}
|
||||
|
||||
state.floors = updatedFloors;
|
||||
state.floors = newFloors;
|
||||
});
|
||||
|
||||
return { removedFloors, updatedFloors };
|
||||
@@ -102,6 +107,7 @@ export const createFloorStore = () => {
|
||||
const updatedFloors: Floor[] = [];
|
||||
|
||||
set(state => {
|
||||
const newFloors: Floor[] = [];
|
||||
|
||||
for (const floor of state.floors) {
|
||||
const indices = floor.points.map((p, i) => ({ uuid: p.pointUuid, index: i }));
|
||||
@@ -110,7 +116,7 @@ export const createFloorStore = () => {
|
||||
const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1;
|
||||
|
||||
if (idxA === -1 || idxB === -1) {
|
||||
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
|
||||
newFloors.push(floor);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -120,7 +126,7 @@ export const createFloorStore = () => {
|
||||
(idxB === 0 && idxA === floor.points.length - 1);
|
||||
|
||||
if (!areAdjacent) {
|
||||
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
|
||||
newFloors.push(floor);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -129,14 +135,15 @@ export const createFloorStore = () => {
|
||||
);
|
||||
|
||||
if (remainingPoints.length > 2) {
|
||||
floor.points = remainingPoints;
|
||||
updatedFloors.push(JSON.parse(JSON.stringify(floor)));
|
||||
const updatedFloor = { ...floor, points: remainingPoints };
|
||||
updatedFloors.push(JSON.parse(JSON.stringify(updatedFloor)));
|
||||
newFloors.push(updatedFloor);
|
||||
} else {
|
||||
removedFloors.push(JSON.parse(JSON.stringify(floor)));
|
||||
}
|
||||
}
|
||||
|
||||
state.floors = updatedFloors;
|
||||
state.floors = newFloors;
|
||||
});
|
||||
|
||||
return { removedFloors, updatedFloors };
|
||||
@@ -253,12 +260,32 @@ export const createFloorStore = () => {
|
||||
});
|
||||
},
|
||||
|
||||
getFloorByPoints: (points) => {
|
||||
return get().floors.find(floor => {
|
||||
const floorPointIds = new Set(floor.points.map(p => p.pointUuid));
|
||||
const givenPointIds = new Set(points.map(p => p.pointUuid));
|
||||
return floorPointIds.size === givenPointIds.size && [...floorPointIds].every(id => givenPointIds.has(id));
|
||||
});
|
||||
getFloorsByPoints: ([pointA, pointB]) => {
|
||||
const Floors: Floor[] = [];
|
||||
|
||||
for (const floor of get().floors) {
|
||||
const indices = floor.points.map((p, i) => ({ uuid: p.pointUuid, index: i }));
|
||||
|
||||
const idxA = indices.find(i => i.uuid === pointA.pointUuid)?.index ?? -1;
|
||||
const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1;
|
||||
|
||||
if (idxA === -1 || idxB === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const areAdjacent =
|
||||
Math.abs(idxA - idxB) === 1 ||
|
||||
(idxA === 0 && idxB === floor.points.length - 1) ||
|
||||
(idxB === 0 && idxA === floor.points.length - 1);
|
||||
|
||||
if (!areAdjacent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Floors.push(JSON.parse(JSON.stringify(floor)));
|
||||
}
|
||||
|
||||
return Floors;
|
||||
},
|
||||
|
||||
getFloorPointById: (pointUuid) => {
|
||||
|
||||
78
app/src/store/builder/useUndoRedo2DStore.ts
Normal file
78
app/src/store/builder/useUndoRedo2DStore.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { create } from 'zustand';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import { undoRedoConfig } from '../../types/world/worldConstants';
|
||||
|
||||
type UndoRedo2DStore = {
|
||||
undoStack: UndoRedo2DTypes[];
|
||||
redoStack: UndoRedo2DTypes[];
|
||||
|
||||
push2D: (entry: UndoRedo2DTypes) => void;
|
||||
undo2D: () => UndoRedo2DTypes | undefined;
|
||||
redo2D: () => UndoRedo2DTypes | undefined;
|
||||
clearUndoRedo2D: () => void;
|
||||
|
||||
peekUndo2D: () => UndoRedo2DTypes | undefined;
|
||||
peekRedo2D: () => UndoRedo2DTypes | undefined;
|
||||
};
|
||||
|
||||
export const createUndoRedo2DStore = () => {
|
||||
return create<UndoRedo2DStore>()(
|
||||
immer((set, get) => ({
|
||||
undoStack: [],
|
||||
redoStack: [],
|
||||
|
||||
push2D: (entry) => {
|
||||
set((state) => {
|
||||
state.undoStack.push(entry);
|
||||
|
||||
if (state.undoStack.length > undoRedoConfig.undoRedoCount) {
|
||||
state.undoStack.shift();
|
||||
}
|
||||
|
||||
state.redoStack = [];
|
||||
});
|
||||
},
|
||||
|
||||
undo2D: () => {
|
||||
let lastAction: UndoRedo2DTypes | undefined;
|
||||
set((state) => {
|
||||
lastAction = state.undoStack.pop();
|
||||
if (lastAction) {
|
||||
state.redoStack.unshift(lastAction);
|
||||
}
|
||||
});
|
||||
return lastAction;
|
||||
},
|
||||
|
||||
redo2D: () => {
|
||||
let redoAction: UndoRedo2DTypes | undefined;
|
||||
set((state) => {
|
||||
redoAction = state.redoStack.shift();
|
||||
if (redoAction) {
|
||||
state.undoStack.push(redoAction);
|
||||
}
|
||||
});
|
||||
return redoAction;
|
||||
},
|
||||
|
||||
clearUndoRedo2D: () => {
|
||||
set((state) => {
|
||||
state.undoStack = [];
|
||||
state.redoStack = [];
|
||||
});
|
||||
},
|
||||
|
||||
peekUndo2D: () => {
|
||||
const stack = get().undoStack;
|
||||
return stack.length > 0 ? stack[stack.length - 1] : undefined;
|
||||
},
|
||||
|
||||
peekRedo2D: () => {
|
||||
const stack = get().redoStack;
|
||||
return stack.length > 0 ? stack[0] : undefined;
|
||||
},
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
export type UndoRedo2DStoreType = ReturnType<typeof createUndoRedo2DStore>;
|
||||
@@ -21,7 +21,7 @@ interface ZoneStore {
|
||||
|
||||
getZoneById: (uuid: string) => Zone | undefined;
|
||||
getZonesByPointId: (uuid: string) => Zone[] | [];
|
||||
getZoneByPoints: (points: Point[]) => Zone | undefined;
|
||||
getZonesByPoints: (points: Point[]) => Zone[] | [];
|
||||
getZonePointById: (uuid: string) => Point | undefined;
|
||||
getConnectedPoints: (uuid: string) => Point[];
|
||||
}
|
||||
@@ -76,10 +76,13 @@ export const createZoneStore = () => {
|
||||
const updatedZones: Zone[] = [];
|
||||
|
||||
set(state => {
|
||||
const newZones: Zone[] = [];
|
||||
|
||||
for (const zone of state.zones) {
|
||||
const pointIndex = zone.points.findIndex(p => p.pointUuid === pointUuid);
|
||||
|
||||
if (pointIndex === -1) {
|
||||
updatedZones.push(JSON.parse(JSON.stringify(zone)));
|
||||
newZones.push(zone);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -89,11 +92,13 @@ export const createZoneStore = () => {
|
||||
removedZones.push(JSON.parse(JSON.stringify(zone)));
|
||||
continue;
|
||||
}
|
||||
zone.points = remainingPoints;
|
||||
updatedZones.push(JSON.parse(JSON.stringify(zone)));
|
||||
|
||||
const updatedZone = { ...zone, points: remainingPoints };
|
||||
updatedZones.push(JSON.parse(JSON.stringify(updatedZone)));
|
||||
newZones.push(updatedZone);
|
||||
}
|
||||
|
||||
state.zones = updatedZones;
|
||||
state.zones = newZones;
|
||||
});
|
||||
|
||||
return { removedZones, updatedZones };
|
||||
@@ -104,6 +109,7 @@ export const createZoneStore = () => {
|
||||
const updatedZones: Zone[] = [];
|
||||
|
||||
set(state => {
|
||||
const newZones: Zone[] = [];
|
||||
|
||||
for (const zone of state.zones) {
|
||||
const indices = zone.points.map((p, i) => ({ uuid: p.pointUuid, index: i }));
|
||||
@@ -112,7 +118,7 @@ export const createZoneStore = () => {
|
||||
const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1;
|
||||
|
||||
if (idxA === -1 || idxB === -1) {
|
||||
updatedZones.push(JSON.parse(JSON.stringify(zone)));
|
||||
newZones.push(zone);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -122,7 +128,7 @@ export const createZoneStore = () => {
|
||||
(idxB === 0 && idxA === zone.points.length - 1);
|
||||
|
||||
if (!areAdjacent) {
|
||||
updatedZones.push(JSON.parse(JSON.stringify(zone)));
|
||||
newZones.push(zone);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -131,14 +137,15 @@ export const createZoneStore = () => {
|
||||
);
|
||||
|
||||
if (remainingPoints.length > 2) {
|
||||
zone.points = remainingPoints;
|
||||
updatedZones.push(JSON.parse(JSON.stringify(zone)));
|
||||
const updatedZone = { ...zone, points: remainingPoints };
|
||||
updatedZones.push(JSON.parse(JSON.stringify(updatedZone)));
|
||||
newZones.push(updatedZone);
|
||||
} else {
|
||||
removedZones.push(JSON.parse(JSON.stringify(zone)));
|
||||
}
|
||||
}
|
||||
|
||||
state.zones = updatedZones;
|
||||
state.zones = newZones;
|
||||
});
|
||||
|
||||
return { removedZones, updatedZones };
|
||||
@@ -180,12 +187,32 @@ export const createZoneStore = () => {
|
||||
});
|
||||
},
|
||||
|
||||
getZoneByPoints: (points) => {
|
||||
return get().zones.find(zone => {
|
||||
const zonePointIds = new Set(zone.points.map(p => p.pointUuid));
|
||||
const givenPointIds = new Set(points.map(p => p.pointUuid));
|
||||
return zonePointIds.size === givenPointIds.size && [...zonePointIds].every(id => givenPointIds.has(id));
|
||||
});
|
||||
getZonesByPoints: ([pointA, pointB]) => {
|
||||
const Zones: Zone[] = [];
|
||||
|
||||
for (const zone of get().zones) {
|
||||
const indices = zone.points.map((p, i) => ({ uuid: p.pointUuid, index: i }));
|
||||
|
||||
const idxA = indices.find(i => i.uuid === pointA.pointUuid)?.index ?? -1;
|
||||
const idxB = indices.find(i => i.uuid === pointB.pointUuid)?.index ?? -1;
|
||||
|
||||
if (idxA === -1 || idxB === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const areAdjacent =
|
||||
Math.abs(idxA - idxB) === 1 ||
|
||||
(idxA === 0 && idxB === zone.points.length - 1) ||
|
||||
(idxB === 0 && idxA === zone.points.length - 1);
|
||||
|
||||
if (!areAdjacent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Zones.push(JSON.parse(JSON.stringify(zone)));
|
||||
}
|
||||
|
||||
return Zones;
|
||||
},
|
||||
|
||||
getZonePointById: (pointUuid) => {
|
||||
|
||||
@@ -27,11 +27,6 @@ interface HumansStore {
|
||||
incrementHumanLoad: (modelUuid: string, incrementBy: 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;
|
||||
setCurrentMaterials: (modelUuid: string, materials: { materialType: string; materialId: string }[]) => void;
|
||||
removeLastMaterial: (modelUuid: string) => { materialType: string; materialId: string } | undefined;
|
||||
@@ -65,7 +60,6 @@ export const createHumanStore = () => {
|
||||
isScheduled: false,
|
||||
idleTime: 0,
|
||||
activeTime: 0,
|
||||
totalLoadCount: 0,
|
||||
currentLoad: 0,
|
||||
currentMaterials: [],
|
||||
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) => {
|
||||
set((state) => {
|
||||
const human = state.humans.find(h => h.modelUuid === modelUuid);
|
||||
|
||||
38
app/src/types/builderTypes.d.ts
vendored
38
app/src/types/builderTypes.d.ts
vendored
@@ -215,3 +215,41 @@ interface Aisle {
|
||||
}
|
||||
|
||||
type Aisles = Aisle[];
|
||||
|
||||
|
||||
// Undo/Redo 2D
|
||||
|
||||
type UndoRedo2DDataTypeSchema =
|
||||
| { type: 'Wall'; lineData: Wall; newData?: Wall; timeStamp: string }
|
||||
| { type: 'Floor'; lineData: Floor; newData?: Floor; timeStamp: string }
|
||||
| { type: 'Zone'; lineData: Zone; newData?: Zone; timeStamp: string }
|
||||
| { type: 'Aisle'; lineData: Aisle; newData?: Aisle; timeStamp: string };
|
||||
|
||||
type UndoRedo2DLineActionSchema = {
|
||||
actionType: 'Line-Create' | 'Line-Update' | 'Line-Delete';
|
||||
point: UndoRedo2DDataTypeSchema;
|
||||
}
|
||||
|
||||
type UndoRedo2DLinesActionSchema = {
|
||||
actionType: 'Lines-Create' | 'Lines-Update' | 'Lines-Delete';
|
||||
points: UndoRedo2DDataTypeSchema[];
|
||||
}
|
||||
|
||||
type UndoRedo2DAction = UndoRedo2DLineActionSchema | UndoRedo2DLinesActionSchema;
|
||||
|
||||
type UndoRedo2DDraw = {
|
||||
type: 'Draw';
|
||||
actions: UndoRedo2DAction[];
|
||||
};
|
||||
|
||||
type UndoRedo2DUi = {
|
||||
type: 'UI';
|
||||
action: any; // Define UI actions as needed
|
||||
}
|
||||
|
||||
type UndoRedo2DTypes = UndoRedo2DDraw | UndoRedo2DUi
|
||||
|
||||
type UndoRedo2D = {
|
||||
undoStack: UndoRedo2DTypes[];
|
||||
redoStack: UndoRedo2DTypes[];
|
||||
};
|
||||
22
app/src/types/simulationTypes.d.ts
vendored
22
app/src/types/simulationTypes.d.ts
vendored
@@ -102,6 +102,7 @@ interface HumanAction {
|
||||
pickUpPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
dropPoint?: { position: [number, number, number] | null; rotation: [number, number, number] | null; }
|
||||
loadCount: number;
|
||||
assemblyCount: number;
|
||||
loadCapacity: number;
|
||||
triggers: TriggerSchema[];
|
||||
}
|
||||
@@ -252,7 +253,6 @@ interface HumanStatus extends HumanEventSchema {
|
||||
isScheduled: boolean;
|
||||
idleTime: number;
|
||||
activeTime: number;
|
||||
totalLoadCount: number;
|
||||
currentLoad: number;
|
||||
currentMaterials: { materialType: string; materialId: string; }[];
|
||||
distanceTraveled: number;
|
||||
@@ -262,6 +262,26 @@ interface HumanStatus extends HumanEventSchema {
|
||||
};
|
||||
}
|
||||
|
||||
type HumanEventState = {
|
||||
humanId: string;
|
||||
actionQueue: {
|
||||
actionType: 'worker' | 'assembly';
|
||||
actionUuid: string;
|
||||
actionName: string;
|
||||
maxLoadCount: number;
|
||||
maxAssemblyCount: number;
|
||||
count?: number;
|
||||
isMonitored: boolean;
|
||||
isCompleted: boolean;
|
||||
callback?: () => void;
|
||||
}[];
|
||||
isCooldown: boolean;
|
||||
};
|
||||
|
||||
type HumanEventManagerState = {
|
||||
humanStates: HumanEventState[];
|
||||
};
|
||||
|
||||
// Materials
|
||||
interface MaterialSchema {
|
||||
materialId: string;
|
||||
|
||||
@@ -1,373 +1,383 @@
|
||||
const savedTheme: string | null = localStorage.getItem("theme");
|
||||
|
||||
export type Controls = {
|
||||
azimuthRotateSpeed: number;
|
||||
polarRotateSpeed: number;
|
||||
truckSpeed: number;
|
||||
minDistance: number;
|
||||
maxDistance: number;
|
||||
maxPolarAngle: number;
|
||||
leftMouse: number;
|
||||
forwardSpeed: number;
|
||||
backwardSpeed: number;
|
||||
leftSpeed: number;
|
||||
rightSpeed: number;
|
||||
azimuthRotateSpeed: number;
|
||||
polarRotateSpeed: number;
|
||||
truckSpeed: number;
|
||||
minDistance: number;
|
||||
maxDistance: number;
|
||||
maxPolarAngle: number;
|
||||
leftMouse: number;
|
||||
forwardSpeed: number;
|
||||
backwardSpeed: number;
|
||||
leftSpeed: number;
|
||||
rightSpeed: number;
|
||||
};
|
||||
|
||||
export type ThirdPersonControls = {
|
||||
azimuthRotateSpeed: number;
|
||||
polarRotateSpeed: number;
|
||||
truckSpeed: number;
|
||||
maxDistance: number;
|
||||
maxPolarAngle: number;
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
targetOffset: number;
|
||||
cameraHeight: number;
|
||||
leftMouse: number;
|
||||
rightMouse: number;
|
||||
wheelMouse: number;
|
||||
middleMouse: number;
|
||||
azimuthRotateSpeed: number;
|
||||
polarRotateSpeed: number;
|
||||
truckSpeed: number;
|
||||
maxDistance: number;
|
||||
maxPolarAngle: number;
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
targetOffset: number;
|
||||
cameraHeight: number;
|
||||
leftMouse: number;
|
||||
rightMouse: number;
|
||||
wheelMouse: number;
|
||||
middleMouse: number;
|
||||
};
|
||||
|
||||
export type ControlsTransition = {
|
||||
leftMouse: number;
|
||||
rightMouse: number;
|
||||
wheelMouse: number;
|
||||
middleMouse: number;
|
||||
leftMouse: number;
|
||||
rightMouse: number;
|
||||
wheelMouse: number;
|
||||
middleMouse: number;
|
||||
};
|
||||
|
||||
export type TwoDimension = {
|
||||
defaultPosition: [x: number, y: number, z: number];
|
||||
defaultTarget: [x: number, y: number, z: number];
|
||||
defaultAzimuth: number;
|
||||
minDistance: number;
|
||||
leftMouse: number;
|
||||
rightMouse: number;
|
||||
defaultPosition: [x: number, y: number, z: number];
|
||||
defaultTarget: [x: number, y: number, z: number];
|
||||
defaultAzimuth: number;
|
||||
minDistance: number;
|
||||
leftMouse: number;
|
||||
rightMouse: number;
|
||||
};
|
||||
|
||||
export type ThreeDimension = {
|
||||
defaultPosition: [x: number, y: number, z: number];
|
||||
defaultTarget: [x: number, y: number, z: number];
|
||||
defaultRotation: [x: number, y: number, z: number];
|
||||
defaultAzimuth: number;
|
||||
boundaryBottom: [x: number, y: number, z: number];
|
||||
boundaryTop: [x: number, y: number, z: number];
|
||||
minDistance: number;
|
||||
leftMouse: number;
|
||||
rightMouse: number;
|
||||
defaultPosition: [x: number, y: number, z: number];
|
||||
defaultTarget: [x: number, y: number, z: number];
|
||||
defaultRotation: [x: number, y: number, z: number];
|
||||
defaultAzimuth: number;
|
||||
boundaryBottom: [x: number, y: number, z: number];
|
||||
boundaryTop: [x: number, y: number, z: number];
|
||||
minDistance: number;
|
||||
leftMouse: number;
|
||||
rightMouse: number;
|
||||
};
|
||||
|
||||
export type GridConfig = {
|
||||
size: number;
|
||||
divisions: number;
|
||||
primaryColor: string;
|
||||
secondaryColor: string;
|
||||
position2D: [x: number, y: number, z: number];
|
||||
position3D: [x: number, y: number, z: number];
|
||||
size: number;
|
||||
divisions: number;
|
||||
primaryColor: string;
|
||||
secondaryColor: string;
|
||||
position2D: [x: number, y: number, z: number];
|
||||
position3D: [x: number, y: number, z: number];
|
||||
};
|
||||
|
||||
export type PlaneConfig = {
|
||||
position2D: [x: number, y: number, z: number];
|
||||
position3D: [x: number, y: number, z: number];
|
||||
rotation: number;
|
||||
width: number;
|
||||
height: number;
|
||||
color: string;
|
||||
position2D: [x: number, y: number, z: number];
|
||||
position3D: [x: number, y: number, z: number];
|
||||
rotation: number;
|
||||
width: number;
|
||||
height: number;
|
||||
color: string;
|
||||
};
|
||||
|
||||
export type ShadowConfig = {
|
||||
shadowOffset: number;
|
||||
shadowmapSizewidth: number;
|
||||
shadowmapSizeheight: number;
|
||||
shadowcamerafar: number;
|
||||
shadowcameranear: number;
|
||||
shadowcameratop: number;
|
||||
shadowcamerabottom: number;
|
||||
shadowcameraleft: number;
|
||||
shadowcameraright: number;
|
||||
shadowbias: number;
|
||||
shadownormalBias: number;
|
||||
shadowMaterialPosition: [x: number, y: number, z: number];
|
||||
shadowMaterialRotation: [x: number, y: number, z: number];
|
||||
shadowMaterialOpacity: number;
|
||||
shadowOffset: number;
|
||||
shadowmapSizewidth: number;
|
||||
shadowmapSizeheight: number;
|
||||
shadowcamerafar: number;
|
||||
shadowcameranear: number;
|
||||
shadowcameratop: number;
|
||||
shadowcamerabottom: number;
|
||||
shadowcameraleft: number;
|
||||
shadowcameraright: number;
|
||||
shadowbias: number;
|
||||
shadownormalBias: number;
|
||||
shadowMaterialPosition: [x: number, y: number, z: number];
|
||||
shadowMaterialRotation: [x: number, y: number, z: number];
|
||||
shadowMaterialOpacity: number;
|
||||
};
|
||||
|
||||
export type SkyConfig = {
|
||||
defaultTurbidity: number;
|
||||
maxTurbidity: number;
|
||||
minTurbidity: number;
|
||||
defaultRayleigh: number;
|
||||
mieCoefficient: number;
|
||||
mieDirectionalG: number;
|
||||
skyDistance: number;
|
||||
defaultTurbidity: number;
|
||||
maxTurbidity: number;
|
||||
minTurbidity: number;
|
||||
defaultRayleigh: number;
|
||||
mieCoefficient: number;
|
||||
mieDirectionalG: number;
|
||||
skyDistance: number;
|
||||
};
|
||||
|
||||
export type AssetConfig = {
|
||||
defaultScaleBeforeGsap: [number, number, number];
|
||||
defaultScaleAfterGsap: [number, number, number];
|
||||
defaultScaleBeforeGsap: [number, number, number];
|
||||
defaultScaleAfterGsap: [number, number, number];
|
||||
};
|
||||
|
||||
export type PointConfig = {
|
||||
defaultInnerColor: string;
|
||||
defaultOuterColor: string;
|
||||
deleteColor: string;
|
||||
boxScale: [number, number, number];
|
||||
wallOuterColor: string;
|
||||
floorOuterColor: string;
|
||||
aisleOuterColor: string;
|
||||
zoneOuterColor: string;
|
||||
snappingThreshold: number;
|
||||
helperColor: string;
|
||||
defaultInnerColor: string;
|
||||
defaultOuterColor: string;
|
||||
deleteColor: string;
|
||||
boxScale: [number, number, number];
|
||||
wallOuterColor: string;
|
||||
floorOuterColor: string;
|
||||
aisleOuterColor: string;
|
||||
zoneOuterColor: string;
|
||||
snappingThreshold: number;
|
||||
helperColor: string;
|
||||
};
|
||||
|
||||
export type LineConfig = {
|
||||
tubularSegments: number;
|
||||
radius: number;
|
||||
radialSegments: number;
|
||||
wallName: string;
|
||||
floorName: string;
|
||||
aisleName: string;
|
||||
zoneName: string;
|
||||
referenceName: string;
|
||||
lineIntersectionPoints: number;
|
||||
defaultColor: string;
|
||||
wallColor: string;
|
||||
floorColor: string;
|
||||
aisleColor: string;
|
||||
zoneColor: string;
|
||||
deleteColor: string;
|
||||
helperColor: string;
|
||||
tubularSegments: number;
|
||||
radius: number;
|
||||
radialSegments: number;
|
||||
wallName: string;
|
||||
floorName: string;
|
||||
aisleName: string;
|
||||
zoneName: string;
|
||||
referenceName: string;
|
||||
lineIntersectionPoints: number;
|
||||
defaultColor: string;
|
||||
wallColor: string;
|
||||
floorColor: string;
|
||||
aisleColor: string;
|
||||
zoneColor: string;
|
||||
deleteColor: string;
|
||||
helperColor: string;
|
||||
};
|
||||
|
||||
export type WallConfig = {
|
||||
defaultColor: string;
|
||||
height: number;
|
||||
width: number;
|
||||
defaultColor: string;
|
||||
height: number;
|
||||
width: number;
|
||||
};
|
||||
|
||||
export type FloorConfig = {
|
||||
defaultColor: string;
|
||||
height: number;
|
||||
textureScale: number;
|
||||
defaultColor: string;
|
||||
height: number;
|
||||
textureScale: number;
|
||||
};
|
||||
|
||||
export type RoofConfig = {
|
||||
defaultColor: string;
|
||||
height: number;
|
||||
defaultColor: string;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export type AisleConfig = {
|
||||
width: number;
|
||||
height: number;
|
||||
defaultColor: string;
|
||||
width: number;
|
||||
height: number;
|
||||
defaultColor: string;
|
||||
};
|
||||
|
||||
export type ZoneConfig = {
|
||||
defaultColor: string;
|
||||
height: number;
|
||||
color: string;
|
||||
defaultColor: string;
|
||||
height: number;
|
||||
color: string;
|
||||
};
|
||||
|
||||
export type ColumnConfig = {
|
||||
defaultColor: string;
|
||||
defaultColor: string;
|
||||
};
|
||||
|
||||
export type OutlineConfig = {
|
||||
assetSelectColor: number;
|
||||
assetDeleteColor: number;
|
||||
assetSelectColor: number;
|
||||
assetDeleteColor: number;
|
||||
};
|
||||
|
||||
export type DistanceConfig = {
|
||||
minDistance: number;
|
||||
maxDistance: number;
|
||||
minDistance: number;
|
||||
maxDistance: number;
|
||||
};
|
||||
|
||||
export type undoRedoCount = {
|
||||
undoRedoCount: number;
|
||||
};
|
||||
|
||||
export const firstPersonControls: Controls = {
|
||||
azimuthRotateSpeed: 0.3, // Speed of rotation around the azimuth axis
|
||||
polarRotateSpeed: 0.3, // Speed of rotation around the polar axis
|
||||
truckSpeed: 10, // Speed of truck movement
|
||||
minDistance: 0, // Minimum distance from the target
|
||||
maxDistance: 0, // Maximum distance from the target
|
||||
maxPolarAngle: Math.PI, // Maximum polar angle
|
||||
leftMouse: 1, // Mouse button for rotation (ROTATE)
|
||||
forwardSpeed: 0.1, // Speed of forward movement
|
||||
backwardSpeed: -0.1, // Speed of backward movement
|
||||
leftSpeed: -0.1, // Speed of left movement
|
||||
rightSpeed: 0.1, // Speed of right movement
|
||||
azimuthRotateSpeed: 0.3, // Speed of rotation around the azimuth axis
|
||||
polarRotateSpeed: 0.3, // Speed of rotation around the polar axis
|
||||
truckSpeed: 10, // Speed of truck movement
|
||||
minDistance: 0, // Minimum distance from the target
|
||||
maxDistance: 0, // Maximum distance from the target
|
||||
maxPolarAngle: Math.PI, // Maximum polar angle
|
||||
leftMouse: 1, // Mouse button for rotation (ROTATE)
|
||||
forwardSpeed: 0.1, // Speed of forward movement
|
||||
backwardSpeed: -0.1, // Speed of backward movement
|
||||
leftSpeed: -0.1, // Speed of left movement
|
||||
rightSpeed: 0.1, // Speed of right movement
|
||||
};
|
||||
|
||||
export const thirdPersonControls: ThirdPersonControls = {
|
||||
azimuthRotateSpeed: 1, // Speed of rotation around the azimuth axis
|
||||
polarRotateSpeed: 1, // Speed of rotation around the polar axis
|
||||
truckSpeed: 2, // Speed of truck movement
|
||||
maxDistance: 100, // Maximum distance from the target
|
||||
maxPolarAngle: Math.PI / 2 - 0.05, // Maximum polar angle
|
||||
minZoom: 6, // Minimum zoom level
|
||||
maxZoom: 100, // Maximum zoom level
|
||||
targetOffset: 20, // Offset of the target from the camera
|
||||
cameraHeight: 30, // Height of the camera
|
||||
leftMouse: 2, // Mouse button for panning
|
||||
rightMouse: 1, // Mouse button for rotation
|
||||
wheelMouse: 8, // Mouse button for zooming
|
||||
middleMouse: 8, // Mouse button for zooming
|
||||
azimuthRotateSpeed: 1, // Speed of rotation around the azimuth axis
|
||||
polarRotateSpeed: 1, // Speed of rotation around the polar axis
|
||||
truckSpeed: 2, // Speed of truck movement
|
||||
maxDistance: 100, // Maximum distance from the target
|
||||
maxPolarAngle: Math.PI / 2 - 0.05, // Maximum polar angle
|
||||
minZoom: 6, // Minimum zoom level
|
||||
maxZoom: 100, // Maximum zoom level
|
||||
targetOffset: 20, // Offset of the target from the camera
|
||||
cameraHeight: 30, // Height of the camera
|
||||
leftMouse: 2, // Mouse button for panning
|
||||
rightMouse: 1, // Mouse button for rotation
|
||||
wheelMouse: 8, // Mouse button for zooming
|
||||
middleMouse: 8, // Mouse button for zooming
|
||||
};
|
||||
|
||||
export const controlsTransition: ControlsTransition = {
|
||||
leftMouse: 0, // Mouse button for no action
|
||||
rightMouse: 0, // Mouse button for no action
|
||||
wheelMouse: 0, // Mouse button for no action
|
||||
middleMouse: 0, // Mouse button for no action
|
||||
leftMouse: 0, // Mouse button for no action
|
||||
rightMouse: 0, // Mouse button for no action
|
||||
wheelMouse: 0, // Mouse button for no action
|
||||
middleMouse: 0, // Mouse button for no action
|
||||
};
|
||||
|
||||
export const twoDimension: TwoDimension = {
|
||||
defaultPosition: [0, 100, 0], // Default position of the camera
|
||||
defaultTarget: [0, 0, 0], // Default target of the camera
|
||||
defaultAzimuth: 0, // Default azimuth of the camera
|
||||
minDistance: 25, // Minimum distance from the target
|
||||
leftMouse: 2, // Mouse button for panning
|
||||
rightMouse: 0, // Mouse button for no action
|
||||
defaultPosition: [0, 100, 0], // Default position of the camera
|
||||
defaultTarget: [0, 0, 0], // Default target of the camera
|
||||
defaultAzimuth: 0, // Default azimuth of the camera
|
||||
minDistance: 25, // Minimum distance from the target
|
||||
leftMouse: 2, // Mouse button for panning
|
||||
rightMouse: 0, // Mouse button for no action
|
||||
};
|
||||
|
||||
export const camPositionUpdateInterval: number = 200; // Interval for updating the camera position
|
||||
|
||||
export const gridConfig: GridConfig = {
|
||||
size: 150, // Size of the grid
|
||||
divisions: 75, // Number of divisions in the grid
|
||||
primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid
|
||||
secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid
|
||||
size: 150, // Size of the grid
|
||||
divisions: 75, // Number of divisions in the grid
|
||||
primaryColor: savedTheme === "dark" ? "#131313" : "#d5d5d5", // Primary color of the grid
|
||||
secondaryColor: savedTheme === "dark" ? "#434343" : "#e3e3e3", // Secondary color of the grid
|
||||
|
||||
position2D: [0, 0.1, 0], // Position of the grid in 2D view
|
||||
position3D: [0, -0.5, 0], // Position of the grid in 3D view
|
||||
position2D: [0, 0.1, 0], // Position of the grid in 2D view
|
||||
position3D: [0, -0.5, 0], // Position of the grid in 3D view
|
||||
};
|
||||
|
||||
export const threeDimension: ThreeDimension = {
|
||||
defaultPosition: [0, 40, 30], // Default position of the camera
|
||||
defaultTarget: [0, 0, 0], // Default target of the camera
|
||||
defaultRotation: [0, 0, 0], // Default rotation of the camera
|
||||
defaultAzimuth: 0, // Default azimuth of the camera
|
||||
boundaryBottom: [-gridConfig.size / 2, 0, -gridConfig.size / 2], // Bottom boundary of the camera movement
|
||||
boundaryTop: [gridConfig.size / 2, 100, gridConfig.size / 2], // Top boundary of the camera movement
|
||||
minDistance: 1, // Minimum distance from the target
|
||||
leftMouse: 2, // Mouse button for panning
|
||||
rightMouse: 1, // Mouse button for rotation
|
||||
defaultPosition: [0, 40, 30], // Default position of the camera
|
||||
defaultTarget: [0, 0, 0], // Default target of the camera
|
||||
defaultRotation: [0, 0, 0], // Default rotation of the camera
|
||||
defaultAzimuth: 0, // Default azimuth of the camera
|
||||
boundaryBottom: [-gridConfig.size / 2, 0, -gridConfig.size / 2], // Bottom boundary of the camera movement
|
||||
boundaryTop: [gridConfig.size / 2, 100, gridConfig.size / 2], // Top boundary of the camera movement
|
||||
minDistance: 1, // Minimum distance from the target
|
||||
leftMouse: 2, // Mouse button for panning
|
||||
rightMouse: 1, // Mouse button for rotation
|
||||
};
|
||||
|
||||
export const planeConfig: PlaneConfig = {
|
||||
position2D: [0, -0.5, 0], // Position of the plane
|
||||
position3D: [0, -0.65, 0], // Position of the plane
|
||||
rotation: -Math.PI / 2, // Rotation of the plane
|
||||
position2D: [0, -0.5, 0], // Position of the plane
|
||||
position3D: [0, -0.65, 0], // Position of the plane
|
||||
rotation: -Math.PI / 2, // Rotation of the plane
|
||||
|
||||
width: 150, // Width of the plane
|
||||
height: 150, // Height of the plane
|
||||
color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane
|
||||
width: 150, // Width of the plane
|
||||
height: 150, // Height of the plane
|
||||
color: savedTheme === "dark" ? "#323232" : "#f3f3f3", // Color of the plane
|
||||
};
|
||||
|
||||
export const shadowConfig: ShadowConfig = {
|
||||
shadowOffset: 50, // Offset of the shadow
|
||||
shadowmapSizewidth: 1024, // Width of the shadow map
|
||||
shadowmapSizeheight: 1024, // Height of the shadow map
|
||||
// shadowmapSizewidth: 8192, // Width of the shadow map
|
||||
// shadowmapSizeheight: 8192, // Height of the shadow map
|
||||
shadowcamerafar: 70, // Far plane of the shadow camera
|
||||
shadowcameranear: 0.1, // Near plane of the shadow camera
|
||||
shadowcameratop: 30, // Top plane of the shadow camera
|
||||
shadowcamerabottom: -30, // Bottom plane of the shadow camera
|
||||
shadowcameraleft: -30, // Left plane of the shadow camera
|
||||
shadowcameraright: 30, // Right plane of the shadow camera
|
||||
shadowbias: -0.001, // Bias of the shadow
|
||||
shadownormalBias: 0.02, // Normal bias of the shadow
|
||||
shadowMaterialPosition: [0, 0.01, 0], // Position of the shadow material
|
||||
shadowMaterialRotation: [-Math.PI / 2, 0, 0], // Rotation of the shadow material
|
||||
shadowMaterialOpacity: 0.1, // Opacity of the shadow material
|
||||
shadowOffset: 50, // Offset of the shadow
|
||||
// shadowmapSizewidth: 1024, // Width of the shadow map
|
||||
// shadowmapSizeheight: 1024, // Height of the shadow map
|
||||
shadowmapSizewidth: 2048, // Width of the shadow map
|
||||
shadowmapSizeheight: 2048, // Height of the shadow map
|
||||
// shadowmapSizewidth: 8192, // Width of the shadow map
|
||||
// shadowmapSizeheight: 8192, // Height of the shadow map
|
||||
shadowcamerafar: 70, // Far plane of the shadow camera
|
||||
shadowcameranear: 0.1, // Near plane of the shadow camera
|
||||
shadowcameratop: 30, // Top plane of the shadow camera
|
||||
shadowcamerabottom: -30, // Bottom plane of the shadow camera
|
||||
shadowcameraleft: -30, // Left plane of the shadow camera
|
||||
shadowcameraright: 30, // Right plane of the shadow camera
|
||||
shadowbias: -0.001, // Bias of the shadow
|
||||
shadownormalBias: 0.02, // Normal bias of the shadow
|
||||
shadowMaterialPosition: [0, 0.01, 0], // Position of the shadow material
|
||||
shadowMaterialRotation: [-Math.PI / 2, 0, 0], // Rotation of the shadow material
|
||||
shadowMaterialOpacity: 0.1, // Opacity of the shadow material
|
||||
};
|
||||
|
||||
export const skyConfig: SkyConfig = {
|
||||
defaultTurbidity: 10.0, // Default turbidity of the sky
|
||||
maxTurbidity: 20.0, // Maximum turbidity of the sky
|
||||
minTurbidity: 0.0, // Minimum turbidity of the sky
|
||||
defaultRayleigh: 1.9, // Default Rayleigh scattering coefficient
|
||||
mieCoefficient: 0.1, // Mie scattering coefficient
|
||||
mieDirectionalG: 1.0, // Mie directional G
|
||||
skyDistance: 2000, // Distance of the sky
|
||||
defaultTurbidity: 10.0, // Default turbidity of the sky
|
||||
maxTurbidity: 20.0, // Maximum turbidity of the sky
|
||||
minTurbidity: 0.0, // Minimum turbidity of the sky
|
||||
defaultRayleigh: 1.9, // Default Rayleigh scattering coefficient
|
||||
mieCoefficient: 0.1, // Mie scattering coefficient
|
||||
mieDirectionalG: 1.0, // Mie directional G
|
||||
skyDistance: 2000, // Distance of the sky
|
||||
};
|
||||
|
||||
export const assetConfig: AssetConfig = {
|
||||
defaultScaleBeforeGsap: [0.1, 0.1, 0.1], // Default scale of the assets
|
||||
defaultScaleAfterGsap: [1, 1, 1], // Default scale of the assets
|
||||
defaultScaleBeforeGsap: [0.1, 0.1, 0.1], // Default scale of the assets
|
||||
defaultScaleAfterGsap: [1, 1, 1], // Default scale of the assets
|
||||
};
|
||||
|
||||
export const pointConfig: PointConfig = {
|
||||
defaultInnerColor: "#ffffff", // Default inner color of the points
|
||||
defaultOuterColor: "#ffffff", // Default outer color of the points
|
||||
deleteColor: "#ff0000", // Color of the points when deleting
|
||||
boxScale: [0.5, 0.5, 0.5], // Scale of the points
|
||||
wallOuterColor: "#C7C7C7", // Outer color of the wall points
|
||||
floorOuterColor: "#808080", // Outer color of the floor points
|
||||
aisleOuterColor: "#FBBC05", // Outer color of the aisle points
|
||||
zoneOuterColor: "#007BFF", // Outer color of the zone points
|
||||
snappingThreshold: 1, // Threshold for snapping
|
||||
helperColor: "#C164FF", // Color of the helper lines
|
||||
defaultInnerColor: "#ffffff", // Default inner color of the points
|
||||
defaultOuterColor: "#ffffff", // Default outer color of the points
|
||||
deleteColor: "#ff0000", // Color of the points when deleting
|
||||
boxScale: [0.5, 0.5, 0.5], // Scale of the points
|
||||
wallOuterColor: "#C7C7C7", // Outer color of the wall points
|
||||
floorOuterColor: "#808080", // Outer color of the floor points
|
||||
aisleOuterColor: "#FBBC05", // Outer color of the aisle points
|
||||
zoneOuterColor: "#007BFF", // Outer color of the zone points
|
||||
snappingThreshold: 1, // Threshold for snapping
|
||||
helperColor: "#C164FF", // Color of the helper lines
|
||||
};
|
||||
|
||||
export const lineConfig: LineConfig = {
|
||||
tubularSegments: 64, // Number of tubular segments
|
||||
radius: 0.15, // Radius of the lines
|
||||
radialSegments: 8, // Number of radial segments
|
||||
wallName: "WallLine", // Name of the wall lines
|
||||
floorName: "FloorLine", // Name of the floor lines
|
||||
aisleName: "AisleLine", // Name of the aisle lines
|
||||
zoneName: "ZoneLine", // Name of the zone lines
|
||||
referenceName: "ReferenceLine", // Name of the reference lines
|
||||
lineIntersectionPoints: 300, // Number of intersection points
|
||||
defaultColor: "#000000", // Default color of the lines
|
||||
wallColor: "#C7C7C7", // Color of the wall lines
|
||||
floorColor: "#808080", // Color of the floor lines
|
||||
aisleColor: "#FBBC05", // Color of the aisle lines
|
||||
zoneColor: "#007BFF", // Color of the zone lines
|
||||
deleteColor: "#ff0000", // Color of the line when deleting
|
||||
helperColor: "#C164FF", // Color of the helper lines
|
||||
tubularSegments: 64, // Number of tubular segments
|
||||
radius: 0.15, // Radius of the lines
|
||||
radialSegments: 8, // Number of radial segments
|
||||
wallName: "WallLine", // Name of the wall lines
|
||||
floorName: "FloorLine", // Name of the floor lines
|
||||
aisleName: "AisleLine", // Name of the aisle lines
|
||||
zoneName: "ZoneLine", // Name of the zone lines
|
||||
referenceName: "ReferenceLine", // Name of the reference lines
|
||||
lineIntersectionPoints: 300, // Number of intersection points
|
||||
defaultColor: "#000000", // Default color of the lines
|
||||
wallColor: "#C7C7C7", // Color of the wall lines
|
||||
floorColor: "#808080", // Color of the floor lines
|
||||
aisleColor: "#FBBC05", // Color of the aisle lines
|
||||
zoneColor: "#007BFF", // Color of the zone lines
|
||||
deleteColor: "#ff0000", // Color of the line when deleting
|
||||
helperColor: "#C164FF", // Color of the helper lines
|
||||
};
|
||||
|
||||
export const wallConfig: WallConfig = {
|
||||
defaultColor: "#f2f2f2", // Default color of the walls
|
||||
height: 7.5, // Height of the walls
|
||||
width: 0.05, // Width of the walls
|
||||
defaultColor: "#f2f2f2", // Default color of the walls
|
||||
height: 7.5, // Height of the walls
|
||||
width: 0.05, // Width of the walls
|
||||
};
|
||||
|
||||
export const floorConfig: FloorConfig = {
|
||||
defaultColor: "grey", // Default color of the floors
|
||||
height: 0.1, // Height of the floors
|
||||
textureScale: 1, // Scale of the floor texture
|
||||
defaultColor: "grey", // Default color of the floors
|
||||
height: 0.1, // Height of the floors
|
||||
textureScale: 1, // Scale of the floor texture
|
||||
};
|
||||
|
||||
export const roofConfig: RoofConfig = {
|
||||
defaultColor: "grey", // Default color of the roofs
|
||||
height: 0.1, // Height of the roofs
|
||||
defaultColor: "grey", // Default color of the roofs
|
||||
height: 0.1, // Height of the roofs
|
||||
};
|
||||
|
||||
export const aisleConfig: AisleConfig = {
|
||||
width: 0.1, // Width of the aisles
|
||||
height: 0.01, // Height of the aisles
|
||||
defaultColor: '#E2AC09', // Default color of the aisles
|
||||
width: 0.1, // Width of the aisles
|
||||
height: 0.01, // Height of the aisles
|
||||
defaultColor: '#E2AC09', // Default color of the aisles
|
||||
};
|
||||
|
||||
export const zoneConfig: ZoneConfig = {
|
||||
defaultColor: "black", // Default color of the zones
|
||||
height: 3,
|
||||
color: "#8656DF", // Color of the zones
|
||||
defaultColor: "black", // Default color of the zones
|
||||
height: 3,
|
||||
color: "#8656DF", // Color of the zones
|
||||
};
|
||||
|
||||
export const columnConfig: ColumnConfig = {
|
||||
defaultColor: "White", // Default color of the columns
|
||||
defaultColor: "White", // Default color of the columns
|
||||
};
|
||||
|
||||
export const outlineConfig: OutlineConfig = {
|
||||
assetSelectColor: 0x0054fe, // Color of the selected assets
|
||||
assetDeleteColor: 0xff0000, // Color of the deleted assets
|
||||
assetSelectColor: 0x0054fe, // Color of the selected assets
|
||||
assetDeleteColor: 0xff0000, // Color of the deleted assets
|
||||
};
|
||||
|
||||
export const distanceConfig: DistanceConfig = {
|
||||
minDistance: 20,
|
||||
maxDistance: 75,
|
||||
minDistance: 20,
|
||||
maxDistance: 75,
|
||||
};
|
||||
|
||||
export const undoRedoConfig: undoRedoCount = {
|
||||
undoRedoCount: 50,
|
||||
}
|
||||
@@ -95,10 +95,15 @@ const KeyPressListener: React.FC = () => {
|
||||
const toggleTo2D = toggleView;
|
||||
setToggleView(!toggleTo2D);
|
||||
setToggleThreeD(toggleTo2D);
|
||||
setToggleUI(toggleTo2D, toggleTo2D);
|
||||
if (toggleTo2D) {
|
||||
setSelectedWallItem(null);
|
||||
setAddAction(null);
|
||||
setToggleUI(
|
||||
localStorage.getItem("navBarUiLeft") !== "false",
|
||||
localStorage.getItem("navBarUiRight") !== "false"
|
||||
);
|
||||
} else {
|
||||
setToggleUI(false, false);
|
||||
}
|
||||
setActiveTool("cursor");
|
||||
setActiveSubTool("cursor");
|
||||
|
||||
Reference in New Issue
Block a user