feat: Extend simulation state types to include ArmBot events and update related components

- Updated RotateControls and SelectionControls to handle ArmBot events in simulation states.
- Enhanced PathConnector to manage connections involving ArmBot and StaticMachine types.
- Added ArmBotMechanics and StaticMachineMechanics components for managing properties of ArmBot and StaticMachine.
- Modified types in worldTypes to include rotation for ArmBot and StaticMachine events.
- Updated store to accommodate new ArmBot event types in simulation states.
This commit is contained in:
Jerald-Golden-B 2025-04-07 18:16:34 +05:30
parent c44629f07d
commit 272317991a
20 changed files with 2095 additions and 1363 deletions

View File

@ -22,6 +22,8 @@ import GlobalProperties from "./properties/GlobalProperties";
import AsstePropertiies from "./properties/AssetProperties";
import ZoneProperties from "./properties/ZoneProperties";
import VehicleMechanics from "./mechanics/VehicleMechanics";
import StaticMachineMechanics from "./mechanics/StaticMachineMechanics";
import ArmBotMechanics from "./mechanics/ArmBotMechanics";
const SideBarRight: React.FC = () => {
const { activeModule } = useModuleStore();
@ -42,8 +44,7 @@ const SideBarRight: React.FC = () => {
<div className="sidebar-actions-container">
{/* {activeModule === "builder" && ( */}
<div
className={`sidebar-action-list ${
subModule === "properties" ? "active" : ""
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
}`}
onClick={() => setSubModule("properties")}
>
@ -53,24 +54,21 @@ const SideBarRight: React.FC = () => {
{activeModule === "simulation" && (
<>
<div
className={`sidebar-action-list ${
subModule === "mechanics" ? "active" : ""
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
}`}
onClick={() => setSubModule("mechanics")}
>
<MechanicsIcon isActive={subModule === "mechanics"} />
</div>
<div
className={`sidebar-action-list ${
subModule === "simulations" ? "active" : ""
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
}`}
onClick={() => setSubModule("simulations")}
>
<SimulationIcon isActive={subModule === "simulations"} />
</div>
<div
className={`sidebar-action-list ${
subModule === "analysis" ? "active" : ""
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
}`}
onClick={() => setSubModule("analysis")}
>
@ -132,10 +130,28 @@ const SideBarRight: React.FC = () => {
</div>
</div>
)}
{subModule === "mechanics" &&
selectedActionSphere &&
selectedActionSphere.path.type === "StaticMachine" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<StaticMachineMechanics />
</div>
</div>
)}
{subModule === "mechanics" &&
selectedActionSphere &&
selectedActionSphere.path.type === "ArmBot" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ArmBotMechanics />
</div>
</div>
)}
{subModule === "mechanics" && !selectedActionSphere && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ConveyorMechanics />
<ConveyorMechanics /> {/* default */}
</div>
</div>
)}

View File

@ -0,0 +1,90 @@
import React, { useRef, useMemo } from "react";
import { InfoIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import * as Types from '../../../../types/world/worldTypes';
import PositionInput from "../customInput/PositionInputs";
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
const ArmBotMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { socket } = useSocketStore();
const propertiesContainerRef = useRef<HTMLDivElement>(null);
const { selectedPoint, connectedPointUuids } = useMemo(() => {
if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null, connectedPointUuids: [] };
const vehiclePaths = simulationStates.filter(
(path): path is Types.ArmBotEventsSchema => path.type === "ArmBot"
);
const points = vehiclePaths.find(
(path) => path.points.uuid === selectedActionSphere.points.uuid
)?.points;
if (!points) return { selectedPoint: null, connectedPointUuids: [] };
const connectedUuids: string[] = [];
if (points.connections?.targets) {
points.connections.targets.forEach(target => {
connectedUuids.push(target.pointUUID);
});
}
return {
selectedPoint: points,
connectedPointUuids: connectedUuids
};
}, [selectedActionSphere, simulationStates]);
const updateBackend = async (updatedPath: Types.ArmBotEventsSchema | undefined) => {
if (!updatedPath) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "ArmBot", points: updatedPath.points }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "ArmBot", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
}
return (
<div className="machine-mechanics-container" key={selectedPoint?.uuid}>
<div className="machine-mechanics-header">
{selectedActionSphere?.path?.modelName || "ArmBot point not found"}
</div>
<div className="machine-mechanics-content-container">
<div className="selected-properties-container" ref={propertiesContainerRef}>
<div className="properties-header">ArmBot Properties</div>
{selectedPoint && (
<>
</>
)}
</div>
<div className="footer">
<InfoIcon />
Configure armbot properties.
</div>
</div>
</div>
);
};
export default React.memo(ArmBotMechanics);

View File

