diff --git a/app/src/modules/builder/asset/functions/assetBoundingBox.tsx b/app/src/modules/builder/asset/functions/assetBoundingBox.tsx index da08edf..7736e29 100644 --- a/app/src/modules/builder/asset/functions/assetBoundingBox.tsx +++ b/app/src/modules/builder/asset/functions/assetBoundingBox.tsx @@ -3,8 +3,15 @@ import { useMemo } from "react"; import { Cylinder } from "@react-three/drei"; 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 { edgeCylinders, cornerSpheres, center, size } = useMemo(() => { + if (!boundingBox) { + return { + edgeCylinders: [], + cornerSpheres: [], + center: new Vector3(), + size: new Vector3(), + }; + } const min = boundingBox.min; const max = boundingBox.max; @@ -50,7 +57,13 @@ export const AssetBoundingBox = ({ name, boundingBox, color, lineWidth, }: { nam }; }); - return { edgeCylinders, center, size }; + const cornerSpheres = corners.map((corner, i) => ({ + key: `corner-sphere-${i}`, + position: corner.clone(), + radius: radius * 1.5, + })); + + return { edgeCylinders, cornerSpheres, center, size }; }, [boundingBox, lineWidth]); if (!boundingBox) return null; @@ -58,11 +71,18 @@ export const AssetBoundingBox = ({ name, boundingBox, color, lineWidth, }: { nam return ( {edgeCylinders.map(({ key, position, rotation, length, radius }) => ( - + ))} + {cornerSpheres.map(({ key, position, radius }) => ( + + + + + ))} + diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index 16f37ee..96076f9 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -449,7 +449,11 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere ) : ( - + <> + {!isSelected && + + } + )} {isSelected && diff --git a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts index 0373fd0..ebe01ff 100644 --- a/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts +++ b/app/src/modules/simulation/actions/storageUnit/actionHandler/useRetrieveHandler.ts @@ -6,12 +6,13 @@ import { useProductContext } from "../../../products/productContext"; import { useHumanEventManager } from "../../../human/eventManager/useHumanEventManager"; export function useRetrieveHandler() { - const { materialStore, armBotStore, machineStore, vehicleStore, storageUnitStore, productStore, humanStore, assetStore } = useSceneContext(); + const { materialStore, armBotStore, machineStore, vehicleStore, storageUnitStore, conveyorStore, 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 { getConveyorById } = conveyorStore(); const { getHumanById, incrementHumanLoad, addCurrentMaterial: addCurrentMaterialToHuman } = humanStore(); const { getAssetById, setCurrentAnimation } = assetStore(); const { selectedProduct } = selectedProductStore(); @@ -60,9 +61,9 @@ export function useRetrieveHandler() { actionUuid: action.actionUuid }, current: { - modelUuid: action.triggers[0]?.triggeredAsset.triggeredModel.modelUuid, - pointUuid: action.triggers[0]?.triggeredAsset.triggeredPoint.pointUuid, - actionUuid: action.triggers[0]?.triggeredAsset.triggeredAction.actionUuid + modelUuid: modelUuid, + pointUuid: pointUuid, + actionUuid: action.actionUuid }, }; @@ -371,6 +372,32 @@ export function useRetrieveHandler() { } return; } + } else if (triggeredModel?.type === 'transfer') { + const model = getConveyorById(triggeredModel.modelUuid); + if (model && !model.isPaused) { + 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) { diff --git a/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx index 236ab55..1469d7f 100644 --- a/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx +++ b/app/src/modules/simulation/events/triggerConnections/triggerConnector.tsx @@ -182,31 +182,23 @@ function TriggerConnector() { const canvasElement = gl.domElement; let drag = false; - let isRightMouseDown = false; + let isLeftMouseDown = false; const onMouseDown = (evt: MouseEvent) => { if (selectedAsset) { clearSelectedAsset(); } - if (evt.button === 2) { - isRightMouseDown = true; + if (evt.button === 0) { + isLeftMouseDown = true; drag = false; } }; const onMouseUp = (evt: MouseEvent) => { - if (evt.button === 2) { - isRightMouseDown = false; + if (evt.button === 0) { + isLeftMouseDown = false; } - } - const onMouseMove = () => { - if (isRightMouseDown) { - drag = true; - } - }; - - const handleRightClick = (evt: MouseEvent) => { if (drag) return; evt.preventDefault(); @@ -368,13 +360,16 @@ function TriggerConnector() { } else if (firstSelectedPoint) { setFirstSelectedPoint(null); } + } + + const onMouseMove = () => { + drag = true; }; if (subModule === 'mechanics' && toolMode === 'cursor' && selectedAction.actionId && selectedAction.actionName) { canvasElement.addEventListener("mousedown", onMouseDown); canvasElement.addEventListener("mouseup", onMouseUp); canvasElement.addEventListener("mousemove", onMouseMove); - canvasElement.addEventListener('contextmenu', handleRightClick); } else { setFirstSelectedPoint(null); } @@ -383,7 +378,6 @@ function TriggerConnector() { canvasElement.removeEventListener("mousedown", onMouseDown); canvasElement.removeEventListener("mouseup", onMouseUp); canvasElement.removeEventListener("mousemove", onMouseMove); - canvasElement.removeEventListener('contextmenu', handleRightClick); }; }, [gl, subModule, selectedProduct, firstSelectedPoint, toolMode, selectedAction]); diff --git a/app/src/modules/simulation/human/instances/instance/humanUi.tsx b/app/src/modules/simulation/human/instances/instance/humanUi.tsx index 17335b1..043811a 100644 --- a/app/src/modules/simulation/human/instances/instance/humanUi.tsx +++ b/app/src/modules/simulation/human/instances/instance/humanUi.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from 'react' -import { useGLTF } from '@react-three/drei'; +import * as THREE from 'three' +import { Tube, useGLTF } from '@react-three/drei'; import { useFrame, useThree } from '@react-three/fiber'; import { useIsDragging, useIsRotating, useSelectedAction, useSelectedEventSphere } from '../../../../../store/simulation/useSimulationStore'; import { useProductContext } from '../../../products/productContext'; @@ -27,7 +28,7 @@ function HumanUi() { const { humanStore, productStore } = useSceneContext(); const { selectedProduct } = selectedProductStore(); const { humans, getHumanById } = humanStore(); - const { updateEvent, updateAction, getActionByUuid } = productStore(); + const { updateEvent, getActionByUuid } = productStore(); const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0]); const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0]); const [assemblyPosition, setAssemblyPosition] = useState<[number, number, number]>([0, 1, 0]); @@ -313,64 +314,49 @@ function HumanUi() { rotation={[0, Math.PI, 0]} > {isAssembly ? ( - { - if (e.object.parent.name === "handle") { - handlePointerDown(e, "assembly", "assembly"); - } else { - handlePointerDown(e, "assembly", "assembly"); - } - }} - onPointerMissed={() => { - setIsDragging(null); - setIsRotating(null); - if (controls) (controls as any).enabled = true; - }} + outerGroupRef={outerGroup} + type="assembly" + subtype="assembly" + color="#0f87f7" + setIsDragging={setIsDragging} + setIsRotating={setIsRotating} + handlePointerDown={handlePointerDown} /> ) : ( <> - { - if (e.object.parent.name === "handle") { - handlePointerDown(e, "start", "start"); - } else { - handlePointerDown(e, "start", "start"); - } - }} - onPointerMissed={() => { - setIsDragging(null); - setIsRotating(null); - if (controls) (controls as any).enabled = true; - }} + outerGroupRef={outerGroup} + type="start" + subtype="start" + color="lightgreen" + setIsDragging={setIsDragging} + setIsRotating={setIsRotating} + handlePointerDown={handlePointerDown} /> - - { - if (e.object.parent.name === "handle") { - handlePointerDown(e, "end", "end"); - } else { - handlePointerDown(e, "end", "end"); - } - }} - onPointerMissed={() => { - setIsDragging(null); - setIsRotating(null); - if (controls) (controls as any).enabled = true; - }} + outerGroupRef={outerGroup} + type="end" + subtype="end" + color="darkorange" + setIsDragging={setIsDragging} + setIsRotating={setIsRotating} + handlePointerDown={handlePointerDown} /> )} @@ -380,4 +366,101 @@ function HumanUi() { ); } -export default HumanUi; \ No newline at end of file +export default HumanUi; + +const MarkerPrimitive = ({ + name, + refProp, + object, + position, + rotation, + outerGroupRef, + type, + subtype, + color, + setIsDragging, + setIsRotating, + handlePointerDown, +}: { + name: string; + refProp: any; + object: THREE.Object3D; + position: [number, number, number]; + rotation: [number, number, number]; + outerGroupRef: React.RefObject; + type: string; + subtype: string; + color: string; + setIsDragging: (val: any) => void; + setIsRotating: (val: any) => void; + handlePointerDown: any; +}) => { + const { controls, scene, camera } = useThree(); + const [hitPoint, setHitPoint] = useState(null); + const [curve, setCurve] = useState(null); + + useFrame(() => { + if (!refProp.current || !outerGroupRef.current || !scene) return; + + const worldPos = new THREE.Vector3(); + refProp.current.getWorldPosition(worldPos); + const localMarkerPos = outerGroupRef.current.worldToLocal(worldPos.clone()); + + const rayOrigin = worldPos.clone(); + const direction = new THREE.Vector3(0, -1, 0); + const raycaster = new THREE.Raycaster(rayOrigin, direction, 0.1, 1000); + raycaster.camera = camera; + const intersects = raycaster.intersectObjects(scene.children, true); + + const hit = intersects.find(i => i.object.name !== name); + + if (hit) { + const localHit = outerGroupRef.current.worldToLocal(hit.point.clone()); + setHitPoint(localHit); + + const newCurve = new THREE.CatmullRomCurve3([ + localMarkerPos.clone(), + localHit.clone(), + ]); + setCurve(newCurve); + } else { + setHitPoint(null); + setCurve(null); + } + }); + + return ( + <> + { + handlePointerDown(e, type, subtype); + }} + onPointerMissed={() => { + setIsDragging(null); + setIsRotating(null); + if (controls) (controls as any).enabled = true; + }} + /> + + {hitPoint && ( + <> + + + + + + {curve && ( + + + + )} + + )} + + ); +}; \ No newline at end of file diff --git a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx index 72cd5fe..318759a 100644 --- a/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx +++ b/app/src/modules/simulation/materials/instances/instance/materialInstance.tsx @@ -61,30 +61,12 @@ function MaterialInstance({ material }: { readonly material: MaterialSchema }) { function getCurrentSpeed(productUuid: string, modelUuid: string) { const event = getEventByModelUuid(productUuid, modelUuid) if (event) { - if (event.type === 'transfer') { - return event.speed; - } - - if (event.type === 'vehicle') { - return event.speed; - } - - if (event.type === 'machine') { + if (event.type === 'transfer' || event.type === 'machine' || event.type === 'storageUnit') { return 1; } - - if (event.type === 'roboticArm') { + if (event.type === 'vehicle' || event.type === 'roboticArm' || event.type === 'human') { return event.speed; } - - if (event.type === 'storageUnit') { - return 1; - } - - if (event.type === 'human') { - return event.speed; - } - } else { return 1; } diff --git a/app/src/modules/simulation/materials/instances/materialInstances.tsx b/app/src/modules/simulation/materials/instances/materialInstances.tsx index 88b127f..35bdaef 100644 --- a/app/src/modules/simulation/materials/instances/materialInstances.tsx +++ b/app/src/modules/simulation/materials/instances/materialInstances.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react' +import { useEffect } from 'react' import MaterialInstance from './instance/materialInstance' import { useSceneContext } from '../../../scene/sceneContext'; diff --git a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx index 1f81c2d..be3a452 100644 --- a/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx +++ b/app/src/modules/simulation/roboticArm/instances/animator/roboticArmAnimator.tsx @@ -33,8 +33,8 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone const [customCurvePoints, setCustomCurvePoints] = useState(null); const { armBotStore, productStore, materialStore } = useSceneContext(); const { getArmBotById } = armBotStore(); - const { getMaterialById } = materialStore(); - const { getEventByModelUuid } = productStore(); + const { getMaterialById, getMaterialPosition } = materialStore(); + const { getEventByModelUuid, getActionByUuid, getPointByUuid } = productStore(); const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); const { scene } = useThree(); @@ -167,13 +167,18 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone let start = currentPath[0]; let end = currentPath[currentPath.length - 1]; + const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBone); const armbotStatus = getArmBotById(armBot.modelUuid); const currentMaterial = armbotStatus?.currentAction?.materialId; - if (armbotStatus && currentMaterial && (currentPhase === 'rest-to-start' || currentPhase === 'start-to-end')) { + const currentAction = getActionByUuid(selectedProduct.productUuid, armbotStatus?.currentAction?.actionUuid || ''); + + if (armbotStatus && currentMaterial && currentAction && (currentPhase === 'rest-to-start' || currentPhase === 'start-to-end' || currentPhase === 'end-to-rest')) { const materialData = getMaterialById(currentMaterial); if (materialData) { const prevModel = getEventByModelUuid(selectedProduct.productUuid, materialData.current.modelUuid); + const nextModel = getEventByModelUuid(selectedProduct.productUuid, currentAction?.triggers[0]?.triggeredAsset?.triggeredModel?.modelUuid || ''); + const nextPoint = getPointByUuid(selectedProduct.productUuid, currentAction?.triggers[0]?.triggeredAsset?.triggeredModel?.modelUuid || '', currentAction?.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid || ''); if (prevModel && prevModel.type === 'transfer') { const material = scene.getObjectByProperty("uuid", currentMaterial); @@ -194,7 +199,69 @@ function RoboticArmAnimator({ HandleCallback, restPosition, ikSolver, targetBone start = [materialLocalPos.x, materialLocalPos.y + 0.35, materialLocalPos.z]; } } + } else if (prevModel && prevModel.type === 'storageUnit') { + const position = getMaterialPosition(prevModel.modelUuid, currentMaterial); + const armbotModel = scene.getObjectByProperty("uuid", armBot.modelUuid); + + if (armbotModel) { + const armbotWorldPos = new THREE.Vector3(); + + let materialWorldPos = new THREE.Vector3(); + + if (position) { + materialWorldPos.copy(position); + } else { + materialWorldPos.copy(bone.getWorldPosition(armbotWorldPos)); + } + + const materialLocalPos = materialWorldPos.clone(); + armbotModel.worldToLocal(materialLocalPos); + + if (currentPhase === 'rest-to-start') { + end = [materialLocalPos.x, materialLocalPos.y + 0.35, materialLocalPos.z]; + } else if (currentPhase === 'start-to-end') { + start = [materialLocalPos.x, materialLocalPos.y + 0.35, materialLocalPos.z]; + } else if (currentPhase === 'end-to-rest') { + start = [materialLocalPos.x, materialLocalPos.y + 0.35, materialLocalPos.z]; + } + } } + + if (nextModel && nextPoint && nextModel.type === 'transfer') { + const conveyorModel = scene.getObjectByProperty("uuid", nextModel.modelUuid); + const armbotModel = scene.getObjectByProperty("uuid", armBot.modelUuid); + if (conveyorModel && armbotModel) { + const localPoint = new THREE.Vector3( + nextPoint.position[0], + nextPoint.position[1], + nextPoint.position[2] + ); + + const worldPoint = conveyorModel.localToWorld(localPoint); + + armbotModel.worldToLocal(worldPoint); + + if (currentPhase === 'start-to-end') { + end = [worldPoint.x, worldPoint.y + 0.35, worldPoint.z]; + } + } + } + } + + } + + if (currentPhase === 'end-to-rest') { + console.log('currentPhase: ', currentPhase); + const armbotModel = scene.getObjectByProperty("uuid", armBot.modelUuid); + const armbotWorldPos = new THREE.Vector3(); + + if (armbotModel) { + let materialWorldPos = new THREE.Vector3(); + materialWorldPos.copy(bone.getWorldPosition(armbotWorldPos)); + + const materialLocalPos = materialWorldPos.clone(); + armbotModel.worldToLocal(materialLocalPos); + start = [materialLocalPos.x, materialLocalPos.y + 0.35, materialLocalPos.z]; } } diff --git a/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx b/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx index 1f4dee8..dfd500a 100644 --- a/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx +++ b/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx @@ -173,13 +173,16 @@ const ArmBotUI = () => { const targetMesh = scene?.getObjectByProperty("uuid", selectedArmBotData?.modelUuid || ''); + const iks = targetMesh?.userData?.iks; + const firstIK = Array.isArray(iks) && iks.length > 0 ? iks[0] : {}; + const { handlePointerDown } = useDraggableGLTF( updatePointToState, { - minDistance: targetMesh?.userData?.iks[0]?.minDistance || 1.2, - maxDistance: targetMesh?.userData?.iks[0]?.maxDistance || 2, - maxheight: targetMesh?.userData?.iks[0]?.maxheight || 0.6, - minheight: targetMesh?.userData?.iks[0]?.minheight || 1.9, + minDistance: firstIK.minDistance ?? 1.2, + maxDistance: firstIK.maxDistance ?? 2, + maxheight: firstIK.maxheight ?? 0.6, + minheight: firstIK.minheight ?? 1.9, } ); diff --git a/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx b/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx index 9bd36b5..e287b8e 100644 --- a/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx +++ b/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx @@ -1,6 +1,7 @@ import { useEffect, useRef, useState } from "react"; import * as Types from "../../../../types/world/worldTypes"; -import { useGLTF } from "@react-three/drei"; +import { Tube, useGLTF } from "@react-three/drei"; +import * as THREE from "three"; import { useFrame, useThree } from "@react-three/fiber"; import { useSelectedEventSphere, useIsDragging, useIsRotating, } from "../../../../store/simulation/useSimulationStore"; import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi"; @@ -16,8 +17,6 @@ import { useVersionContext } from "../../../builder/version/versionContext"; const VehicleUI = () => { const { scene: startScene } = useGLTF(startPoint) as any; const { scene: endScene } = useGLTF(startEnd) as any; - const startMarker = useRef(null); - const endMarker = useRef(null); const prevMousePos = useRef({ x: 0, y: 0 }); const { selectedEventSphere } = useSelectedEventSphere(); const { selectedProductStore } = useProductContext(); @@ -162,7 +161,7 @@ const VehicleUI = () => { let globalStartPosition = null; let globalEndPosition = null; - if (outerGroup.current && startMarker.current && endMarker.current) { + if (outerGroup.current) { const worldPosStart = new Vector3(...startPosition); globalStartPosition = outerGroup.current.localToWorld( worldPosStart.clone() @@ -277,24 +276,19 @@ const VehicleUI = () => { const currentPointerX = state.pointer.x; const deltaX = currentPointerX - prevMousePos.current.x; prevMousePos.current.x = currentPointerX; - const marker = - isRotating === "start" ? startMarker.current : endMarker.current; - if (marker) { - const rotationSpeed = 10; - marker.rotation.y += deltaX * rotationSpeed; - if (isRotating === "start") { - setStartRotation([ - marker.rotation.x, - marker.rotation.y, - marker.rotation.z, - ]); - } else { - setEndRotation([ - marker.rotation.x, - marker.rotation.y, - marker.rotation.z, - ]); - } + const rotationSpeed = 10; + if (isRotating === "start") { + setStartRotation([ + startRotation[0], + startRotation[1] + deltaX * rotationSpeed, + startRotation[2], + ]); + } else { + setEndRotation([ + endRotation[0], + endRotation[1] + deltaX * rotationSpeed, + endRotation[2], + ]); } }); @@ -342,41 +336,130 @@ const VehicleUI = () => { {/* Start Marker */} - { - e.stopPropagation(); - handlePointerDown(e, "start", "start"); - }} + outerGroupRef={outerGroup} + color="green" + handlePointerDown={handlePointerDown} + setIsDragging={setIsDragging} + setIsRotating={setIsRotating} + /> + + {/* End Marker */} + + + ) : null; +}; + +export default VehicleUI; + +export const VehicleMarkerPrimitive = ({ + name, + object, + position, + rotation, + outerGroupRef, + color, + handlePointerDown, + setIsDragging, + setIsRotating, +}: { + name: string; + object: THREE.Object3D; + position: [number, number, number]; + rotation: [number, number, number]; + outerGroupRef: React.RefObject; + color: string; + handlePointerDown: (e: any, type: "start" | "end", rotation: "start" | "end") => void; + setIsDragging: (val: any) => void; + setIsRotating: (val: any) => void; +}) => { + const { scene, camera } = useThree(); + const markerRef = useRef(null); + const [hitPoint, setHitPoint] = useState(null); + const [curve, setCurve] = useState(null); + + useFrame(() => { + if (!markerRef.current || !outerGroupRef.current) return; + + const worldPos = new THREE.Vector3(); + markerRef.current.getWorldPosition(worldPos); + const localMarkerPos = outerGroupRef.current.worldToLocal(worldPos.clone()); + + const rayOrigin = worldPos.clone(); + const direction = new THREE.Vector3(0, -1, 0); + const raycaster = new THREE.Raycaster(rayOrigin, direction, 0.1, 1000); + raycaster.camera = camera; + const intersects = raycaster.intersectObjects(scene.children, true); + + const hit = intersects.find(i => i.object.name !== name); + if (hit) { + const localHit = outerGroupRef.current.worldToLocal(hit.point.clone()); + setHitPoint(localHit); + + const newCurve = new THREE.CatmullRomCurve3([ + localMarkerPos.clone(), + localHit.clone(), + ]); + setCurve(newCurve); + } else { + setHitPoint(null); + setCurve(null); + } + }); + + return ( + <> + + handlePointerDown( + e, + name === "startMarker" ? "start" : "end", + name === "startMarker" ? "start" : "end" + ) + } onPointerMissed={() => { - controls.enabled = true; setIsDragging(null); setIsRotating(null); }} /> - {/* End Marker */} - { - e.stopPropagation(); - handlePointerDown(e, "end", "end"); - }} - onPointerMissed={() => { - controls.enabled = true; - setIsDragging(null); - setIsRotating(null); - }} - /> - - ) : null; -}; -export default VehicleUI; + {hitPoint && ( + <> + + + + + + {curve && ( + + + + )} + + )} + + ); +}; \ No newline at end of file diff --git a/app/src/modules/simulation/storageUnit/instances/animator/MaterialAnimator.tsx b/app/src/modules/simulation/storageUnit/instances/animator/MaterialAnimator.tsx index 2a95262..87ffd32 100644 --- a/app/src/modules/simulation/storageUnit/instances/animator/MaterialAnimator.tsx +++ b/app/src/modules/simulation/storageUnit/instances/animator/MaterialAnimator.tsx @@ -1,72 +1,84 @@ -import { useRef, useMemo } from "react"; -import { MaterialModel } from "../../../materials/instances/material/materialModel"; +import { useEffect, useMemo } from "react"; import { Object3D, Box3, Vector3 } from "three"; import { useThree } from "@react-three/fiber"; import { useLoadingProgress } from "../../../../../store/builder/store"; +import { MaterialModel } from "../../../materials/instances/material/materialModel"; +import { useSceneContext } from "../../../../scene/sceneContext"; const MaterialAnimator = ({ - storage, + storage, }: Readonly<{ storage: StorageUnitStatus }>) => { - const meshRef = useRef(null!); - const { scene } = useThree(); - const padding = 0.1; - const { loadingProgress } = useLoadingProgress(); + const { scene } = useThree(); + const padding = 0.1; + const { loadingProgress } = useLoadingProgress(); + const { materialStore } = useSceneContext(); + const { materialPositions, setMaterialPositions } = materialStore(); - const storageModel = useMemo(() => { - return scene.getObjectByProperty("uuid", storage.modelUuid) as Object3D; - }, [scene, storage.modelUuid, loadingProgress]); + const storageModel = useMemo(() => { + return scene.getObjectByProperty("uuid", storage.modelUuid) as Object3D; + }, [scene, storage.modelUuid, loadingProgress]); - const materialPositions = useMemo(() => { - if (!storageModel || storage.currentMaterials.length === 0) return []; + useEffect(() => { + if (!storageModel || storage.currentMaterials.length === 0) { + setMaterialPositions(storage.modelUuid, []); + return; + } - const box = new Box3().setFromObject(storageModel); - const size = new Vector3(); - box.getSize(size); + const box = new Box3().setFromObject(storageModel); + const size = new Vector3(); + box.getSize(size); - const matCount = storage.currentMaterials.length; + const materialWidth = 0.45; + const materialDepth = 0.45; + const materialHeight = 0.3; + const cols = Math.floor(size.x / materialWidth); + const rows = Math.floor(size.z / materialDepth); + const itemsPerLayer = cols * rows; - // Assumed size each material needs in world units - const materialWidth = 0.45; - const materialDepth = 0.45; - const materialHeight = 0.3; + const origin = new Vector3( + box.min.x + materialWidth / 2, + box.max.y + padding, + box.min.z + materialDepth / 2 + ); - const cols = Math.floor(size.x / materialWidth); - const rows = Math.floor(size.z / materialDepth); - const itemsPerLayer = cols * rows; + const newMaterials = storage.currentMaterials.map((mat, i) => { + const layer = Math.floor(i / itemsPerLayer); + const layerIndex = i % itemsPerLayer; + const row = Math.floor(layerIndex / cols); + const col = layerIndex % cols; - const origin = new Vector3( - box.min.x + materialWidth / 2, - box.max.y + padding, // slightly above the surface - box.min.z + materialDepth / 2 + const position = new Vector3( + origin.x + col * materialWidth, + origin.y + layer * (materialHeight + padding), + origin.z + row * materialDepth + ); + + return { + materialId: mat.materialId, + position, + }; + }); + + setMaterialPositions(storage.modelUuid, newMaterials); + }, [storageModel, storage.currentMaterials]); + + return ( + + {(materialPositions[storage.modelUuid] || []).map(({ materialId, position }) => { + const mat = storage.currentMaterials.find((m) => m.materialId === materialId); + return ( + + ); + })} + ); - return Array.from({ length: matCount }, (_, i) => { - const layer = Math.floor(i / itemsPerLayer); - const layerIndex = i % itemsPerLayer; - const row = Math.floor(layerIndex / cols); - const col = layerIndex % cols; - - return new Vector3( - origin.x + col * materialWidth, - origin.y + layer * (materialHeight + padding), - origin.z + row * materialDepth - ); - }); - }, [storageModel, storage.currentMaterials]); - - return ( - - {storage.currentMaterials.map((mat, index) => ( - - ))} - - ); }; export default MaterialAnimator; diff --git a/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx b/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx index d9faf29..cebc4f9 100644 --- a/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx +++ b/app/src/modules/simulation/storageUnit/instances/storageUnitInstances.tsx @@ -7,7 +7,6 @@ import { useViewSceneStore } from "../../../../store/builder/store"; function StorageUnitInstances() { const { storageUnitStore } = useSceneContext(); const { storageUnits } = storageUnitStore(); - // console.log('storageUnits: ', storageUnits); const { viewSceneLabels } = useViewSceneStore(); return ( diff --git a/app/src/modules/simulation/storageUnit/storageUnit.tsx b/app/src/modules/simulation/storageUnit/storageUnit.tsx index eee0875..8d7f1b1 100644 --- a/app/src/modules/simulation/storageUnit/storageUnit.tsx +++ b/app/src/modules/simulation/storageUnit/storageUnit.tsx @@ -1,4 +1,3 @@ -import React from 'react' import StorageUnitInstances from './instances/storageUnitInstances' function StorageUnit() { diff --git a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts index 04e7fa7..b98a527 100644 --- a/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts +++ b/app/src/modules/simulation/triggers/triggerHandler/useTriggerHandler.ts @@ -370,40 +370,18 @@ export function useTriggerHandler() { } } } else { - if (human.isActive === false && human.state === 'idle') { - - // Handle current action from arm bot - setIsPaused(materialId, true); - handleAction(action, materialId); - - } else { - - // Handle current action using Event Manager - setHumanScheduled(human.modelUuid, true); - setIsPaused(materialId, true); - addHumanToMonitor(human.modelUuid, () => { - handleAction(action, materialId) - }, action.actionUuid); - - } - } - } else { - if (human.isActive === false && human.state === 'idle') { - - // Handle current action from arm bot - setIsPaused(materialId, true); setHumanScheduled(human.modelUuid, true); - handleAction(action, materialId); - - } else { - - // Handle current action using Event Manager setIsPaused(materialId, true); - setHumanScheduled(human.modelUuid, true); addHumanToMonitor(human.modelUuid, () => { handleAction(action, materialId) }, action.actionUuid); } + } else { + setHumanScheduled(human.modelUuid, true); + setIsPaused(materialId, true); + addHumanToMonitor(human.modelUuid, () => { + handleAction(action, materialId) + }, action.actionUuid); } } } @@ -1260,12 +1238,6 @@ export function useTriggerHandler() { actionUuid: material.current.actionUuid, }) - setCurrentLocation(material.materialId, { - modelUuid: material.current.modelUuid, - pointUuid: material.current.pointUuid, - actionUuid: material.current.actionUuid, - }); - setIsPaused(material.materialId, true); setIsVisible(material.materialId, true); @@ -1279,6 +1251,13 @@ export function useTriggerHandler() { if (model?.type === 'roboticArm') { addArmBotToMonitor(model.modelUuid, () => { + + setCurrentLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }); + setNextLocation(material.materialId, { modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid || '', pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid || '', @@ -1288,6 +1267,13 @@ export function useTriggerHandler() { }) } else if (model?.type === 'vehicle') { addVehicleToMonitor(model.modelUuid, () => { + + setCurrentLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }); + setNextLocation(material.materialId, { modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid || '', pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid || '', @@ -1296,6 +1282,13 @@ export function useTriggerHandler() { setIsPaused(material.materialId, false); }) } else if (model?.type === 'transfer') { + + setCurrentLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }); + setNextLocation(material.materialId, { modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid || '', pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid || '', @@ -1303,15 +1296,48 @@ export function useTriggerHandler() { setIsPaused(material.materialId, false); } else if (model?.type === 'human') { - addHumanToMonitor(model.modelUuid, () => { - setNextLocation(material.materialId, { - modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid || '', - pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid || '', - }) - + if (fromEvent.modelUuid === model.modelUuid) { setIsPaused(material.materialId, false); - }, action.actionUuid) + + setCurrentLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }); + + setTimeout(() => { + setIsPaused(material.materialId, false); + setNextLocation(material.materialId, { + modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid || '', + pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid || '', + }) + }, 0) + + } else { + addHumanToMonitor(model.modelUuid, () => { + + setCurrentLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }); + + setNextLocation(material.materialId, { + modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid || '', + pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid || '', + }) + + setIsPaused(material.materialId, false); + }, action.actionUuid) + } } else { + + setCurrentLocation(material.materialId, { + modelUuid: material.current.modelUuid, + pointUuid: material.current.pointUuid, + actionUuid: material.current.actionUuid, + }); + setNextLocation(material.materialId, { modelUuid: trigger.triggeredAsset?.triggeredModel.modelUuid || '', pointUuid: trigger.triggeredAsset?.triggeredPoint?.pointUuid || '', diff --git a/app/src/store/simulation/useMaterialStore.ts b/app/src/store/simulation/useMaterialStore.ts index 98a0928..68839a7 100644 --- a/app/src/store/simulation/useMaterialStore.ts +++ b/app/src/store/simulation/useMaterialStore.ts @@ -1,15 +1,29 @@ +import * as THREE from 'three'; import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; + +interface MaterialPosition { + materialId: string; + position: THREE.Vector3; +} + +type MaterialPositionsMap = Record; + type MaterialsStore = { materials: MaterialsSchema; materialHistory: MaterialHistorySchema; + materialPositions: MaterialPositionsMap; + addMaterial: (material: MaterialSchema) => MaterialSchema | undefined; removeMaterial: (materialId: string) => MaterialSchema | undefined; clearMaterials: () => void; updateMaterial: (materialId: string, updates: Partial) => MaterialSchema | undefined; + setMaterialPositions: (modelUuid: string, materialPositions: MaterialPosition[]) => void; + getMaterialPosition: (modelUuid: string, materialId: string) => THREE.Vector3 | undefined; + setPreviousLocation: ( materialId: string, location: { @@ -61,6 +75,7 @@ export const createMaterialStore = () => { immer((set, get) => ({ materials: [], materialHistory: [], + materialPositions: {}, addMaterial: (material) => { let updatedMaterial: MaterialSchema | undefined; @@ -262,6 +277,16 @@ export const createMaterialStore = () => { return updatedMaterial; }, + setMaterialPositions: (modelUuid, positions) => { + set((state) => { + state.materialPositions[modelUuid] = positions; + }); + }, + + getMaterialPosition: (modelUuid, materialId) => { + return get().materialPositions[modelUuid]?.find(p => p.materialId === materialId)?.position; + }, + getMaterialById: (materialId) => { return get().materials.find(m => m.materialId === materialId); },