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

This commit is contained in:
2025-07-08 17:33:55 +05:30
12 changed files with 535 additions and 298 deletions

View File

@@ -16,11 +16,11 @@ interface ConnectionLine {
}
export function Arrows({ connections }: { readonly connections: ConnectionLine[] }) {
const [hoveredLineKey, setHoveredLineKey] = useState<string | null>(null);
const [hoveredArrowTrigger, setHoveredArrowTrigger] = useState<string | null>(null);
const groupRef = useRef<THREE.Group>(null);
const { scene } = useThree();
const { toolMode } = useToolMode();
const { eventStore, productStore } = useSceneContext();
const { productStore } = useSceneContext();
const { removeTrigger } = productStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
@@ -103,15 +103,15 @@ export function Arrows({ connections }: { readonly connections: ConnectionLine[]
return (
<group
key={key}
onPointerOver={() => setHoveredLineKey(trigger.triggerUuid)}
onPointerOut={() => setHoveredLineKey(null)}
onPointerOver={() => setHoveredArrowTrigger(trigger.triggerUuid)}
onPointerOut={() => setHoveredArrowTrigger(null)}
onClick={() => { removeConnection(trigger) }}
>
<mesh
geometry={shaftGeometry}
>
<meshStandardMaterial
color={toolMode === '3D-Delete' && hoveredLineKey === trigger.triggerUuid ? "red" : "#42a5f5"}
color={toolMode === '3D-Delete' && hoveredArrowTrigger === trigger.triggerUuid ? "red" : "#42a5f5"}
/>
</mesh>
<mesh
@@ -120,7 +120,7 @@ export function Arrows({ connections }: { readonly connections: ConnectionLine[]
geometry={headGeometry}
>
<meshStandardMaterial
color={toolMode === '3D-Delete' && hoveredLineKey === trigger.triggerUuid ? "red" : "#42a5f5"}
color={toolMode === '3D-Delete' && hoveredArrowTrigger === trigger.triggerUuid ? "red" : "#42a5f5"}
/>
</mesh>
</group>

View File

@@ -1,38 +1,46 @@
import { useEffect, useRef, useState } from "react";
import * as THREE from "three";
import useModuleStore, { useSubModuleStore } from "../../../../../store/useModuleStore";
import { useToolMode } from "../../../../../store/builder/store";
import { TransformControls } from "@react-three/drei";
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
import { useSelectedEventSphere, useSelectedEventData, } from "../../../../../store/simulation/useSimulationStore";
import { useSelectedEventSphere, useSelectedEventData, useDeletableEventSphere } from "../../../../../store/simulation/useSimulationStore";
import { useThree } from "@react-three/fiber";
import { usePlayButtonStore } from "../../../../../store/usePlayButtonStore";
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../products/productContext";
import { useParams } from "react-router-dom";
import { useToolMode } from "../../../../../store/builder/store";
import { useVersionContext } from "../../../../builder/version/versionContext";
import { useSceneContext } from "../../../../scene/sceneContext";
import PointInstances from "../instances/pointInstances";
function PointsCreator() {
const { gl, raycaster, scene, pointer, camera } = useThree();
const { subModule } = useSubModuleStore();
const { toolMode } = useToolMode();
const { activeModule } = useModuleStore();
const { selectedProductStore } = useProductContext();
const { eventStore, productStore } = useSceneContext();
const { events, updatePoint, getPointByUuid, getEventByModelUuid } = eventStore();
const { getEventByModelUuid: getEventByModelUuidFromProduct, updatePoint: updatePointFromProduct, getEventByModelUuid: getEventByModelUuidFromProduct2, getPointByUuid: getPointByUuidFromProduct } = productStore();
const { getEventByModelUuid } = eventStore();
const { getEventByModelUuid: getEventByModelUuidFromProduct, updatePoint: updatePointFromProduct, getPointByUuid: getPointByUuidFromProduct, getTriggersByTriggeredPointUuid, removeTrigger, removePoint } = productStore();
const { selectedProduct } = selectedProductStore();
const { activeModule } = useModuleStore();
const transformRef = useRef<any>(null);
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const { selectedEventSphere, setSelectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere();
const { selectedEventSphere, clearSelectedEventSphere, } = useSelectedEventSphere();
const { clearDeletableEventSphere } = useDeletableEventSphere();
const { setSelectedEventData, clearSelectedEventData } = useSelectedEventData();
const { isPlaying } = usePlayButtonStore();
const { toolMode } = useToolMode();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
useEffect(() => {
clearSelectedEventSphere();
clearSelectedEventData();
clearDeletableEventSphere();
}, [toolMode, activeModule])
const updateBackend = (
productName: string,
productUuid: string,
@@ -75,65 +83,87 @@ function PointsCreator() {
if (keyCombination === "R") {
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
}
if (keyCombination === 'DELETE') {
deletePointfromConveyor(selectedEventSphere);
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [selectedEventSphere]);
const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
let point: PointsScheme = JSON.parse(
JSON.stringify(
getPointByUuid(
selectedEventSphere.userData.modelUuid,
selectedEventSphere.userData.pointUuid
)
)
);
if (point) {
point.position = [
selectedEventSphere.position.x,
selectedEventSphere.position.y,
selectedEventSphere.position.z,
];
point.rotation = [
selectedEventSphere.rotation.x,
selectedEventSphere.rotation.y,
selectedEventSphere.rotation.z,
];
const deletePointfromConveyor = (selectedEventSphere: THREE.Mesh) => {
const eventModel = getEventByModelUuidFromProduct(selectedProduct.productUuid, selectedEventSphere.userData.modelUuid);
if (!eventModel || eventModel.type !== 'transfer' || eventModel.points.length < 2) return;
const event = getEventByModelUuidFromProduct(selectedProduct.productUuid, selectedEventSphere.userData.modelUuid);
const triggers = getTriggersByTriggeredPointUuid(selectedProduct.productUuid, selectedEventSphere.userData.pointUuid);
if (event && selectedProduct.productUuid !== '') {
const updatedEvents: EventsSchema[] = [];
if (triggers.length > 0) {
triggers.map((trigger) => {
const event = removeTrigger(selectedProduct.productUuid, trigger.triggerUuid);
if (event) {
updatedEvents.push(event);
}
})
}
const updatedPoint = JSON.parse(
JSON.stringify(
getPointByUuidFromProduct(selectedProduct.productUuid, selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid)
)
const event = removePoint(selectedProduct.productUuid, selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid)
if (event) {
updatedEvents.push(event);
}
if (updatedEvents.length > 0) {
updatedEvents.map((updatedEvent) => {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
updatedEvent
);
if (updatedPoint) {
updatedPoint.position = point.position;
updatedPoint.rotation = point.rotation;
})
}
}
const updatedEvent = updatePointFromProduct(
const updatePointToState = (selectedEventSphere: THREE.Mesh) => {
const position = [
selectedEventSphere.position.x,
selectedEventSphere.position.y,
selectedEventSphere.position.z,
];
const rotation = [
selectedEventSphere.rotation.x,
selectedEventSphere.rotation.y,
selectedEventSphere.rotation.z,
];
const event = getEventByModelUuidFromProduct(selectedProduct.productUuid, selectedEventSphere.userData.modelUuid);
if (event && selectedProduct.productUuid !== '') {
const updatedPoint = JSON.parse(
JSON.stringify(
getPointByUuidFromProduct(selectedProduct.productUuid, selectedEventSphere.userData.modelUuid, selectedEventSphere.userData.pointUuid)
)
);
if (updatedPoint) {
updatedPoint.position = position;
updatedPoint.rotation = rotation;
const updatedEvent = updatePointFromProduct(
selectedProduct.productUuid,
selectedEventSphere.userData.modelUuid,
selectedEventSphere.userData.pointUuid,
updatedPoint
)
if (updatedEvent) {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
selectedEventSphere.userData.modelUuid,
selectedEventSphere.userData.pointUuid,
updatedPoint
)
if (updatedEvent) {
updatePoint(
selectedEventSphere.userData.modelUuid,
selectedEventSphere.userData.pointUuid,
point
)
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
updatedEvent
);
}
projectId || '',
updatedEvent
);
}
}
}
@@ -153,9 +183,7 @@ function PointsCreator() {
const onMouseUp = () => {
if (selectedEventSphere && !drag) {
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster
.intersectObjects(scene.children, true)
.filter((intersect) => intersect.object.name === "Event-Sphere");
const intersects = raycaster.intersectObjects(scene.children, true).filter((intersect) => intersect.object.name === "Event-Sphere");
if (intersects.length === 0) {
clearSelectedEventSphere();
setTransformMode(null);
@@ -188,205 +216,7 @@ function PointsCreator() {
{activeModule === "simulation" && (
<>
<group name="EventPointsGroup" visible={!isPlaying}>
{events.map((event, index) => {
const updatedEvent = selectedProduct.productUuid !== ''
? getEventByModelUuidFromProduct2(selectedProduct.productUuid, event.modelUuid)
: null;
const usedEvent = updatedEvent || event;
if (usedEvent.type === "transfer") {
return (
<group
key={`${index}-${usedEvent.modelUuid}`}
position={usedEvent.position}
rotation={usedEvent.rotation}
>
{usedEvent.points.map((point, j) => (
<mesh
name="Event-Sphere"
uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
if (toolMode === 'cursor') {
setSelectedEventSphere(
sphereRefs.current[point.uuid]
);
}
}}
key={`${index}-${point.uuid}`}
position={new THREE.Vector3(...point.position)}
userData={{
modelUuid: usedEvent.modelUuid,
pointUuid: point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="orange" />
</mesh>
))}
</group>
);
} else if (usedEvent.type === "vehicle") {
const point = usedEvent.point;
return (
<group
key={`${index}-${usedEvent.modelUuid}`}
position={usedEvent.position}
rotation={usedEvent.rotation}
>
<mesh
name="Event-Sphere"
uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
if (toolMode === 'cursor') {
setSelectedEventSphere(
sphereRefs.current[point.uuid]
);
}
}}
position={new THREE.Vector3(...point.position)}
userData={{
modelUuid: usedEvent.modelUuid,
pointUuid: point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="blue" />
</mesh>
</group>
);
} else if (usedEvent.type === "roboticArm") {
const point = usedEvent.point;
return (
<group
key={`${index}-${usedEvent.modelUuid}`}
position={usedEvent.position}
rotation={usedEvent.rotation}
>
<mesh
name="Event-Sphere"
uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
if (toolMode === 'cursor') {
setSelectedEventSphere(
sphereRefs.current[point.uuid]
);
}
}}
position={new THREE.Vector3(...point.position)}
userData={{
modelUuid: usedEvent.modelUuid,
pointUuid: point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="green" />
</mesh>
</group>
);
} else if (usedEvent.type === "machine") {
const point = usedEvent.point;
return (
<group
key={`${index}-${usedEvent.modelUuid}`}
position={usedEvent.position}
rotation={usedEvent.rotation}
>
<mesh
name="Event-Sphere"
uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
if (toolMode === 'cursor') {
setSelectedEventSphere(
sphereRefs.current[point.uuid]
);
}
}}
position={new THREE.Vector3(...point.position)}
userData={{
modelUuid: usedEvent.modelUuid,
pointUuid: point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="purple" />
</mesh>
</group>
);
} else if (usedEvent.type === "storageUnit") {
const point = usedEvent.point;
return (
<group
key={`${index}-${usedEvent.modelUuid}`}
position={usedEvent.position}
rotation={usedEvent.rotation}
>
<mesh
name="Event-Sphere"
uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
if (toolMode === 'cursor') {
setSelectedEventSphere(
sphereRefs.current[point.uuid]
);
}
}}
position={new THREE.Vector3(...point.position)}
userData={{
modelUuid: usedEvent.modelUuid,
pointUuid: point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh>
</group>
);
} else if (usedEvent.type === "human") {
const point = usedEvent.point;
return (
<group
key={`${index}-${usedEvent.modelUuid}`}
position={usedEvent.position}
rotation={usedEvent.rotation}
>
<mesh
name="Event-Sphere"
uuid={point.uuid}
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
e.stopPropagation();
if (toolMode === 'cursor') {
setSelectedEventSphere(
sphereRefs.current[point.uuid]
);
}
}}
position={new THREE.Vector3(...point.position)}
userData={{
modelUuid: usedEvent.modelUuid,
pointUuid: point.uuid,
}}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="white" />
</mesh>
</group>
);
} else {
return null;
}
})}
<PointInstances />
</group>
{selectedEventSphere && transformMode && (

View File

@@ -0,0 +1,121 @@
import * as THREE from "three";
import { MeshProps } from "@react-three/fiber";
import { useRef } from "react";
import { useToolMode } from "../../../../../../store/builder/store";
import { useDeletableEventSphere, useSelectedEventSphere } from "../../../../../../store/simulation/useSimulationStore";
import { useSceneContext } from "../../../../../scene/sceneContext";
import { useProductContext } from "../../../../products/productContext";
import { useVersionContext } from "../../../../../builder/version/versionContext";
import { useParams } from "react-router-dom";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
interface PointInstanceProps extends Omit<MeshProps, 'ref'> {
point: {
uuid: string;
position: [number, number, number];
};
modelUuid: string;
color: string;
}
export default function PointInstance({
point,
modelUuid,
color,
...meshProps
}: PointInstanceProps) {
const ref = useRef<THREE.Mesh>(null);
const { setSelectedEventSphere } = useSelectedEventSphere();
const { deletableEventSphere, setDeletableEventSphere, clearDeletableEventSphere } = useDeletableEventSphere();
const { toolMode } = useToolMode();
const { productStore } = useSceneContext();
const { getEventByModelUuid, getTriggersByTriggeredPointUuid, removeTrigger, removePoint } = productStore();
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams();
const updateBackend = (
productName: string,
productUuid: string,
projectId: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productUuid: productUuid,
projectId: projectId,
eventDatas: eventData,
versionId: selectedVersion?.versionId || '',
})
}
const handleEventPointDelete = () => {
const eventModel = getEventByModelUuid(selectedProduct.productUuid, modelUuid);
if (!eventModel || eventModel.type !== 'transfer' || eventModel.points.length < 2) return;
const triggers = getTriggersByTriggeredPointUuid(selectedProduct.productUuid, point.uuid);
const updatedEvents: EventsSchema[] = [];
if (triggers.length > 0) {
triggers.map((trigger) => {
const event = removeTrigger(selectedProduct.productUuid, trigger.triggerUuid);
if (event) {
updatedEvents.push(event);
}
})
}
const event = removePoint(selectedProduct.productUuid, modelUuid, point.uuid)
if (event) {
updatedEvents.push(event);
}
if (updatedEvents.length > 0) {
updatedEvents.map((updatedEvent) => {
updateBackend(
selectedProduct.productName,
selectedProduct.productUuid,
projectId || '',
updatedEvent
);
})
}
}
return (
<mesh
{...meshProps}
ref={ref}
name="Event-Sphere"
uuid={point.uuid}
position={new THREE.Vector3(...point.position)}
onClick={(e) => {
e.stopPropagation();
if (toolMode === 'cursor') {
setSelectedEventSphere(ref.current);
} else if (toolMode === '3D-Delete') {
handleEventPointDelete()
}
}}
onPointerOver={(e) => {
e.stopPropagation();
if (toolMode === '3D-Delete' && (!deletableEventSphere || deletableEventSphere.uuid !== point.uuid)) {
setDeletableEventSphere(ref.current);
}
}}
onPointerOut={(e) => {
e.stopPropagation();
if (toolMode === '3D-Delete' && deletableEventSphere && deletableEventSphere.uuid === point.uuid) {
clearDeletableEventSphere();
}
}}
userData={{ modelUuid, pointUuid: point.uuid }}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color={color} />
</mesh>
);
}

View File

@@ -0,0 +1,56 @@
import { useProductContext } from "../../../products/productContext";
import { useSceneContext } from "../../../../scene/sceneContext";
import PointInstance from "./instance/pointInstance";
function PointInstances() {
const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore();
const { eventStore, productStore } = useSceneContext();
const { events } = eventStore();
const { getEventByModelUuid } = productStore();
const colorByType: Record<string, string> = {
transfer: "orange",
vehicle: "blue",
roboticArm: "green",
machine: "purple",
storageUnit: "red",
human: "white",
};
return (
<>
{events.map((event, index) => {
const updatedEvent = selectedProduct.productUuid !== '' ? getEventByModelUuid(selectedProduct.productUuid, event.modelUuid) : null;
const usedEvent = updatedEvent || event;
const color = colorByType[usedEvent.type];
if (!color) return null;
const points = usedEvent.type === "transfer" ? usedEvent.points : [usedEvent.point];
return (
<group
key={`${index}-${usedEvent.modelUuid}`}
position={usedEvent.position}
rotation={usedEvent.rotation}
>
{points.map((point) => (
<PointInstance
key={point.uuid}
point={point}
modelUuid={usedEvent.modelUuid}
color={color}
/>
))}
</group>
);
})}
</>
);
}
export default PointInstances;

View File

@@ -54,9 +54,16 @@ export function useHumanEventManager() {
callbacksRef.current.forEach(({ humanId, callback }) => {
const human = getHumanById(humanId);
if (human && human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) {
callback();
removeHumanFromMonitor(humanId); // Remove after triggering
if (human?.point.action.actionType === 'worker') {
if (human && human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) {
callback();
removeHumanFromMonitor(humanId); // Remove after triggering
}
} else if (human?.point.action.actionType === 'assembly') {
if (human && human.isActive === false && human.state === 'idle') {
callback();
removeHumanFromMonitor(humanId); // Remove after triggering
}
}
});
});

View File

@@ -212,7 +212,6 @@ function RoboticArmInstance({ armBot }: { readonly armBot: ArmBotStatus }) {
}
}, [isReset, isPlaying])
function animate(currentTime: number) {
if (previousTimeRef.current === null) {
previousTimeRef.current = currentTime;