@ -332,9 +332,7 @@ const ConveyorMechanics: React.FC = () => {
const updatedPath = updatedPaths.find(
(path): path is Types.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
path.modeluuid === selectedPath.path.modeluuid
);
updateBackend(updatedPath);

View File

@ -0,0 +1,90 @@
import React, { useRef, useMemo } from "react";
import { InfoIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import * as Types from '../../../../types/world/worldTypes';
import PositionInput from "../customInput/PositionInputs";
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
const StaticMachineMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { socket } = useSocketStore();
const propertiesContainerRef = useRef<HTMLDivElement>(null);
const { selectedPoint, connectedPointUuids } = useMemo(() => {
if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null, connectedPointUuids: [] };
const vehiclePaths = simulationStates.filter(
(path): path is Types.StaticMachineEventsSchema => path.type === "StaticMachine"
);
const points = vehiclePaths.find(
(path) => path.points.uuid === selectedActionSphere.points.uuid
)?.points;
if (!points) return { selectedPoint: null, connectedPointUuids: [] };
const connectedUuids: string[] = [];
if (points.connections?.targets) {
points.connections.targets.forEach(target => {
connectedUuids.push(target.pointUUID);
});
}
return {
selectedPoint: points,
connectedPointUuids: connectedUuids
};
}, [selectedActionSphere, simulationStates]);
const updateBackend = async (updatedPath: Types.StaticMachineEventsSchema | undefined) => {
if (!updatedPath) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "StaticMachine", points: updatedPath.points }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "StaticMachine", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
}
return (
<div className="machine-mechanics-container" key={selectedPoint?.uuid}>
<div className="machine-mechanics-header">
{selectedActionSphere?.path?.modelName || "Machine point not found"}
</div>
<div className="machine-mechanics-content-container">
<div className="selected-properties-container" ref={propertiesContainerRef}>
<div className="properties-header">Machine Properties</div>
{selectedPoint && (
<>
</>
)}
</div>
<div className="footer">
<InfoIcon />
Configure machine properties.
</div>
</div>
</div>
);
};
export default React.memo(StaticMachineMechanics);

View File

