diff --git a/app/src/components/icons/SimulationIcons.tsx b/app/src/components/icons/SimulationIcons.tsx index 1e89a51..29b8de4 100644 --- a/app/src/components/icons/SimulationIcons.tsx +++ b/app/src/components/icons/SimulationIcons.tsx @@ -1,4 +1,4 @@ -export function AnalysisIcon({ isActive }: { isActive: boolean }) { +export function AnalysisIcon({ isActive }: Readonly<{ isActive: boolean }>) { return ( ) { return ( ) { return ( ) { return ( ); } + +// simulation card icons + +export function ExpandIcon({ + color = "#6F42C1", +}: Readonly<{ color?: string }>) { + return ( + + + + + ); +} + +export function SimulationStatusIcon({ + color = "#21FF59", +}: Readonly<{ color?: string }>) { + return ( + + + + + ); +} + +export function IndicationArrow() { + return ( + + + + ); +} + +export function CartBagIcon() { + return ( + + + + + + + + ); +} diff --git a/app/src/components/ui/simulation/AssetDetailsCard.tsx b/app/src/components/ui/simulation/AssetDetailsCard.tsx index deb5375..ff7accd 100644 --- a/app/src/components/ui/simulation/AssetDetailsCard.tsx +++ b/app/src/components/ui/simulation/AssetDetailsCard.tsx @@ -1,11 +1,114 @@ -import React from 'react' +import React, { useState } from "react"; +import { + CartBagIcon, + ExpandIcon, + IndicationArrow, + SimulationStatusIcon, +} from "../../icons/SimulationIcons"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; -const AssetDetailsCard:React.FC = () => { - return ( -
- -
- ) +interface AssetDetailsCardInterface { + name: string; + status: string; + count?: number; + totalCapacity?: number; + assetDetails?: { + assetName: string; + const: string; + performance: string; + }; } -export default AssetDetailsCard \ No newline at end of file +const GetStatus = (status: string) => { + // "idle" | "running" | "stopped" | "disabled" | "error" + switch (status) { + case "idle": + return ( +
+
+ +
+
Idle
+
+ ); + case "running": + return ( +
+
+ +
+
Running
+
+ ); + case "stopped": + return ( +
+
+ +
+
Stopped
+
+ ); + } +}; + +const AssetDetailsCard: React.FC = ({ + name, + status, + count, + totalCapacity, + assetDetails, +}) => { + const [moreDetails, setMoreDetails] = useState(false); + // hooks + const { isPlaying } = usePlayButtonStore(); + + return ( +
+
+
+
+
{name}
+
{GetStatus(status)}
+
+ +
+ + {totalCapacity && ( +
+
+
+ +
+
{count?.toString()}
+
+
+ )} + + {status === "running" && ( +
+
+
+ )} +
+ +
+
+
+ ); +}; + +export default AssetDetailsCard; diff --git a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx index 0acc1d9..fb00e5a 100644 --- a/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx +++ b/app/src/modules/simulation/roboticArm/instances/roboticArmInstances.tsx @@ -1,16 +1,40 @@ -import RoboticArmInstance from './armInstance/roboticArmInstance'; -import { useArmBotStore } from '../../../../store/simulation/useArmBotStore'; +import RoboticArmInstance from "./armInstance/roboticArmInstance"; +import { useArmBotStore } from "../../../../store/simulation/useArmBotStore"; +import { Html } from "@react-three/drei"; +import AssetDetailsCard from "../../../../components/ui/simulation/AssetDetailsCard"; +import { Vector3 } from "three"; function RoboticArmInstances() { - const { armBots } = useArmBotStore(); + const { armBots } = useArmBotStore(); - return ( + return ( + <> + {armBots?.map((robot: ArmBotStatus) => ( <> - {armBots?.map((robot: ArmBotStatus) => ( - - ))} + + + + - ) + ))} + + ); } -export default RoboticArmInstances; \ No newline at end of file +export default RoboticArmInstances; diff --git a/app/src/modules/simulation/roboticArm/roboticArm.tsx b/app/src/modules/simulation/roboticArm/roboticArm.tsx index 91ccb55..ffdb312 100644 --- a/app/src/modules/simulation/roboticArm/roboticArm.tsx +++ b/app/src/modules/simulation/roboticArm/roboticArm.tsx @@ -2,19 +2,15 @@ import { useEffect, useState } from "react"; import { useArmBotStore } from "../../../store/simulation/useArmBotStore"; import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; -import ArmBotUI from "../ui/arm/armBotUI"; import RoboticArmInstances from "./instances/roboticArmInstances"; +import ArmBotUI from "../spatialUI/arm/armBotUI"; function RoboticArm() { - const { armBots, getArmBotById } = useArmBotStore(); + const { getArmBotById } = useArmBotStore(); const { selectedEventSphere } = useSelectedEventSphere(); const { isPlaying } = usePlayButtonStore(); const [isArmBotSelected, setIsArmBotSelected] = useState(false); - useEffect(() => { - // console.log('armBots: ', armBots); - }, [armBots]) - useEffect(() => { if (selectedEventSphere) { const selectedArmBot = getArmBotById(selectedEventSphere.userData.modelUuid); @@ -24,7 +20,7 @@ function RoboticArm() { setIsArmBotSelected(false); } } - }, [selectedEventSphere]) + }, [getArmBotById, selectedEventSphere]) return ( <> diff --git a/app/src/modules/simulation/ui/arm/PickDropPoints.tsx b/app/src/modules/simulation/spatialUI/arm/PickDropPoints.tsx similarity index 100% rename from app/src/modules/simulation/ui/arm/PickDropPoints.tsx rename to app/src/modules/simulation/spatialUI/arm/PickDropPoints.tsx diff --git a/app/src/modules/simulation/ui/arm/armBotUI.tsx b/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx similarity index 98% rename from app/src/modules/simulation/ui/arm/armBotUI.tsx rename to app/src/modules/simulation/spatialUI/arm/armBotUI.tsx index 12fdde8..4e1b01c 100644 --- a/app/src/modules/simulation/ui/arm/armBotUI.tsx +++ b/app/src/modules/simulation/spatialUI/arm/armBotUI.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { useSelectedAction, useSelectedEventData, useSelectedEventSphere, useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; +import { useSelectedAction, useSelectedEventSphere, useSelectedProduct } from '../../../../store/simulation/useSimulationStore'; import { useGLTF } from '@react-three/drei'; import { useThree } from '@react-three/fiber'; import { useProductStore } from '../../../../store/simulation/useProductStore'; diff --git a/app/src/modules/simulation/ui/arm/useDraggableGLTF.ts b/app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts similarity index 100% rename from app/src/modules/simulation/ui/arm/useDraggableGLTF.ts rename to app/src/modules/simulation/spatialUI/arm/useDraggableGLTF.ts diff --git a/app/src/modules/simulation/ui/temp.md b/app/src/modules/simulation/spatialUI/temp.md similarity index 100% rename from app/src/modules/simulation/ui/temp.md rename to app/src/modules/simulation/spatialUI/temp.md diff --git a/app/src/modules/simulation/ui/vehicle/useDraggableGLTF.ts b/app/src/modules/simulation/spatialUI/vehicle/useDraggableGLTF.ts similarity index 100% rename from app/src/modules/simulation/ui/vehicle/useDraggableGLTF.ts rename to app/src/modules/simulation/spatialUI/vehicle/useDraggableGLTF.ts diff --git a/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx b/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx new file mode 100644 index 0000000..843b81b --- /dev/null +++ b/app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx @@ -0,0 +1,409 @@ +import { useEffect, useRef, useState } from "react"; +import * as Types from "../../../../types/world/worldTypes"; +import startPoint from "../../../../assets/gltf-glb/arrow_green.glb"; +import startEnd from "../../../../assets/gltf-glb/arrow_red.glb"; +import { useGLTF } from "@react-three/drei"; +import { useFrame, useThree } from "@react-three/fiber"; +import { + useSelectedEventSphere, + useIsDragging, + useSelectedProduct, + useIsRotating, +} from "../../../../store/simulation/useSimulationStore"; +import { useVehicleStore } from "../../../../store/simulation/useVehicleStore"; +import { useProductStore } from "../../../../store/simulation/useProductStore"; +import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; +import { DoubleSide, Group, Plane, Vector3 } from "three"; + +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 { selectedProduct } = useSelectedProduct(); + const { getVehicleById } = useVehicleStore(); + const { updateEvent } = useProductStore(); + const [startPosition, setStartPosition] = useState<[number, number, number]>([ + 0, 1, 0, + ]); + + const [endPosition, setEndPosition] = useState<[number, number, number]>([ + 0, 1, 0, + ]); + const [startRotation, setStartRotation] = useState<[number, number, number]>([ + 0, 0, 0, + ]); + + const [endRotation, setEndRotation] = useState<[number, number, number]>([ + 0, 0, 0, + ]); + const [steeringRotation, setSteeringRotation] = useState< + [number, number, number] + >([0, 0, 0]); + + const { isDragging, setIsDragging } = useIsDragging(); + const { isRotating, setIsRotating } = useIsRotating(); + const { raycaster } = useThree(); + const [point, setPoint] = useState<[number, number, number]>([0, 0, 0]); + const plane = useRef(new Plane(new Vector3(0, 1, 0), 0)); + const [tubeRotation, setTubeRotation] = useState(false); + const tubeRef = useRef(null); + const outerGroup = useRef(null); + const state: Types.ThreeState = useThree(); + const controls: any = state.controls; + const [selectedVehicleData, setSelectedVechicleData] = useState<{ + position: [number, number, number]; + rotation: [number, number, number]; + }>({ position: [0, 0, 0], rotation: [0, 0, 0] }); + const CIRCLE_RADIUS = 0.8; + const email = localStorage.getItem("email"); + const organization = email!.split("@")[1].split(".")[0]; + + const updateBackend = ( + productName: string, + productId: string, + organization: string, + eventData: EventsSchema + ) => { + upsertProductOrEventApi({ + productName: productName, + productId: productId, + organization: organization, + eventDatas: eventData, + }); + }; + + useEffect(() => { + if (!selectedEventSphere) return; + const selectedVehicle = getVehicleById( + selectedEventSphere.userData.modelUuid + ); + + if (selectedVehicle) { + setSelectedVechicleData({ + position: selectedVehicle.position, + rotation: selectedVehicle.rotation, + }); + setPoint(selectedVehicle.point.position); + } + + setTimeout(() => { + if (selectedVehicle?.point?.action) { + const { pickUpPoint, unLoadPoint, steeringAngle } = + selectedVehicle.point.action; + + if (pickUpPoint && outerGroup.current) { + const worldPos = new Vector3( + pickUpPoint.position.x, + pickUpPoint.position.y, + pickUpPoint.position.z + ); + const localPosition = outerGroup.current.worldToLocal( + worldPos.clone() + ); + + setStartPosition([ + localPosition.x, + selectedVehicle.point.position[1], + localPosition.z, + ]); + setStartRotation([ + pickUpPoint.rotation.x, + pickUpPoint.rotation.y, + pickUpPoint.rotation.z, + ]); + } else { + setStartPosition([0, selectedVehicle.point.position[1] + 0.1, 1.5]); + setStartRotation([0, 0, 0]); + } + // end point + if (unLoadPoint && outerGroup.current) { + const worldPos = new Vector3( + unLoadPoint.position.x, + unLoadPoint.position.y, + unLoadPoint.position.z + ); + const localPosition = outerGroup.current.worldToLocal(worldPos); + + setEndPosition([ + localPosition.x, + selectedVehicle.point.position[1], + localPosition.z, + ]); + setEndRotation([ + unLoadPoint.rotation.x, + unLoadPoint.rotation.y, + unLoadPoint.rotation.z, + ]); + } else { + setEndPosition([0, selectedVehicle.point.position[1] + 0.1, -1.5]); + setEndRotation([0, 0, 0]); + } + setSteeringRotation([0, steeringAngle, 0]); + } + }, 10); + }, [selectedEventSphere, outerGroup.current]); + + const handlePointerDown = ( + e: any, + state: "start" | "end", + rotation: "start" | "end" + ) => { + if (e.object.name === "handle") { + const normalizedX = (e.clientX / window.innerWidth) * 2 - 1; + const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1; + prevMousePos.current = { x: normalizedX, y: normalizedY }; + setIsRotating(rotation); + if (controls) controls.enabled = false; + setIsDragging(null); + } else { + setIsDragging(state); + setIsRotating(null); + if (controls) controls.enabled = false; + } + }; + + const handlePointerUp = () => { + controls.enabled = true; + setIsDragging(null); + setIsRotating(null); + + if (selectedEventSphere?.userData.modelUuid) { + const updatedVehicle = getVehicleById( + selectedEventSphere.userData.modelUuid + ); + + let globalStartPosition = null; + let globalEndPosition = null; + + if (outerGroup.current && startMarker.current && endMarker.current) { + const worldPosStart = new Vector3(...startPosition); + globalStartPosition = outerGroup.current.localToWorld( + worldPosStart.clone() + ); + const worldPosEnd = new Vector3(...endPosition); + globalEndPosition = outerGroup.current.localToWorld( + worldPosEnd.clone() + ); + } + if (updatedVehicle && globalEndPosition && globalStartPosition) { + const event = updateEvent( + selectedProduct.productId, + selectedEventSphere.userData.modelUuid, + { + point: { + ...updatedVehicle.point, + action: { + ...updatedVehicle.point?.action, + pickUpPoint: { + position: { + x: globalStartPosition.x, + y: 0, + z: globalStartPosition.z, + }, + rotation: { x: 0, y: startRotation[1], z: 0 }, + }, + unLoadPoint: { + position: { + x: globalEndPosition.x, + y: 0, + z: globalEndPosition.z, + }, + rotation: { x: 0, y: endRotation[1], z: 0 }, + }, + steeringAngle: steeringRotation[1], + }, + }, + } + ); + + if (event) { + updateBackend( + selectedProduct.productName, + selectedProduct.productId, + organization, + event + ); + } + } + } + }; + + useFrame(() => { + if (!isDragging || !plane.current || !raycaster || !outerGroup.current) + return; + const intersectPoint = new Vector3(); + const intersects = raycaster.ray.intersectPlane( + plane.current, + intersectPoint + ); + if (!intersects) return; + const localPoint = outerGroup?.current.worldToLocal(intersectPoint.clone()); + if (isDragging === "start") { + setStartPosition([localPoint.x, point[1], localPoint.z]); + } else if (isDragging === "end") { + setEndPosition([localPoint.x, point[1], localPoint.z]); + } + }); + + useEffect(() => { + const handleGlobalPointerUp = () => { + setIsDragging(null); + setIsRotating(null); + setTubeRotation(false); + if (controls) controls.enabled = true; + handlePointerUp(); + }; + + if (isDragging || isRotating || tubeRotation) { + window.addEventListener("pointerup", handleGlobalPointerUp); + } + + return () => { + window.removeEventListener("pointerup", handleGlobalPointerUp); + }; + }, [ + isDragging, + isRotating, + startPosition, + startRotation, + endPosition, + endRotation, + tubeRotation, + steeringRotation, + outerGroup.current, + tubeRef.current, + ]); + + const prevSteeringY = useRef(0); + useFrame((state) => { + if (tubeRotation) { + const currentPointerX = state.pointer.x; + const deltaX = currentPointerX - prevMousePos.current.x; + prevMousePos.current.x = currentPointerX; + + const marker = tubeRef.current; + if (marker) { + const rotationSpeed = 2; + marker.rotation.y += deltaX * rotationSpeed; + setSteeringRotation([ + marker.rotation.x, + marker.rotation.y, + marker.rotation.z, + ]); + } + } else { + prevSteeringY.current = 0; + } + }); + useFrame((state) => { + if (!isRotating) return; + 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, + ]); + } + } + }); + + return selectedVehicleData ? ( + + { + e.stopPropagation(); + setTubeRotation(true); + prevMousePos.current.x = e.pointer.x; + controls.enabled = false; + }} + onPointerMissed={() => { + controls.enabled = true; + setTubeRotation(false); + }} + onPointerUp={() => { + controls.enabled = true; + setTubeRotation(false); + }} + > + ( + + + + + + + + + ) + + + {/* Start Marker */} + { + e.stopPropagation(); + handlePointerDown(e, "start", "start"); + }} + 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; diff --git a/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx b/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx deleted file mode 100644 index 33ab372..0000000 --- a/app/src/modules/simulation/ui/vehicle/vehicleUI.tsx +++ /dev/null @@ -1,372 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; -import * as Types from "../../../../types/world/worldTypes"; -import startPoint from "../../../../assets/gltf-glb/arrow_green.glb"; -import startEnd from "../../../../assets/gltf-glb/arrow_red.glb"; -import { useGLTF } from "@react-three/drei"; -import { useFrame, useThree } from "@react-three/fiber"; -import { - useSelectedEventSphere, - useIsDragging, - useIsRotating, -} from "../../../../store/simulation/useSimulationStore"; -import { useVehicleStore } from "../../../../store/simulation/useVehicleStore"; -import { useProductStore } from "../../../../store/simulation/useProductStore"; -import { useSelectedProduct } from "../../../../store/simulation/useSimulationStore"; -import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertProductOrEventApi"; -import { Box3, DoubleSide, Euler, Group, Mesh, Plane, Quaternion, Vector3 } from "three"; -import { position } from "html2canvas/dist/types/css/property-descriptors/position"; - -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 { selectedProduct } = useSelectedProduct(); - const { getVehicleById } = useVehicleStore(); - const { updateEvent } = useProductStore(); - const [startPosition, setStartPosition] = useState<[number, number, number]>([ - 0, 1, 0, - ]); - - - const [endPosition, setEndPosition] = useState<[number, number, number]>([ - 0, 1, 0, - ]); - const [startRotation, setStartRotation] = useState<[number, number, number]>([ - 0, 0, 0, - ]); - - const [endRotation, setEndRotation] = useState<[number, number, number]>([ - 0, 0, 0, - ]); - const [steeringRotation, setSteeringRotation] = useState< - [number, number, number] - >([0, 0, 0]); - - const { isDragging, setIsDragging } = useIsDragging(); - const { isRotating, setIsRotating } = useIsRotating(); - const { raycaster } = useThree(); - const [point, setPoint] = useState< - [number, number, number] - >([0, 0, 0]); - const plane = useRef(new Plane(new Vector3(0, 1, 0), 0)); - const [tubeRotation, setTubeRotation] = useState(false); - const tubeRef = useRef(null); - const outerGroup = useRef(null); - const state: Types.ThreeState = useThree(); - const controls: any = state.controls; - const [selectedVehicleData, setSelectedVechicleData] = useState< - { position: [number, number, number], rotation: [number, number, number], } - >({ position: [0, 0, 0], rotation: [0, 0, 0], }); - const CIRCLE_RADIUS = 0.8; - const email = localStorage.getItem("email"); - const organization = email!.split("@")[1].split(".")[0]; - - const updateBackend = ( - productName: string, - productId: string, - organization: string, - eventData: EventsSchema - ) => { - upsertProductOrEventApi({ - productName: productName, - productId: productId, - organization: organization, - eventDatas: eventData, - }); - }; - - useEffect(() => { - if (!selectedEventSphere) return; - const selectedVehicle = getVehicleById( - selectedEventSphere.userData.modelUuid - ); - - if (selectedVehicle) { - setSelectedVechicleData({ position: selectedVehicle.position, rotation: selectedVehicle.rotation }); - setPoint(selectedVehicle.point.position) - } - - - setTimeout(() => { - if (selectedVehicle?.point?.action) { - const { pickUpPoint, unLoadPoint, steeringAngle } = selectedVehicle.point.action; - - if (pickUpPoint && outerGroup.current) { - const worldPos = new Vector3( - pickUpPoint.position.x, - pickUpPoint.position.y, - pickUpPoint.position.z - ); - const localPosition = outerGroup.current.worldToLocal(worldPos.clone()); - - - setStartPosition([localPosition.x, selectedVehicle.point.position[1], localPosition.z, - ]); - setStartRotation([pickUpPoint.rotation.x, pickUpPoint.rotation.y, pickUpPoint.rotation.z,]) - } else { - setStartPosition([0, selectedVehicle.point.position[1] + 0.1, 1.5]); - setStartRotation([0, 0, 0]); - } - // end point - if (unLoadPoint && outerGroup.current) { - const worldPos = new Vector3(unLoadPoint.position.x, unLoadPoint.position.y, unLoadPoint.position.z); - const localPosition = outerGroup.current.worldToLocal(worldPos); - - setEndPosition([localPosition.x, selectedVehicle.point.position[1], localPosition.z]); - setEndRotation([unLoadPoint.rotation.x, unLoadPoint.rotation.y, unLoadPoint.rotation.z, - ]); - } else { - setEndPosition([0, selectedVehicle.point.position[1] + 0.1, -1.5]); - setEndRotation([0, 0, 0]); - } - setSteeringRotation([0, steeringAngle, 0]) - } - }, 10); - - - }, [selectedEventSphere, outerGroup.current]); - - const handlePointerDown = ( - e: any, - state: "start" | "end", - rotation: "start" | "end" - ) => { - if (e.object.name === "handle") { - const normalizedX = (e.clientX / window.innerWidth) * 2 - 1; - const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1; - prevMousePos.current = { x: normalizedX, y: normalizedY }; - setIsRotating(rotation); - if (controls) controls.enabled = false; - setIsDragging(null); - } else { - setIsDragging(state); - setIsRotating(null); - if (controls) controls.enabled = false; - } - }; - - const handlePointerUp = () => { - controls.enabled = true; - setIsDragging(null); - setIsRotating(null); - - if (selectedEventSphere?.userData.modelUuid) { - const updatedVehicle = getVehicleById( - selectedEventSphere.userData.modelUuid - ); - - let globalStartPosition = null; - let globalEndPosition = null; - - if (outerGroup.current && startMarker.current && endMarker.current) { - const worldPosStart = new Vector3(...startPosition); - globalStartPosition = outerGroup.current.localToWorld(worldPosStart.clone()); - const worldPosEnd = new Vector3(...endPosition); - globalEndPosition = outerGroup.current.localToWorld(worldPosEnd.clone()); - } - if (updatedVehicle && globalEndPosition && globalStartPosition) { - const event = updateEvent( - selectedProduct.productId, - selectedEventSphere.userData.modelUuid, - { - point: { - ...updatedVehicle.point, - action: { - ...updatedVehicle.point?.action, - pickUpPoint: { - position: { - x: globalStartPosition.x, - y: 0, - z: globalStartPosition.z, - }, - rotation: { x: 0, y: startRotation[1], z: 0 }, - }, - unLoadPoint: { - position: { - x: globalEndPosition.x, - y: 0, - z: globalEndPosition.z, - }, - rotation: { x: 0, y: endRotation[1], z: 0 }, - }, - steeringAngle: steeringRotation[1] - }, - }, - } - ); - - if (event) { - updateBackend( - selectedProduct.productName, - selectedProduct.productId, - organization, - event - ); - } - } - } - }; - - useFrame(() => { - if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return; - const intersectPoint = new Vector3(); - const intersects = raycaster.ray.intersectPlane(plane.current, intersectPoint); - if (!intersects) return; - const localPoint = outerGroup?.current.worldToLocal(intersectPoint.clone()); - if (isDragging === "start") { - if (startMarker.current) { - - } - setStartPosition([localPoint.x, point[1], localPoint.z]); - } else if (isDragging === "end") { - setEndPosition([localPoint.x, point[1], localPoint.z]); - } - }); - - useEffect(() => { - const handleGlobalPointerUp = () => { - setIsDragging(null); - setIsRotating(null); - setTubeRotation(false); - if (controls) controls.enabled = true; - handlePointerUp(); - - }; - - if (isDragging || isRotating || tubeRotation) { - window.addEventListener("pointerup", handleGlobalPointerUp); - } - - return () => { - window.removeEventListener("pointerup", handleGlobalPointerUp); - }; - }, [ - isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, tubeRotation, steeringRotation, outerGroup.current, tubeRef.current - ]); - - - const prevSteeringY = useRef(0); - useFrame((state) => { - if (tubeRotation) { - const currentPointerX = state.pointer.x; - const deltaX = currentPointerX - prevMousePos.current.x; - prevMousePos.current.x = currentPointerX; - - const marker = tubeRef.current; - if (marker) { - const rotationSpeed = 2; - marker.rotation.y += deltaX * rotationSpeed; - setSteeringRotation([marker.rotation.x, marker.rotation.y, marker.rotation.z]); - } - } else { - prevSteeringY.current = 0; - } - }); - useFrame((state) => { - if (!isRotating) return; - 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, - ]); - } - } - }); - - return selectedVehicleData ? ( - - { - e.stopPropagation(); - setTubeRotation(true); - prevMousePos.current.x = e.pointer.x; - controls.enabled = false; - }} - onPointerMissed={() => { - controls.enabled = true; - setTubeRotation(false); - }} - onPointerUp={() => { - controls.enabled = true; - setTubeRotation(false); - }} - > - ( - - - - - - - - - - ) - - - {/* Start Marker */} - { - e.stopPropagation(); - handlePointerDown(e, "start", "start"); - }} - 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; - - diff --git a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx index 7b29e2f..8660f90 100644 --- a/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx +++ b/app/src/modules/simulation/vehicle/instances/vehicleInstances.tsx @@ -1,20 +1,46 @@ -import React from 'react' -import VehicleInstance from './instance/vehicleInstance' -import { useVehicleStore } from '../../../../store/simulation/useVehicleStore' +import React from "react"; +import VehicleInstance from "./instance/vehicleInstance"; +import { useVehicleStore } from "../../../../store/simulation/useVehicleStore"; +import { Html } from "@react-three/drei"; +import { Vector3 } from "three"; +import AssetDetailsCard from "../../../../components/ui/simulation/AssetDetailsCard"; function VehicleInstances() { + const { vehicles } = useVehicleStore(); - const { vehicles } = useVehicleStore(); - - return ( + return ( + <> + {vehicles.map((vehicle: VehicleStatus) => ( <> - - {vehicles.map((vehicle: VehicleStatus) => - - )} - + + + + - ) + ))} + + ); } -export default VehicleInstances \ No newline at end of file +export default VehicleInstances; diff --git a/app/src/modules/simulation/vehicle/vehicles.tsx b/app/src/modules/simulation/vehicle/vehicles.tsx index 2b92403..b9293cd 100644 --- a/app/src/modules/simulation/vehicle/vehicles.tsx +++ b/app/src/modules/simulation/vehicle/vehicles.tsx @@ -2,19 +2,15 @@ import { useEffect, useState } from "react"; import { useVehicleStore } from "../../../store/simulation/useVehicleStore"; import { useSelectedEventSphere } from "../../../store/simulation/useSimulationStore"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; -import VehicleUI from "../ui/vehicle/vehicleUI"; import VehicleInstances from "./instances/vehicleInstances"; +import VehicleUI from "../spatialUI/vehicle/vehicleUI"; function Vehicles() { - const { vehicles, getVehicleById } = useVehicleStore(); + const { getVehicleById } = useVehicleStore(); const { selectedEventSphere } = useSelectedEventSphere(); const { isPlaying } = usePlayButtonStore(); const [isVehicleSelected, setIsVehicleSelected] = useState(false); - useEffect(() => { - // console.log('vehicles: ', vehicles); - }, [vehicles]) - useEffect(() => { if (selectedEventSphere) { const selectedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid); @@ -24,7 +20,7 @@ function Vehicles() { setIsVehicleSelected(false); } } - }, [selectedEventSphere]) + }, [getVehicleById, selectedEventSphere]) return ( <> diff --git a/app/src/styles/components/simulation/simulation.scss b/app/src/styles/components/simulation/simulation.scss index fb9b6ad..c28bb58 100644 --- a/app/src/styles/components/simulation/simulation.scss +++ b/app/src/styles/components/simulation/simulation.scss @@ -315,7 +315,6 @@ } .open { - .start-displayer, .end-displayer { display: none; @@ -401,4 +400,138 @@ } } } -} \ No newline at end of file +} + +.asset-details-card-wrapper { + pointer-events: none; + .asset-details-card-container { + position: relative; + padding: 8px; + background: var(--background-color); + backdrop-filter: blur(6px); + border-radius: #{$border-radius-large}; + transform: translate(0, -100%); + z-index: 0; + box-shadow: inset 0px 10px 50px #80808075; + min-width: 124px; + + &::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + background: linear-gradient(135deg, var(--accent-color), #ff00f000); + background-size: 400% 400%; + animation: borderAnimation 5s linear infinite; + -webkit-mask: linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + z-index: -1; + padding: 1px; + } + + .asset-details-header { + @include flex-space-between; + gap: 12px; + .content { + .name { + text-wrap: nowrap; + max-width: 140px; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 4px; + text-transform: capitalize; + } + .status-container { + .status { + display: flex; + align-items: center; + gap: 6px; + .icon { + @include flex-center; + } + } + } + } + } + + .process-running-container { + width: 100%; + height: 8px; + background: var(--background-color-solid); + margin-top: 12px; + border-radius: #{$border-radius-small}; + overflow: hidden; + position: relative; + .process-running { + height: 100%; + width: 35%; + border-radius: #{$border-radius-small}; + background: var(--process-color); + animation: playing-process 1s ease-in-out infinite; + } + } + + .indication-arrow { + position: absolute; + left: 50%; + bottom: 0; + transform: translate(-50%, 10px); + filter: drop-shadow(0px 0px 4px #ffffff); + } + + .count-ui-wrapper { + position: absolute; + right: -42px; + top: 5px; + padding: 4px; + padding-right: 8px; + .count-ui-container { + @include flex-center; + gap: 6px; + .icon{ + @include flex-center; + padding: 3px; + border-radius: #{$border-radius-circle}; + background: var(--background-color-accent); + svg { + scale: 0.6; + } + } + .value{ + position: absolute; + width: 48px; + background: var(--background-color-solid-gradient); + border-radius: #{$border-radius-large}; + outline: 1px solid var(--border-color); + padding: 4px 10px; + padding-left: 16px; + transform: translateX(28px); + z-index: -1; + } + } + } + } +} + +@keyframes playing-process { + from { + transform: translateX(-100%); + } + to { + transform: translateX(300%); + } +} + +@keyframes borderAnimation { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index 1982dae..9a072e7 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -962,8 +962,6 @@ } } - - .widget-placeholder { background-color: gray; border-radius: 6px; @@ -971,7 +969,3 @@ justify-content: center; align-items: center; } - -.dragging { - display: none; -} \ No newline at end of file