feat: Implement ArmBot simulation with IK animation and event handling
- Added ArmBot component to manage ArmBot instances in the simulation. - Created ArmBotInstances component to render individual ArmBot models. - Developed IKAnimationController for handling inverse kinematics during animations. - Introduced IkInstances component to load and manage IK-enabled arm models. - Defined simulation types for ArmBot events and connections in TypeScript. - Enhanced type definitions for better clarity and maintainability.
This commit is contained in:
62
app/src/modules/simulation/armbot/ArmBot.tsx
Normal file
62
app/src/modules/simulation/armbot/ArmBot.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
import { useSimulationStates } from "../../../store/store";
|
||||
import * as SimulationTypes from '../../../types/simulation';
|
||||
import { ArmbotInstances } from "./ArmBotInstances";
|
||||
|
||||
interface ArmBotState {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
status: string;
|
||||
material: string;
|
||||
triggerId: string;
|
||||
connections: any
|
||||
}
|
||||
|
||||
const ArmBot: React.FC = () => {
|
||||
const { activeModule } = useModuleStore();
|
||||
const { scene } = useThree();
|
||||
const { simulationStates } = useSimulationStates();
|
||||
const [armBots, setArmBots] = useState<ArmBotState[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot");
|
||||
const initialStates: ArmBotState[] = filtered.map(bot => ({
|
||||
uuid: bot.modeluuid,
|
||||
position: bot.position,
|
||||
rotation: bot.rotation,
|
||||
status: "idle",
|
||||
material: "default",
|
||||
triggerId: '',
|
||||
connections: bot.points.connections
|
||||
}));
|
||||
setArmBots(initialStates);
|
||||
}, [simulationStates]);
|
||||
|
||||
useEffect(() => {
|
||||
armBots.forEach((bot) => {
|
||||
const object = scene.getObjectByProperty("uuid", bot.uuid);
|
||||
if (object) {
|
||||
object.visible = activeModule !== "simulation";
|
||||
}
|
||||
});
|
||||
}, [scene, activeModule, armBots]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{activeModule === "simulation" &&
|
||||
armBots.map((bot, i) => (
|
||||
<ArmbotInstances
|
||||
key={i}
|
||||
index={i}
|
||||
armBot={bot}
|
||||
setArmBots={setArmBots}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ArmBot;
|
||||
25
app/src/modules/simulation/armbot/ArmBotInstances.tsx
Normal file
25
app/src/modules/simulation/armbot/ArmBotInstances.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import IkInstances from "./IkInstances";
|
||||
import armModel from "../../../assets/gltf-glb/rigged/ik_arm_4.glb";
|
||||
|
||||
interface ArmBotState {
|
||||
uuid: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
status: string;
|
||||
material: string;
|
||||
triggerId: string;
|
||||
connections: any
|
||||
}
|
||||
|
||||
interface ArmbotInstancesProps {
|
||||
index: number;
|
||||
armBot: ArmBotState;
|
||||
setArmBots: (armBots: ArmBotState[]) => void;
|
||||
}
|
||||
|
||||
export const ArmbotInstances: React.FC<ArmbotInstancesProps> = ({ index, armBot, setArmBots }) => {
|
||||
|
||||
return (
|
||||
<IkInstances key={index} modelUrl={armModel} position={armBot.position} rotation={armBot.rotation} />
|
||||
);
|
||||
};
|
||||
101
app/src/modules/simulation/armbot/IKAnimationController.tsx
Normal file
101
app/src/modules/simulation/armbot/IKAnimationController.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
|
||||
const IKAnimationController = ({
|
||||
ikSolver,
|
||||
process,
|
||||
selectedTrigger,
|
||||
targetBoneName,
|
||||
}: {
|
||||
ikSolver: any;
|
||||
process: {
|
||||
trigger: string;
|
||||
start: THREE.Vector3;
|
||||
end: THREE.Vector3;
|
||||
speed: number;
|
||||
}[];
|
||||
selectedTrigger: string;
|
||||
targetBoneName: string;
|
||||
}) => {
|
||||
const [progress, setProgress] = useState(0);
|
||||
const restSpeed = 0.1;
|
||||
|
||||
useEffect(() => {
|
||||
setProgress(0);
|
||||
}, [selectedTrigger]);
|
||||
|
||||
const processedCurves = useMemo(() => {
|
||||
const restPosition = new THREE.Vector3(0.2, 2.3, 1.6);
|
||||
return process.map((p) => {
|
||||
const mid = new THREE.Vector3(
|
||||
(p.start.x + p.end.x) / 1,
|
||||
Math.max(p.start.y, p.end.y) + 0.8,
|
||||
(p.start.z + p.end.z) / 0.9
|
||||
);
|
||||
const points = [
|
||||
restPosition.clone(),
|
||||
p.start.clone(),
|
||||
mid.clone(),
|
||||
p.end.clone(),
|
||||
restPosition.clone(),
|
||||
];
|
||||
const curve = new THREE.CatmullRomCurve3(points);
|
||||
const restToStartDist = points[0].distanceTo(points[1]);
|
||||
const startToEndDist = points[1].distanceTo(points[3]);
|
||||
const endToRestDist = points[3].distanceTo(points[4]);
|
||||
|
||||
const totalDist = restToStartDist + startToEndDist + endToRestDist;
|
||||
const restToStartRange = [0, restToStartDist / totalDist];
|
||||
const startToEndRange = [
|
||||
restToStartRange[1],
|
||||
restToStartRange[1] + startToEndDist / totalDist,
|
||||
];
|
||||
const endToRestRange = [startToEndRange[1], 1];
|
||||
|
||||
return {
|
||||
trigger: p.trigger,
|
||||
curve,
|
||||
speed: p.speed,
|
||||
restToStartRange,
|
||||
startToEndRange,
|
||||
endToRestRange,
|
||||
};
|
||||
});
|
||||
}, [process]);
|
||||
|
||||
const activeCurve = useMemo(() => {
|
||||
return processedCurves.find((c) => c.trigger === selectedTrigger);
|
||||
}, [processedCurves, selectedTrigger]);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (!ikSolver || !activeCurve) return;
|
||||
|
||||
const { curve, speed, startToEndRange } = activeCurve;
|
||||
const targetBone = ikSolver.mesh.skeleton.bones.find(
|
||||
(b: any) => b.name === targetBoneName
|
||||
);
|
||||
if (!targetBone) return;
|
||||
|
||||
let currentSpeed = restSpeed;
|
||||
if (progress >= startToEndRange[0] && progress < startToEndRange[1]) {
|
||||
currentSpeed = speed;
|
||||
}
|
||||
|
||||
setProgress((prev) => {
|
||||
const next = prev + delta * currentSpeed;
|
||||
if (next >= 1) {
|
||||
targetBone.position.copy(curve.getPoint(1));
|
||||
return 1;
|
||||
}
|
||||
targetBone.position.copy(curve.getPoint(next));
|
||||
return next;
|
||||
});
|
||||
|
||||
ikSolver.update();
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default IKAnimationController;
|
||||
128
app/src/modules/simulation/armbot/IkInstances.tsx
Normal file
128
app/src/modules/simulation/armbot/IkInstances.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import * as THREE from "three";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useLoader } from "@react-three/fiber";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import { clone } from "three/examples/jsm/utils/SkeletonUtils";
|
||||
import { CCDIKSolver, CCDIKHelper, } from "three/examples/jsm/animation/CCDIKSolver";
|
||||
import IKAnimationController from "./IKAnimationController";
|
||||
|
||||
const IkInstances = ({ modelUrl, position, rotation }: { modelUrl: string; position: [number, number, number]; rotation: [number, number, number]; }) => {
|
||||
const [ikSolver, setIkSolver] = useState<any>(null);
|
||||
const [selectedTrigger, setSelectedTrigger] = useState<string>("idle");
|
||||
const gltf = useLoader(GLTFLoader, modelUrl, (loader) => {
|
||||
const draco = new DRACOLoader();
|
||||
draco.setDecoderPath(
|
||||
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/"
|
||||
);
|
||||
loader.setDRACOLoader(draco);
|
||||
});
|
||||
const cloned = useMemo(() => clone(gltf.scene), [gltf]);
|
||||
const groupRef = useRef<any>(null);
|
||||
const [selectedArm, setSelectedArm] = useState<THREE.Group>();
|
||||
const targetBoneName = "Target";
|
||||
const skinnedMeshName = "link_0";
|
||||
|
||||
const process = useMemo(() => [
|
||||
{
|
||||
trigger: "Trigger1",
|
||||
start: new THREE.Vector3(-0.75, 1.5, -2.2),
|
||||
end: new THREE.Vector3(0, 1.2, 2.2),
|
||||
speed: 0.25,
|
||||
},
|
||||
{
|
||||
trigger: "Trigger2",
|
||||
start: new THREE.Vector3(0, 1.2, 2.2),
|
||||
end: new THREE.Vector3(0.75, 1.5, -2.2),
|
||||
speed: 0.22,
|
||||
}
|
||||
], []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!gltf) return;
|
||||
const OOI: any = {};
|
||||
cloned.traverse((n: any) => {
|
||||
if (n.name === targetBoneName) OOI.Target_Bone = n;
|
||||
if (n.name === skinnedMeshName) OOI.Skinned_Mesh = n;
|
||||
});
|
||||
|
||||
if (!OOI.Target_Bone || !OOI.Skinned_Mesh) return;
|
||||
|
||||
const iks = [
|
||||
{
|
||||
target: 7,
|
||||
effector: 6,
|
||||
links: [
|
||||
{
|
||||
index: 5,
|
||||
enabled: true,
|
||||
rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
|
||||
rotationMax: new THREE.Vector3(Math.PI / 2, 0, 0),
|
||||
},
|
||||
{
|
||||
index: 4,
|
||||
enabled: true,
|
||||
rotationMin: new THREE.Vector3(-Math.PI / 2, 0, 0),
|
||||
rotationMax: new THREE.Vector3(0, 0, 0),
|
||||
},
|
||||
{
|
||||
index: 3,
|
||||
enabled: true,
|
||||
rotationMin: new THREE.Vector3(0, 0, 0),
|
||||
rotationMax: new THREE.Vector3(2, 0, 0),
|
||||
},
|
||||
{ index: 1, enabled: true, limitation: new THREE.Vector3(0, 1, 0) },
|
||||
{ index: 0, enabled: false, limitation: new THREE.Vector3(0, 0, 0) },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const solver = new CCDIKSolver(OOI.Skinned_Mesh, iks);
|
||||
setIkSolver(solver);
|
||||
|
||||
const helper = new CCDIKHelper(OOI.Skinned_Mesh, iks, 0.05);
|
||||
// groupRef.current.add(helper);
|
||||
|
||||
}, [gltf]);
|
||||
|
||||
useEffect(() => {
|
||||
const triggers = ['Trigger1', 'Trigger2'];
|
||||
let index = 0;
|
||||
|
||||
const cycleTriggers = setInterval(() => {
|
||||
setSelectedTrigger(triggers[index]);
|
||||
index = (index + 1) % triggers.length;
|
||||
}, 10000);
|
||||
|
||||
return () => clearInterval(cycleTriggers);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<group
|
||||
ref={groupRef}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setSelectedArm(groupRef.current?.getObjectByName(targetBoneName))
|
||||
}}
|
||||
>
|
||||
<primitive
|
||||
object={cloned}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
scale={[1, 1, 1]}
|
||||
name={`arm-bot`}
|
||||
/>
|
||||
</group>
|
||||
<IKAnimationController
|
||||
ikSolver={ikSolver}
|
||||
process={process}
|
||||
selectedTrigger={selectedTrigger}
|
||||
targetBoneName={targetBoneName}
|
||||
/>
|
||||
{/* {selectedArm && <TransformControls object={selectedArm} />} */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default IkInstances;
|
||||
@@ -1,88 +0,0 @@
|
||||
import { useFloorItems, useSimulationStates } from '../../../store/store';
|
||||
import * as THREE from 'three';
|
||||
import * as Types from '../../../types/world/worldTypes';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
function Behaviour() {
|
||||
const { setSimulationStates } = useSimulationStates();
|
||||
const { floorItems } = useFloorItems();
|
||||
|
||||
useEffect(() => {
|
||||
const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[] = [];
|
||||
|
||||
// floorItems.forEach((item: Types.FloorItemType) => {
|
||||
// if (item.modelfileID === "672a090f80d91ac979f4d0bd") {
|
||||
// const point1Position = new THREE.Vector3(0, 0.85, 2.2);
|
||||
// const middlePointPosition = new THREE.Vector3(0, 0.85, 0);
|
||||
// const point2Position = new THREE.Vector3(0, 0.85, -2.2);
|
||||
|
||||
// const point1UUID = THREE.MathUtils.generateUUID();
|
||||
// const middlePointUUID = THREE.MathUtils.generateUUID();
|
||||
// const point2UUID = THREE.MathUtils.generateUUID();
|
||||
|
||||
// const newPath: Types.ConveyorEventsSchema = {
|
||||
// modeluuid: item.modeluuid,
|
||||
// modelName: item.modelname,
|
||||
// type: 'Conveyor',
|
||||
// points: [
|
||||
// {
|
||||
// uuid: point1UUID,
|
||||
// position: [point1Position.x, point1Position.y, point1Position.z],
|
||||
// rotation: [0, 0, 0],
|
||||
// actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }],
|
||||
// triggers: [],
|
||||
// connections: { source: { modelUUID: item.modeluuid, pointUUID: point1UUID }, targets: [] },
|
||||
// },
|
||||
// {
|
||||
// uuid: middlePointUUID,
|
||||
// position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z],
|
||||
// rotation: [0, 0, 0],
|
||||
// actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }],
|
||||
// triggers: [],
|
||||
// connections: { source: { modelUUID: item.modeluuid, pointUUID: middlePointUUID }, targets: [] },
|
||||
// },
|
||||
// {
|
||||
// uuid: point2UUID,
|
||||
// position: [point2Position.x, point2Position.y, point2Position.z],
|
||||
// rotation: [0, 0, 0],
|
||||
// actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }],
|
||||
// triggers: [],
|
||||
// connections: { source: { modelUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] },
|
||||
// },
|
||||
// ],
|
||||
// position: [...item.position],
|
||||
// rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||
// speed: 'Inherit',
|
||||
// };
|
||||
|
||||
// newPaths.push(newPath);
|
||||
// } else if (item.modelfileID === "67e3da19c2e8f37134526e6a") {
|
||||
// const pointUUID = THREE.MathUtils.generateUUID();
|
||||
// const pointPosition = new THREE.Vector3(0, 1.3, 0);
|
||||
|
||||
// const newVehiclePath: Types.VehicleEventsSchema = {
|
||||
// modeluuid: item.modeluuid,
|
||||
// modelName: item.modelname,
|
||||
// type: 'Vehicle',
|
||||
// point: {
|
||||
// uuid: pointUUID,
|
||||
// position: [pointPosition.x, pointPosition.y, pointPosition.z],
|
||||
// actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Start', start: {}, hitCount: 1, end: {}, buffer: 0 },
|
||||
// connections: { source: { modelUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
|
||||
// speed: 2,
|
||||
// },
|
||||
// position: [...item.position],
|
||||
// };
|
||||
|
||||
// newPaths.push(newVehiclePath);
|
||||
// }
|
||||
// });
|
||||
|
||||
// setSimulationStates(newPaths);
|
||||
// console.log('floorItems: ', floorItems);
|
||||
}, [floorItems]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default Behaviour;
|
||||
@@ -1,56 +0,0 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
|
||||
// store
|
||||
import useModuleStore from "../../../store/useModuleStore";
|
||||
|
||||
// functions
|
||||
import { findLinkObjects } from "./functions/findLinkObjects";
|
||||
// components
|
||||
import { MultiGLTFInstances } from "./MultiGLTFInstances";
|
||||
|
||||
// impory model from model folder
|
||||
import armModel from "../../../assets/gltf-glb/rigged/ik_arm_4.glb";
|
||||
|
||||
// Main component to include the logic
|
||||
const ArmReplace: React.FC = () => {
|
||||
const { activeModule } = useModuleStore();
|
||||
|
||||
const { scene } = useThree(); // Access the Three.js scene from the React Fiber context
|
||||
|
||||
// State to store positions, rotations, and count
|
||||
const [positions, setPositions] = React.useState<[number, number, number][]>(
|
||||
[]
|
||||
);
|
||||
const [rotations, setRotations] = React.useState<[number, number, number][]>(
|
||||
[]
|
||||
);
|
||||
const [count, setCount] = React.useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
// Call the function to find objects and update states
|
||||
findLinkObjects(
|
||||
scene,
|
||||
setPositions,
|
||||
setRotations,
|
||||
setCount,
|
||||
activeModule === "simulation" ? false : true
|
||||
);
|
||||
}, [scene, activeModule]); // Re-run this effect if the scene changes or activeModule changes
|
||||
|
||||
return (
|
||||
<>
|
||||
{useModuleStore.getState().activeModule === "simulation" &&
|
||||
count.map((_, i: number) => (
|
||||
<MultiGLTFInstances
|
||||
index={i}
|
||||
modelUrl={armModel}
|
||||
position={positions[i]}
|
||||
rotation={rotations[i]}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ArmReplace;
|
||||
@@ -1,43 +0,0 @@
|
||||
import { useLoader } from "@react-three/fiber";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
|
||||
import { clone } from "three/examples/jsm/utils/SkeletonUtils";
|
||||
|
||||
interface MultiGLTFInstancesProps {
|
||||
index: number;
|
||||
modelUrl: string;
|
||||
position: [number, number, number];
|
||||
rotation: [number, number, number];
|
||||
}
|
||||
|
||||
export const MultiGLTFInstances: React.FC<MultiGLTFInstancesProps> = ({
|
||||
index,
|
||||
modelUrl,
|
||||
position,
|
||||
rotation,
|
||||
}) => {
|
||||
// Load GLTF model with DRACO loader for compression
|
||||
const originalGltf = useLoader(GLTFLoader, modelUrl, (loader) => {
|
||||
const draco = new DRACOLoader();
|
||||
draco.setDecoderPath(
|
||||
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/"
|
||||
);
|
||||
loader.setDRACOLoader(draco);
|
||||
});
|
||||
|
||||
// Clone the model for independent transformations
|
||||
const cloned = clone(originalGltf.scene);
|
||||
|
||||
// Render the cloned model
|
||||
return (
|
||||
<mesh>
|
||||
<primitive
|
||||
name="rigged_arm"
|
||||
key={index}
|
||||
object={cloned}
|
||||
position={position}
|
||||
rotation={rotation}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
import { Object3D, Vector3 } from "three";
|
||||
|
||||
// Function to find objects named 'link_0' and update positions, rotations, and count
|
||||
export const findLinkObjects = (
|
||||
scene: Object3D,
|
||||
setPositions: React.Dispatch<
|
||||
React.SetStateAction<[number, number, number][]>
|
||||
>,
|
||||
setRotations: React.Dispatch<
|
||||
React.SetStateAction<[number, number, number][]>
|
||||
>,
|
||||
setCount: React.Dispatch<React.SetStateAction<string[]>>,
|
||||
visibility: boolean
|
||||
) => {
|
||||
const positions: [number, number, number][] = [];
|
||||
const rotations: [number, number, number][] = [];
|
||||
const count: string[] = [];
|
||||
let i = 0;
|
||||
|
||||
scene.traverse((object) => {
|
||||
if (object.name === "link_0") {
|
||||
if (object.parent && object.type !== "SkinnedMesh") {
|
||||
// count
|
||||
count[i] = object.uuid;
|
||||
i++;
|
||||
// Save the position and rotation of the parent object
|
||||
const { x: px, y: py, z: pz } = object.parent.position;
|
||||
positions.push([px, py, pz]);
|
||||
|
||||
const { x: rx, y: ry, z: rz } = object.parent.rotation;
|
||||
rotations.push([rx, ry, rz]);
|
||||
|
||||
// Change visibility of the object
|
||||
object.visible = visibility;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Update the state with the collected positions, rotations, and count
|
||||
setPositions(positions);
|
||||
setRotations(rotations);
|
||||
setCount(count);
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { useFrame, useThree } from '@react-three/fiber';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import * as Types from '../../../types/world/worldTypes';
|
||||
import * as SimulationTypes from '../../../types/simulation';
|
||||
import { QuadraticBezierLine } from '@react-three/drei';
|
||||
import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store';
|
||||
import useModuleStore from '../../../store/useModuleStore';
|
||||
@@ -339,7 +340,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
||||
updateBackend(updatedPathDetails);
|
||||
};
|
||||
|
||||
const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
|
||||
const updateBackend = async (updatedPaths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
|
||||
if (updatedPaths.length === 0) return;
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email ? email.split("@")[1].split(".")[0] : "";
|
||||
@@ -852,11 +853,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
||||
}
|
||||
});
|
||||
|
||||
const removeConnections = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => {
|
||||
const removeConnection = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => {
|
||||
const updatedStates = simulationStates.map(state => {
|
||||
// Handle Conveyor (which has multiple points)
|
||||
if (state.type === 'Conveyor') {
|
||||
const updatedConveyor: Types.ConveyorEventsSchema = {
|
||||
const updatedConveyor: SimulationTypes.ConveyorEventsSchema = {
|
||||
...state,
|
||||
points: state.points.map(point => {
|
||||
// Check if this point is either connection1 or connection2
|
||||
@@ -887,7 +888,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
||||
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
|
||||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
|
||||
|
||||
const updatedVehicle: Types.VehicleEventsSchema = {
|
||||
const updatedVehicle: SimulationTypes.VehicleEventsSchema = {
|
||||
...state,
|
||||
points: {
|
||||
...state.points,
|
||||
@@ -913,7 +914,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
||||
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
|
||||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
|
||||
|
||||
const updatedStaticMachine: Types.StaticMachineEventsSchema = {
|
||||
const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema = {
|
||||
...state,
|
||||
points: {
|
||||
...state.points,
|
||||
@@ -939,7 +940,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
||||
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
|
||||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
|
||||
|
||||
const updatedArmBot: Types.ArmBotEventsSchema = {
|
||||
const updatedArmBot: SimulationTypes.ArmBotEventsSchema = {
|
||||
...state,
|
||||
points: {
|
||||
...state.points,
|
||||
@@ -1030,10 +1031,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
||||
const connection1 = { model: path.modeluuid, point: point.uuid }
|
||||
const connection2 = { model: target.modelUUID, point: target.pointUUID }
|
||||
|
||||
removeConnections(connection1, connection2)
|
||||
removeConnection(connection1, connection2)
|
||||
|
||||
}
|
||||
}}
|
||||
depthWrite={false}
|
||||
userData={target}
|
||||
/>
|
||||
);
|
||||
@@ -1082,9 +1084,10 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
||||
const connection1 = { model: path.modeluuid, point: path.points.uuid }
|
||||
const connection2 = { model: target.modelUUID, point: target.pointUUID }
|
||||
|
||||
removeConnections(connection1, connection2)
|
||||
removeConnection(connection1, connection2)
|
||||
}
|
||||
}}
|
||||
depthWrite={false}
|
||||
userData={target}
|
||||
/>
|
||||
);
|
||||
@@ -1135,10 +1138,11 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
||||
const connection1 = { model: path.modeluuid, point: path.points.uuid }
|
||||
const connection2 = { model: target.modelUUID, point: target.pointUUID }
|
||||
|
||||
removeConnections(connection1, connection2)
|
||||
removeConnection(connection1, connection2)
|
||||
|
||||
}
|
||||
}}
|
||||
depthWrite={false}
|
||||
userData={target}
|
||||
/>
|
||||
);
|
||||
@@ -1160,6 +1164,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
|
||||
dashed
|
||||
dashSize={1}
|
||||
dashScale={20}
|
||||
depthWrite={false}
|
||||
/>
|
||||
)}
|
||||
</group>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from "three";
|
||||
import * as Types from "../../../types/world/worldTypes";
|
||||
import * as SimulationTypes from "../../../types/simulation";
|
||||
import { useRef, useState, useEffect, useMemo } from "react";
|
||||
import { Sphere, TransformControls } from "@react-three/drei";
|
||||
import {
|
||||
@@ -94,7 +94,7 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}) as Types.ConveyorEventsSchema[];
|
||||
}) as SimulationTypes.ConveyorEventsSchema[];
|
||||
|
||||
const updatedPath = updatedPaths.find(
|
||||
(path) => path.type === "Conveyor" && path.points.some((point) => point.uuid === selectedActionSphere.points.uuid)
|
||||
@@ -161,7 +161,7 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
|
||||
};
|
||||
}, [eyeDropMode, editingPoint, previewPosition]);
|
||||
|
||||
const updateBackend = async (updatedPath: Types.VehicleEventsSchema | undefined) => {
|
||||
const updateBackend = async (updatedPath: SimulationTypes.VehicleEventsSchema | undefined) => {
|
||||
if (!updatedPath) return;
|
||||
const email = localStorage.getItem("email");
|
||||
const organization = email ? email.split("@")[1].split(".")[0] : "";
|
||||
@@ -191,7 +191,7 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
|
||||
return path;
|
||||
});
|
||||
|
||||
const updatedPath = updatedPaths.find((path): path is Types.VehicleEventsSchema => path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid);
|
||||
const updatedPath = updatedPaths.find((path): path is SimulationTypes.VehicleEventsSchema => path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid);
|
||||
updateBackend(updatedPath);
|
||||
|
||||
setSimulationStates(updatedPaths);
|
||||
@@ -282,6 +282,7 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
|
||||
key={path.modeluuid}
|
||||
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
|
||||
position={path.position}
|
||||
rotation={path.rotation}
|
||||
onClick={(e) => {
|
||||
if (isConnecting || eyeDropMode) return;
|
||||
e.stopPropagation();
|
||||
@@ -335,6 +336,7 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
|
||||
key={path.modeluuid}
|
||||
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
|
||||
position={path.position}
|
||||
rotation={path.rotation}
|
||||
onClick={(e) => {
|
||||
if (isConnecting || eyeDropMode) return;
|
||||
e.stopPropagation();
|
||||
@@ -388,6 +390,7 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
|
||||
key={path.modeluuid}
|
||||
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
|
||||
position={path.position}
|
||||
rotation={path.rotation}
|
||||
onClick={(e) => {
|
||||
if (isConnecting || eyeDropMode) return;
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -458,7 +458,7 @@ import { useThree } from "@react-three/fiber";
|
||||
import {
|
||||
ConveyorEventsSchema,
|
||||
VehicleEventsSchema,
|
||||
} from "../../../types/world/worldTypes";
|
||||
} from "../../../types/simulation";
|
||||
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
|
||||
|
||||
// Type definitions
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
import { useState, useEffect, useRef, useMemo } from "react";
|
||||
import {
|
||||
useSelectedActionSphere,
|
||||
useSelectedPath,
|
||||
useSimulationStates,
|
||||
} from "../../store/store";
|
||||
import { useState, useRef } from "react";
|
||||
import * as THREE from "three";
|
||||
import Behaviour from "./behaviour/behaviour";
|
||||
import PathCreation from "./path/pathCreation";
|
||||
import PathConnector from "./path/pathConnector";
|
||||
import useModuleStore from "../../store/useModuleStore";
|
||||
import ProcessContainer from "./process/processContainer";
|
||||
import Agv from "../builder/agv/agv";
|
||||
import ArmBot from "./armbot/ArmBot";
|
||||
|
||||
function Simulation() {
|
||||
const { activeModule } = useModuleStore();
|
||||
@@ -21,24 +16,28 @@ function Simulation() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Behaviour />
|
||||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<PathCreation pathsGroupRef={pathsGroupRef} />
|
||||
|
||||
<PathConnector pathsGroupRef={pathsGroupRef} />
|
||||
|
||||
<ProcessContainer
|
||||
processes={processes}
|
||||
setProcesses={setProcesses}
|
||||
agvRef={agvRef}
|
||||
MaterialRef={MaterialRef}
|
||||
/>
|
||||
|
||||
<Agv
|
||||
processes={processes}
|
||||
agvRef={agvRef}
|
||||
MaterialRef={MaterialRef}
|
||||
/>
|
||||
|
||||
</>
|
||||
)}
|
||||
<ArmBot />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user