@ -64,9 +64,7 @@ const RealTimeVisulization: React.FC = () => {
const { rightClickSelected, setRightClickSelected } = useRightClickSelected()
const [openConfirmationPopup, setOpenConfirmationPopup] = useState(false);
const [floatingWidgets, setFloatingWidgets] = useState<
Record<string, { zoneName: string; zoneId: string; objects: any[] }>
>({});
const [floatingWidgets, setFloatingWidgets] = useState<Record<string, { zoneName: string; zoneId: string; objects: any[] }>>({});
const { widgetSelect, setWidgetSelect } = useAsset3dWidget();
const { widgetSubOption, setWidgetSubOption } = useWidgetSubOption();
const { visualizationSocket } = useSocketStore();
@ -78,7 +76,6 @@ const RealTimeVisulization: React.FC = () => {
const organization = email?.split("@")[1]?.split(".")[0];
try {
const response = await getZone2dData(organization);
// console.log('response: ', response);
if (!Array.isArray(response)) {
return;

View File

@ -29,12 +29,15 @@ export default function PathNavigator({
const [dropPickupPath, setDropPickupPath] = useState<[number, number, number][]>([]);
const [initialPosition, setInitialPosition] = useState<THREE.Vector3 | null>(null);
const [initialRotation, setInitialRotation] = useState<THREE.Euler | null>(null);
const [targetPosition] = useState(new THREE.Vector3());
const [smoothPosition] = useState(new THREE.Vector3());
const [targetQuaternion] = useState(new THREE.Quaternion());
const distancesRef = useRef<number[]>([]);
const totalDistanceRef = useRef(0);
const progressRef = useRef(0);
const isWaiting = useRef(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const pathTransitionProgress = useRef(0);
const { scene } = useThree();
const { isPlaying } = usePlayButtonStore();
@ -44,6 +47,9 @@ export default function PathNavigator({
if (object) {
setInitialPosition(object.position.clone());
setInitialRotation(object.rotation.clone());
smoothPosition.copy(object.position.clone());
targetPosition.copy(object.position.clone());
targetQuaternion.setFromEuler(object.rotation.clone());
}
}, [scene, id]);
@ -65,22 +71,23 @@ export default function PathNavigator({
setPath([]);
setCurrentPhase('initial');
setPickupDropPath([]);
setDropPickupPath([]);
distancesRef.current = [];
totalDistanceRef.current = 0;
progressRef.current = 0;
isWaiting.current = false;
pathTransitionProgress.current = 0;
if (initialPosition && initialRotation) {
const object = scene.getObjectByProperty("uuid", id);
if (object) {
if (object && initialPosition && initialRotation) {
object.position.copy(initialPosition);
object.rotation.copy(initialRotation);
}
smoothPosition.copy(initialPosition);
targetPosition.copy(initialPosition);
targetQuaternion.setFromEuler(initialRotation);
}
};
useEffect(() => {
if (!isPlaying) {
resetState();
@ -171,16 +178,16 @@ export default function PathNavigator({
const end = new THREE.Vector3(...path[index + 1]);
const dist = distancesRef.current[index];
const t = THREE.MathUtils.clamp((covered - accumulated) / dist, 0, 1);
const position = start.clone().lerp(end, t);
object.position.copy(position);
targetPosition.copy(start).lerp(end, t);
smoothPosition.lerp(targetPosition, 0.1);
object.position.copy(smoothPosition);
const direction = new THREE.Vector3().subVectors(end, start).normalize();
const targetRotationY = Math.atan2(direction.x, direction.z);
let angleDifference = targetRotationY - object.rotation.y;
angleDifference = ((angleDifference + Math.PI) % (Math.PI * 2)) - Math.PI;
object.rotation.y += angleDifference * 0.1;
targetQuaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), targetRotationY);
object.quaternion.slerp(targetQuaternion, 0.1);
});
useEffect(() => {

View File

@ -53,7 +53,7 @@ export default function addFloorToScene(
const mesh = new THREE.Mesh(geometry, material);
mesh.receiveShadow = true;
mesh.position.y = layer;
mesh.position.y = (layer) * CONSTANTS.wallConfig.height;
mesh.rotateX(Math.PI / 2);
mesh.name = `Floor_Layer_${layer}`;

View File

@ -171,7 +171,7 @@ function loadOnlyFloors(
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.position.y = (floor[0][0][2] - 1) * CONSTANTS.wallConfig.height + 0.03;
mesh.position.y = (floor[0][0][2] - 1) * CONSTANTS.wallConfig.height;
mesh.rotateX(Math.PI / 2);
mesh.name = `Only_Floor_Line_${floor[0][0][2]}`;

View File

@ -1,20 +1,5 @@
import { useFrame, useThree } from "@react-three/fiber";
import {
useActiveTool,
useAsset3dWidget,
useCamMode,
useDeletableFloorItem,
useDeleteModels,
useFloorItems,
useLoadingProgress,
useRenderDistance,
useselectedFloorItem,
useSelectedItem,
useSimulationStates,
useSocketStore,
useToggleView,
useTransformMode,
} from "../../../store/store";
import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSimulationStates, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store";
import assetVisibility from "../geomentries/assets/assetVisibility";
import { useEffect } from "react";
import * as THREE from "three";
@ -31,28 +16,10 @@ import addAssetModel from "../geomentries/assets/addAssetModel";
import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
import useModuleStore from "../../../store/useModuleStore";
// import { retrieveGLTF } from "../../../utils/indexDB/idbUtils";
const assetManagerWorker = new Worker(
new URL(
"../../../services/factoryBuilder/webWorkers/assetManagerWorker.js",
import.meta.url
)
);
const gltfLoaderWorker = new Worker(
new URL(
"../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js",
import.meta.url
)
);
const assetManagerWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/assetManagerWorker.js", import.meta.url));
const gltfLoaderWorker = new Worker(new URL("../../../services/factoryBuilder/webWorkers/gltfLoaderWorker.js", import.meta.url));
const FloorItemsGroup = ({
itemsGroup,
hoveredDeletableFloorItem,
AttachedObject,
floorGroup,
tempLoader,
isTempLoader,
plane,
}: any) => {
const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject, floorGroup, tempLoader, isTempLoader, plane, }: any) => {
const state: Types.ThreeState = useThree();
const { raycaster, controls }: any = state;
const { renderDistance } = useRenderDistance();
@ -72,9 +39,7 @@ const FloorItemsGroup = ({
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath(
"https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/"
);
dracoLoader.setDecoderPath("https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
useEffect(() => {
@ -99,10 +64,7 @@ const FloorItemsGroup = ({
getFloorAssets(organization).then((data) => {
if (data.length > 0) {
const uniqueItems = (data as Types.FloorItems).filter(
(item, index, self) =>
index === self.findIndex((t) => t.modelfileID === item.modelfileID)
);
const uniqueItems = (data as Types.FloorItems).filter((item, index, self) => index === self.findIndex((t) => t.modelfileID === item.modelfileID));
totalAssets = uniqueItems.length;
if (totalAssets === 0) {
updateLoadingProgress(100);
@ -149,17 +111,10 @@ const FloorItemsGroup = ({
if (toggleView) return;
const uuids: string[] = [];
itemsGroup.current?.children.forEach((child: any) => {
uuids.push(child.uuid);
});
itemsGroup.current?.children.forEach((child: any) => { uuids.push(child.uuid); });
const cameraPosition = state.camera.position;
assetManagerWorker.postMessage({
floorItems,
cameraPosition,
uuids,
renderDistance,
});
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance, });
}, [camMode, renderDistance]);
useEffect(() => {
@ -173,17 +128,10 @@ const FloorItemsGroup = ({
if (toggleView) return;
const uuids: string[] = [];
itemsGroup.current?.children.forEach((child: any) => {
uuids.push(child.uuid);
});
itemsGroup.current?.children.forEach((child: any) => { uuids.push(child.uuid); });
const cameraPosition = camera.position;
assetManagerWorker.postMessage({
floorItems,
cameraPosition,
uuids,
renderDistance,
});
assetManagerWorker.postMessage({ floorItems, cameraPosition, uuids, renderDistance, });
};
const startInterval = () => {
@ -244,13 +192,7 @@ const FloorItemsGroup = ({
if (drag) return;
if (deleteModels) {
DeleteFloorItems(
itemsGroup,
hoveredDeletableFloorItem,
setFloorItems,
setSimulationStates,
socket
);
DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, setSimulationStates, socket);
}
const Mode = transformMode;
@ -260,12 +202,7 @@ const FloorItemsGroup = ({
itemsGroup.current.children,
true
);
if (
intersects.length > 0 &&
intersects[0]?.object?.parent?.parent?.position &&
intersects[0]?.object?.parent?.parent?.scale &&
intersects[0]?.object?.parent?.parent?.rotation
) {
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
// let currentObject = intersects[0].object;
// while (currentObject) {
// if (currentObject.name === "Scene") {
@ -295,16 +232,8 @@ const FloorItemsGroup = ({
if (Mode !== null || activeTool === "cursor") {
if (!itemsGroup.current) return;
let intersects = raycaster.intersectObjects(
itemsGroup.current.children,
true
);
if (
intersects.length > 0 &&
intersects[0]?.object?.parent?.parent?.position &&
intersects[0]?.object?.parent?.parent?.scale &&
intersects[0]?.object?.parent?.parent?.rotation
) {
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
if (intersects.length > 0 && intersects[0]?.object?.parent?.parent?.position && intersects[0]?.object?.parent?.parent?.scale && intersects[0]?.object?.parent?.parent?.rotation) {
let currentObject = intersects[0].object;
while (currentObject) {
@ -317,9 +246,7 @@ const FloorItemsGroup = ({
AttachedObject.current = currentObject as any;
// controls.fitToSphere(AttachedObject.current!, true);
const bbox = new THREE.Box3().setFromObject(
AttachedObject.current
);
const bbox = new THREE.Box3().setFromObject(AttachedObject.current);
const size = bbox.getSize(new THREE.Vector3());
const center = bbox.getCenter(new THREE.Vector3());
@ -328,24 +255,11 @@ const FloorItemsGroup = ({
front.sub(AttachedObject.current.position).normalize();
const distance = Math.max(size.x, size.y, size.z) * 2;
const newPosition = center
.clone()
.addScaledVector(front, distance);
const newPosition = center.clone().addScaledVector(front, distance);
controls.setPosition(
newPosition.x,
newPosition.y,
newPosition.z,
true
);
controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true);
controls.setTarget(center.x, center.y, center.z, true);
controls.fitToBox(AttachedObject.current!, true, {
cover: true,
paddingTop: 5,
paddingLeft: 5,
paddingBottom: 5,
paddingRight: 5,
});
controls.fitToBox(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, });
setselectedFloorItem(AttachedObject.current!);
}
@ -362,21 +276,7 @@ const FloorItemsGroup = ({
if (!event.dataTransfer?.files[0]) return;
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) {
addAssetModel(
raycaster,
state.camera,
state.pointer,
floorGroup,
setFloorItems,
itemsGroup,
isTempLoader,
tempLoader,
socket,
selectedItem,
setSelectedItem,
setSimulationStates,
plane
);
addAssetModel(raycaster, state.camera, state.pointer, floorGroup, setFloorItems, itemsGroup, isTempLoader, tempLoader, socket, selectedItem, setSelectedItem, setSimulationStates, plane);
}
};
@ -407,28 +307,13 @@ const FloorItemsGroup = ({
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [
deleteModels,
transformMode,
controls,
selectedItem,
state.camera,
state.pointer,
activeTool,
activeModule,
]);
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]);
useFrame(() => {
if (controls)
assetVisibility(itemsGroup, state.camera.position, renderDistance);
if (deleteModels) {
DeletableHoveredFloorItems(
state,
itemsGroup,
hoveredDeletableFloorItem,
setDeletableFloorItem
);
if (deleteModels && activeModule === "builder") {
DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem);
} else if (!deleteModels) {
if (hoveredDeletableFloorItem.current) {
hoveredDeletableFloorItem.current = undefined;

View File

@ -9,9 +9,13 @@ import handleMeshMissed from "../eventFunctions/handleMeshMissed";
import DeleteWallItems from "../geomentries/walls/deleteWallItems";
import loadInitialWallItems from "../../scene/IntialLoad/loadInitialWallItems";
import AddWallItems from "../geomentries/walls/addWallItems";
import useModuleStore from "../../../store/useModuleStore";
const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletableWallItem, selectedItemsIndex, setSelectedItemsIndex, CSGGroup }: any) => {
const state = useThree();
const { socket } = useSocketStore();
const { pointer, camera, raycaster } = state;
const { deleteModels, setDeleteModels } = useDeleteModels();
const { wallItems, setWallItems } = useWallItems();
const { objectPosition, setObjectPosition } = useObjectPosition();
@ -19,10 +23,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
const { objectRotation, setObjectRotation } = useObjectRotation();
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
const { socket } = useSocketStore();
const state = useThree();
const { pointer, camera, raycaster } = state;
const { activeModule } = useModuleStore();
useEffect(() => {
// Load Wall Items from the backend
@ -209,7 +210,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
const onMouseUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = false;
if (!drag && deleteModels) {
if (!drag && deleteModels && activeModule === "builder") {
DeleteWallItems(hoveredDeletableWallItem, setWallItems, wallItems, socket);
}
}
@ -259,7 +260,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
}, [deleteModels, wallItems])
useEffect(() => {
if (deleteModels) {
if (deleteModels && activeModule === "builder") {
handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex);
setSelectedWallItem(null);
setSelectedItemsIndex(null);

View File

@ -192,7 +192,7 @@ function processLoadedModel(
},
]);
if (item.eventData || item.modelfileID === '67e3db95c2e8f37134526fb2') {
if (item.eventData || item.modelfileID === '67e3db5ac2e8f37134526f40' || item.modelfileID === '67eb7904c2e8f37134527eae') {
processEventData(item, setSimulationStates);
}
@ -227,10 +227,10 @@ function processEventData(item: Types.EventData, setSimulationStates: any) {
data as Types.VehicleEventsSchema
]);
} else if (item.modelfileID === '67e3db95c2e8f37134526fb2') {
} else if (item.modelfileID === '67e3db5ac2e8f37134526f40') {
const pointUUID = THREE.MathUtils.generateUUID();
const pointPosition = new THREE.Vector3(0, 1.75, 0);
const pointPosition = new THREE.Vector3(0, 1.5, -0.5);
const staticMachine: Types.StaticMachineEventsSchema = {
modeluuid: item.modeluuid,
@ -243,13 +243,39 @@ function processEventData(item: Types.EventData, setSimulationStates: any) {
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
connections: { source: { modelUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
},
position: item.position
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
};
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
...(prevEvents || []),
staticMachine as Types.StaticMachineEventsSchema
]);
} else if (item.modelfileID === '67eb7904c2e8f37134527eae') {
const pointUUID = THREE.MathUtils.generateUUID();
const pointPosition = new THREE.Vector3(0, 2.75, -0.5);
const armBot: Types.ArmBotEventsSchema = {
modeluuid: item.modeluuid,
modelName: item.modelname,
type: "ArmBot",
points: {
uuid: pointUUID,
position: [pointPosition.x, pointPosition.y, pointPosition.z],
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 1, processes: [] },
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
connections: { source: { modelUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
},
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
}
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
...(prevEvents || []),
armBot as Types.ArmBotEventsSchema
]);
}
}

View File

@ -151,7 +151,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
return updatedItems;
});
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
@ -234,7 +234,7 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
newEventData.position = newFloorItem.position;
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as Types.ConveyorEventsSchema
]);
@ -313,13 +313,44 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
newEventData.modelName = newFloorItem.modelname;
newEventData.position = newFloorItem.position;
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as Types.VehicleEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else {
//REST
// await setFloorItemApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = {
organization,
modeluuid: newFloorItem.modeluuid,
modelname: newFloorItem.modelname,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id,
};
socket.emit("v2:model-asset:add", data);
}
} else {

View File

@ -132,7 +132,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
return updatedItems;
});
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
@ -216,7 +216,7 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
newEventData.position = newFloorItem.position;
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as Types.ConveyorEventsSchema
]);
@ -295,13 +295,44 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
newEventData.modelName = newFloorItem.modelname;
newEventData.position = newFloorItem.position;
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as Types.VehicleEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else {
//REST
// await setFloorItemApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = {
organization,
modeluuid: newFloorItem.modeluuid,
modelname: newFloorItem.modelname,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id,
};
socket.emit("v2:model-asset:add", data);
}
} else {

View File

@ -180,12 +180,12 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
return updatedItems;
});
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
if (eventData && eventData.type !== 'StaticMachine') {
if (eventData) {
if (eventData.type === 'Conveyor' && eventData) {
const backendEventData = {
@ -229,7 +229,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
newEventData.position = newFloorItem.position;
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }
@ -280,7 +280,113 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
newEventData.modelName = newFloorItem.modelname;
newEventData.position = newFloorItem.position;
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }
: event
);
return updatedEvents;
});
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'StaticMachine' && eventData) {
const backendEventData = {
type: 'StaticMachine',
points: eventData.points,
};
// REST
// await setFloorItemApi(
// organization,
// obj.uuid,
// obj.userData.name,
// obj.userData.modelId,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// false,
// true,
// { type: backendEventData.type, points: backendEventData.points }
// );
//SOCKET
const data = {
organization,
modeluuid: newFloorItem.modeluuid,
modelname: newFloorItem.modelname,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
// eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id,
};
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
newEventData.modeluuid = newFloorItem.modeluuid;
newEventData.modelName = newFloorItem.modelname;
newEventData.position = newFloorItem.position;
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }
: event
);
return updatedEvents;
});
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'ArmBot' && eventData) {
const backendEventData = {
type: 'ArmBot',
points: eventData.points,
};
// REST
// await setFloorItemApi(
// organization,
// obj.uuid,
// obj.userData.name,
// obj.userData.modelId,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// false,
// true,
// { type: backendEventData.type, points: backendEventData.points }
// );
//SOCKET
const data = {
organization,
modeluuid: newFloorItem.modeluuid,
modelname: newFloorItem.modelname,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
// eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id,
};
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
newEventData.modeluuid = newFloorItem.modeluuid;
newEventData.modelName = newFloorItem.modelname;
newEventData.position = newFloorItem.position;
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }

View File

@ -184,13 +184,12 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
return updatedItems;
});
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
console.log('eventData: ', eventData);
let eventData: Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema | undefined = simulationStates.find((events) => events.modeluuid === obj.userData.modeluuid);
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
if (eventData && eventData.type !== 'StaticMachine') {
if (eventData) {
if (eventData.type === 'Conveyor' && eventData) {
const backendEventData = {
@ -233,9 +232,8 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
newEventData.modelName = newFloorItem.modelname;
newEventData.position = newFloorItem.position;
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
console.log('newEventData: ', newEventData);
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }
@ -287,7 +285,113 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
newEventData.modelName = newFloorItem.modelname;
newEventData.position = newFloorItem.position;
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }
: event
);
return updatedEvents;
});
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'StaticMachine' && eventData) {
const backendEventData = {
type: 'StaticMachine',
points: eventData.points,
};
// REST
// await setFloorItemApi(
// organization,
// obj.uuid,
// obj.userData.name,
// obj.userData.modelId,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// false,
// true,
// { type: backendEventData.type, points: backendEventData.points }
// );
//SOCKET
const data = {
organization,
modeluuid: newFloorItem.modeluuid,
modelname: newFloorItem.modelname,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
// eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id,
};
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
newEventData.modeluuid = newFloorItem.modeluuid;
newEventData.modelName = newFloorItem.modelname;
newEventData.position = newFloorItem.position;
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }
: event
);
return updatedEvents;
});
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'ArmBot' && eventData) {
const backendEventData = {
type: 'ArmBot',
points: eventData.points,
};
// REST
// await setFloorItemApi(
// organization,
// obj.uuid,
// obj.userData.name,
// obj.userData.modelId,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// false,
// true,
// { type: backendEventData.type, points: backendEventData.points }
// );
//SOCKET
const data = {
organization,
modeluuid: newFloorItem.modeluuid,
modelname: newFloorItem.modelname,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
// eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id,
};
const newEventData: any = { type: backendEventData.type, points: backendEventData.points };
newEventData.modeluuid = newFloorItem.modeluuid;
newEventData.modelName = newFloorItem.modelname;
newEventData.position = newFloorItem.position;
newEventData.rotation = [obj.rotation.x, obj.rotation.y, obj.rotation.z];
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }

View File

@ -240,7 +240,7 @@ const SelectionControls: React.FC = () => {
}
});
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).filter(event => event.modeluuid !== selectedMesh.uuid);
return updatedEvents;
});

View File

@ -1,9 +1,9 @@
import { useFrame, useThree } from '@react-three/fiber';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import { QuadraticBezierLine } from '@react-three/drei';
import { useIsConnecting, useSimulationStates, useSocketStore } from '../../../store/store';
import { useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store';
import useModuleStore from '../../../store/useModuleStore';
import { usePlayButtonStore } from '../../../store/usePlayButtonStore';
import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/setEventsApt';
@ -11,28 +11,21 @@ import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/
function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) {
const { activeModule } = useModuleStore();
const { gl, raycaster, scene, pointer, camera } = useThree();
const { renderDistance } = useRenderDistance();
const { setIsConnecting } = useIsConnecting();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { isPlaying } = usePlayButtonStore();
const { socket } = useSocketStore();
const groupRefs = useRef<{ [key: string]: any }>({});
const [firstSelected, setFirstSelected] = useState<{
modelUUID: string;
sphereUUID: string;
position: THREE.Vector3;
isCorner: boolean;
} | null>(null);
const [firstSelected, setFirstSelected] = useState<{ modelUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null);
const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3, end: THREE.Vector3, mid: THREE.Vector3 } | null>(null);
const [helperlineColor, setHelperLineColor] = useState<string>('red');
const updatePathConnections = (
fromModelUUID: string,
fromPointUUID: string,
toModelUUID: string,
toPointUUID: string
) => {
const updatePathConnections = (fromModelUUID: string, fromPointUUID: string, toModelUUID: string, toPointUUID: string) => {
const updatedPaths = simulationStates.map(path => {
if (path.type === 'Conveyor') {
// Handle outgoing connections from Conveyor
if (path.modeluuid === fromModelUUID) {
return {
...path,
@ -61,6 +54,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
})
};
}
// Handle incoming connections to Conveyor
else if (path.modeluuid === toModelUUID) {
return {
...path,
@ -167,82 +161,170 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
return path;
}
// else if (path.type === 'StaticMachine') {
// if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) {
// const newTarget = {
// modelUUID: toModelUUID,
// pointUUID: toPointUUID
// };
// const existingTargets = path.points.connections.targets || [];
else if (path.type === 'StaticMachine') {
// Handle outgoing connections from StaticMachine
if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) {
const newTarget = {
modelUUID: toModelUUID,
pointUUID: toPointUUID
};
// // Check if target is an ArmBot
// const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
// if (toPath?.type !== 'ArmBot') {
// console.log("StaticMachine can only connect to ArmBot");
// return path;
// }
// Ensure target is an ArmBot
const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
if (toPath?.type !== 'ArmBot') {
console.log("StaticMachine can only connect to ArmBot");
return path;
}
// // Check if already has a connection
// if (existingTargets.length >= 1) {
// console.log("StaticMachine can have only one connection");
// return path;
// }
const existingTargets = path.points.connections.targets || [];
// if (!existingTargets.some(target =>
// target.modelUUID === newTarget.modelUUID &&
// target.pointUUID === newTarget.pointUUID
// )) {
// return {
// ...path,
// points: {
// ...path.points,
// connections: {
// ...path.points.connections,
// targets: [...existingTargets, newTarget]
// }
// }
// };
// }
// }
// // Handle incoming connections to StaticMachine
// else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) {
// const reverseTarget = {
// modelUUID: fromModelUUID,
// pointUUID: fromPointUUID
// };
// const existingTargets = path.points.connections.targets || [];
// Allow only one connection
if (existingTargets.length >= 1) {
console.log("StaticMachine can only have one connection");
return path;
}
// // Check if source is an ArmBot
// const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
// if (fromPath?.type !== 'ArmBot') {
// console.log("StaticMachine can only connect to ArmBot");
// return path;
// }
if (!existingTargets.some(target =>
target.modelUUID === newTarget.modelUUID &&
target.pointUUID === newTarget.pointUUID
)) {
return {
...path,
points: {
...path.points,
connections: {
...path.points.connections,
targets: [...existingTargets, newTarget]
}
}
};
}
}
// // Check if already has a connection
// if (existingTargets.length >= 1) {
// console.log("StaticMachine can have only one connection");
// return path;
// }
// Handle incoming connections to StaticMachine
else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) {
const reverseTarget = {
modelUUID: fromModelUUID,
pointUUID: fromPointUUID
};
const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
if (fromPath?.type !== 'ArmBot') {
console.log("StaticMachine can only be connected from ArmBot");
return path;
}
const existingTargets = path.points.connections.targets || [];
if (existingTargets.length >= 1) {
console.log("StaticMachine can only have one connection");
return path;
}
if (!existingTargets.some(target =>
target.modelUUID === reverseTarget.modelUUID &&
target.pointUUID === reverseTarget.pointUUID
)) {
return {
...path,
points: {
...path.points,
connections: {
...path.points.connections,
targets: [...existingTargets, reverseTarget]
}
}
};
}
}
return path;
}
else if (path.type === 'ArmBot') {
// Handle outgoing connections from ArmBot
if (path.modeluuid === fromModelUUID && path.points.uuid === fromPointUUID) {
const newTarget = {
modelUUID: toModelUUID,
pointUUID: toPointUUID
};
const toPath = simulationStates.find(p => p.modeluuid === toModelUUID);
if (!toPath) return path;
const existingTargets = path.points.connections.targets || [];
// Check if connecting to a StaticMachine and already connected to one
const alreadyConnectedToStatic = existingTargets.some(target => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
return targetPath?.type === 'StaticMachine';
});
if (toPath.type === 'StaticMachine') {
if (alreadyConnectedToStatic) {
console.log("ArmBot can only connect to one StaticMachine");
return path;
}
}
if (!existingTargets.some(target =>
target.modelUUID === newTarget.modelUUID &&
target.pointUUID === newTarget.pointUUID
)) {
return {
...path,
points: {
...path.points,
connections: {
...path.points.connections,
targets: [...existingTargets, newTarget]
}
}
};
}
}
// Handle incoming connections to ArmBot
else if (path.modeluuid === toModelUUID && path.points.uuid === toPointUUID) {
const reverseTarget = {
modelUUID: fromModelUUID,
pointUUID: fromPointUUID
};
const fromPath = simulationStates.find(p => p.modeluuid === fromModelUUID);
if (!fromPath) return path;
const existingTargets = path.points.connections.targets || [];
const alreadyConnectedFromStatic = existingTargets.some(target => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
return targetPath?.type === 'StaticMachine';
});
if (fromPath.type === 'StaticMachine') {
if (alreadyConnectedFromStatic) {
console.log("ArmBot can only be connected from one StaticMachine");
return path;
}
}
if (!existingTargets.some(target =>
target.modelUUID === reverseTarget.modelUUID &&
target.pointUUID === reverseTarget.pointUUID
)) {
return {
...path,
points: {
...path.points,
connections: {
...path.points.connections,
targets: [...existingTargets, reverseTarget]
}
}
};
}
}
return path;
}
// if (!existingTargets.some(target =>
// target.modelUUID === reverseTarget.modelUUID &&
// target.pointUUID === reverseTarget.pointUUID
// )) {
// return {
// ...path,
// points: {
// ...path.points,
// connections: {
// ...path.points.connections,
// targets: [...existingTargets, reverseTarget]
// }
// }
// };
// }
// }
// return path;
// }
return path;
});
@ -252,10 +334,10 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID
);
updateBackend(updatedPathDetails);
// updateBackend(updatedPathDetails);
};
const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => {
const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
if (updatedPaths.length === 0) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
@ -437,6 +519,69 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return;
}
// Check if StaticMachine is involved in the connection
if ((firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') ||
(secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot')) {
console.log("StaticMachine can only connect to ArmBot");
return;
}
// Check if StaticMachine already has a connection
if (firstPath?.type === 'StaticMachine') {
const staticConnections = firstPath.points.connections.targets.length;
if (staticConnections >= 1) {
console.log("StaticMachine can only have one connection");
return;
}
}
if (secondPath?.type === 'StaticMachine') {
const staticConnections = secondPath.points.connections.targets.length;
if (staticConnections >= 1) {
console.log("StaticMachine can only have one connection");
return;
}
}
// Check if ArmBot is involved
if ((firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') ||
(secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine')) {
const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath;
const staticPath = firstPath?.type === 'StaticMachine' ? firstPath : secondPath;
const armBotConnections = armBotPath.points.connections.targets || [];
const alreadyConnectedToStatic = armBotConnections.some(target => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
return targetPath?.type === 'StaticMachine';
});
if (alreadyConnectedToStatic) {
console.log("ArmBot can only connect to one StaticMachine");
return;
}
const staticConnections = staticPath.points.connections.targets.length;
if (staticConnections >= 1) {
console.log("StaticMachine can only have one connection");
return;
}
}
// Prevent ArmBot ↔ ArmBot
if (firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot') {
console.log("Cannot connect two ArmBots together");
return;
}
// If one is ArmBot, ensure the other is StaticMachine or Conveyor
if (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') {
const otherType = firstPath?.type === 'ArmBot' ? secondPath?.type : firstPath?.type;
if (otherType !== 'StaticMachine' && otherType !== 'Conveyor') {
console.log("ArmBot can only connect to Conveyors or one StaticMachine");
return;
}
}
// At least one must be start/end point
if (!firstSelected.isCorner && !isStartOrEnd) {
console.log("At least one of the selected spheres must be a start or end point.");
@ -489,6 +634,15 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
};
}, [camera, scene, raycaster, firstSelected, simulationStates]);
useFrame(() => {
Object.values(groupRefs.current).forEach((group) => {
if (group) {
const distance = new THREE.Vector3(...group.position.toArray()).distanceTo(camera.position);
group.visible = ((distance <= renderDistance) && !isPlaying);
}
});
});
useFrame(() => {
if (firstSelected) {
raycaster.setFromCamera(pointer, camera);
@ -574,12 +728,50 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
(firstPath?.type === 'Vehicle' && secondPath?.type !== 'Conveyor') ||
(secondPath?.type === 'Vehicle' && firstPath?.type !== 'Conveyor');
// Check if StaticMachine is connecting to non-ArmBot
const isStaticMachineToNonArmBot =
(firstPath?.type === 'StaticMachine' && secondPath?.type !== 'ArmBot') ||
(secondPath?.type === 'StaticMachine' && firstPath?.type !== 'ArmBot');
// Check if StaticMachine already has a connection
const isStaticMachineAtMaxConnections =
(firstPath?.type === 'StaticMachine' && firstPath.points.connections.targets.length >= 1) ||
(secondPath?.type === 'StaticMachine' && secondPath.points.connections.targets.length >= 1);
// Check if ArmBot is connecting to StaticMachine
const isArmBotToStaticMachine =
(firstPath?.type === 'ArmBot' && secondPath?.type === 'StaticMachine') ||
(secondPath?.type === 'ArmBot' && firstPath?.type === 'StaticMachine');
// Prevent multiple StaticMachine connections to ArmBot
let isArmBotAlreadyConnectedToStatic = false;
if (isArmBotToStaticMachine) {
const armBotPath = firstPath?.type === 'ArmBot' ? firstPath : secondPath;
isArmBotAlreadyConnectedToStatic = armBotPath.points.connections.targets.some(target => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
return targetPath?.type === 'StaticMachine';
});
}
// Prevent ArmBot to ArmBot
const isArmBotToArmBot = firstPath?.type === 'ArmBot' && secondPath?.type === 'ArmBot';
// If ArmBot is involved, other must be Conveyor or StaticMachine
const isArmBotToInvalidType = (firstPath?.type === 'ArmBot' || secondPath?.type === 'ArmBot') &&
!(firstPath?.type === 'Conveyor' || firstPath?.type === 'StaticMachine' ||
secondPath?.type === 'Conveyor' || secondPath?.type === 'StaticMachine');
if (
!isDuplicateConnection &&
!isVehicleToVehicle &&
!isNonVehicleAlreadyConnected &&
!isVehicleAtMaxConnections &&
!isVehicleConnectingToNonConveyor &&
!isStaticMachineToNonArmBot &&
!isStaticMachineAtMaxConnections &&
!isArmBotToArmBot &&
!isArmBotToInvalidType &&
!isArmBotAlreadyConnectedToStatic &&
firstSelected.sphereUUID !== sphereUUID &&
firstSelected.modelUUID !== modelUUID &&
(firstSelected.isCorner || isConnectable) &&
@ -596,6 +788,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
} else {
isInvalidConnection = true;
}
}
if (snappedSphere) {
@ -633,7 +826,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
});
return (
<group name='simulationConnectionGroup' visible={!isPlaying} >
<group name='simulationConnectionGroup' visible={!isPlaying}>
{simulationStates.flatMap(path => {
if (path.type === 'Conveyor') {
return path.points.flatMap(point =>
@ -652,7 +845,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
@ -662,6 +854,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return (
<QuadraticBezierLine
key={`${point.uuid}-${target.pointUUID}-${index}`}
ref={(el) => (groupRefs.current[`${point.uuid}-${target.pointUUID}-${index}`] = el!)}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
@ -676,7 +869,9 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return null;
})
);
} else if (path.type === 'Vehicle') {
}
if (path.type === 'Vehicle') {
return path.points.connections.targets.map((target, index) => {
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
@ -689,7 +884,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
@ -699,6 +893,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return (
<QuadraticBezierLine
key={`${path.points.uuid}-${target.pointUUID}-${index}`}
ref={(el) => (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
@ -713,6 +908,48 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return null;
});
}
if (path.type === 'StaticMachine') {
return path.points.connections.targets.map((target, index) => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
if (targetPath?.type !== 'ArmBot') return null;
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', path.points.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
if (fromSphere && toSphere) {
const fromWorldPosition = new THREE.Vector3();
const toWorldPosition = new THREE.Vector3();
fromSphere.getWorldPosition(fromWorldPosition);
toSphere.getWorldPosition(toWorldPosition);
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
return (
<QuadraticBezierLine
key={`${path.points.uuid}-${target.pointUUID}-${index}`}
ref={(el) => (groupRefs.current[`${path.points.uuid}-${target.pointUUID}-${index}`] = el!)}
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="#42a5f5"
lineWidth={4}
dashed
dashSize={0.75}
dashScale={20}
/>
);
}
return null;
});
}
return [];
})}
@ -730,6 +967,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
)}
</group>
);
}
export default PathConnector;

View File

@ -206,6 +206,7 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
return (
<group
name={`${path.modeluuid}-${path.type}-path`}
uuid={path.modeluuid}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
@ -271,10 +272,11 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
})}
</group>
);
} else if (path.type === "Vehicle" || path.type === "StaticMachine") {
} else if (path.type === "Vehicle") {
return (
<group
name={`${path.modeluuid}-${path.type}-path`}
uuid={path.modeluuid}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
@ -323,6 +325,114 @@ function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObjec
</Sphere>
</group>
);
} else if (path.type === "StaticMachine") {
return (
<group
name={`${path.modeluuid}-${path.type}-path`}
uuid={path.modeluuid}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
rotation={path.rotation}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({
path,
group: groupRefs.current[path.modeluuid],
});
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule("mechanics");
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule("properties");
}}
>
<Sphere
key={path.points.uuid}
uuid={path.points.uuid}
position={path.points.position}
args={[0.15, 32, 32]}
name="events-sphere"
ref={(el) => (sphereRefs.current[path.points.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
points: sphereRefs.current[path.points.uuid],
});
setSubModule("mechanics");
setSelectedPath(null);
}}
userData={{ points: path.points, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule("properties");
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="yellow" />
</Sphere>
</group>
);
} else if (path.type === "ArmBot") {
return (
<group
name={`${path.modeluuid}-${path.type}-path`}
uuid={path.modeluuid}
key={path.modeluuid}
ref={(el) => (groupRefs.current[path.modeluuid] = el!)}
position={path.position}
rotation={path.rotation}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedPath({
path,
group: groupRefs.current[path.modeluuid],
});
setSelectedActionSphere(null);
setTransformMode(null);
setSubModule("mechanics");
}}
onPointerMissed={() => {
if (eyeDropMode) return;
setSelectedPath(null);
setSubModule("properties");
}}
>
<Sphere
key={path.points.uuid}
uuid={path.points.uuid}
position={path.points.position}
args={[0.15, 32, 32]}
name="events-sphere"
ref={(el) => (sphereRefs.current[path.points.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
points: sphereRefs.current[path.points.uuid],
});
setSubModule("mechanics");
setSelectedPath(null);
}}
userData={{ points: path.points, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule("properties");
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial color="pink" />
</Sphere>
</group>
);
}
return null;
})}

View File

@ -347,12 +347,12 @@ export const useSelectedPath = create<any>((set: any) => ({
}));
interface SimulationPathsStore {
simulationStates: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[];
simulationStates: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[];
setSimulationStates: (
paths:
| (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]
| ((prev: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]
) => (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[])
| (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]
| ((prev: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]
) => (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[])
) => void;
}

View File

@ -329,6 +329,7 @@ interface StaticMachineEventsSchema {
connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] };
};
position: [number, number, number];
rotation: [number, number, number];
}
interface ArmBotEventsSchema {
@ -343,6 +344,7 @@ interface ArmBotEventsSchema {
connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] };
};
position: [number, number, number];
rotation: [number, number, number];
}
export type EventData = {