feat: Enhance vehicle simulation UI with draggable points and asset details
- Updated VehicleInstances component to include asset details card for each vehicle. - Refactored Vehicles component to remove unused vehicle state logging. - Added new styles for asset details card in simulation.scss. - Removed unnecessary styles from realTimeViz.scss. - Implemented PickDropPoints component for draggable pick and drop locations. - Created ArmBotUI component to manage robotic arm interactions and actions. - Developed useDraggableGLTF hook for handling drag-and-drop functionality for GLTF models. - Introduced VehicleUI component to visualize vehicle start and end points with rotation controls. - Updated backend integration for vehicle actions and positions.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
export function AnalysisIcon({ isActive }: { isActive: boolean }) {
|
||||
export function AnalysisIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
@@ -27,7 +27,7 @@ export function AnalysisIcon({ isActive }: { isActive: boolean }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function MechanicsIcon({ isActive }: { isActive: boolean }) {
|
||||
export function MechanicsIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
@@ -52,7 +52,7 @@ export function MechanicsIcon({ isActive }: { isActive: boolean }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function PropertiesIcon({ isActive }: { isActive: boolean }) {
|
||||
export function PropertiesIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
@@ -83,7 +83,7 @@ export function PropertiesIcon({ isActive }: { isActive: boolean }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function SimulationIcon({ isActive }: { isActive: boolean }) {
|
||||
export function SimulationIcon({ isActive }: Readonly<{ isActive: boolean }>) {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
@@ -222,3 +222,111 @@ export function MoveArrowLeft() {
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
// simulation card icons
|
||||
|
||||
export function ExpandIcon({
|
||||
color = "#6F42C1",
|
||||
}: Readonly<{ color?: string }>) {
|
||||
return (
|
||||
<svg
|
||||
width="30"
|
||||
height="30"
|
||||
viewBox="0 0 30 30"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="15" cy="15" r="15" fill={color} />
|
||||
<path
|
||||
d="M20.4993 11.0263C20.5138 10.7505 20.302 10.5152 20.0263 10.5007L15.5325 10.2642C15.2567 10.2497 15.0214 10.4614 15.0069 10.7372C14.9924 11.013 15.2042 11.2483 15.4799 11.2628L19.4744 11.473L19.2642 15.4675C19.2497 15.7433 19.4614 15.9786 19.7372 15.9931C20.013 16.0076 20.2483 15.7958 20.2628 15.5201L20.4993 11.0263ZM10 20L10.3345 20.3716L20.3345 11.3716L20 11L19.6655 10.6284L9.66552 19.6284L10 20Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function SimulationStatusIcon({
|
||||
color = "#21FF59",
|
||||
}: Readonly<{ color?: string }>) {
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M7.46647 12.6024C10.3014 12.6024 12.5997 10.3041 12.5997 7.46916C12.5997 4.63416 10.3014 2.33594 7.46647 2.33594C4.63147 2.33594 2.33325 4.63416 2.33325 7.46916C2.33325 10.3041 4.63147 12.6024 7.46647 12.6024Z"
|
||||
fill={color}
|
||||
fillOpacity="0.3"
|
||||
/>
|
||||
<path
|
||||
d="M8.79738 8.04925L6.83063 9.26848C6.51405 9.46474 6.125 9.20928 6.125 8.80516V6.3667C6.125 5.96258 6.51405 5.70714 6.83063 5.9034L8.79738 7.12263C9.12309 7.32458 9.12309 7.8473 8.79738 8.04925Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function IndicationArrow() {
|
||||
return (
|
||||
<svg
|
||||
width="15"
|
||||
height="6"
|
||||
viewBox="0 0 15 6"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.09697 5.03095L14.5 0L0 0L6.88778 5.05104C7.24969 5.31644 7.74408 5.30822 8.09697 5.03095Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function CartBagIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2 3L2.26491 3.0883C3.58495 3.52832 4.24497 3.74832 4.62248 4.2721C5 4.79587 5 5.49159 5 6.88304V9.5C5 12.3284 5 13.7426 5.87868 14.6213C6.75736 15.5 8.17157 15.5 11 15.5H19"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
opacity="0.5"
|
||||
d="M7.5 18C8.32843 18 9 18.6716 9 19.5C9 20.3284 8.32843 21 7.5 21C6.67157 21 6 20.3284 6 19.5C6 18.6716 6.67157 18 7.5 18Z"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
opacity="0.5"
|
||||
d="M16.5 18C17.3284 18 18 18.6715 18 19.5C18 20.3284 17.3284 21 16.5 21C15.6716 21 15 20.3284 15 19.5C15 18.6715 15.6716 18 16.5 18Z"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
opacity="0.5"
|
||||
d="M11 9H8"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M5 6H16.4504C18.5054 6 19.5328 6 19.9775 6.67426C20.4221 7.34853 20.0173 8.29294 19.2078 10.1818L18.7792 11.1818C18.4013 12.0636 18.2123 12.5045 17.8366 12.7523C17.4609 13 16.9812 13 16.0218 13H5"
|
||||
stroke="#FFFFFF"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div className="asset-details-card-wrapper">
|
||||
|
||||
</div>
|
||||
)
|
||||
interface AssetDetailsCardInterface {
|
||||
name: string;
|
||||
status: string;
|
||||
count?: number;
|
||||
totalCapacity?: number;
|
||||
assetDetails?: {
|
||||
assetName: string;
|
||||
const: string;
|
||||
performance: string;
|
||||
};
|
||||
}
|
||||
|
||||
export default AssetDetailsCard
|
||||
const GetStatus = (status: string) => {
|
||||
// "idle" | "running" | "stopped" | "disabled" | "error"
|
||||
switch (status) {
|
||||
case "idle":
|
||||
return (
|
||||
<div className="status">
|
||||
<div className="icon">
|
||||
<SimulationStatusIcon color="#FFD321" />
|
||||
</div>
|
||||
<div className="value">Idle</div>
|
||||
</div>
|
||||
);
|
||||
case "running":
|
||||
return (
|
||||
<div className="status">
|
||||
<div className="icon">
|
||||
<SimulationStatusIcon color="#21FF59" />
|
||||
</div>
|
||||
<div className="value">Running</div>
|
||||
</div>
|
||||
);
|
||||
case "stopped":
|
||||
return (
|
||||
<div className="status">
|
||||
<div className="icon">
|
||||
<SimulationStatusIcon color="#FF1010" />
|
||||
</div>
|
||||
<div className="value">Stopped</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const AssetDetailsCard: React.FC<AssetDetailsCardInterface> = ({
|
||||
name,
|
||||
status,
|
||||
count,
|
||||
totalCapacity,
|
||||
assetDetails,
|
||||
}) => {
|
||||
const [moreDetails, setMoreDetails] = useState(false);
|
||||
// hooks
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
|
||||
return (
|
||||
<div
|
||||
className="asset-details-card-wrapper"
|
||||
style={{ display: isPlaying ? "" : "none" }}
|
||||
>
|
||||
<div className="asset-details-card-container">
|
||||
<div className="asset-details-header">
|
||||
<div className="content">
|
||||
<div className="name">{name}</div>
|
||||
<div className="status-container">{GetStatus(status)}</div>
|
||||
</div>
|
||||
<button
|
||||
className="expand-button"
|
||||
onClick={() => {
|
||||
setMoreDetails(!moreDetails);
|
||||
}}
|
||||
>
|
||||
<ExpandIcon />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{totalCapacity && (
|
||||
<div className="count-ui-wrapper">
|
||||
<div className="count-ui-container">
|
||||
<div className="icon">
|
||||
<CartBagIcon />
|
||||
</div>
|
||||
<div className="value">{count?.toString()}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === "running" && (
|
||||
<div className={`process-running-container ${status}`}>
|
||||
<div
|
||||
className="process-running"
|
||||
style={{ "--process-color": "#21FF59" } as React.CSSProperties}
|
||||
></div>
|
||||
</div>
|
||||
)}
|
||||
<div className="indication-arrow">
|
||||
<IndicationArrow />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssetDetailsCard;
|
||||
|
||||
@@ -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) => (
|
||||
<RoboticArmInstance key={robot.modelUuid} armBot={robot} />
|
||||
))}
|
||||
<RoboticArmInstance key={robot.modelUuid} armBot={robot} />
|
||||
<Html
|
||||
// data
|
||||
position={
|
||||
new Vector3(
|
||||
robot.position[0],
|
||||
robot.point.position[1],
|
||||
robot.position[2]
|
||||
)
|
||||
}
|
||||
// class none
|
||||
// other
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
center
|
||||
distanceFactor={20}
|
||||
>
|
||||
<AssetDetailsCard name={robot.modelName} status={robot.state} />
|
||||
</Html>
|
||||
</>
|
||||
)
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default RoboticArmInstances;
|
||||
@@ -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 (
|
||||
<>
|
||||
|
||||
@@ -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';
|
||||
409
app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx
Normal file
409
app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx
Normal file
@@ -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<Group>(null);
|
||||
const endMarker = useRef<Group>(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<boolean>(false);
|
||||
const tubeRef = useRef<Group>(null);
|
||||
const outerGroup = useRef<Group>(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 ? (
|
||||
<group
|
||||
position={selectedVehicleData.position}
|
||||
rotation={selectedVehicleData.rotation}
|
||||
ref={outerGroup}
|
||||
>
|
||||
<group
|
||||
position={[0, 0, 0]}
|
||||
ref={tubeRef}
|
||||
rotation={steeringRotation}
|
||||
onPointerDown={(e) => {
|
||||
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);
|
||||
}}
|
||||
>
|
||||
(
|
||||
<mesh
|
||||
position={[0, point[1], 0]}
|
||||
rotation={[-Math.PI / 2, 0, 0]}
|
||||
name="steering"
|
||||
>
|
||||
<ringGeometry args={[CIRCLE_RADIUS, CIRCLE_RADIUS + 0.2, 36]} />
|
||||
<meshBasicMaterial color="yellow" side={DoubleSide} />
|
||||
</mesh>
|
||||
<mesh
|
||||
position={[0, point[1], CIRCLE_RADIUS + 0.24]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
>
|
||||
<coneGeometry args={[0.1, 0.3, 12]} />
|
||||
<meshBasicMaterial color="yellow" side={DoubleSide} />
|
||||
</mesh>
|
||||
)
|
||||
</group>
|
||||
|
||||
{/* Start Marker */}
|
||||
<primitive
|
||||
name="startMarker"
|
||||
object={startScene}
|
||||
ref={startMarker}
|
||||
position={startPosition}
|
||||
rotation={startRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "start", "start");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
controls.enabled = true;
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* End Marker */}
|
||||
<primitive
|
||||
name="endMarker"
|
||||
object={endScene}
|
||||
ref={endMarker}
|
||||
position={endPosition}
|
||||
rotation={endRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "end", "end");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
controls.enabled = true;
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
}}
|
||||
/>
|
||||
</group>
|
||||
) : null;
|
||||
};
|
||||
export default VehicleUI;
|
||||
@@ -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<Group>(null);
|
||||
const endMarker = useRef<Group>(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<boolean>(false);
|
||||
const tubeRef = useRef<Group>(null);
|
||||
const outerGroup = useRef<Group>(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 ? (
|
||||
<group position={selectedVehicleData.position} rotation={selectedVehicleData.rotation} ref={outerGroup}>
|
||||
<group
|
||||
position={[0, 0, 0]}
|
||||
ref={tubeRef}
|
||||
rotation={steeringRotation}
|
||||
onPointerDown={(e) => {
|
||||
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);
|
||||
}}
|
||||
>
|
||||
(
|
||||
<mesh
|
||||
position={[0, point[1], 0]}
|
||||
rotation={[-Math.PI / 2, 0, 0]}
|
||||
name="steering"
|
||||
>
|
||||
<ringGeometry args={[CIRCLE_RADIUS, CIRCLE_RADIUS + 0.2, 36]} />
|
||||
<meshBasicMaterial color="yellow" side={DoubleSide} />
|
||||
|
||||
</mesh>
|
||||
<mesh position={[0, point[1], CIRCLE_RADIUS + 0.24]} rotation={[Math.PI / 2, 0, 0]}>
|
||||
<coneGeometry args={[0.1, 0.3, 12]} />
|
||||
<meshBasicMaterial color="yellow" side={DoubleSide} />
|
||||
</mesh>
|
||||
)
|
||||
</group>
|
||||
|
||||
{/* Start Marker */}
|
||||
<primitive
|
||||
name="startMarker"
|
||||
object={startScene}
|
||||
ref={startMarker}
|
||||
position={startPosition}
|
||||
rotation={startRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "start", "start");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
controls.enabled = true;
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* End Marker */}
|
||||
<primitive
|
||||
name="endMarker"
|
||||
object={endScene}
|
||||
ref={endMarker}
|
||||
position={endPosition}
|
||||
rotation={endRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "end", "end");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
controls.enabled = true;
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
}}
|
||||
/>
|
||||
</group>
|
||||
) : null;
|
||||
};
|
||||
export default VehicleUI;
|
||||
|
||||
|
||||
@@ -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) =>
|
||||
<VehicleInstance agvDetail={vehicle} key={vehicle.modelUuid} />
|
||||
)}
|
||||
|
||||
<VehicleInstance agvDetail={vehicle} key={vehicle.modelUuid} />
|
||||
<Html
|
||||
// data
|
||||
position={
|
||||
new Vector3(
|
||||
vehicle.position[0],
|
||||
vehicle.point.position[1],
|
||||
vehicle.position[2]
|
||||
)
|
||||
}
|
||||
// class none
|
||||
// other
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
center
|
||||
distanceFactor={20}
|
||||
>
|
||||
<AssetDetailsCard
|
||||
name={vehicle.modelName}
|
||||
status={vehicle.state}
|
||||
count={vehicle.currentLoad}
|
||||
totalCapacity={vehicle.point.action.loadCapacity}
|
||||
/>
|
||||
</Html>
|
||||
</>
|
||||
)
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default VehicleInstances
|
||||
export default VehicleInstances;
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
|
||||
@@ -315,7 +315,6 @@
|
||||
}
|
||||
|
||||
.open {
|
||||
|
||||
.start-displayer,
|
||||
.end-displayer {
|
||||
display: none;
|
||||
@@ -402,3 +401,137 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user