folder structure change

This commit is contained in:
2025-04-21 11:53:42 +05:30
parent 31561428ef
commit 54cc3deb98
118 changed files with 3014 additions and 13419 deletions

View File

@@ -5,8 +5,8 @@ import Header from "./Header";
import useToggleStore from "../../../store/useUIToggleStore"; import useToggleStore from "../../../store/useUIToggleStore";
import Assets from "./Assets"; import Assets from "./Assets";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
import Widgets from "./visualization/widgets/Widgets"; import Widgets from ".//visualization/widgets/Widgets";
import Templates from "../../../modules/visualization/template/Templates"; import Templates from "../../../modules//visualization/template/Templates";
import Search from "../../ui/inputs/Search"; import Search from "../../ui/inputs/Search";
const SideBarLeft: React.FC = () => { const SideBarLeft: React.FC = () => {

View File

@@ -5,11 +5,11 @@ import {
GlobeIcon, GlobeIcon,
WalletIcon, WalletIcon,
} from "../../../../icons/3dChartIcons"; } from "../../../../icons/3dChartIcons";
import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard"; import SimpleCard from "../../../../../modules//visualization/widgets/floating/cards/SimpleCard";
import WarehouseThroughput from "../../../../../modules/visualization/widgets/floating/cards/WarehouseThroughput"; import WarehouseThroughput from "../../../../../modules//visualization/widgets/floating/cards/WarehouseThroughput";
import ProductivityDashboard from "../../../../../modules/visualization/widgets/floating/cards/ProductivityDashboard"; import ProductivityDashboard from "../../../../../modules//visualization/widgets/floating/cards/ProductivityDashboard";
import FleetEfficiency from "../../../../../modules/visualization/widgets/floating/cards/FleetEfficiency"; import FleetEfficiency from "../../../../../modules//visualization/widgets/floating/cards/FleetEfficiency";
interface Widget { interface Widget {
id: string; id: string;

View File

@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import { AppDockIcon } from "../../icons/HeaderIcons"; import { AppDockIcon } from "../../icons/HeaderIcons";
import orgImg from "../../../assets/orgTemp.png"; import orgImg from "../../../assets/orgTemp.png";
import { useActiveUsers } from "../../../store/store"; import { useActiveUsers } from "../../../store/store";
import { getAvatarColor } from "../../../modules/collaboration/users/functions/getAvatarColor"; import { getAvatarColor } from "../../../functions/users/functions/getAvatarColor";
import { ActiveUser } from "../../../types/users"; import { ActiveUser } from "../../../types/users";
import CollaborationPopup from "../../templates/CollaborationPopup"; import CollaborationPopup from "../../templates/CollaborationPopup";

View File

@@ -10,25 +10,19 @@ import {
SimulationIcon, SimulationIcon,
} from "../../icons/SimulationIcons"; } from "../../icons/SimulationIcons";
import useToggleStore from "../../../store/useUIToggleStore"; import useToggleStore from "../../../store/useUIToggleStore";
import ConveyorMechanics from "./mechanics/ConveyorMechanics";
import Visualization from "./visualization/Visualization"; import Visualization from "./visualization/Visualization";
import Analysis from "./analysis/Analysis"; import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations"; import Simulations from "./simulation/Simulations";
import { import {
useSelectedActionSphere,
useSelectedFloorItem, useSelectedFloorItem,
} from "../../../store/store"; } from "../../../store/store";
import GlobalProperties from "./properties/GlobalProperties"; import GlobalProperties from "./properties/GlobalProperties";
import AsstePropertiies from "./properties/AssetProperties"; import AsstePropertiies from "./properties/AssetProperties";
import ZoneProperties from "./properties/ZoneProperties"; 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 SideBarRight: React.FC = () => {
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { toggleUI } = useToggleStore(); const { toggleUI } = useToggleStore();
const { selectedActionSphere } = useSelectedActionSphere();
const { subModule, setSubModule } = useSubModuleStore(); const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useSelectedFloorItem(); const { selectedFloorItem } = useSelectedFloorItem();
// Reset activeList whenever activeModule changes // Reset activeList whenever activeModule changes
@@ -112,46 +106,9 @@ const SideBarRight: React.FC = () => {
{toggleUI && activeModule === "simulation" && ( {toggleUI && activeModule === "simulation" && (
<> <>
{subModule === "mechanics" && {subModule === "mechanics" && (
selectedActionSphere &&
selectedActionSphere.path.type === "Conveyor" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ConveyorMechanics />
</div>
</div>
)}
{subModule === "mechanics" &&
selectedActionSphere &&
selectedActionSphere.path.type === "Vehicle" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<VehicleMechanics />
</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-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
<ConveyorMechanics /> {/* default */}
</div> </div>
</div> </div>
)} )}

View File

@@ -1,411 +0,0 @@
import React, { useRef, useMemo, useCallback, useState } from "react";
import { InfoIcon, AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import * as SimulationTypes from '../../../../types/simulationTypes';
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import { handleResize } from "../../../../functions/handleResizePannel";
interface ConnectedModel {
modelUUID: string;
modelName: string;
points: {
uuid: string;
position: [number, number, number];
index?: number;
}[];
triggers?: {
uuid: string;
name: string;
type: string;
isUsed: boolean;
}[];
}
const ArmBotMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { socket } = useSocketStore();
const [selectedProcessIndex, setSelectedProcessIndex] = useState<number | null>(null);
const actionsContainerRef = useRef<HTMLDivElement>(null);
// Get connected models and their triggers
const connectedModels = useMemo<ConnectedModel[]>(() => {
if (!selectedActionSphere?.points?.uuid) return [];
const armBotPaths = simulationStates.filter(
(path): path is SimulationTypes.ArmBotEventsSchema => path.type === "ArmBot"
);
const currentPoint = armBotPaths.find(
(path) => path.points.uuid === selectedActionSphere.points.uuid
)?.points;
if (!currentPoint?.connections?.targets) return [];
return currentPoint.connections.targets.reduce<ConnectedModel[]>((acc, target) => {
const connectedModel = simulationStates.find(
(model) => model.modeluuid === target.modelUUID
);
if (!connectedModel) return acc;
let triggers: { uuid: string; name: string; type: string; isUsed: boolean }[] = [];
let points: { uuid: string; position: [number, number, number] }[] = [];
if (connectedModel.type === "Conveyor") {
const conveyor = connectedModel as SimulationTypes.ConveyorEventsSchema;
const connectedPointUUIDs = currentPoint?.connections?.targets
.filter(t => t.modelUUID === connectedModel.modeluuid)
.map(t => t.pointUUID) || [];
points = conveyor.points
.map((point, idx) => ({
uuid: point.uuid,
position: point.position,
index: idx
}))
.filter(point => connectedPointUUIDs.includes(point.uuid));
triggers = conveyor.points.flatMap(p => p.triggers?.filter(t => t.isUsed) || []);
}
else if (connectedModel.type === "StaticMachine") {
const staticMachine = connectedModel as SimulationTypes.StaticMachineEventsSchema;
points = [{
uuid: staticMachine.points.uuid,
position: staticMachine.points.position
}];
triggers = staticMachine.points.triggers ?
[{
uuid: staticMachine.points.triggers.uuid,
name: staticMachine.points.triggers.name,
type: staticMachine.points.triggers.type,
isUsed: true // StaticMachine triggers are always considered used
}] : [];
}
if (!acc.some(m => m.modelUUID === connectedModel.modeluuid)) {
acc.push({
modelUUID: connectedModel.modeluuid,
modelName: connectedModel.modelName,
points,
triggers
});
}
return acc;
}, []);
}, [selectedActionSphere, simulationStates]);
// Get triggers from connected models
const connectedTriggers = useMemo(() => {
return connectedModels.flatMap(model =>
(model.triggers || []).map(trigger => ({
...trigger,
displayName: `${model.modelName} - ${trigger.name}`,
modelUUID: model.modelUUID
}))
);
}, [connectedModels]);
// Get all points from connected models
const connectedPoints = useMemo(() => {
return connectedModels.flatMap(model =>
model.points.map(point => ({
...point,
displayName: `${model.modelName} - Point${typeof point.index === 'number' ? ` ${point.index}` : ''}`,
modelUUID: model.modelUUID
}))
);
}, [connectedModels]);
const { selectedPoint } = useMemo(() => {
if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null };
const armBotPaths = simulationStates.filter(
(path): path is SimulationTypes.ArmBotEventsSchema => path.type === "ArmBot"
);
const points = armBotPaths.find(
(path) => path.points.uuid === selectedActionSphere.points.uuid
)?.points;
return {
selectedPoint: points || null
};
}, [selectedActionSphere, simulationStates]);
const updateBackend = async (updatedPath: SimulationTypes.ArmBotEventsSchema | undefined) => {
if (!updatedPath) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "ArmBot", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
}
const handleActionUpdate = useCallback((updatedAction: Partial<SimulationTypes.ArmBotEventsSchema['points']['actions']>) => {
if (!selectedActionSphere?.points?.uuid || !selectedPoint) return;
const updatedPaths = simulationStates.map((path) => {
if (path.type === "ArmBot" && path.points.uuid === selectedActionSphere.points.uuid) {
return {
...path,
points: {
...path.points,
actions: {
...path.points.actions,
...updatedAction
}
}
};
}
return path;
});
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ArmBotEventsSchema =>
path.type === "ArmBot" &&
path.points.uuid === selectedActionSphere.points.uuid
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
}, [selectedActionSphere?.points?.uuid, selectedPoint, simulationStates, setSimulationStates]);
const handleSpeedChange = useCallback((speed: number) => {
handleActionUpdate({ speed });
}, [handleActionUpdate]);
const handleProcessChange = useCallback((processes: SimulationTypes.ArmBotEventsSchema['points']['actions']['processes']) => {
handleActionUpdate({ processes });
}, [handleActionUpdate]);
const handleAddProcess = useCallback(() => {
if (!selectedPoint) return;
const newProcess: any = {
triggerId: "",
startPoint: "",
endPoint: ""
};
const updatedProcesses = selectedPoint.actions.processes ? [...selectedPoint.actions.processes, newProcess] : [newProcess];
handleProcessChange(updatedProcesses);
setSelectedProcessIndex(updatedProcesses.length - 1);
}, [selectedPoint, handleProcessChange]);
const handleDeleteProcess = useCallback((index: number) => {
if (!selectedPoint?.actions.processes) return;
const updatedProcesses = [...selectedPoint.actions.processes];
updatedProcesses.splice(index, 1);
handleProcessChange(updatedProcesses);
// Reset selection if deleting the currently selected process
if (selectedProcessIndex === index) {
setSelectedProcessIndex(null);
} else if (selectedProcessIndex !== null && selectedProcessIndex > index) {
// Adjust selection index if needed
setSelectedProcessIndex(selectedProcessIndex - 1);
}
}, [selectedPoint, selectedProcessIndex, handleProcessChange]);
const handleTriggerSelect = useCallback((displayName: string, index: number) => {
const availableOptions = getFilteredTriggerOptions(index);
const selectedDisplayIndex = availableOptions.indexOf(displayName);
const filteredTriggers = connectedTriggers.filter(trigger =>
!selectedPoint?.actions.processes
?.filter((_, i) => i !== index)
.map(p => p.triggerId)
.includes(trigger.uuid)
);
const selected = filteredTriggers[selectedDisplayIndex];
if (!selected || !selectedPoint?.actions.processes) return;
const oldProcess = selectedPoint.actions.processes[index];
const updatedProcesses = [...selectedPoint.actions.processes];
updatedProcesses[index] = {
...oldProcess,
triggerId: selected.uuid,
startPoint: oldProcess.startPoint || "",
endPoint: oldProcess.endPoint || ""
};
handleProcessChange(updatedProcesses);
}, [connectedTriggers, selectedPoint, handleProcessChange]);
const handleStartPointSelect = useCallback((displayName: string, index: number) => {
if (!selectedPoint?.actions.processes) return;
const point = connectedPoints.find(p => p.displayName === displayName);
if (!point) return;
const updatedProcesses = [...selectedPoint.actions.processes];
updatedProcesses[index] = {
...updatedProcesses[index],
startPoint: point.uuid
};
handleProcessChange(updatedProcesses);
}, [selectedPoint, connectedPoints, handleProcessChange]);
const handleEndPointSelect = useCallback((displayName: string, index: number) => {
if (!selectedPoint?.actions.processes) return;
const point = connectedPoints.find(p => p.displayName === displayName);
if (!point) return;
const updatedProcesses = [...selectedPoint.actions.processes];
updatedProcesses[index] = {
...updatedProcesses[index],
endPoint: point.uuid
};
handleProcessChange(updatedProcesses);
}, [selectedPoint, connectedPoints, handleProcessChange]);
const getProcessByIndex = useCallback((index: number) => {
if (!selectedPoint?.actions.processes || index >= selectedPoint.actions.processes.length) return null;
return selectedPoint.actions.processes[index];
}, [selectedPoint]);
const getFilteredTriggerOptions = (currentIndex: number) => {
const usedTriggerUUIDs = selectedPoint?.actions.processes?.filter((_, i) => i !== currentIndex).map(p => p.triggerId).filter(Boolean) ?? [];
return connectedTriggers.filter(trigger => !usedTriggerUUIDs.includes(trigger.uuid)).map(trigger => trigger.displayName);
};
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">
<div className="properties-header">ArmBot Properties</div>
{selectedPoint && (
<>
<InputWithDropDown
key={`speed-${selectedPoint.uuid}`}
label="ArmBot Speed"
min={0.1}
step={0.1}
value={selectedPoint.actions.speed.toString()}
onChange={(value) => handleSpeedChange(parseFloat(value))}
/>
<div className="actions">
<div className="header">
<div className="header-value">Processes</div>
<div className="add-button" onClick={handleAddProcess}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{selectedPoint.actions.processes?.map((process, index) => (
<div
key={`process-${index}`}
className={`list-item ${selectedProcessIndex === index ? "active" : ""}`}
>
<div
className="value"
onClick={() => setSelectedProcessIndex(index)}
>
Process {index + 1}
</div>
<div
className="remove-button"
onClick={() => handleDeleteProcess(index)}
>
<RemoveIcon />
</div>
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
{selectedProcessIndex !== null && (
<div className="process-configuration">
<LabledDropdown
key={`trigger-select-${selectedProcessIndex}`}
label="Select Trigger"
defaultOption={
connectedTriggers.find(t =>
t.uuid === getProcessByIndex(selectedProcessIndex)?.triggerId
)?.displayName || 'Select a trigger'
}
onSelect={(value) => handleTriggerSelect(value, selectedProcessIndex)}
options={getFilteredTriggerOptions(selectedProcessIndex)}
/>
<LabledDropdown
key={`start-point-${selectedProcessIndex}`}
label="Start Point"
defaultOption={
connectedPoints.find(p =>
p.uuid === getProcessByIndex(selectedProcessIndex)?.startPoint
)?.displayName || 'Select start point'
}
onSelect={(value) => handleStartPointSelect(value, selectedProcessIndex)}
options={connectedPoints.map(point => point.displayName)}
/>
<LabledDropdown
key={`end-point-${selectedProcessIndex}`}
label="End Point"
defaultOption={
connectedPoints.find(p =>
p.uuid === getProcessByIndex(selectedProcessIndex)?.endPoint
)?.displayName || 'Select end point'
}
onSelect={(value) => handleEndPointSelect(value, selectedProcessIndex)}
options={connectedPoints.map(point => point.displayName)}
/>
</div>
)}
</>
)}
</div>
<div className="footer">
<InfoIcon />
Configure ArmBot properties and trigger-based processes.
</div>
</div>
</div>
);
};
export default React.memo(ArmBotMechanics);

View File

@@ -1,879 +0,0 @@
import React, { useRef, useState, useMemo, useEffect } from "react";
import {
AddIcon,
InfoIcon,
RemoveIcon,
ResizeHeightIcon,
} from "../../../icons/ExportCommonIcons";
import RenameInput from "../../../ui/inputs/RenameInput";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import { handleResize } from "../../../../functions/handleResizePannel";
import {
useFloorItems,
useSelectedActionSphere,
useSelectedPath,
useSimulationStates,
useSocketStore,
} from "../../../../store/store";
import * as THREE from "three";
import * as SimulationTypes from "../../../../types/simulationTypes";
import InputToggle from "../../../ui/inputs/InputToggle";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
const ConveyorMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { selectedPath, setSelectedPath } = useSelectedPath();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const actionsContainerRef = useRef<HTMLDivElement>(null);
const triggersContainerRef = useRef<HTMLDivElement>(null);
const selectedPoint = useMemo(() => {
if (!selectedActionSphere) return null;
return simulationStates
.filter(
(path): path is SimulationTypes.ConveyorEventsSchema => path.type === "Conveyor"
)
.flatMap((path) => path.points)
.find((point) => point.uuid === selectedActionSphere.points.uuid);
}, [selectedActionSphere, simulationStates]);
const updateBackend = async (updatedPath: SimulationTypes.ConveyorEventsSchema | undefined) => {
if (!updatedPath) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed }
}
socket.emit('v2:model-asset:updateEventData', data);
}
const handleAddAction = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) => {
if (path.type === "Conveyor") {
return {
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.points.uuid) {
const actionIndex = point.actions.length;
const newAction = {
uuid: THREE.MathUtils.generateUUID(),
name: `Action ${actionIndex + 1}`,
type: "Inherit",
material: "Inherit",
delay: "Inherit",
spawnInterval: "Inherit",
isUsed: false,
};
return { ...point, actions: [...point.actions, newAction] };
}
return point;
}),
};
}
return path;
});
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
};
const handleDeleteAction = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
actions: point.actions.filter(
(action) => action.uuid !== uuid
),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
};
const handleActionSelect = (uuid: string, actionType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid
? {
...action,
type: actionType,
material:
actionType === "Spawn" || actionType === "Swap"
? "Inherit"
: action.material,
delay:
actionType === "Delay" ? "Inherit" : action.delay,
spawnInterval:
actionType === "Spawn"
? "Inherit"
: action.spawnInterval,
}
: action
),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
// Update the selected item to reflect changes
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
const updatedAction = updatedPaths
.filter(
(path): path is SimulationTypes.ConveyorEventsSchema => path.type === "Conveyor"
)
.flatMap((path) => path.points)
.find((p) => p.uuid === selectedActionSphere.points.uuid)
?.actions.find((a) => a.uuid === uuid);
if (updatedAction) {
setSelectedItem({
type: "action",
item: updatedAction,
});
}
}
};
// Modified handleMaterialSelect to ensure it only applies to relevant action types
const handleMaterialSelect = (uuid: string, material: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid &&
(action.type === "Spawn" || action.type === "Swap")
? { ...action, material }
: action
),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
// Update selected item if it's the current action
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
material,
},
});
}
};
const handleDelayChange = (uuid: string, delay: number | string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid ? { ...action, delay } : action
),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
};
const handleSpawnIntervalChange = (
uuid: string,
spawnInterval: number | string
) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
actions: point.actions.map((action) =>
action.uuid === uuid
? { ...action, spawnInterval }
: action
),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
};
const handleSpeedChange = (speed: number | string) => {
if (!selectedPath) return;
const updatedPaths = simulationStates.map((path) =>
path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.modeluuid === selectedPath.path.modeluuid
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
};
const handleAddTrigger = () => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) => {
if (point.uuid === selectedActionSphere.points.uuid) {
const triggerIndex = point.triggers.length;
const newTrigger = {
uuid: THREE.MathUtils.generateUUID(),
name: `Trigger ${triggerIndex + 1}`,
type: "",
bufferTime: 0,
isUsed: false,
};
return { ...point, triggers: [...point.triggers, newTrigger] };
}
return point;
}),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
};
const handleDeleteTrigger = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
triggers: point.triggers.filter(
(trigger) => trigger.uuid !== uuid
),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
};
const handleTriggerSelect = (uuid: string, triggerType: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
triggers: point.triggers.map((trigger) =>
trigger.uuid === uuid
? { ...trigger, type: triggerType }
: trigger
),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
// Ensure the selectedItem is updated immediately
const updatedTrigger = updatedPaths
.flatMap((path) => (path.type === "Conveyor" ? path.points : []))
.flatMap((point) => point.triggers)
.find((trigger) => trigger.uuid === uuid);
if (updatedTrigger) {
setSelectedItem({ type: "trigger", item: updatedTrigger });
}
};
// Update the toggle handlers to immediately update the selected item
const handleActionToggle = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
actions: point.actions.map((action) => ({
...action,
isUsed: action.uuid === uuid ? !action.isUsed : false,
})),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "action" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed,
},
});
}
};
// Do the same for trigger toggle
const handleTriggerToggle = (uuid: string) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
triggers: point.triggers.map((trigger) => ({
...trigger,
isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
})),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
// Immediately update the selected item if it's the one being toggled
if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
isUsed: !selectedItem.item.isUsed,
},
});
}
};
const handleTriggerBufferTimeChange = (uuid: string, bufferTime: number) => {
if (!selectedActionSphere) return;
const updatedPaths = simulationStates.map((path) =>
path.type === "Conveyor"
? {
...path,
points: path.points.map((point) =>
point.uuid === selectedActionSphere.points.uuid
? {
...point,
triggers: point.triggers.map((trigger) =>
trigger.uuid === uuid
? { ...trigger, bufferTime }
: trigger
),
}
: point
),
}
: path
);
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.ConveyorEventsSchema =>
path.type === "Conveyor" &&
path.points.some(
(point) => point.uuid === selectedActionSphere.points.uuid
)
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
// Immediately update selectedItem if it's the currently selected trigger
if (selectedItem?.type === "trigger" && selectedItem.item.uuid === uuid) {
setSelectedItem({
...selectedItem,
item: {
...selectedItem.item,
bufferTime,
},
});
}
};
const [selectedItem, setSelectedItem] = useState<{
type: "action" | "trigger";
item: any;
} | null>(null);
useEffect(() => {
setSelectedItem(null);
}, [selectedActionSphere]);
return (
<div className="machine-mechanics-container">
{!selectedPath && (
<div className="machine-mechanics-header">
{selectedActionSphere?.path?.modelName || "point name not found"}
</div>
)}
{selectedPath && (
<div className="machine-mechanics-header">
{selectedPath.path.modelName || "path name not found"}
</div>
)}
<div className="machine-mechanics-content-container">
{!selectedPath && (
<>
<div className="actions">
<div className="header">
<div className="header-value">Actions</div>
<div className="add-button" onClick={handleAddAction}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={actionsContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{selectedPoint?.actions.map((action) => (
<div
key={action.uuid}
className={`list-item ${selectedItem?.type === "action" &&
selectedItem.item?.uuid === action.uuid
? "active"
: ""
}`}
>
<div
className="value"
onClick={() =>
setSelectedItem({ type: "action", item: action })
}
>
<input type="radio" name="action" id="action" checked={action.isUsed} readOnly />
<RenameInput value={action.name} />
</div>
<div
className="remove-button"
onClick={() => handleDeleteAction(action.uuid)}
>
<RemoveIcon />
</div>
</div>
))}
</div>
<div
className="resize-icon"
id="action-resize"
onMouseDown={(e) => handleResize(e, actionsContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
<div className="triggers">
<div className="header">
<div className="header-value">Triggers</div>
<div className="add-button" onClick={handleAddTrigger}>
<AddIcon /> Add
</div>
</div>
<div
className="lists-main-container"
ref={triggersContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{selectedPoint?.triggers.map((trigger) => (
<div
key={trigger.uuid}
className={`list-item ${selectedItem?.type === "trigger" &&
selectedItem.item?.uuid === trigger.uuid
? "active"
: ""
}`}
>
<div
className="value"
onClick={() =>
setSelectedItem({ type: "trigger", item: trigger })
}
>
<input type="radio" name="trigger" id="trigger" checked={trigger.isUsed} readOnly />
<RenameInput value={trigger.name} />
</div>
<div
className="remove-button"
onClick={() => handleDeleteTrigger(trigger.uuid)}
>
<RemoveIcon />
</div>
</div>
))}
</div>
<div
className="resize-icon"
id="trigger-resize"
onMouseDown={(e) => handleResize(e, triggersContainerRef)}
>
<ResizeHeightIcon />
</div>
</div>
</div>
</>
)}
<div className="selected-properties-container">
{selectedItem && (
<>
<div className="properties-header">{selectedItem.item.name}</div>
{selectedItem.type === "action" && (
<>
<InputToggle
inputKey="enableAction"
label="Enable Action"
value={selectedItem.item.isUsed}
onClick={() => handleActionToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
defaultOption={selectedItem.item.type}
options={["Inherit", "Spawn", "Swap", "Despawn", "Delay"]}
onSelect={(option) =>
handleActionSelect(selectedItem.item.uuid, option)
}
/>
{/* Only show material dropdown for Spawn/Swap actions */}
{(selectedItem.item.type === "Spawn" ||
selectedItem.item.type === "Swap") && (
<LabledDropdown
label={
selectedItem.item.type === "Spawn"
? "Spawn Material"
: "Swap Material"
}
defaultOption={selectedItem.item.material}
options={["Inherit", "Crate", "Box"]}
onSelect={(option) =>
handleMaterialSelect(selectedItem.item.uuid, option)
}
/>
)}
{/* Only show delay input for Delay actions */}
{selectedItem.item.type === "Delay" && (
<InputWithDropDown
label="Delay Time"
value={
selectedItem.item.delay === "Inherit"
? undefined
: selectedItem.item.delay
}
onChange={(value) => {
const numValue = parseInt(value);
handleDelayChange(
selectedItem.item.uuid,
!value ? "Inherit" : numValue
);
}}
/>
)}
{/* Only show spawn interval for Spawn actions */}
{selectedItem.item.type === "Spawn" && (
<InputWithDropDown
label="Spawn Interval"
min={0}
defaultValue={
selectedItem.item.spawnInterval === "Inherit"
? ""
: selectedItem.item.spawnInterval.toString()
}
value={
selectedItem.item.spawnInterval === "Inherit"
? ""
: selectedItem.item.spawnInterval.toString()
}
onChange={(value) => {
handleSpawnIntervalChange(
selectedItem.item.uuid,
value === "" ? "Inherit" : parseInt(value)
);
}}
/>
)}
</>
)}
{selectedItem.type === "trigger" && (
<>
<InputToggle
inputKey="enableTrigger"
label="Enable Trigger"
value={selectedItem.item.isUsed}
onClick={() => handleTriggerToggle(selectedItem.item.uuid)}
/>
<LabledDropdown
defaultOption={
selectedItem.item.type || "Select Trigger Type"
}
options={["On-Hit", "Buffer"]}
onSelect={(option) =>
handleTriggerSelect(selectedItem.item.uuid, option)
}
/>
{selectedItem.item.type === "Buffer" && (
<InputWithDropDown
label="Buffer Time"
value={selectedItem.item.bufferTime.toString()}
onChange={(value) => {
handleTriggerBufferTimeChange(
selectedItem.item.uuid,
parseInt(value)
);
}}
/>
)}
</>
)}
</>
)}
{selectedPath && !selectedItem && (
<div
key={selectedPath?.path.modeluuid || "none"}
className="speed-control"
>
<InputWithDropDown
label="Conveyor Speed"
min={0}
value={
selectedPath.path.speed === "Inherit"
? ""
: selectedPath.path.speed.toString()
}
onChange={(value) =>
handleSpeedChange(value === "" ? "Inherit" : parseInt(value))
}
/>
</div>
)}
</div>
{!selectedPath && (
<div className="footer">
<InfoIcon />
Configure the point's action and trigger properties.
</div>
)}
{selectedPath && (
<div className="footer">
<InfoIcon />
Configure the path properties.
</div>
)}
</div>
</div>
);
};
export default ConveyorMechanics;

View File

@@ -1,188 +0,0 @@
import React, { useRef, useMemo, useCallback } from "react";
import { InfoIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import * as SimulationTypes from '../../../../types/simulationTypes';
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
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 staticMachinePaths = simulationStates.filter(
(path): path is SimulationTypes.StaticMachineEventsSchema => path.type === "StaticMachine"
);
const points = staticMachinePaths.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: SimulationTypes.StaticMachineEventsSchema | undefined) => {
if (!updatedPath) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "Vehicle", points: updatedPath.points }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "StaticMachine", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
}
const handleActionUpdate = useCallback((updatedAction: Partial<SimulationTypes.StaticMachineEventsSchema['points']['actions']>) => {
if (!selectedActionSphere?.points?.uuid) return;
const updatedPaths = simulationStates.map((path) => {
if (path.type === "StaticMachine" && path.points.uuid === selectedActionSphere.points.uuid) {
return {
...path,
points: {
...path.points,
actions: {
...path.points.actions,
...updatedAction
}
}
};
}
return path;
});
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.StaticMachineEventsSchema =>
path.type === "StaticMachine" &&
path.points.uuid === selectedActionSphere.points.uuid
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
}, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
const handleBufferChange = useCallback((buffer: number) => {
handleActionUpdate({ buffer });
}, [handleActionUpdate]);
const handleMaterialChange = useCallback((material: string) => {
handleActionUpdate({ material });
}, [handleActionUpdate]);
const handleTriggerChange = useCallback((updatedTrigger: Partial<SimulationTypes.StaticMachineEventsSchema['points']['triggers']>) => {
if (!selectedActionSphere?.points?.uuid) return;
const updatedPaths = simulationStates.map((path) => {
if (path.type === "StaticMachine" && path.points.uuid === selectedActionSphere.points.uuid) {
return {
...path,
points: {
...path.points,
triggers: {
...path.points.triggers,
...updatedTrigger
}
}
};
}
return path;
});
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.StaticMachineEventsSchema =>
path.type === "StaticMachine" &&
path.points.uuid === selectedActionSphere.points.uuid
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
}, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
const handleTriggerTypeChange = useCallback((type: string) => {
handleTriggerChange({ type });
}, [handleTriggerChange]);
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 && (
<>
<InputWithDropDown
key={`buffer-${selectedPoint.uuid}`}
label="Buffer Time"
value={selectedPoint.actions.buffer.toString()}
onChange={(value) => handleBufferChange(parseInt(value))}
/>
<LabledDropdown
key={`material-${selectedPoint.uuid}`}
label="Material"
defaultOption={selectedPoint.actions.material}
onSelect={(value) => handleMaterialChange(value)}
options={["Inherit", "Crate", "Box"]}
/>
<LabledDropdown
key={`trigger-type-${selectedPoint.uuid}`}
label="Trigger Type"
defaultOption={selectedPoint.triggers.type}
onSelect={(value) => handleTriggerTypeChange(value)}
options={["OnComplete", "OnStart"]}
/>
{/* <LabeledButton
label="Reset"
value="Reset Settings"
onClick={() => {
// Implement reset functionality if needed
}}
/> */}
</>
)}
</div>
<div className="footer">
<InfoIcon />
Configure machine interaction properties and triggers.
</div>
</div>
</div>
);
};
export default React.memo(StaticMachineMechanics);

View File

@@ -1,265 +0,0 @@
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 SimulationTypes from '../../../../types/simulationTypes';
import PositionInput from "../customInput/PositionInputs";
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
import LabeledButton from "../../../ui/inputs/LabledButton";
const VehicleMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
const { editingPoint, setEditingPoint } = useEditingPoint();
const { previewPosition, setPreviewPosition } = usePreviewPosition();
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 SimulationTypes.VehicleEventsSchema => path.type === "Vehicle"
);
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: SimulationTypes.VehicleEventsSchema | undefined) => {
if (!updatedPath) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "Vehicle", points: updatedPath.points }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "Vehicle", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
}
const handleActionUpdate = React.useCallback((updatedAction: Partial<SimulationTypes.VehicleEventsSchema['points']['actions']>) => {
if (!selectedActionSphere?.points?.uuid) return;
const updatedPaths = simulationStates.map((path) => {
if (path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid) {
return {
...path,
points: {
...path.points,
actions: {
...path.points.actions,
...updatedAction
}
}
};
}
return path;
});
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.VehicleEventsSchema =>
path.type === "Vehicle" &&
path.points.uuid === selectedActionSphere.points.uuid
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
}, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
const handleHitCountChange = React.useCallback((hitCount: number) => {
handleActionUpdate({ hitCount });
}, [handleActionUpdate]);
const handleBufferChange = React.useCallback((buffer: number) => {
handleActionUpdate({ buffer });
}, [handleActionUpdate]);
const handleSpeedChange = React.useCallback((speed: number) => {
if (!selectedActionSphere?.points?.uuid) return;
const updatedPaths = simulationStates.map((path) => {
if (path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid) {
return {
...path,
points: {
...path.points,
speed: speed
}
};
}
return path;
});
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.VehicleEventsSchema =>
path.type === "Vehicle" &&
path.points.uuid === selectedActionSphere.points.uuid
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
}, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
const ResetVehicleState = React.useCallback(() => {
if (!selectedActionSphere?.points?.uuid) return;
const updatedPaths = simulationStates.map((state) => {
if (state.type === "Vehicle" && state.points.uuid === selectedActionSphere.points.uuid) {
return {
...state,
points: {
...state.points,
actions: { ...state.points.actions, start: {}, end: {} }
}
};
}
return state;
});
const updatedPath = updatedPaths.find(
(path): path is SimulationTypes.VehicleEventsSchema =>
path.type === "Vehicle" &&
path.points.uuid === selectedActionSphere.points.uuid
);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
}, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
const handleStartEyeDropClick = () => {
setEditingPoint('start');
setEyeDropMode(true);
};
const handleEndEyeDropClick = () => {
setEditingPoint('end');
setEyeDropMode(true);
};
return (
<div className="machine-mechanics-container" key={selectedPoint?.uuid}>
<div className="machine-mechanics-header">
{selectedActionSphere?.path?.modelName || "Vehicle point not found"}
</div>
<div className="machine-mechanics-content-container">
<div className="selected-properties-container" ref={propertiesContainerRef}>
<div className="properties-header">Vehicle Properties</div>
{selectedPoint && (
<>
<PositionInput
label="Start Point"
onChange={() => { }}
disabled={true}
value1={
editingPoint === 'start' && previewPosition
? parseFloat(previewPosition.x.toFixed(4))
: selectedPoint.actions.start && 'x' in selectedPoint.actions.start
? parseFloat(selectedPoint.actions.start.x.toFixed(4))
: 0
}
value2={
editingPoint === 'start' && previewPosition
? parseFloat(previewPosition.y.toFixed(4))
: selectedPoint.actions.start && 'y' in selectedPoint.actions.start
? parseFloat(selectedPoint.actions.start.y.toFixed(4))
: 0
}
isEyedrop={true}
handleEyeDropClick={handleStartEyeDropClick}
/>
<PositionInput
label="End Point"
onChange={() => { }}
disabled={true}
value1={
editingPoint === 'end' && previewPosition
? parseFloat(previewPosition.x.toFixed(4))
: selectedPoint.actions.end && 'x' in selectedPoint.actions.end
? parseFloat(selectedPoint.actions.end.x.toFixed(4))
: 0
}
value2={
editingPoint === 'end' && previewPosition
? parseFloat(previewPosition.y.toFixed(4))
: selectedPoint.actions.end && 'y' in selectedPoint.actions.end
? parseFloat(selectedPoint.actions.end.y.toFixed(4))
: 0
}
isEyedrop={true}
handleEyeDropClick={handleEndEyeDropClick}
/>
<LabeledButton
label="Reset"
value="Clear Points"
onClick={() => {
ResetVehicleState();
}}
/>
<InputWithDropDown
key={`hitcount-${selectedPoint.uuid}`}
label="Hit Count"
value={selectedPoint.actions.hitCount.toString()}
onChange={(value) => handleHitCountChange(parseInt(value))}
/>
<InputWithDropDown
key={`buffer-${selectedPoint.uuid}`}
label="Buffer Time"
value={selectedPoint.actions.buffer.toString()}
onChange={(value) => handleBufferChange(parseInt(value))}
/>
<InputWithDropDown
key={`speed-${selectedPoint.uuid}`}
label="Vehicle Speed"
value={selectedPoint.speed.toString()}
onChange={(value) => handleSpeedChange(parseFloat(value))}
/>
</>
)}
</div>
<div className="footer">
<InfoIcon />
Configure vehicle's movement and interaction properties.
</div>
</div>
</div>
);
};
export default React.memo(VehicleMechanics);

View File

@@ -3,7 +3,7 @@ import RenameInput from "../../../ui/inputs/RenameInput";
import Vector3Input from "../customInput/Vector3Input"; import Vector3Input from "../customInput/Vector3Input";
import { useSelectedZoneStore } from "../../../../store/useZoneStore"; import { useSelectedZoneStore } from "../../../../store/useZoneStore";
import { useEditPosition, usezonePosition, useZones, usezoneTarget } from "../../../../store/store"; import { useEditPosition, usezonePosition, useZones, usezoneTarget } from "../../../../store/store";
import { zoneCameraUpdate } from "../../../../services/realTimeVisulization/zoneData/zoneCameraUpdation"; import { zoneCameraUpdate } from "../../../../services/visulization/zone/zoneCameraUpdation";
const ZoneProperties: React.FC = () => { const ZoneProperties: React.FC = () => {
const { Edit, setEdit } = useEditPosition(); const { Edit, setEdit } = useEditPosition();

View File

@@ -1,9 +1,9 @@
import { useState, useEffect, useRef } from "react"; import { useState, useEffect, useRef } from "react";
import { useWidgetStore } from "../../../../../store/useWidgetStore"; import { useWidgetStore } from "../../../../../store/useWidgetStore";
import ChartComponent from "../../../sidebarLeft/visualization/widgets/ChartComponent"; import ChartComponent from "../../../sidebarLeft//visualization/widgets/ChartComponent";
import RegularDropDown from "../../../../ui/inputs/RegularDropDown"; import RegularDropDown from "../../../../ui/inputs/RegularDropDown";
import { WalletIcon } from "../../../../icons/3dChartIcons"; import { WalletIcon } from "../../../../icons/3dChartIcons";
import SimpleCard from "../../../../../modules/visualization/widgets/floating/cards/SimpleCard"; import SimpleCard from "../../../../../modules//visualization/widgets/floating/cards/SimpleCard";
interface Widget { interface Widget {
id: string; id: string;

View File

@@ -15,7 +15,7 @@ import {
} from "../icons/ExportToolsIcons"; } from "../icons/ExportToolsIcons";
import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons"; import { ArrowIcon, TickIcon } from "../icons/ExportCommonIcons";
import useModuleStore, { useThreeDStore } from "../../store/useModuleStore"; import useModuleStore, { useThreeDStore } from "../../store/useModuleStore";
import { handleSaveTemplate } from "../../modules/visualization/functions/handleSaveTemplate"; import { handleSaveTemplate } from "../../modules//visualization/functions/handleSaveTemplate";
import { usePlayButtonStore } from "../../store/usePlayButtonStore"; import { usePlayButtonStore } from "../../store/usePlayButtonStore";
import useTemplateStore from "../../store/useTemplateStore"; import useTemplateStore from "../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../store/useZoneStore"; import { useSelectedZoneStore } from "../../store/useZoneStore";

View File

@@ -4,7 +4,6 @@ import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons";
import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect"; import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect";
import { useFloorItems, useZones } from "../../../store/store"; import { useFloorItems, useZones } from "../../../store/store";
import { useSelectedZoneStore } from "../../../store/useZoneStore"; import { useSelectedZoneStore } from "../../../store/useZoneStore";
import { getZone2dData } from "../../../services/realTimeVisulization/zoneData/getZoneData";
interface DropDownListProps { interface DropDownListProps {
value?: string; // Value to display in the DropDownList value?: string; // Value to display in the DropDownList

View File

@@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import RenameInput from "../inputs/RenameInput"; import RenameInput from "../inputs/RenameInput";
import { useSelectedZoneStore } from "../../../store/useZoneStore"; import { useSelectedZoneStore } from "../../../store/useZoneStore";
import { getZoneData } from "../../../services/realTimeVisulization/zoneData/getZones"; import { getZoneData } from "../../../services/visulization/zone/getZones";
import useModuleStore, { import useModuleStore, {
useSubModuleStore, useSubModuleStore,
} from "../../../store/useModuleStore"; } from "../../../store/useModuleStore";
@@ -14,7 +14,7 @@ import {
} from "../../icons/ExportCommonIcons"; } from "../../icons/ExportCommonIcons";
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store"; import { useFloorItems, useZoneAssetId, useZones } from "../../../store/store";
import { zoneCameraUpdate } from "../../../services/realTimeVisulization/zoneData/zoneCameraUpdation"; import { zoneCameraUpdate } from "../../../services/visulization/zone/zoneCameraUpdation";
import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi"; import { setFloorItemApi } from "../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
interface Asset { interface Asset {

View File

@@ -1,31 +1,31 @@
import React from "react"; import React from "react";
import CustomAvatar from "./users/Avatar"; import CustomAvatar from "./users/Avatar";
interface CollabUserIconProps { interface CollabUserIconProps {
userName: string; userName: string;
userImage?: string; userImage?: string;
color: string; color: string;
} }
const CollabUserIcon: React.FC<CollabUserIconProps> = ({ const CollabUserIcon: React.FC<CollabUserIconProps> = ({
userImage, userImage,
userName, userName,
color, color,
}) => { }) => {
return ( return (
<div className="collab-user-live-container"> <div className="collab-user-live-container">
<div className="user-image-container"> <div className="user-image-container">
{userImage ? ( {userImage ? (
<img className="user-image" src={userImage} alt={userName} /> <img className="user-image" src={userImage} alt={userName} />
) : ( ) : (
<CustomAvatar name={userName} color={color} /> <CustomAvatar name={userName} color={color} />
)} )}
</div> </div>
<div className="user-name" style={{ backgroundColor: color }}> <div className="user-name" style={{ backgroundColor: color }}>
{userName} {userName}
</div> </div>
</div> </div>
); );
}; };
export default CollabUserIcon; export default CollabUserIcon;

View File

@@ -1,270 +1,202 @@
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import gsap from 'gsap'; import gsap from 'gsap';
import * as THREE from 'three'; import * as THREE from 'three';
import * as CONSTANTS from '../../../types/world/worldConstants'; import * as CONSTANTS from '../../../types/world/worldConstants';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
import * as SimulationTypes from "../../../types/simulationTypes"; import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils';
import { initializeDB, retrieveGLTF, storeGLTF } from '../../../utils/indexDB/idbUtils'; import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi';
import { getCamera } from '../../../services/factoryBuilder/camera/getCameraApi'; import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
import { getFloorAssets } from '../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
async function loadInitialFloorItems(
async function loadInitialFloorItems( itemsGroup: Types.RefGroup,
itemsGroup: Types.RefGroup, setFloorItems: Types.setFloorItemSetState,
setFloorItems: Types.setFloorItemSetState, ): Promise<void> {
setSimulationStates: (paths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => void if (!itemsGroup.current) return;
): Promise<void> { let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
if (!itemsGroup.current) return; const email = localStorage.getItem('email');
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; const organization = (email!.split("@")[1]).split(".")[0];
const email = localStorage.getItem('email');
const organization = (email!.split("@")[1]).split(".")[0]; const items = await getFloorAssets(organization);
localStorage.setItem("FloorItems", JSON.stringify(items));
const items = await getFloorAssets(organization); await initializeDB();
localStorage.setItem("FloorItems", JSON.stringify(items));
await initializeDB(); if (items.message === "floorItems not found") return;
if (items.message === "floorItems not found") return; if (items) {
const storedFloorItems: Types.FloorItems = items;
if (items) { const loader = new GLTFLoader();
const storedFloorItems: SimulationTypes.EventData[] = items; const dracoLoader = new DRACOLoader();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
loader.setDRACOLoader(dracoLoader);
dracoLoader.setDecoderPath('https://cdn.jsdelivr.net/npm/three@0.160.0/examples/jsm/libs/draco/gltf/');
loader.setDRACOLoader(dracoLoader); let modelsLoaded = 0;
const modelsToLoad = storedFloorItems.length;
let modelsLoaded = 0;
const modelsToLoad = storedFloorItems.length; const camData = await getCamera(organization, localStorage.getItem('userId')!);
let storedPosition;
const camData = await getCamera(organization, localStorage.getItem('userId')!); if (camData && camData.position) {
let storedPosition; storedPosition = camData?.position;
if (camData && camData.position) { } else {
storedPosition = camData?.position; storedPosition = new THREE.Vector3(0, 40, 30);
} else { }
storedPosition = new THREE.Vector3(0, 40, 30); if (!storedPosition) return;
} const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);
if (!storedPosition) return;
const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z); storedFloorItems.sort((a, b) => {
const aPosition = new THREE.Vector3(a.position[0], a.position[1], a.position[2]);
storedFloorItems.sort((a, b) => { const bPosition = new THREE.Vector3(b.position[0], b.position[1], b.position[2]);
const aPosition = new THREE.Vector3(a.position[0], a.position[1], a.position[2]); return cameraPosition.distanceTo(aPosition) - cameraPosition.distanceTo(bPosition);
const bPosition = new THREE.Vector3(b.position[0], b.position[1], b.position[2]); });
return cameraPosition.distanceTo(aPosition) - cameraPosition.distanceTo(bPosition);
}); for (const item of storedFloorItems) {
if (!item.modelfileID) return;
for (const item of storedFloorItems) { const itemPosition = new THREE.Vector3(item.position[0], item.position[1], item.position[2]);
if (!item.modelfileID) return; let storedPosition;
const itemPosition = new THREE.Vector3(item.position[0], item.position[1], item.position[2]); if (localStorage.getItem("cameraPosition")) {
let storedPosition; storedPosition = JSON.parse(localStorage.getItem("cameraPosition")!);
if (localStorage.getItem("cameraPosition")) { } else {
storedPosition = JSON.parse(localStorage.getItem("cameraPosition")!); storedPosition = new THREE.Vector3(0, 40, 30);
} else { }
storedPosition = new THREE.Vector3(0, 40, 30);
} const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z);
const cameraPosition = new THREE.Vector3(storedPosition.x, storedPosition.y, storedPosition.z); if (cameraPosition.distanceTo(itemPosition) < 50) {
await new Promise<void>(async (resolve) => {
if (cameraPosition.distanceTo(itemPosition) < 50) {
await new Promise<void>(async (resolve) => { // Check Three.js Cache
const cachedModel = THREE.Cache.get(item.modelfileID!);
// Check Three.js Cache if (cachedModel) {
const cachedModel = THREE.Cache.get(item.modelfileID!); // console.log(`[Cache] Fetching ${item.modelname}`);
if (cachedModel) { processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems);
// console.log(`[Cache] Fetching ${item.modelname}`); modelsLoaded++;
processLoadedModel(cachedModel.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates); checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
modelsLoaded++; return;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); }
return;
} // Check IndexedDB
const indexedDBModel = await retrieveGLTF(item.modelfileID!);
// Check IndexedDB if (indexedDBModel) {
const indexedDBModel = await retrieveGLTF(item.modelfileID!); // console.log(`[IndexedDB] Fetching ${item.modelname}`);
if (indexedDBModel) { const blobUrl = URL.createObjectURL(indexedDBModel);
// console.log(`[IndexedDB] Fetching ${item.modelname}`); loader.load(blobUrl, (gltf) => {
const blobUrl = URL.createObjectURL(indexedDBModel); URL.revokeObjectURL(blobUrl);
loader.load(blobUrl, (gltf) => { THREE.Cache.remove(blobUrl);
URL.revokeObjectURL(blobUrl); THREE.Cache.add(item.modelfileID!, gltf);
THREE.Cache.remove(blobUrl); processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems);
THREE.Cache.add(item.modelfileID!, gltf); modelsLoaded++;
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates); checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
modelsLoaded++; },
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); undefined,
}, (error) => {
undefined, toast.error(`[IndexedDB] Error loading ${item.modelname}:`);
(error) => { URL.revokeObjectURL(blobUrl);
toast.error(`[IndexedDB] Error loading ${item.modelname}:`); resolve();
URL.revokeObjectURL(blobUrl); }
resolve(); );
} return;
); }
return;
} // Fetch from Backend
// console.log(`[Backend] Fetching ${item.modelname}`);
// Fetch from Backend const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`;
// console.log(`[Backend] Fetching ${item.modelname}`); loader.load(modelUrl, async (gltf) => {
const modelUrl = `${url_Backend_dwinzo}/api/v2/AssetFile/${item.modelfileID!}`; const modelBlob = await fetch(modelUrl).then((res) => res.blob());
loader.load(modelUrl, async (gltf) => { await storeGLTF(item.modelfileID!, modelBlob);
const modelBlob = await fetch(modelUrl).then((res) => res.blob()); THREE.Cache.add(item.modelfileID!, gltf);
await storeGLTF(item.modelfileID!, modelBlob); processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems);
THREE.Cache.add(item.modelfileID!, gltf); modelsLoaded++;
processLoadedModel(gltf.scene.clone(), item, itemsGroup, setFloorItems, setSimulationStates); checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve);
modelsLoaded++; },
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, resolve); undefined,
}, (error) => {
undefined, toast.error(`[Backend] Error loading ${item.modelname}:`);
(error) => { resolve();
toast.error(`[Backend] Error loading ${item.modelname}:`); }
resolve(); );
} });
); } else {
}); // console.log(`Item ${item.modelname} is not near`);
} else { setFloorItems((prevItems) => [
// console.log(`Item ${item.modelname} is not near`); ...(prevItems || []),
setFloorItems((prevItems) => [ {
...(prevItems || []), modeluuid: item.modeluuid,
{ modelname: item.modelname,
modeluuid: item.modeluuid, position: item.position,
modelname: item.modelname, rotation: item.rotation,
position: item.position, modelfileID: item.modelfileID,
rotation: item.rotation, isLocked: item.isLocked,
modelfileID: item.modelfileID, isVisible: item.isVisible,
isLocked: item.isLocked, },
isVisible: item.isVisible, ]);
},
]); modelsLoaded++;
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { });
if (item.eventData) { }
processEventData(item, setSimulationStates); }
}
// Dispose loader after all models
modelsLoaded++; dracoLoader.dispose();
checkLoadingCompletion(modelsLoaded, modelsToLoad, dracoLoader, () => { }); }
} }
}
// Dispose loader after all models function processLoadedModel(
dracoLoader.dispose(); gltf: any,
} item: Types.FloorItemType,
} itemsGroup: Types.RefGroup,
setFloorItems: Types.setFloorItemSetState,
) {
function processLoadedModel( const model = gltf;
gltf: any, model.uuid = item.modeluuid;
item: SimulationTypes.EventData, model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap);
itemsGroup: Types.RefGroup, model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid };
setFloorItems: Types.setFloorItemSetState, model.position.set(...item.position);
setSimulationStates: (paths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => void model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z);
) {
const model = gltf; model.traverse((child: any) => {
model.uuid = item.modeluuid; if (child.isMesh) {
model.scale.set(...CONSTANTS.assetConfig.defaultScaleBeforeGsap); // Clone the material to ensure changes are independent
model.userData = { name: item.modelname, modelId: item.modelfileID, modeluuid: item.modeluuid }; // child.material = child.material.clone();
model.position.set(...item.position);
model.rotation.set(item.rotation.x, item.rotation.y, item.rotation.z); child.castShadow = true;
child.receiveShadow = true;
model.traverse((child: any) => { }
if (child.isMesh) { });
// Clone the material to ensure changes are independent
// child.material = child.material.clone();
itemsGroup?.current?.add(model);
child.castShadow = true;
child.receiveShadow = true; setFloorItems((prevItems) => [
} ...(prevItems || []),
}); {
modeluuid: item.modeluuid,
modelname: item.modelname,
itemsGroup?.current?.add(model); position: item.position,
rotation: item.rotation,
setFloorItems((prevItems) => [ modelfileID: item.modelfileID,
...(prevItems || []), isLocked: item.isLocked,
{ isVisible: item.isVisible,
modeluuid: item.modeluuid, },
modelname: item.modelname, ]);
position: item.position,
rotation: item.rotation, gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' });
modelfileID: item.modelfileID, gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' });
isLocked: item.isLocked, }
isVisible: item.isVisible,
}, function checkLoadingCompletion(
]); modelsLoaded: number,
modelsToLoad: number,
if (item.eventData) { dracoLoader: DRACOLoader,
processEventData(item, setSimulationStates); resolve: () => void
} ) {
if (modelsLoaded === modelsToLoad) {
gsap.to(model.position, { y: item.position[1], duration: 1.5, ease: 'power2.out' }); toast.success("Models Loaded!");
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: 'power2.out' }); dracoLoader.dispose();
} }
resolve();
function processEventData(item: SimulationTypes.EventData, setSimulationStates: any) { }
if (item.eventData?.type === 'Conveyor') {
const data: any = item.eventData;
data.modeluuid = item.modeluuid;
data.modelName = item.modelname;
data.position = item.position;
data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z];
setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
data as SimulationTypes.ConveyorEventsSchema
]);
} else if (item.eventData?.type === 'Vehicle') {
const data: any = item.eventData;
data.modeluuid = item.modeluuid;
data.modelName = item.modelname;
data.position = item.position;
data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z];
setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
data as SimulationTypes.VehicleEventsSchema
]);
} else if (item.eventData?.type === 'StaticMachine') {
const data: any = item.eventData;
data.modeluuid = item.modeluuid;
data.modelName = item.modelname;
data.position = item.position;
data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z];
setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
data as SimulationTypes.StaticMachineEventsSchema
]);
} else if (item.eventData?.type === 'ArmBot') {
const data: any = item.eventData;
data.modeluuid = item.modeluuid;
data.modelName = item.modelname;
data.position = item.position;
data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z];
setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
data as SimulationTypes.ArmBotEventsSchema
]);
}
}
function checkLoadingCompletion(
modelsLoaded: number,
modelsToLoad: number,
dracoLoader: DRACOLoader,
resolve: () => void
) {
if (modelsLoaded === modelsToLoad) {
toast.success("Models Loaded!");
dracoLoader.dispose();
}
resolve();
}
export default loadInitialFloorItems; export default loadInitialFloorItems;

View File

@@ -1,30 +1,30 @@
import addLineToScene from '../../builder/geomentries/lines/addLineToScene'; import addLineToScene from '../../builder/geomentries/lines/addLineToScene';
import * as CONSTANTS from '../../../types/world/worldConstants'; import * as CONSTANTS from '../../../types/world/worldConstants';
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
function loadInitialLine( function loadInitialLine(
floorPlanGroupLine: Types.RefGroup, floorPlanGroupLine: Types.RefGroup,
lines: Types.RefLines lines: Types.RefLines
): void { ): void {
if (!floorPlanGroupLine.current) return if (!floorPlanGroupLine.current) return
////////// Load the Lines initially if there are any ////////// ////////// Load the Lines initially if there are any //////////
floorPlanGroupLine.current.children = []; floorPlanGroupLine.current.children = [];
lines.current.forEach((line) => { lines.current.forEach((line) => {
let colour; let colour;
if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.wallName) { if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.wallName) {
colour = CONSTANTS.lineConfig.wallColor; colour = CONSTANTS.lineConfig.wallColor;
} else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.floorName) { } else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.floorName) {
colour = CONSTANTS.lineConfig.floorColor; colour = CONSTANTS.lineConfig.floorColor;
} else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.aisleName) { } else if (line[0][3] && line[1][3] === CONSTANTS.lineConfig.aisleName) {
colour = CONSTANTS.lineConfig.aisleColor; colour = CONSTANTS.lineConfig.aisleColor;
} }
if (colour) { if (colour) {
addLineToScene(line[0][0], line[1][0], colour, line, floorPlanGroupLine); addLineToScene(line[0][0], line[1][0], colour, line, floorPlanGroupLine);
} }
}); });
} }
export default loadInitialLine; export default loadInitialLine;

View File

@@ -1,87 +1,87 @@
import * as THREE from 'three'; import * as THREE from 'three';
import * as CONSTANTS from '../../../types/world/worldConstants'; import * as CONSTANTS from '../../../types/world/worldConstants';
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
////////// Load the Boxes initially if there are any ////////// ////////// Load the Boxes initially if there are any //////////
function loadInitialPoint( function loadInitialPoint(
lines: Types.RefLines, lines: Types.RefLines,
floorPlanGroupPoint: Types.RefGroup, floorPlanGroupPoint: Types.RefGroup,
currentLayerPoint: Types.RefMeshArray, currentLayerPoint: Types.RefMeshArray,
dragPointControls: Types.RefDragControl dragPointControls: Types.RefDragControl
): void { ): void {
if (!floorPlanGroupPoint.current) return if (!floorPlanGroupPoint.current) return
floorPlanGroupPoint.current.children = []; floorPlanGroupPoint.current.children = [];
currentLayerPoint.current = []; currentLayerPoint.current = [];
lines.current.forEach((line) => { lines.current.forEach((line) => {
const colour = getPointColor(line[0][3]); const colour = getPointColor(line[0][3]);
line.forEach((pointData) => { line.forEach((pointData) => {
const [point, id] = pointData; const [point, id] = pointData;
/////////// Check if a box with this id already exists ////////// /////////// Check if a box with this id already exists //////////
const existingBox = floorPlanGroupPoint.current?.getObjectByProperty('uuid', id); const existingBox = floorPlanGroupPoint.current?.getObjectByProperty('uuid', id);
if (existingBox) { if (existingBox) {
return; return;
} }
const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale); const geometry = new THREE.BoxGeometry(...CONSTANTS.pointConfig.boxScale);
const material = new THREE.ShaderMaterial({ const material = new THREE.ShaderMaterial({
uniforms: { uniforms: {
uColor: { value: new THREE.Color(colour) }, // Blue color for the border uColor: { value: new THREE.Color(colour) }, // Blue color for the border
uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor) }, // White color for the inner square uInnerColor: { value: new THREE.Color(CONSTANTS.pointConfig.defaultInnerColor) }, // White color for the inner square
}, },
vertexShader: ` vertexShader: `
varying vec2 vUv; varying vec2 vUv;
void main() { void main() {
vUv = uv; vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
} }
`, `,
fragmentShader: ` fragmentShader: `
varying vec2 vUv; varying vec2 vUv;
uniform vec3 uColor; uniform vec3 uColor;
uniform vec3 uInnerColor; uniform vec3 uInnerColor;
void main() { void main() {
// Define the size of the white square as a proportion of the face // Define the size of the white square as a proportion of the face
float borderThickness = 0.2; // Adjust this value for border thickness float borderThickness = 0.2; // Adjust this value for border thickness
if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness && if (vUv.x > borderThickness && vUv.x < 1.0 - borderThickness &&
vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) { vUv.y > borderThickness && vUv.y < 1.0 - borderThickness) {
gl_FragColor = vec4(uInnerColor, 1.0); // White inner square gl_FragColor = vec4(uInnerColor, 1.0); // White inner square
} else { } else {
gl_FragColor = vec4(uColor, 1.0); // Blue border gl_FragColor = vec4(uColor, 1.0); // Blue border
} }
} }
`, `,
}); });
const box = new THREE.Mesh(geometry, material); const box = new THREE.Mesh(geometry, material);
box.name = "point"; box.name = "point";
box.uuid = id; box.uuid = id;
box.userData = { type: line[0][3], color: colour }; box.userData = { type: line[0][3], color: colour };
box.position.set(point.x, point.y, point.z); box.position.set(point.x, point.y, point.z);
currentLayerPoint.current.push(box); currentLayerPoint.current.push(box);
floorPlanGroupPoint.current?.add(box); floorPlanGroupPoint.current?.add(box);
}); });
}); });
function getPointColor(lineType: string | undefined): string { function getPointColor(lineType: string | undefined): string {
switch (lineType) { switch (lineType) {
case CONSTANTS.lineConfig.wallName: return CONSTANTS.pointConfig.wallOuterColor; case CONSTANTS.lineConfig.wallName: return CONSTANTS.pointConfig.wallOuterColor;
case CONSTANTS.lineConfig.floorName: return CONSTANTS.pointConfig.floorOuterColor; case CONSTANTS.lineConfig.floorName: return CONSTANTS.pointConfig.floorOuterColor;
case CONSTANTS.lineConfig.aisleName: return CONSTANTS.pointConfig.aisleOuterColor; case CONSTANTS.lineConfig.aisleName: return CONSTANTS.pointConfig.aisleOuterColor;
default: return CONSTANTS.pointConfig.defaultOuterColor; default: return CONSTANTS.pointConfig.defaultOuterColor;
} }
} }
if (dragPointControls.current) { if (dragPointControls.current) {
dragPointControls.current!.objects = currentLayerPoint.current; dragPointControls.current!.objects = currentLayerPoint.current;
} }
} }
export default loadInitialPoint; export default loadInitialPoint;

View File

@@ -1,54 +1,54 @@
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
import { getWallItems } from '../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi'; import { getWallItems } from '../../../services/factoryBuilder/assest/wallAsset/getWallItemsApi';
////////// Load the Wall Items's intially of there is any ////////// ////////// Load the Wall Items's intially of there is any //////////
async function loadInitialWallItems( async function loadInitialWallItems(
setWallItems: Types.setWallItemSetState, setWallItems: Types.setWallItemSetState,
AssetConfigurations: Types.AssetConfigurations AssetConfigurations: Types.AssetConfigurations
): Promise<void> { ): Promise<void> {
const email = localStorage.getItem('email') const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0]; const organization = (email!.split("@")[1]).split(".")[0];
const items = await getWallItems(organization); const items = await getWallItems(organization);
localStorage.setItem("WallItems", JSON.stringify(items)); localStorage.setItem("WallItems", JSON.stringify(items));
if (items.length > 0) { if (items.length > 0) {
const storedWallItems: Types.wallItems = items; const storedWallItems: Types.wallItems = items;
const loadedWallItems = await Promise.all(storedWallItems.map(async (item) => { const loadedWallItems = await Promise.all(storedWallItems.map(async (item) => {
const loader = new GLTFLoader(); const loader = new GLTFLoader();
return new Promise<Types.WallItem>((resolve) => { return new Promise<Types.WallItem>((resolve) => {
loader.load(AssetConfigurations[item.modelname!].modelUrl, (gltf) => { loader.load(AssetConfigurations[item.modelname!].modelUrl, (gltf) => {
const model = gltf.scene; const model = gltf.scene;
model.uuid = item.modeluuid!; model.uuid = item.modeluuid!;
model.children[0].children.forEach((child: any) => { model.children[0].children.forEach((child: any) => {
if (child.name !== "CSG_REF") { if (child.name !== "CSG_REF") {
child.castShadow = true; child.castShadow = true;
child.receiveShadow = true; child.receiveShadow = true;
} }
}); });
resolve({ resolve({
type: item.type, type: item.type,
model: model, model: model,
modelname: item.modelname, modelname: item.modelname,
scale: item.scale, scale: item.scale,
csgscale: item.csgscale, csgscale: item.csgscale,
csgposition: item.csgposition, csgposition: item.csgposition,
position: item.position, position: item.position,
quaternion: item.quaternion, quaternion: item.quaternion,
}); });
}); });
}); });
})); }));
setWallItems(loadedWallItems); setWallItems(loadedWallItems);
} }
} }
export default loadInitialWallItems; export default loadInitialWallItems;

View File

@@ -1,109 +0,0 @@
import { useEffect, useRef, useState } from "react";
import { Line } from "@react-three/drei";
import {
useNavMesh,
usePlayAgv,
useSimulationStates,
} from "../../../store/store";
import PathNavigator from "./pathNavigator";
import { useAnimationPlaySpeed, usePlayButtonStore, useResetButtonStore } from "../../../store/usePlayButtonStore";
type PathPoints = {
modelUuid: string;
modelSpeed: number;
bufferTime: number;
points: { x: number; y: number; z: number }[];
hitCount: number;
};
interface ProcessContainerProps {
processes: any[];
agvRef: any;
MaterialRef: any;
}
const Agv: React.FC<ProcessContainerProps> = ({
processes,
agvRef,
MaterialRef,
}) => {
const [pathPoints, setPathPoints] = useState<PathPoints[]>([]);
const { simulationStates } = useSimulationStates();
const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore();
const { isReset, setReset } = useResetButtonStore();
const { speed } = useAnimationPlaySpeed();
const globalSpeed = useRef(1);
useEffect(() => { globalSpeed.current = speed }, [speed])
useEffect(() => {
if (!isPlaying || isReset) {
agvRef.current = [];
}
}, [isPlaying, isReset])
useEffect(() => {
if (simulationStates.length > 0) {
const agvModels = simulationStates.filter(
(val) => val.modelName === "agv" && val.type === "Vehicle"
);
const newPathPoints = agvModels
.filter(
(model: any) =>
model.points &&
model.points.actions &&
typeof model.points.actions.start === "object" &&
typeof model.points.actions.end === "object" &&
"x" in model.points.actions.start &&
"y" in model.points.actions.start &&
"x" in model.points.actions.end &&
"y" in model.points.actions.end
)
.map((model: any) => ({
modelUuid: model.modeluuid,
modelSpeed: model.points.speed,
bufferTime: model.points.actions.buffer,
hitCount: model.points.actions.hitCount,
points: [
{ x: model.position[0], y: model.position[1], z: model.position[2], },
{ x: model.points.actions.start.x, y: 0, z: model.points.actions.start.y, },
{ x: model.points.actions.end.x, y: 0, z: model.points.actions.end.y, },
],
}));
setPathPoints(newPathPoints);
}
}, [simulationStates]);
return (
<>
{pathPoints.map((pair, i) => (
<group key={i}>
<PathNavigator
key={i}
navMesh={navMesh}
pathPoints={pair.points}
id={pair.modelUuid}
speed={pair.modelSpeed}
globalSpeed={globalSpeed.current}
bufferTime={pair.bufferTime}
hitCount={pair.hitCount}
processes={processes}
agvRef={agvRef}
MaterialRef={MaterialRef}
/>
{pair.points.slice(1).map((point, idx) => (
<mesh position={[point.x, point.y, point.z]} key={idx} visible={!isPlaying}>
<sphereGeometry args={[0.3, 15, 15]} />
<meshStandardMaterial color="red" />
</mesh>
))}
</group>
))}
</>
);
};
export default Agv;

View File

@@ -1,31 +0,0 @@
import { useRef } from "react";
import { useNavMesh } from "../../../store/store";
import PolygonGenerator from "./polygonGenerator";
import NavMeshDetails from "./navMeshDetails";
import * as CONSTANTS from "../../../types/world/worldConstants";
import * as Types from "../../../types/world/worldTypes";
type NavMeshCreatorProps = {
lines: Types.RefLines
};
function NavMeshCreator({ lines }: NavMeshCreatorProps) {
let groupRef = useRef() as Types.RefGroup;
const { setNavMesh } = useNavMesh();
return (
<>
<PolygonGenerator groupRef={groupRef} lines={lines} />
<NavMeshDetails lines={lines} setNavMesh={setNavMesh} groupRef={groupRef} />
<group ref={groupRef} visible={false} name="Meshes">
<mesh rotation-x={CONSTANTS.planeConfig.rotation} position={CONSTANTS.planeConfig.position3D} receiveShadow>
<planeGeometry args={[300, 300]} />
<meshBasicMaterial color={CONSTANTS.planeConfig.color} />
</mesh>
</group>
</>
)
}
export default NavMeshCreator

View File

@@ -1,64 +0,0 @@
import React, { useEffect, useState } from "react";
import { init as initRecastNavigation } from "@recast-navigation/core";
import { generateSoloNavMesh } from "@recast-navigation/generators";
import { DebugDrawer, getPositionsAndIndices } from "@recast-navigation/three";
import { useThree } from "@react-three/fiber";
import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes";
interface NavMeshDetailsProps {
setNavMesh: (navMesh: any) => void;
groupRef: React.MutableRefObject<THREE.Group | null>;
lines: Types.RefLines;
}
export default function NavMeshDetails({
lines,
setNavMesh,
groupRef,
}: NavMeshDetailsProps) {
const { scene } = useThree();
useEffect(() => {
const initializeNavigation = async () => {
try {
await initRecastNavigation();
if (!groupRef.current || groupRef.current.children.length === 0) {
return;
}
const meshes = groupRef?.current?.children as THREE.Mesh[];
const [positions, indices] = getPositionsAndIndices(meshes);
const cellSize = 0.2;
const cellHeight = 0.7;
const walkableRadius = 0.5;
const { success, navMesh } = generateSoloNavMesh(positions, indices, {
cs: cellSize,
ch: cellHeight,
walkableRadius: Math.round(walkableRadius / cellHeight),
});
if (!success || !navMesh) {
return;
}
setNavMesh(navMesh);
scene.children
.filter((child) => child instanceof DebugDrawer)
.forEach((child) => scene.remove(child));
const debugDrawer = new DebugDrawer();
debugDrawer.drawNavMesh(navMesh);
// scene.add(debugDrawer);
} catch (error) { }
};
initializeNavigation();
}, [scene, groupRef, lines.current]);
return null;
}

View File

@@ -1,480 +0,0 @@
import React, { useEffect, useState, useRef, useMemo } from "react";
import * as THREE from "three";
import { useFrame, useThree } from "@react-three/fiber";
import { NavMeshQuery } from "@recast-navigation/core";
import { Line } from "@react-three/drei";
import {
useAnimationPlaySpeed,
usePlayButtonStore,
} from "../../../store/usePlayButtonStore";
import { usePlayAgv } from "../../../store/store";
interface PathNavigatorProps {
navMesh: any;
pathPoints: any;
id: string;
speed: number;
globalSpeed: number;
bufferTime: number;
hitCount: number;
processes: any[];
agvRef: any;
MaterialRef: any;
}
interface AGVData {
processId: string;
vehicleId: string;
hitCount: number;
totalHits: number;
}
type Phase = "initial" | "toDrop" | "toPickup";
type MaterialType = "Box" | "Crate";
export default function PathNavigator({
navMesh,
pathPoints,
id,
speed,
globalSpeed,
bufferTime,
hitCount,
processes,
agvRef,
MaterialRef,
}: PathNavigatorProps) {
const [currentPhase, setCurrentPhase] = useState<Phase>("initial");
const [path, setPath] = useState<[number, number, number][]>([]);
const [toPickupPath, setToPickupPath] = useState<[number, number, number][]>(
[]
);
const [pickupDropPath, setPickupDropPath] = useState<
[number, number, number][]
>([]);
const [dropPickupPath, setDropPickupPath] = useState<
[number, number, number][]
>([]);
const [initialPosition, setInitialPosition] = useState<THREE.Vector3 | null>(
null
);
const [initialRotation, setInitialRotation] = useState<THREE.Euler | null>(
null
);
const [boxVisible, setBoxVisible] = useState(false);
const distancesRef = useRef<number[]>([]);
const totalDistanceRef = useRef(0);
const progressRef = useRef(0);
const isWaiting = useRef(false);
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const hasStarted = useRef(false);
const hasReachedPickup = useRef(false);
const { scene } = useThree();
const { isPlaying } = usePlayButtonStore();
const { PlayAgv, setPlayAgv } = usePlayAgv();
const boxRef = useRef<THREE.Mesh | null>(null);
const baseMaterials = useMemo(
() => ({
Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
Default: new THREE.MeshStandardMaterial({ color: 0xcccccc }),
}),
[]
);
useEffect(() => {
const object = scene.getObjectByProperty("uuid", id);
if (object) {
setInitialPosition(object.position.clone());
setInitialRotation(object.rotation.clone());
}
}, [scene, id]);
const computePath = (start: any, end: any) => {
try {
const navMeshQuery = new NavMeshQuery(navMesh);
const { path: segmentPath } = navMeshQuery.computePath(start, end);
return (
segmentPath?.map(
({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]
) || []
);
} catch {
return [];
}
};
const resetState = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
setPath([]);
setCurrentPhase("initial");
setToPickupPath([]);
setPickupDropPath([]);
setDropPickupPath([]);
setBoxVisible(false);
distancesRef.current = [];
totalDistanceRef.current = 0;
progressRef.current = 0;
isWaiting.current = false;
hasStarted.current = false;
hasReachedPickup.current = false;
if (initialPosition && initialRotation) {
const object = scene.getObjectByProperty("uuid", id);
if (object) {
object.position.copy(initialPosition);
object.rotation.copy(initialRotation);
}
}
};
useEffect(() => {
if (!isPlaying) {
resetState();
}
if (!navMesh || pathPoints.length < 2) return;
const [pickup, drop] = pathPoints.slice(-2);
const object = scene.getObjectByProperty("uuid", id);
if (!object) return;
const currentPosition = object.position;
const toPickupPath = computePath(currentPosition, pickup);
const pickupToDropPath = computePath(pickup, drop);
const dropToPickupPath = computePath(drop, pickup);
if (
toPickupPath.length &&
pickupToDropPath.length &&
dropToPickupPath.length
) {
setPickupDropPath(pickupToDropPath);
setDropPickupPath(dropToPickupPath);
setToPickupPath(toPickupPath);
setPath(toPickupPath);
setCurrentPhase("initial");
}
}, [navMesh, pathPoints, hitCount, isPlaying, PlayAgv]);
useEffect(() => {
if (path.length < 2) return;
let total = 0;
const segmentDistances = path.slice(0, -1).map((point, i) => {
const dist = new THREE.Vector3(...point).distanceTo(
new THREE.Vector3(...path[i + 1])
);
total += dist;
return dist;
});
distancesRef.current = segmentDistances;
totalDistanceRef.current = total;
progressRef.current = 0;
isWaiting.current = false;
}, [path]);
function logAgvStatus(id: string, status: string) {
// console.log(
// `AGV ${id}: ${status}`
// );
}
function findProcessByTargetModelUUID(processes: any, targetModelUUID: any) {
for (const process of processes) {
for (const path of process.paths) {
for (const point of path.points) {
if (
point.connections?.targets?.some(
(target: any) => target.modelUUID === targetModelUUID
)
) {
return process.id;
}
}
}
}
return null;
}
useEffect(() => {
if (!scene || !boxRef || !processes || !MaterialRef.current) return;
const existingObject = scene.getObjectByProperty("uuid", id);
if (!existingObject) return;
if (boxRef.current?.parent) {
boxRef.current.parent.remove(boxRef.current);
boxRef.current = null;
}
if (boxVisible) {
const matchedProcess = findProcessByTargetModelUUID(processes, id);
let materialType: "Box" | "Crate" | "Default" = "Default";
if (matchedProcess) {
const materialEntry = MaterialRef.current.find((item: any) =>
item.objects.some((obj: any) => obj.processId === matchedProcess)
);
if (materialEntry) {
materialType = materialEntry.material;
}
}
const boxGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const boxMesh = new THREE.Mesh(boxGeometry, baseMaterials[materialType]);
boxMesh.position.y = 1;
boxMesh.name = `box-${id}`;
existingObject.add(boxMesh);
boxRef.current = boxMesh;
}
return () => {
if (boxRef.current?.parent) {
boxRef.current.parent.remove(boxRef.current);
}
};
}, [processes, MaterialRef, boxVisible, scene, id, baseMaterials]);
useFrame((_, delta) => {
const currentAgv = (agvRef.current || []).find(
(agv: AGVData) => agv.vehicleId === id
);
if (!scene || !id || !isPlaying) return;
const object = scene.getObjectByProperty("uuid", id);
if (!object) return;
if (isPlaying && !hasStarted.current) {
hasStarted.current = false;
progressRef.current = 0;
isWaiting.current = false;
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
}
const isAgvReady = () => {
if (!agvRef.current || agvRef.current.length === 0) return false;
if (!currentAgv) return false;
return currentAgv.isActive && hitCount >= currentAgv.maxHitCount;
};
if (isPlaying && !hasStarted.current && toPickupPath.length > 0) {
setBoxVisible(false);
const startPoint = new THREE.Vector3(...toPickupPath[0]);
object.position.copy(startPoint);
if (toPickupPath.length > 1) {
const nextPoint = new THREE.Vector3(...toPickupPath[1]);
const direction = nextPoint.clone().sub(startPoint).normalize();
object.rotation.y = Math.atan2(direction.x, direction.z);
}
hasStarted.current = true;
progressRef.current = 0;
hasReachedPickup.current = false;
setToPickupPath(toPickupPath.slice(-1));
logAgvStatus(id, "Started from station, heading to pickup");
return;
}
if (isPlaying && currentPhase === "initial" && !hasReachedPickup.current) {
const reached = moveAlongPath(
object,
path,
distancesRef.current,
speed,
delta,
progressRef
);
if (reached) {
hasReachedPickup.current = true;
if (currentAgv) {
currentAgv.status = "picking";
}
logAgvStatus(id, "Reached pickup point, Waiting for material");
}
return;
}
if (isPlaying && currentAgv?.isActive && currentPhase === "initial") {
if (!isAgvReady()) return;
setTimeout(() => {
setBoxVisible(true);
setPath([...pickupDropPath]);
setCurrentPhase("toDrop");
progressRef.current = 0;
logAgvStatus(id, "Started from pickup point, heading to drop point");
if (currentAgv) {
currentAgv.status = "toDrop";
}
}, 0);
return;
}
if (isPlaying && currentPhase === "toDrop") {
const reached = moveAlongPath(
object,
path,
distancesRef.current,
speed,
delta,
progressRef
);
if (reached && !isWaiting.current) {
isWaiting.current = true;
logAgvStatus(id, "Reached drop point");
if (currentAgv) {
currentAgv.status = "droping";
currentAgv.hitCount = currentAgv.hitCount--;
}
timeoutRef.current = setTimeout(() => {
setPath([...dropPickupPath]);
setCurrentPhase("toPickup");
progressRef.current = 0;
isWaiting.current = false;
setBoxVisible(false);
if (currentAgv) {
currentAgv.status = "toPickup";
}
logAgvStatus(
id,
"Started from droping point, heading to pickup point"
);
}, bufferTime * 1000);
}
return;
}
if (isPlaying && currentPhase === "toPickup") {
const reached = moveAlongPath(
object,
path,
distancesRef.current,
speed,
delta,
progressRef
);
if (reached) {
if (currentAgv) {
currentAgv.isActive = false;
}
setCurrentPhase("initial");
if (currentAgv) {
currentAgv.status = "picking";
}
logAgvStatus(id, "Reached pickup point again, cycle complete");
}
return;
}
moveAlongPath(
object,
path,
distancesRef.current,
speed,
delta,
progressRef
);
});
function moveAlongPath(
object: THREE.Object3D,
path: [number, number, number][],
distances: number[],
speed: number,
delta: number,
progressRef: React.MutableRefObject<number>
): boolean {
if (path.length < 2) return false;
progressRef.current += delta * (speed * globalSpeed);
let covered = progressRef.current;
let accumulated = 0;
let index = 0;
for (; index < distances.length; index++) {
const dist = distances[index];
if (accumulated + dist >= covered) break;
accumulated += dist;
}
if (index >= path.length - 1) {
if (path.length > 1) {
const lastDirection = new THREE.Vector3(...path[path.length - 1])
.sub(new THREE.Vector3(...path[path.length - 2]))
.normalize();
object.rotation.y = Math.atan2(lastDirection.x, lastDirection.z);
}
return true;
}
const start = new THREE.Vector3(...path[index]);
const end = new THREE.Vector3(...path[index + 1]);
const dist = distances[index];
const t = THREE.MathUtils.clamp((covered - accumulated) / dist, 0, 1);
object.position.copy(start.clone().lerp(end, t));
if (dist > 0.1) {
const targetDirection = end.clone().sub(start).normalize();
const targetRotationY = Math.atan2(targetDirection.x, targetDirection.z);
const rotationSpeed = Math.min(5 * delta, 1);
object.rotation.y = THREE.MathUtils.lerp(
object.rotation.y,
targetRotationY,
rotationSpeed
);
}
return false;
}
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
return (
<group name="path-navigator-lines" visible={!isPlaying} >
{toPickupPath.length > 0 && (
<Line
points={toPickupPath}
color="blue"
lineWidth={3}
dashed
dashSize={0.75}
dashScale={2}
/>
)}
{pickupDropPath.length > 0 && (
<Line points={pickupDropPath} color="red" lineWidth={3} />
)}
{dropPickupPath.length > 0 && (
<Line points={dropPickupPath} color="red" lineWidth={3} />
)}
</group>
);
}

View File

@@ -1,118 +0,0 @@
import * as THREE from "three";
import { useEffect, useState } from "react";
import * as turf from "@turf/turf";
import * as Types from "../../../types/world/worldTypes";
import arrayLinesToObject from "../geomentries/lines/lineConvertions/arrayLinesToObject";
interface PolygonGeneratorProps {
groupRef: React.MutableRefObject<THREE.Group | null>;
lines: Types.RefLines;
}
export default function PolygonGenerator({
groupRef,
lines,
}: PolygonGeneratorProps) {
useEffect(() => {
let allLines = arrayLinesToObject(lines.current);
const wallLines = allLines?.filter((line) => line?.type === "WallLine");
const aisleLines = allLines?.filter((line) => line?.type === "AisleLine");
const wallPoints = wallLines
.map((pair) => pair?.line.map((vals) => vals.position))
.filter((wall): wall is THREE.Vector3[] => !!wall);
const result = aisleLines.map((pair) =>
pair?.line.map((point) => ({
position: [point.position.x, point.position.z],
uuid: point.uuid,
}))
);
if (!result || result.some((line) => !line)) {
return;
}
const lineFeatures = result?.map((line: any) =>
turf.lineString(line.map((p: any) => p?.position))
);
const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
renderWallGeometry(wallPoints);
if (polygons.features.length > 1) {
polygons.features.forEach((feature) => {
if (feature.geometry.type === "Polygon") {
const shape = new THREE.Shape();
const coords = feature.geometry.coordinates[0];
shape.moveTo(coords[0][0], coords[0][1]);
for (let i = 1; i < coords.length; i++) {
shape.lineTo(coords[i][0], coords[i][1]);
}
shape.lineTo(coords[0][0], coords[0][1]);
const extrudeSettings = {
depth: 5,
bevelEnabled: false,
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const material = new THREE.MeshBasicMaterial({ color: "blue", transparent: true, opacity: 0.5 });
const mesh = new THREE.Mesh(geometry, material);
mesh.rotateX(Math.PI / 2);
mesh.name = "agv-collider";
mesh.position.y = 5;
mesh.receiveShadow = true;
groupRef.current?.add(mesh);
}
});
}
}, [lines.current]);
const renderWallGeometry = (walls: THREE.Vector3[][]) => {
walls.forEach((wall) => {
if (wall.length < 2) return;
for (let i = 0; i < wall.length - 1; i++) {
const start = new THREE.Vector3(wall[i].x, wall[i].y, wall[i].z);
const end = new THREE.Vector3(
wall[i + 1].x,
wall[i + 1].y,
wall[i + 1].z
);
const wallHeight = 10;
const direction = new THREE.Vector3().subVectors(end, start);
const length = direction.length();
direction.normalize();
const wallGeometry = new THREE.BoxGeometry(length, wallHeight);
const wallMaterial = new THREE.MeshBasicMaterial({
color: "#aaa",
transparent: true,
opacity: 0.5,
});
const wallMesh = new THREE.Mesh(wallGeometry, wallMaterial);
const midPoint = new THREE.Vector3()
.addVectors(start, end)
.multiplyScalar(0.5);
wallMesh.position.set(midPoint.x, wallHeight / 2, midPoint.z);
const quaternion = new THREE.Quaternion();
quaternion.setFromUnitVectors(new THREE.Vector3(1, 0, 0), direction);
wallMesh.quaternion.copy(quaternion);
groupRef.current?.add(wallMesh);
}
});
};
return null;
}

View File

@@ -1,374 +1,373 @@
////////// Three and React Three Fiber Imports ////////// ////////// Three and React Three Fiber Imports //////////
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useThree, useFrame } from "@react-three/fiber"; import { useThree, useFrame } from "@react-three/fiber";
////////// Component Imports ////////// ////////// Component Imports //////////
import DistanceText from "../../builder/geomentries/lines/distanceText/distanceText"; import DistanceText from "./geomentries/lines/distanceText/distanceText";
import ReferenceDistanceText from "../../builder/geomentries/lines/distanceText/referenceDistanceText"; import ReferenceDistanceText from "./geomentries/lines/distanceText/referenceDistanceText";
////////// Assests Imports ////////// ////////// Assests Imports //////////
import arch from "../../../assets/gltf-glb/arch.glb"; import arch from "../../assets/gltf-glb/arch.glb";
import door from "../../../assets/gltf-glb/door.glb"; import door from "../../assets/gltf-glb/door.glb";
import Window from "../../../assets/gltf-glb/window.glb"; import Window from "../../assets/gltf-glb/window.glb";
////////// Zustand State Imports ////////// ////////// Zustand State Imports //////////
import { import {
useToggleView, useToggleView,
useDeletePointOrLine, useDeletePointOrLine,
useMovePoint, useMovePoint,
useActiveLayer, useActiveLayer,
useSocketStore, useSocketStore,
useWallVisibility, useWallVisibility,
useRoofVisibility, useRoofVisibility,
useShadows, useShadows,
useUpdateScene, useUpdateScene,
useWalls, useWalls,
useToolMode, useToolMode,
useRefTextUpdate, useRefTextUpdate,
useRenderDistance, useRenderDistance,
useLimitDistance, useLimitDistance,
} from "../../../store/store"; } from "../../store/store";
////////// 3D Function Imports ////////// ////////// 3D Function Imports //////////
import loadWalls from "../../builder/geomentries/walls/loadWalls"; import loadWalls from "./geomentries/walls/loadWalls";
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../types/world/worldTypes";
import SocketResponses from "../../collaboration/socketResponses.dev"; import SocketResponses from "../collaboration/socketResponses.dev";
import FloorItemsGroup from "../../builder/groups/floorItemsGroup"; import FloorItemsGroup from "./groups/floorItemsGroup";
import FloorPlanGroup from "../../builder/groups/floorPlanGroup"; import FloorPlanGroup from "./groups/floorPlanGroup";
import FloorGroup from "../../builder/groups/floorGroup"; import FloorGroup from "./groups/floorGroup";
import FloorGroupAilse from "../../builder/groups/floorGroupAisle"; import FloorGroupAilse from "./groups/floorGroupAisle";
import Draw from "../../builder/functions/draw"; import Draw from "./functions/draw";
import WallsAndWallItems from "../../builder/groups/wallsAndWallItems"; import WallsAndWallItems from "./groups/wallsAndWallItems";
import Ground from "../environment/ground"; import Ground from "../scene/environment/ground";
// import ZoneGroup from "../groups/zoneGroup1"; // import ZoneGroup from "../groups/zoneGroup1";
import { findEnvironment } from "../../../services/factoryBuilder/environment/findEnvironment"; import { findEnvironment } from "../../services/factoryBuilder/environment/findEnvironment";
import Layer2DVisibility from "../../builder/geomentries/layers/layer2DVisibility"; import Layer2DVisibility from "./geomentries/layers/layer2DVisibility";
import DrieHtmlTemp from "../mqttTemp/drieHtmlTemp"; import DrieHtmlTemp from "..//visualization/mqttTemp/drieHtmlTemp";
import ZoneGroup from "../../builder/groups/zoneGroup"; import ZoneGroup from "./groups/zoneGroup";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../store/useModuleStore";
import NavMeshCreator from "../../builder/agv/navMeshCreator"; import MeasurementTool from "../scene/tools/measurementTool";
export default function World() { export default function Builder() {
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements. const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
const csg = useRef(); // Reference for CSG object, used for 3D modeling. const csg = useRef(); // Reference for CSG object, used for 3D modeling.
const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects. const CSGGroup = useRef() as Types.RefMesh; // Reference to a group of CSG objects.
const scene = useRef() as Types.RefScene; // Reference to the scene. const scene = useRef() as Types.RefScene; // Reference to the scene.
const camera = useRef() as Types.RefCamera; // Reference to the camera object. const camera = useRef() as Types.RefCamera; // Reference to the camera object.
const controls = useRef<any>(); // Reference to the controls object. const controls = useRef<any>(); // Reference to the controls object.
const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene. const raycaster = useRef() as Types.RefRaycaster; // Reference for raycaster used for detecting objects being pointed at in the scene.
const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control. const dragPointControls = useRef() as Types.RefDragControl; // Reference for drag point controls, an array for drag control.
// Assigning the scene and camera from the Three.js state to the references. // Assigning the scene and camera from the Three.js state to the references.
scene.current = state.scene; scene.current = state.scene;
camera.current = state.camera; camera.current = state.camera;
controls.current = state.controls; controls.current = state.controls;
raycaster.current = state.raycaster; raycaster.current = state.raycaster;
const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference. const plane = useRef<THREE.Mesh>(null); // Reference for a plane object for raycaster reference.
const grid = useRef() as any; // Reference for a grid object for raycaster reference. const grid = useRef() as any; // Reference for a grid object for raycaster reference.
const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line. const snappedPoint = useRef() as Types.RefVector3; // Reference for storing a snapped point at the (end = isSnapped) and (start = ispreSnapped) of the line.
const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end). const isSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (end).
const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc... const anglesnappedPoint = useRef() as Types.RefVector3; // Reference for storing an angle-snapped point when the line is in 90 degree etc...
const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active. const isAngleSnapped = useRef(false) as Types.RefBoolean; // Boolean to indicate if angle snapping is active.
const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point. const isSnappedUUID = useRef() as Types.RefString; // UUID reference to identify the snapped point.
const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start). const ispreSnapped = useRef(false) as Types.RefBoolean; // Boolean reference to indicate if an object is snapped at the (start).
const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items. const tempLoader = useRef() as Types.RefMesh; // Reference for a temporary loader for the floor items.
const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active. const isTempLoader = useRef() as Types.RefBoolean; // Reference to check if a temporary loader is active.
const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation. const Tube = useRef() as Types.RefTubeGeometry; // Reference for tubes used for reference line creation and updation.
const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn. const line = useRef([]) as Types.RefLine; // Reference for line which stores the current line that is being drawn.
const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn. const lines = useRef([]) as Types.RefLines; // Reference for lines which stores all the lines that are ever drawn.
const onlyFloorline = useRef<Types.OnlyFloorLine>([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn. const onlyFloorline = useRef<Types.OnlyFloorLine>([]); // Reference for floor lines which does not have walls or roof and have only floor used to store the current line that is being drawn.
const onlyFloorlines = useRef<Types.OnlyFloorLines>([]); // Reference for all the floor lines that are ever drawn. const onlyFloorlines = useRef<Types.OnlyFloorLines>([]); // Reference for all the floor lines that are ever drawn.
const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw. const ReferenceLineMesh = useRef() as Types.RefMesh; // Reference for storing the mesh of the reference line for moving it during draw.
const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not. const LineCreated = useRef(false) as Types.RefBoolean; // Boolean to track whether the reference line is created or not.
const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed. const referencePole = useRef() as Types.RefMesh; // Reference for a pole that is used as the reference for the user to show where it is placed.
const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf). const itemsGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the floor items (Gltf).
const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors. const floorGroup = useRef() as Types.RefGroup; // Reference to the THREE.Group that has the roofs and the floors.
const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation. const AttachedObject = useRef() as Types.RefMesh; // Reference for an object that is attached using dbl click for transform controls rotation.
const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group. const floorPlanGroup = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines group and the points group.
const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn. const floorPlanGroupLine = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the lines that are drawn.
const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created. const floorPlanGroupPoint = useRef() as Types.RefGroup; // Reference for a THREE.Group that has the points that are created.
const floorGroupAisle = useRef() as Types.RefGroup; const floorGroupAisle = useRef() as Types.RefGroup;
const zoneGroup = useRef() as Types.RefGroup; const zoneGroup = useRef() as Types.RefGroup;
const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls. const currentLayerPoint = useRef([]) as Types.RefMeshArray; // Reference for points that re in the current layer used to update the points in drag controls.
const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted. const hoveredDeletablePoint = useRef() as Types.RefMesh; // Reference for the currently hovered point that can be deleted.
const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted. const hoveredDeletableLine = useRef() as Types.RefMesh; // Reference for the currently hovered line that can be deleted.
const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted. const hoveredDeletableFloorItem = useRef() as Types.RefMesh; // Reference for the currently hovered floor item that can be deleted.
const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted. const hoveredDeletableWallItem = useRef() as Types.RefMesh; // Reference for the currently hovered wall item that can be deleted.
const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted. const hoveredDeletablePillar = useRef() as Types.RefMesh; // Reference for the currently hovered pillar that can be deleted.
const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc... const currentWallItem = useRef() as Types.RefMesh; // Reference for the currently selected wall item that can be scaled, dragged etc...
const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position. const cursorPosition = new THREE.Vector3(); // 3D vector for storing the cursor position.
const [selectedItemsIndex, setSelectedItemsIndex] = useState<Types.Number | null>(null); // State for tracking the index of the selected item. const [selectedItemsIndex, setSelectedItemsIndex] = useState<Types.Number | null>(null); // State for tracking the index of the selected item.
const { activeLayer, setActiveLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx. const { activeLayer, setActiveLayer } = useActiveLayer(); // State that changes based on which layer the user chooses in Layers.jsx.
const { toggleView, setToggleView } = useToggleView(); // State for toggling between 2D and 3D. const { toggleView, setToggleView } = useToggleView(); // State for toggling between 2D and 3D.
const { toolMode, setToolMode } = useToolMode(); const { toolMode, setToolMode } = useToolMode();
const { movePoint, setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not. const { movePoint, setMovePoint } = useMovePoint(); // State that stores a boolean which represents whether the move mode is active or not.
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine(); const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const { roofVisibility, setRoofVisibility } = useRoofVisibility(); const { roofVisibility, setRoofVisibility } = useRoofVisibility();
const { wallVisibility, setWallVisibility } = useWallVisibility(); const { wallVisibility, setWallVisibility } = useWallVisibility();
const { shadows, setShadows } = useShadows(); const { shadows, setShadows } = useShadows();
const { renderDistance, setRenderDistance } = useRenderDistance(); const { renderDistance, setRenderDistance } = useRenderDistance();
const { limitDistance, setLimitDistance } = useLimitDistance(); const { limitDistance, setLimitDistance } = useLimitDistance();
const { updateScene, setUpdateScene } = useUpdateScene(); const { updateScene, setUpdateScene } = useUpdateScene();
const { walls, setWalls } = useWalls(); const { walls, setWalls } = useWalls();
const { refTextupdate, setRefTextUpdate } = useRefTextUpdate(); const { refTextupdate, setRefTextUpdate } = useRefTextUpdate();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
// const loader = new GLTFLoader(); // const loader = new GLTFLoader();
// const dracoLoader = new DRACOLoader(); // 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); // loader.setDRACOLoader(dracoLoader);
////////// Assest Configuration Values ////////// ////////// Assest Configuration Values //////////
const AssetConfigurations: Types.AssetConfigurations = { const AssetConfigurations: Types.AssetConfigurations = {
arch: { arch: {
modelUrl: arch, modelUrl: arch,
scale: [0.75, 0.75, 0.75], scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5], csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0], csgposition: [0, 2, 0],
positionY: () => 0, positionY: () => 0,
type: "Fixed-Move", type: "Fixed-Move",
}, },
door: { door: {
modelUrl: door, modelUrl: door,
scale: [0.75, 0.75, 0.75], scale: [0.75, 0.75, 0.75],
csgscale: [2, 4, 0.5], csgscale: [2, 4, 0.5],
csgposition: [0, 2, 0], csgposition: [0, 2, 0],
positionY: () => 0, positionY: () => 0,
type: "Fixed-Move", type: "Fixed-Move",
}, },
window: { window: {
modelUrl: Window, modelUrl: Window,
scale: [0.75, 0.75, 0.75], scale: [0.75, 0.75, 0.75],
csgscale: [5, 3, 0.5], csgscale: [5, 3, 0.5],
csgposition: [0, 1.5, 0], csgposition: [0, 1.5, 0],
positionY: (intersectionPoint) => intersectionPoint.point.y, positionY: (intersectionPoint) => intersectionPoint.point.y,
type: "Free-Move", type: "Free-Move",
}, },
}; };
////////// All Toggle's ////////// ////////// All Toggle's //////////
useEffect(() => { useEffect(() => {
setRefTextUpdate((prevUpdate: number) => prevUpdate - 1); setRefTextUpdate((prevUpdate: number) => prevUpdate - 1);
if (dragPointControls.current) { if (dragPointControls.current) {
dragPointControls.current.enabled = false; dragPointControls.current.enabled = false;
} }
if (toggleView) { if (toggleView) {
Layer2DVisibility( Layer2DVisibility(
activeLayer, activeLayer,
floorPlanGroup, floorPlanGroup,
floorPlanGroupLine, floorPlanGroupLine,
floorPlanGroupPoint, floorPlanGroupPoint,
currentLayerPoint, currentLayerPoint,
dragPointControls dragPointControls
); );
} else { } else {
setToolMode(null); setToolMode(null);
setDeletePointOrLine(false); setDeletePointOrLine(false);
setMovePoint(false); setMovePoint(false);
loadWalls(lines, setWalls); loadWalls(lines, setWalls);
setUpdateScene(true); setUpdateScene(true);
line.current = []; line.current = [];
} }
}, [toggleView]); }, [toggleView]);
useEffect(() => { useEffect(() => {
THREE.Cache.clear(); THREE.Cache.clear();
THREE.Cache.enabled = true; THREE.Cache.enabled = true;
}, []); }, []);
useEffect(() => { useEffect(() => {
const email = localStorage.getItem("email"); const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
async function fetchVisibility() { async function fetchVisibility() {
const visibility = await findEnvironment( const visibility = await findEnvironment(
organization, organization,
localStorage.getItem("userId")! localStorage.getItem("userId")!
); );
if (visibility) { if (visibility) {
setRoofVisibility(visibility.roofVisibility); setRoofVisibility(visibility.roofVisibility);
setWallVisibility(visibility.wallVisibility); setWallVisibility(visibility.wallVisibility);
setShadows(visibility.shadowVisibility); setShadows(visibility.shadowVisibility);
setRenderDistance(visibility.renderDistance); setRenderDistance(visibility.renderDistance);
setLimitDistance(visibility.limitDistance); setLimitDistance(visibility.limitDistance);
} }
} }
fetchVisibility(); fetchVisibility();
}, []); }, []);
////////// UseFrame is Here ////////// ////////// UseFrame is Here //////////
useFrame(() => { useFrame(() => {
if (toolMode) { if (toolMode) {
Draw( Draw(
state, state,
plane, plane,
cursorPosition, cursorPosition,
floorPlanGroupPoint, floorPlanGroupPoint,
floorPlanGroupLine, floorPlanGroupLine,
snappedPoint, snappedPoint,
isSnapped, isSnapped,
isSnappedUUID, isSnappedUUID,
line, line,
lines, lines,
ispreSnapped, ispreSnapped,
floorPlanGroup, floorPlanGroup,
ReferenceLineMesh, ReferenceLineMesh,
LineCreated, LineCreated,
setRefTextUpdate, setRefTextUpdate,
Tube, Tube,
anglesnappedPoint, anglesnappedPoint,
isAngleSnapped, isAngleSnapped,
toolMode toolMode
); );
} }
}); });
////////// Return ////////// ////////// Return //////////
return ( return (
<> <>
<Ground grid={grid} plane={plane} /> <Ground grid={grid} plane={plane} />
<DistanceText key={toggleView} /> <DistanceText key={toggleView} />
<ReferenceDistanceText <ReferenceDistanceText
key={refTextupdate} key={refTextupdate}
line={ReferenceLineMesh.current} line={ReferenceLineMesh.current}
/> />
<SocketResponses <SocketResponses
floorPlanGroup={floorPlanGroup} floorPlanGroup={floorPlanGroup}
lines={lines} lines={lines}
floorGroup={floorGroup} floorGroup={floorGroup}
floorGroupAisle={floorGroupAisle} floorGroupAisle={floorGroupAisle}
scene={scene} scene={scene}
onlyFloorlines={onlyFloorlines} onlyFloorlines={onlyFloorlines}
AssetConfigurations={AssetConfigurations} AssetConfigurations={AssetConfigurations}
itemsGroup={itemsGroup} itemsGroup={itemsGroup}
isTempLoader={isTempLoader} isTempLoader={isTempLoader}
tempLoader={tempLoader} tempLoader={tempLoader}
currentLayerPoint={currentLayerPoint} currentLayerPoint={currentLayerPoint}
floorPlanGroupPoint={floorPlanGroupPoint} floorPlanGroupPoint={floorPlanGroupPoint}
floorPlanGroupLine={floorPlanGroupLine} floorPlanGroupLine={floorPlanGroupLine}
zoneGroup={zoneGroup} zoneGroup={zoneGroup}
dragPointControls={dragPointControls} dragPointControls={dragPointControls}
/> />
<WallsAndWallItems <WallsAndWallItems
CSGGroup={CSGGroup} CSGGroup={CSGGroup}
AssetConfigurations={AssetConfigurations} AssetConfigurations={AssetConfigurations}
setSelectedItemsIndex={setSelectedItemsIndex} setSelectedItemsIndex={setSelectedItemsIndex}
selectedItemsIndex={selectedItemsIndex} selectedItemsIndex={selectedItemsIndex}
currentWallItem={currentWallItem} currentWallItem={currentWallItem}
csg={csg} csg={csg}
lines={lines} lines={lines}
hoveredDeletableWallItem={hoveredDeletableWallItem} hoveredDeletableWallItem={hoveredDeletableWallItem}
/> />
<FloorItemsGroup <FloorItemsGroup
itemsGroup={itemsGroup} itemsGroup={itemsGroup}
hoveredDeletableFloorItem={hoveredDeletableFloorItem} hoveredDeletableFloorItem={hoveredDeletableFloorItem}
AttachedObject={AttachedObject} AttachedObject={AttachedObject}
floorGroup={floorGroup} floorGroup={floorGroup}
tempLoader={tempLoader} tempLoader={tempLoader}
isTempLoader={isTempLoader} isTempLoader={isTempLoader}
plane={plane} plane={plane}
/> />
<FloorGroup <FloorGroup
floorGroup={floorGroup} floorGroup={floorGroup}
lines={lines} lines={lines}
referencePole={referencePole} referencePole={referencePole}
hoveredDeletablePillar={hoveredDeletablePillar} hoveredDeletablePillar={hoveredDeletablePillar}
/> />
<FloorPlanGroup <FloorPlanGroup
floorPlanGroup={floorPlanGroup} floorPlanGroup={floorPlanGroup}
floorPlanGroupLine={floorPlanGroupLine} floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint} floorPlanGroupPoint={floorPlanGroupPoint}
floorGroup={floorGroup} floorGroup={floorGroup}
currentLayerPoint={currentLayerPoint} currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls} dragPointControls={dragPointControls}
hoveredDeletablePoint={hoveredDeletablePoint} hoveredDeletablePoint={hoveredDeletablePoint}
hoveredDeletableLine={hoveredDeletableLine} hoveredDeletableLine={hoveredDeletableLine}
plane={plane} plane={plane}
line={line} line={line}
lines={lines} lines={lines}
onlyFloorline={onlyFloorline} onlyFloorline={onlyFloorline}
onlyFloorlines={onlyFloorlines} onlyFloorlines={onlyFloorlines}
ReferenceLineMesh={ReferenceLineMesh} ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated} LineCreated={LineCreated}
isSnapped={isSnapped} isSnapped={isSnapped}
ispreSnapped={ispreSnapped} ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint} snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID} isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped} isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint} anglesnappedPoint={anglesnappedPoint}
/> />
{/* <ZoneGroup {/* <ZoneGroup
zoneGroup={zoneGroup} zoneGroup={zoneGroup}
plane={plane} plane={plane}
floorPlanGroupLine={floorPlanGroupLine} floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint} floorPlanGroupPoint={floorPlanGroupPoint}
line={line} line={line}
lines={lines} lines={lines}
currentLayerPoint={currentLayerPoint} currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls} dragPointControls={dragPointControls}
floorPlanGroup={floorPlanGroup} floorPlanGroup={floorPlanGroup}
ReferenceLineMesh={ReferenceLineMesh} ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated} LineCreated={LineCreated}
isSnapped={isSnapped} isSnapped={isSnapped}
ispreSnapped={ispreSnapped} ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint} snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID} isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped} isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint} anglesnappedPoint={anglesnappedPoint}
/> */} /> */}
<ZoneGroup /> <ZoneGroup />
<FloorGroupAilse <FloorGroupAilse
floorGroupAisle={floorGroupAisle} floorGroupAisle={floorGroupAisle}
plane={plane} plane={plane}
floorPlanGroupLine={floorPlanGroupLine} floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint} floorPlanGroupPoint={floorPlanGroupPoint}
line={line} line={line}
lines={lines} lines={lines}
currentLayerPoint={currentLayerPoint} currentLayerPoint={currentLayerPoint}
dragPointControls={dragPointControls} dragPointControls={dragPointControls}
floorPlanGroup={floorPlanGroup} floorPlanGroup={floorPlanGroup}
ReferenceLineMesh={ReferenceLineMesh} ReferenceLineMesh={ReferenceLineMesh}
LineCreated={LineCreated} LineCreated={LineCreated}
isSnapped={isSnapped} isSnapped={isSnapped}
ispreSnapped={ispreSnapped} ispreSnapped={ispreSnapped}
snappedPoint={snappedPoint} snappedPoint={snappedPoint}
isSnappedUUID={isSnappedUUID} isSnappedUUID={isSnappedUUID}
isAngleSnapped={isAngleSnapped} isAngleSnapped={isAngleSnapped}
anglesnappedPoint={anglesnappedPoint} anglesnappedPoint={anglesnappedPoint}
/> />
{/* <DrieHtmlTemp itemsGroup={itemsGroup} /> */} <MeasurementTool />
<NavMeshCreator lines={lines} /> {/* <DrieHtmlTemp itemsGroup={itemsGroup} /> */}
</>
</> );
); }
}

View File

@@ -5,12 +5,10 @@ import { toast } from 'react-toastify';
import TempLoader from './tempLoader'; import TempLoader from './tempLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulationTypes";
import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils'; import { retrieveGLTF, storeGLTF } from '../../../../utils/indexDB/idbUtils';
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; // import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import * as CONSTANTS from '../../../../types/world/worldConstants'; import * as CONSTANTS from '../../../../types/world/worldConstants';
import { getAssetEventType } from '../../../../services/simulation/getAssetEventType';
import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi'; import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
async function addAssetModel( async function addAssetModel(
@@ -25,7 +23,6 @@ async function addAssetModel(
socket: Socket<any>, socket: Socket<any>,
selectedItem: any, selectedItem: any,
setSelectedItem: any, setSelectedItem: any,
setSimulationStates: any,
plane: Types.RefMesh, plane: Types.RefMesh,
): Promise<void> { ): Promise<void> {
@@ -66,7 +63,7 @@ async function addAssetModel(
const cachedModel = THREE.Cache.get(selectedItem.id); const cachedModel = THREE.Cache.get(selectedItem.id);
if (cachedModel) { if (cachedModel) {
// console.log(`[Cache] Fetching ${selectedItem.name}`); // console.log(`[Cache] Fetching ${selectedItem.name}`);
handleModelLoad(cachedModel, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, setSimulationStates, socket); handleModelLoad(cachedModel, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, socket);
return; return;
} else { } else {
const cachedModelBlob = await retrieveGLTF(selectedItem.id); const cachedModelBlob = await retrieveGLTF(selectedItem.id);
@@ -79,7 +76,7 @@ async function addAssetModel(
URL.revokeObjectURL(blobUrl); URL.revokeObjectURL(blobUrl);
THREE.Cache.remove(blobUrl); THREE.Cache.remove(blobUrl);
THREE.Cache.add(selectedItem.id, gltf); THREE.Cache.add(selectedItem.id, gltf);
handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, setSimulationStates, socket); handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, socket);
}, },
() => { () => {
TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup);
@@ -91,7 +88,7 @@ async function addAssetModel(
const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`).then((res) => res.blob()); const modelBlob = await fetch(`${url_Backend_dwinzo}/api/v2/AssetFile/${selectedItem.id}`).then((res) => res.blob());
await storeGLTF(selectedItem.id, modelBlob); await storeGLTF(selectedItem.id, modelBlob);
THREE.Cache.add(selectedItem.id, gltf); THREE.Cache.add(selectedItem.id, gltf);
await handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, setSimulationStates, socket); await handleModelLoad(gltf, intersectPoint!, selectedItem, itemsGroup, tempLoader, isTempLoader, setFloorItems, socket);
}, },
() => { () => {
TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup); TempLoader(intersectPoint!, isTempLoader, tempLoader, itemsGroup);
@@ -114,7 +111,6 @@ async function handleModelLoad(
tempLoader: Types.RefMesh, tempLoader: Types.RefMesh,
isTempLoader: Types.RefBoolean, isTempLoader: Types.RefBoolean,
setFloorItems: Types.setFloorItemSetState, setFloorItems: Types.setFloorItemSetState,
setSimulationStates: any,
socket: Socket<any> socket: Socket<any>
) { ) {
const model = gltf.scene.clone(); const model = gltf.scene.clone();
@@ -137,7 +133,7 @@ async function handleModelLoad(
tempLoader.current = undefined; tempLoader.current = undefined;
} }
const newFloorItem: SimulationTypes.EventData = { const newFloorItem: Types.FloorItemType = {
modeluuid: model.uuid, modeluuid: model.uuid,
modelname: selectedItem.name, modelname: selectedItem.name,
modelfileID: selectedItem.id, modelfileID: selectedItem.id,
@@ -150,316 +146,44 @@ async function handleModelLoad(
const email = localStorage.getItem("email"); const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : ""; const organization = email ? email.split("@")[1].split(".")[0] : "";
getAssetEventType(selectedItem.id, organization).then(async (res) => { // API
if (res.type === "Conveyor") { // await setFloorItemApi(
const pointUUIDs = res.points.map(() => THREE.MathUtils.generateUUID()); // organization,
// newFloorItem.modeluuid,
// newFloorItem.modelname,
// newFloorItem.modelfileID,
// newFloorItem.position,
// { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
// false,
// true,
// );
const backendEventData: Extract<SimulationTypes.EventData['eventData'], { type: 'Conveyor' }> = { // SOCKET
type: 'Conveyor',
points: res.points.map((point: any, index: number) => ({
uuid: pointUUIDs[index],
position: point.position as [number, number, number],
rotation: point.rotation as [number, number, number],
actions: [{
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: true
}],
triggers: [],
connections: {
source: { modelUUID: model.uuid, pointUUID: pointUUIDs[index] },
targets: []
}
})),
speed: 'Inherit'
};
// API const data = {
organization,
// await setFloorItemApi( modeluuid: newFloorItem.modeluuid,
// organization, modelname: newFloorItem.modelname,
// newFloorItem.modeluuid, modelfileID: newFloorItem.modelfileID,
// newFloorItem.modelname, position: newFloorItem.position,
// newFloorItem.modelfileID, rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
// newFloorItem.position, isLocked: false,
// { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z }, isVisible: true,
// false, socketId: socket.id
// true, };
// { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }
// );
// SOCKET
const data = {
organization,
modeluuid: newFloorItem.modeluuid,
modelname: newFloorItem.modelname,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
isLocked: false,
isVisible: true,
eventData: backendEventData,
socketId: socket.id
};
setFloorItems((prevItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
const eventData: any = backendEventData;
eventData.modeluuid = newFloorItem.modeluuid;
eventData.modelName = newFloorItem.modelname;
eventData.position = newFloorItem.position;
eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z];
setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
eventData as SimulationTypes.ConveyorEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (res.type === "Vehicle") {
const pointUUID = THREE.MathUtils.generateUUID();
const backendEventData: Extract<SimulationTypes.EventData['eventData'], { type: 'Vehicle' }> = {
type: "Vehicle",
points: {
uuid: pointUUID,
position: res.points.position as [number, number, number],
rotation: res.points.rotation as [number, number, number],
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: '', start: {}, hitCount: 1, end: {}, buffer: 0 },
connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] },
speed: 2,
}
}
// API
// await setFloorItemApi(
// organization,
// newFloorItem.modeluuid,
// newFloorItem.modelname,
// newFloorItem.modelfileID,
// newFloorItem.position,
// { x: model.rotation.x, y: model.rotation.y, z: model.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: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
isLocked: false,
isVisible: true,
eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id
};
const eventData: any = backendEventData;
eventData.modeluuid = newFloorItem.modeluuid;
eventData.modelName = newFloorItem.modelname;
eventData.position = newFloorItem.position;
setFloorItems((prevItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
eventData as SimulationTypes.VehicleEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (res.type === "StaticMachine") {
const pointUUID = THREE.MathUtils.generateUUID();
const backendEventData: Extract<SimulationTypes.EventData['eventData'], { type: 'StaticMachine' }> = {
type: "StaticMachine",
points: {
uuid: pointUUID,
position: res.points.position as [number, number, number],
rotation: res.points.rotation as [number, number, number],
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', buffer: 0, material: 'Inherit' },
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] },
}
}
// API
// await setFloorItemApi(
// organization,
// newFloorItem.modeluuid,
// newFloorItem.modelname,
// newFloorItem.modelfileID,
// newFloorItem.position,
// { x: model.rotation.x, y: model.rotation.y, z: model.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: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
isLocked: false,
isVisible: true,
eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id
};
const eventData: any = backendEventData;
eventData.modeluuid = newFloorItem.modeluuid;
eventData.modelName = newFloorItem.modelname;
eventData.position = newFloorItem.position;
eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z];
setFloorItems((prevItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
eventData as SimulationTypes.StaticMachineEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (res.type === "ArmBot") {
const pointUUID = THREE.MathUtils.generateUUID();
const backendEventData: Extract<SimulationTypes.EventData['eventData'], { type: 'ArmBot' }> = {
type: "ArmBot",
points: {
uuid: pointUUID,
position: res.points.position as [number, number, number],
rotation: res.points.rotation as [number, number, number],
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 0.2, processes: [] },
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
connections: { source: { modelUUID: model.uuid, pointUUID: pointUUID }, targets: [] },
}
}
// API
// await setFloorItemApi(
// organization,
// newFloorItem.modeluuid,
// newFloorItem.modelname,
// newFloorItem.modelfileID,
// newFloorItem.position,
// { x: model.rotation.x, y: model.rotation.y, z: model.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: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
isLocked: false,
isVisible: true,
eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id
};
const eventData: any = backendEventData;
eventData.modeluuid = newFloorItem.modeluuid;
eventData.modelName = newFloorItem.modelname;
eventData.position = newFloorItem.position;
eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z];
setFloorItems((prevItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
eventData as SimulationTypes.ArmBotEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else {
// API
// await setFloorItemApi(
// organization,
// newFloorItem.modeluuid,
// newFloorItem.modelname,
// newFloorItem.modelfileID,
// newFloorItem.position,
// { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
// false,
// true,
// );
// SOCKET
const data = {
organization,
modeluuid: newFloorItem.modeluuid,
modelname: newFloorItem.modelname,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: model.rotation.x, y: model.rotation.y, z: model.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id
};
setFloorItems((prevItems) => { setFloorItems((prevItems) => {
const updatedItems = [...(prevItems || []), newFloorItem]; const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems)); localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems; return updatedItems;
});
socket.emit("v2:model-asset:add", data);
}
gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" });
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } });
}); });
socket.emit("v2:model-asset:add", data);
gsap.to(model.position, { y: newFloorItem.position[1], duration: 1.5, ease: "power2.out" });
gsap.to(model.scale, { x: 1, y: 1, z: 1, duration: 1.5, ease: "power2.out", onComplete: () => { toast.success("Model Added!"); } });
} }
export default addAssetModel; export default addAssetModel;

View File

@@ -2,7 +2,6 @@ import { toast } from 'react-toastify';
import * as THREE from 'three'; import * as THREE from 'three';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulationTypes";
// import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi'; import { getFloorAssets } from '../../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi';
@@ -11,7 +10,6 @@ async function DeleteFloorItems(
itemsGroup: Types.RefGroup, itemsGroup: Types.RefGroup,
hoveredDeletableFloorItem: Types.RefMesh, hoveredDeletableFloorItem: Types.RefMesh,
setFloorItems: Types.setFloorItemSetState, setFloorItems: Types.setFloorItemSetState,
setSimulationStates: any,
socket: Socket<any> socket: Socket<any>
): Promise<void> { ): Promise<void> {
@@ -77,11 +75,6 @@ async function DeleteFloorItems(
} }
setFloorItems(updatedItems); setFloorItems(updatedItems);
setSimulationStates((prevEvents: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).filter(event => event.modeluuid !== removedItem.modeluuid);
return updatedEvents;
});
toast.success("Model Removed!"); toast.success("Model Removed!");
} }
} }

View File

@@ -1,5 +1,5 @@
import { useFrame, useThree } from "@react-three/fiber"; import { useFrame, useThree } from "@react-three/fiber";
import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSimulationStates, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store"; import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store";
import assetVisibility from "../geomentries/assets/assetVisibility"; import assetVisibility from "../geomentries/assets/assetVisibility";
import { useEffect } from "react"; import { useEffect } from "react";
import * as THREE from "three"; import * as THREE from "three";
@@ -11,7 +11,7 @@ import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems"; import DeletableHoveredFloorItems from "../geomentries/assets/deletableHoveredFloorItems";
import DeleteFloorItems from "../geomentries/assets/deleteFloorItems"; import DeleteFloorItems from "../geomentries/assets/deleteFloorItems";
import loadInitialFloorItems from "../../scene/IntialLoad/loadInitialFloorItems"; import loadInitialFloorItems from "../IntialLoad/loadInitialFloorItems";
import addAssetModel from "../geomentries/assets/addAssetModel"; import addAssetModel from "../geomentries/assets/addAssetModel";
import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi"; import { getFloorAssets } from "../../../services/factoryBuilder/assest/floorAsset/getFloorItemsApi";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
@@ -32,7 +32,6 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
const { setSelectedFloorItem } = useSelectedFloorItem(); const { setSelectedFloorItem } = useSelectedFloorItem();
const { activeTool } = useActiveTool(); const { activeTool } = useActiveTool();
const { selectedItem, setSelectedItem } = useSelectedItem(); const { selectedItem, setSelectedItem } = useSelectedItem();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { setLoadingProgress } = useLoadingProgress(); const { setLoadingProgress } = useLoadingProgress();
const { activeModule } = useModuleStore(); const { activeModule } = useModuleStore();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
@@ -73,7 +72,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
gltfLoaderWorker.postMessage({ floorItems: data }); gltfLoaderWorker.postMessage({ floorItems: data });
} else { } else {
gltfLoaderWorker.postMessage({ floorItems: [] }); gltfLoaderWorker.postMessage({ floorItems: [] });
loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationStates); loadInitialFloorItems(itemsGroup, setFloorItems);
updateLoadingProgress(100); updateLoadingProgress(100);
} }
}); });
@@ -92,7 +91,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
updateLoadingProgress(progress); updateLoadingProgress(progress);
if (loadedAssets === totalAssets) { if (loadedAssets === totalAssets) {
loadInitialFloorItems(itemsGroup, setFloorItems, setSimulationStates); loadInitialFloorItems(itemsGroup, setFloorItems);
updateLoadingProgress(100); updateLoadingProgress(100);
} }
}); });
@@ -192,9 +191,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
if (drag) return; if (drag) return;
if (deleteTool) { if (deleteTool) {
DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, setSimulationStates, socket); DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, socket);
// Remove EventData if there are any in the asset.
} }
const Mode = transformMode; const Mode = transformMode;
@@ -278,7 +275,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
if (!event.dataTransfer?.files[0]) return; if (!event.dataTransfer?.files[0]) return;
if (selectedItem.id !== "" && event.dataTransfer?.files[0]) { 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, plane);
} }
}; };

View File

@@ -9,15 +9,14 @@ import removeReferenceLine from "../geomentries/lines/removeReferenceLine";
import DeleteLayer from "../geomentries/layers/deleteLayer"; import DeleteLayer from "../geomentries/layers/deleteLayer";
import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi"; import { getLines } from "../../../services/factoryBuilder/lines/getLinesApi";
import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray"; import objectLinesToArray from "../geomentries/lines/lineConvertions/objectLinesToArray";
import loadInitialPoint from "../../scene/IntialLoad/loadInitialPoint"; import loadInitialPoint from "../IntialLoad/loadInitialPoint";
import loadInitialLine from "../../scene/IntialLoad/loadInitialLine"; import loadInitialLine from "../IntialLoad/loadInitialLine";
import deletePoint from "../geomentries/points/deletePoint"; import deletePoint from "../geomentries/points/deletePoint";
import deleteLine from "../geomentries/lines/deleteLine"; import deleteLine from "../geomentries/lines/deleteLine";
import drawWall from "../geomentries/lines/drawWall"; import drawWall from "../geomentries/lines/drawWall";
import drawOnlyFloor from "../geomentries/floors/drawOnlyFloor"; import drawOnlyFloor from "../geomentries/floors/drawOnlyFloor";
import addDragControl from "../eventDeclaration/dragControlDeclaration"; import addDragControl from "../eventDeclaration/dragControlDeclaration";
const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, floorGroup, currentLayerPoint, dragPointControls, hoveredDeletablePoint, hoveredDeletableLine, plane, line, lines, onlyFloorline, onlyFloorlines, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => { const FloorPlanGroup = ({ floorPlanGroup, floorPlanGroupLine, floorPlanGroupPoint, floorGroup, currentLayerPoint, dragPointControls, hoveredDeletablePoint, hoveredDeletableLine, plane, line, lines, onlyFloorline, onlyFloorlines, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => {
const state = useThree(); const state = useThree();
const { scene, camera, gl, raycaster, controls } = state; const { scene, camera, gl, raycaster, controls } = state;

View File

@@ -7,7 +7,7 @@ import * as THREE from "three";
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
import handleMeshMissed from "../eventFunctions/handleMeshMissed"; import handleMeshMissed from "../eventFunctions/handleMeshMissed";
import DeleteWallItems from "../geomentries/walls/deleteWallItems"; import DeleteWallItems from "../geomentries/walls/deleteWallItems";
import loadInitialWallItems from "../../scene/IntialLoad/loadInitialWallItems"; import loadInitialWallItems from "../IntialLoad/loadInitialWallItems";
import AddWallItems from "../geomentries/walls/addWallItems"; import AddWallItems from "../geomentries/walls/addWallItems";
import useModuleStore from "../../../store/useModuleStore"; import useModuleStore from "../../../store/useModuleStore";
@@ -37,51 +37,6 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
////////// Update the Rotation value changes in the selected item ////////// ////////// Update the Rotation value changes in the selected item //////////
useEffect(() => {
if (objectScale.x && objectScale.y && objectScale.z) {
let ScaledWallItems: Types.wallItems = [];
wallItems.forEach((items: any) => {
if (items.model?.uuid === currentWallItem.current?.parent?.uuid) {
items.scale = [objectScale.x, objectScale.y, objectScale.z];
}
ScaledWallItems.push(items);
});
setWallItems(ScaledWallItems);
}
}, [objectScale]);
useEffect(() => {
if (objectPosition.x && objectPosition.y && objectPosition.z) {
let ScaledWallItems: Types.wallItems = [];
wallItems.forEach((items: any) => {
if (items.model?.uuid === currentWallItem.current?.parent?.uuid) {
items.position = [objectPosition.x, objectPosition.y, objectPosition.z];
}
ScaledWallItems.push(items);
});
setWallItems(ScaledWallItems);
}
}, [objectPosition]);
useEffect(() => {
if (objectRotation.x && objectRotation.y && objectRotation.z) {
let ScaledWallItems: Types.wallItems = [];
wallItems.forEach((items: any) => {
if (items.model?.uuid === currentWallItem.current?.parent?.uuid) {
const radiansX = objectRotation.x * (Math.PI / 180);
const radiansY = objectRotation.y * (Math.PI / 180);
const radiansZ = objectRotation.z * (Math.PI / 180);
const quaternion = new THREE.Quaternion().setFromEuler(
new THREE.Euler(radiansX, radiansY, radiansZ)
);
items.quaternion = [quaternion.x, quaternion.y, quaternion.z, quaternion.w];
}
ScaledWallItems.push(items);
});
setWallItems(ScaledWallItems);
}
}, [objectRotation]);
useEffect(() => { useEffect(() => {
const canvasElement = state.gl.domElement; const canvasElement = state.gl.domElement;
function handlePointerMove(e: any) { function handlePointerMove(e: any) {

View File

@@ -0,0 +1,231 @@
import * as THREE from "three";
import { useEffect, useRef, useState } from "react";
import { useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import camModel from "../../assets/gltf-glb/camera face 2.gltf";
import getActiveUsersData from "../../../services/factoryBuilder/collab/getActiveUsers";
import { useActiveUsers, useSocketStore } from "../../../store/store";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { useNavigate } from "react-router-dom";
import { Html } from "@react-three/drei";
import CollabUserIcon from "../../../functions/collabUserIcon";
import { getAvatarColor } from "../../../functions/users/functions/getAvatarColor";
import useModuleStore from "../../../store/useModuleStore";
const CamModelsGroup = () => {
const navigate = useNavigate();
const groupRef = useRef<THREE.Group>(null);
const email = localStorage.getItem("email");
const { setActiveUsers } = useActiveUsers();
const { socket } = useSocketStore();
const { activeModule } = useModuleStore();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
const [cams, setCams] = useState<any[]>([]);
const [models, setModels] = useState<Record<string, { targetPosition: THREE.Vector3; targetRotation: THREE.Euler }>>({});
const dedupeCams = (cams: any[]) => {
const seen = new Set();
return cams.filter((cam) => {
if (seen.has(cam.uuid)) return false;
seen.add(cam.uuid);
return true;
});
};
const dedupeUsers = (users: any[]) => {
const seen = new Set();
return users.filter((user) => {
if (seen.has(user._id)) return false;
seen.add(user._id);
return true;
});
};
useEffect(() => {
if (!email) navigate("/");
if (!socket) return;
const organization = email!.split("@")[1].split(".")[0];
socket.on("userConnectResponse", (data: any) => {
if (!groupRef.current) return;
if (data.data.userData.email === email) return;
if (socket.id === data.socketId || organization !== data.organization)
return;
const model = groupRef.current.getObjectByProperty(
"uuid",
data.data.userData._id
);
if (model) {
groupRef.current.remove(model);
}
loader.load(camModel, (gltf) => {
const newModel = gltf.scene.clone();
newModel.uuid = data.data.userData._id;
newModel.position.set(
data.data.position.x,
data.data.position.y,
data.data.position.z
);
newModel.rotation.set(
data.data.rotation.x,
data.data.rotation.y,
data.data.rotation.z
);
newModel.userData = data.data.userData;
setCams((prev) => dedupeCams([...prev, newModel]));
setActiveUsers((prev: any) =>
dedupeUsers([...prev, data.data.userData])
);
});
});
socket.on("userDisConnectResponse", (data: any) => {
if (!groupRef.current) return;
if (socket.id === data.socketId || organization !== data.organization)
return;
setCams((prev) =>
prev.filter((cam) => cam.uuid !== data.data.userData._id)
);
setActiveUsers((prev: any) =>
prev.filter((user: any) => user._id !== data.data.userData._id)
);
});
socket.on("cameraUpdateResponse", (data: any) => {
if (
!groupRef.current ||
socket.id === data.socketId ||
organization !== data.organization
)
return;
setModels((prev) => ({
...prev,
[data.data.userId]: {
targetPosition: new THREE.Vector3(
data.data.position.x,
data.data.position.y,
data.data.position.z
),
targetRotation: new THREE.Euler(
data.data.rotation.x,
data.data.rotation.y,
data.data.rotation.z
),
},
}));
});
return () => {
socket.off("userConnectResponse");
socket.off("userDisConnectResponse");
socket.off("cameraUpdateResponse");
};
}, [socket]);
useFrame(() => {
if (!groupRef.current) return;
Object.keys(models).forEach((uuid) => {
const model = groupRef.current!.getObjectByProperty("uuid", uuid);
if (!model) return;
const { targetPosition, targetRotation } = models[uuid];
model.position.lerp(targetPosition, 0.1);
model.rotation.x = THREE.MathUtils.lerp(
model.rotation.x,
targetRotation.x,
0.1
);
model.rotation.y = THREE.MathUtils.lerp(
model.rotation.y,
targetRotation.y,
0.1
);
model.rotation.z = THREE.MathUtils.lerp(
model.rotation.z,
targetRotation.z,
0.1
);
});
});
useEffect(() => {
if (!groupRef.current) return;
const organization = email!.split("@")[1].split(".")[0];
getActiveUsersData(organization).then((data) => {
const filteredData = data.cameraDatas.filter(
(camera: any) => camera.userData.email !== email
);
if (filteredData.length > 0) {
loader.load(camModel, (gltf) => {
const newCams = filteredData.map((cam: any) => {
const newModel = gltf.scene.clone();
newModel.uuid = cam.userData._id;
newModel.position.set(
cam.position.x,
cam.position.y,
cam.position.z
);
newModel.rotation.set(
cam.rotation.x,
cam.rotation.y,
cam.rotation.z
);
newModel.userData = cam.userData;
return newModel;
});
const users = filteredData.map((cam: any) => cam.userData);
setActiveUsers((prev: any) => dedupeUsers([...prev, ...users]));
setCams((prev) => dedupeCams([...prev, ...newCams]));
});
}
});
}, []);
return (
<group
ref={groupRef}
name="Cam-Model-Group"
visible={activeModule !== "visualization" ? true : false}
>
{cams.map((cam, index) => (
<primitive key={cam.uuid} object={cam}>
<Html
as="div"
center
zIndexRange={[1, 0]}
sprite
style={{
color: "white",
textAlign: "center",
fontFamily: "Arial, sans-serif",
display: `${activeModule !== "visualization" ? "" : "none"}`,
}}
position={[-0.015, 0, 0.7]}
>
<CollabUserIcon
userImage={cam.userData.userImage || ""}
userName={cam.userData.userName}
color={getAvatarColor(index, cam.userData.userName)}
/>
</Html>
</primitive>
))}
</group>
);
};
export default CamModelsGroup;

View File

@@ -1,231 +0,0 @@
import * as THREE from "three";
import { useEffect, useRef, useState } from "react";
import { useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import camModel from "../../assets/gltf-glb/camera face 2.gltf";
import getActiveUsersData from "../../services/factoryBuilder/collab/getActiveUsers";
import { useActiveUsers, useSocketStore } from "../../store/store";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";
import { useNavigate } from "react-router-dom";
import { Html } from "@react-three/drei";
import CollabUserIcon from "./collabUserIcon";
import { getAvatarColor } from "./users/functions/getAvatarColor";
import useModuleStore from "../../store/useModuleStore";
const CamModelsGroup = () => {
const navigate = useNavigate();
const groupRef = useRef<THREE.Group>(null);
const email = localStorage.getItem("email");
const { setActiveUsers } = useActiveUsers();
const { socket } = useSocketStore();
const { activeModule } = useModuleStore();
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath("three/examples/jsm/libs/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
const [cams, setCams] = useState<any[]>([]);
const [models, setModels] = useState<Record<string, { targetPosition: THREE.Vector3; targetRotation: THREE.Euler }>>({});
const dedupeCams = (cams: any[]) => {
const seen = new Set();
return cams.filter((cam) => {
if (seen.has(cam.uuid)) return false;
seen.add(cam.uuid);
return true;
});
};
const dedupeUsers = (users: any[]) => {
const seen = new Set();
return users.filter((user) => {
if (seen.has(user._id)) return false;
seen.add(user._id);
return true;
});
};
useEffect(() => {
if (!email) navigate("/");
if (!socket) return;
const organization = email!.split("@")[1].split(".")[0];
socket.on("userConnectResponse", (data: any) => {
if (!groupRef.current) return;
if (data.data.userData.email === email) return;
if (socket.id === data.socketId || organization !== data.organization)
return;
const model = groupRef.current.getObjectByProperty(
"uuid",
data.data.userData._id
);
if (model) {
groupRef.current.remove(model);
}
loader.load(camModel, (gltf) => {
const newModel = gltf.scene.clone();
newModel.uuid = data.data.userData._id;
newModel.position.set(
data.data.position.x,
data.data.position.y,
data.data.position.z
);
newModel.rotation.set(
data.data.rotation.x,
data.data.rotation.y,
data.data.rotation.z
);
newModel.userData = data.data.userData;
setCams((prev) => dedupeCams([...prev, newModel]));
setActiveUsers((prev: any) =>
dedupeUsers([...prev, data.data.userData])
);
});
});
socket.on("userDisConnectResponse", (data: any) => {
if (!groupRef.current) return;
if (socket.id === data.socketId || organization !== data.organization)
return;
setCams((prev) =>
prev.filter((cam) => cam.uuid !== data.data.userData._id)
);
setActiveUsers((prev: any) =>
prev.filter((user: any) => user._id !== data.data.userData._id)
);
});
socket.on("cameraUpdateResponse", (data: any) => {
if (
!groupRef.current ||
socket.id === data.socketId ||
organization !== data.organization
)
return;
setModels((prev) => ({
...prev,
[data.data.userId]: {
targetPosition: new THREE.Vector3(
data.data.position.x,
data.data.position.y,
data.data.position.z
),
targetRotation: new THREE.Euler(
data.data.rotation.x,
data.data.rotation.y,
data.data.rotation.z
),
},
}));
});
return () => {
socket.off("userConnectResponse");
socket.off("userDisConnectResponse");
socket.off("cameraUpdateResponse");
};
}, [socket]);
useFrame(() => {
if (!groupRef.current) return;
Object.keys(models).forEach((uuid) => {
const model = groupRef.current!.getObjectByProperty("uuid", uuid);
if (!model) return;
const { targetPosition, targetRotation } = models[uuid];
model.position.lerp(targetPosition, 0.1);
model.rotation.x = THREE.MathUtils.lerp(
model.rotation.x,
targetRotation.x,
0.1
);
model.rotation.y = THREE.MathUtils.lerp(
model.rotation.y,
targetRotation.y,
0.1
);
model.rotation.z = THREE.MathUtils.lerp(
model.rotation.z,
targetRotation.z,
0.1
);
});
});
useEffect(() => {
if (!groupRef.current) return;
const organization = email!.split("@")[1].split(".")[0];
getActiveUsersData(organization).then((data) => {
const filteredData = data.cameraDatas.filter(
(camera: any) => camera.userData.email !== email
);
if (filteredData.length > 0) {
loader.load(camModel, (gltf) => {
const newCams = filteredData.map((cam: any) => {
const newModel = gltf.scene.clone();
newModel.uuid = cam.userData._id;
newModel.position.set(
cam.position.x,
cam.position.y,
cam.position.z
);
newModel.rotation.set(
cam.rotation.x,
cam.rotation.y,
cam.rotation.z
);
newModel.userData = cam.userData;
return newModel;
});
const users = filteredData.map((cam: any) => cam.userData);
setActiveUsers((prev: any) => dedupeUsers([...prev, ...users]));
setCams((prev) => dedupeCams([...prev, ...newCams]));
});
}
});
}, []);
return (
<group
ref={groupRef}
name="Cam-Model-Group"
visible={activeModule !== "visualization" ? true : false}
>
{cams.map((cam, index) => (
<primitive key={cam.uuid} object={cam}>
<Html
as="div"
center
zIndexRange={[1, 0]}
sprite
style={{
color: "white",
textAlign: "center",
fontFamily: "Arial, sans-serif",
display: `${activeModule !== "visualization" ? "" : "none"}`,
}}
position={[-0.015, 0, 0.7]}
>
<CollabUserIcon
userImage={cam.userData.userImage || ""}
userName={cam.userData.userName}
color={getAvatarColor(index, cam.userData.userName)}
/>
</Html>
</primitive>
))}
</group>
);
};
export default CamModelsGroup;

View File

@@ -0,0 +1,14 @@
import React from 'react'
import CamModelsGroup from './camera/collabCams'
const Collaboration = () => {
return (
<>
<CamModelsGroup />
</>
)
}
export default Collaboration

View File

@@ -9,128 +9,135 @@ import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi"
import updateCamPosition from "../camera/updateCameraPosition"; import updateCamPosition from "../camera/updateCameraPosition";
import CamMode from "../camera/camMode"; import CamMode from "../camera/camMode";
import SwitchView from "../camera/switchView"; import SwitchView from "../camera/switchView";
import SelectionControls from "./selectionControls/selectionControls";
export default function Controls() { export default function Controls() {
const controlsRef = useRef<CameraControls>(null); const controlsRef = useRef<CameraControls>(null);
const { toggleView } = useToggleView(); const { toggleView } = useToggleView();
const { resetCamera, setResetCamera } = useResetCamera(); const { resetCamera, setResetCamera } = useResetCamera();
const { socket } = useSocketStore(); const { socket } = useSocketStore();
const state = useThree(); const state = useThree();
useEffect(() => { useEffect(() => {
if (controlsRef.current) { if (controlsRef.current) {
(controlsRef.current as any).mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse; (controlsRef.current as any).mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse;
(controlsRef.current as any).mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse; (controlsRef.current as any).mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse;
} }
const email = localStorage.getItem("email"); const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0]; const organization = email!.split("@")[1].split(".")[0];
getCamera(organization, localStorage.getItem("userId")!).then((data) => { getCamera(organization, localStorage.getItem("userId")!).then((data) => {
if (data && data.position && data.target) { if (data && data.position && data.target) {
controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z); controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z);
controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z); controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z);
} else { } else {
controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition); controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget); controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
} }
}) })
.catch((error) => console.error("Failed to fetch camera data:", error)); .catch((error) => console.error("Failed to fetch camera data:", error));
}, []); }, []);
useEffect(() => { useEffect(() => {
if (resetCamera) { if (resetCamera) {
controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition); controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget); controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
controlsRef.current?.rotateAzimuthTo(CONSTANTS.threeDimension.defaultAzimuth); controlsRef.current?.rotateAzimuthTo(CONSTANTS.threeDimension.defaultAzimuth);
localStorage.setItem("cameraPosition", JSON.stringify(new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition))); localStorage.setItem("cameraPosition", JSON.stringify(new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition)));
localStorage.setItem("controlTarget", JSON.stringify(new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget))); localStorage.setItem("controlTarget", JSON.stringify(new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget)));
const email = localStorage.getItem('email') const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0]; const organization = (email!.split("@")[1]).split(".")[0];
const camData = { const camData = {
organization: organization, organization: organization,
userId: localStorage.getItem('userId')!, userId: localStorage.getItem('userId')!,
position: new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition), position: new THREE.Vector3(...CONSTANTS.threeDimension.defaultPosition),
target: new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget), target: new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget),
rotation: new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation), rotation: new THREE.Vector3(...CONSTANTS.threeDimension.defaultRotation),
socketId: socket.id socketId: socket.id
}; };
socket.emit('v1:Camera:set', camData) socket.emit('v1:Camera:set', camData)
setResetCamera(false); setResetCamera(false);
} }
}, [resetCamera]); }, [resetCamera]);
useEffect(() => { useEffect(() => {
controlsRef.current?.setBoundary(new THREE.Box3(new THREE.Vector3(...CONSTANTS.threeDimension.boundaryBottom), new THREE.Vector3(...CONSTANTS.threeDimension.boundaryTop))); controlsRef.current?.setBoundary(new THREE.Box3(new THREE.Vector3(...CONSTANTS.threeDimension.boundaryBottom), new THREE.Vector3(...CONSTANTS.threeDimension.boundaryTop)));
// state.scene.add(new THREE.Box3Helper(new THREE.Box3(new THREE.Vector3(...CONSTANTS.threeDimension.boundaryBottom), new THREE.Vector3(...CONSTANTS.threeDimension.boundaryTop)), 0xffff00)); // state.scene.add(new THREE.Box3Helper(new THREE.Box3(new THREE.Vector3(...CONSTANTS.threeDimension.boundaryBottom), new THREE.Vector3(...CONSTANTS.threeDimension.boundaryTop)), 0xffff00));
let hasInteracted = false; let hasInteracted = false;
let intervalId: NodeJS.Timeout | null = null; let intervalId: NodeJS.Timeout | null = null;
const handleRest = () => { const handleRest = () => {
if (hasInteracted && controlsRef.current && state.camera.position && !toggleView) { if (hasInteracted && controlsRef.current && state.camera.position && !toggleView) {
const position = state.camera.position; const position = state.camera.position;
if (position.x === 0 && position.y === 0 && position.z === 0) return; if (position.x === 0 && position.y === 0 && position.z === 0) return;
updateCamPosition(controlsRef, socket, position, state.camera.rotation); updateCamPosition(controlsRef, socket, position, state.camera.rotation);
stopInterval(); stopInterval();
} }
}; };
const startInterval = () => { const startInterval = () => {
hasInteracted = true; hasInteracted = true;
if (!intervalId) { if (!intervalId) {
intervalId = setInterval(() => { intervalId = setInterval(() => {
if (controlsRef.current && !toggleView) { if (controlsRef.current && !toggleView) {
handleRest(); handleRest();
} }
}, CONSTANTS.camPositionUpdateInterval); }, CONSTANTS.camPositionUpdateInterval);
} }
}; };
const stopInterval = () => { const stopInterval = () => {
if (intervalId) { if (intervalId) {
clearInterval(intervalId); clearInterval(intervalId);
intervalId = null; intervalId = null;
} }
}; };
const controls = controlsRef.current; const controls = controlsRef.current;
if (controls) { if (controls) {
controls.addEventListener("sleep", handleRest); controls.addEventListener("sleep", handleRest);
controls.addEventListener("control", startInterval); controls.addEventListener("control", startInterval);
controls.addEventListener("controlend", stopInterval); controls.addEventListener("controlend", stopInterval);
} }
return () => { return () => {
if (controls) { if (controls) {
controls.removeEventListener("sleep", handleRest); controls.removeEventListener("sleep", handleRest);
controls.removeEventListener("control", startInterval); controls.removeEventListener("control", startInterval);
controls.removeEventListener("controlend", stopInterval); controls.removeEventListener("controlend", stopInterval);
} }
stopInterval(); stopInterval();
}; };
}, [toggleView, state, socket]); }, [toggleView, state, socket]);
return ( return (
<> <>
<CameraControls <CameraControls
makeDefault makeDefault
ref={controlsRef} ref={controlsRef}
minDistance={toggleView ? CONSTANTS.twoDimension.minDistance : CONSTANTS.threeDimension.minDistance} minDistance={toggleView ? CONSTANTS.twoDimension.minDistance : CONSTANTS.threeDimension.minDistance}
maxDistance={CONSTANTS.thirdPersonControls.maxDistance} maxDistance={CONSTANTS.thirdPersonControls.maxDistance}
minZoom={CONSTANTS.thirdPersonControls.minZoom} minZoom={CONSTANTS.thirdPersonControls.minZoom}
maxZoom={CONSTANTS.thirdPersonControls.maxZoom} maxZoom={CONSTANTS.thirdPersonControls.maxZoom}
maxPolarAngle={CONSTANTS.thirdPersonControls.maxPolarAngle} maxPolarAngle={CONSTANTS.thirdPersonControls.maxPolarAngle}
camera={state.camera} camera={state.camera}
verticalDragToForward={true} verticalDragToForward={true}
boundaryEnclosesCamera={true} boundaryEnclosesCamera={true}
dollyToCursor={toggleView} dollyToCursor={toggleView}
> >
<SwitchView />
<CamMode /> <SwitchView />
</CameraControls>
</> <CamMode />
);
</CameraControls>
<SelectionControls />
</>
);
} }

View File

@@ -1,581 +0,0 @@
import * as THREE from "three";
import { useEffect, useMemo } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, useToggleView } from "../../../../store/store";
import { toast } from "react-toastify";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulationTypes";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { simulationStates, setSimulationStates } = useSimulationStates();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore()
useEffect(() => {
if (!camera || !scene || toggleView) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && pastedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
event.preventDefault();
addPastedObjects();
}
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (keyCombination === "Ctrl+C" && movedObjects.length === 0 && rotatedObjects.length === 0) {
copySelection();
}
if (keyCombination === "Ctrl+V" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
pasteCopiedObjects();
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, floorItems, rotatedObjects]);
useFrame(() => {
if (pastedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current?.getWorldPosition(position)
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
} else {
const box = new THREE.Box3();
pastedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone()));
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z));
}
}
}
});
const copySelection = () => {
if (selectedAssets.length > 0) {
const newClones = selectedAssets.map((asset: any) => {
const clone = asset.clone();
clone.position.copy(asset.position);
return clone;
});
setCopiedObjects(newClones);
toast.info("Objects copied!");
}
};
const pasteCopiedObjects = () => {
if (copiedObjects.length > 0 && pastedObjects.length === 0) {
const newClones = copiedObjects.map((obj: THREE.Object3D) => {
const clone = obj.clone();
clone.position.copy(obj.position);
return clone;
});
selectionGroup.current.add(...newClones);
setpastedObjects([...newClones]);
setSelectedAssets([...newClones]);
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current?.getWorldPosition(position)
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
} else {
const box = new THREE.Box3();
newClones.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone()));
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z));
}
}
}
};
const addPastedObjects = () => {
if (pastedObjects.length === 0) return;
pastedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
obj.getWorldPosition(worldPosition);
obj.position.copy(worldPosition);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modeluuid: obj.uuid,
modelname: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
isLocked: false,
isVisible: true
};
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
let eventData: SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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) {
if (eventData.type === 'Conveyor' && eventData) {
const createConveyorPoint = (index: number) => {
const pointUUID = THREE.MathUtils.generateUUID();
const hasActions = (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].actions.length > 0;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: true
};
return {
uuid: pointUUID,
position: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].position,
rotation: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].rotation,
actions: hasActions
? (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].actions.map(action => ({
...action,
uuid: THREE.MathUtils.generateUUID()
}))
: [defaultAction],
triggers: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({
...trigger,
uuid: THREE.MathUtils.generateUUID()
})),
connections: {
source: { modelUUID: obj.uuid, pointUUID },
targets: []
}
};
};
const backendEventData = {
type: 'Conveyor',
points: [
createConveyorPoint(0), // point1
createConveyorPoint(1), // middlePoint
createConveyorPoint(2) // point2
],
speed: (eventData as SimulationTypes.ConveyorEventsSchema)?.speed
};
//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,
// backendEventData
// );
//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, speed: backendEventData.speed },
socketId: socket.id,
};
const newEventData: any = { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed };
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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as SimulationTypes.ConveyorEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'Vehicle' && eventData) {
const createVehiclePoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as SimulationTypes.VehicleEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
type: 'Inherit',
start: {},
hitCount: 0,
end: {},
buffer: 0
};
return {
uuid: pointUUID,
position: vehiclePoint?.position,
// rotation: vehiclePoint?.rotation,
actions: hasActions
? {
...vehiclePoint.actions,
uuid: THREE.MathUtils.generateUUID()
}
: defaultAction,
connections: {
source: { modelUUID: obj.uuid, pointUUID },
targets: []
},
speed: vehiclePoint?.speed || 1
};
};
const backendEventData = {
type: 'Vehicle',
points: createVehiclePoint(),
};
// API
// 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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as SimulationTypes.VehicleEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'StaticMachine' && eventData) {
const createStaticMachinePoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points;
const hasActions = staticMachinePoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
buffer: 0,
material: 'Inherit',
};
return {
uuid: pointUUID,
position: staticMachinePoint?.position,
rotation: staticMachinePoint?.rotation,
actions: hasActions
? {
...staticMachinePoint.actions,
uuid: THREE.MathUtils.generateUUID()
}
: defaultAction,
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
connections: {
source: { modelUUID: obj.uuid, pointUUID },
targets: []
}
};
};
const backendEventData = {
type: 'StaticMachine',
points: createStaticMachinePoint()
};
// API
// 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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as SimulationTypes.StaticMachineEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'ArmBot' && eventData) {
const createArmBotPoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points;
const hasActions = armBotPoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
buffer: 0,
material: 'Inherit',
};
return {
uuid: pointUUID,
position: armBotPoint?.position,
rotation: armBotPoint?.rotation,
actions: hasActions
? {
...armBotPoint.actions,
uuid: THREE.MathUtils.generateUUID(),
processes: []
}
: defaultAction,
triggers: {
uuid: THREE.MathUtils.generateUUID(),
name: armBotPoint.triggers.name,
type: armBotPoint.triggers.type,
},
connections: {
source: { modelUUID: obj.uuid, pointUUID },
targets: []
}
};
};
const backendEventData = {
type: 'ArmBot',
points: createArmBotPoint()
};
// API
// 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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as SimulationTypes.ArmBotEventsSchema
]);
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 {
//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);
}
obj.userData.modeluuid = newFloorItem.modeluuid;
itemsGroupRef.current.add(obj);
}
});
toast.success("Object added!");
clearSelection();
};
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setMovedObjects([]);
setpastedObjects([]);
setDuplicatedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
}
return null; // No visible output, but the component handles copy-paste functionality
};
export default CopyPasteControls;

View File

@@ -1,560 +0,0 @@
import * as THREE from "three";
import { useEffect, useMemo } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, useToggleView } from "../../../../store/store";
import { toast } from "react-toastify";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulationTypes";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedObjects, setpastedObjects, selectionGroup, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { simulationStates, setSimulationStates } = useSimulationStates();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
useEffect(() => {
if (!camera || !scene || toggleView) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && duplicatedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
event.preventDefault();
addDuplicatedAssets();
}
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (keyCombination === "Ctrl+D" && selectedAssets.length > 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
duplicateSelection();
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
}, [camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects]);
useFrame(() => {
if (duplicatedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current?.getWorldPosition(position)
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
} else {
const box = new THREE.Box3();
duplicatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone()));
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z));
}
}
}
});
const duplicateSelection = () => {
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
const newClones = selectedAssets.map((asset: any) => {
const clone = asset.clone();
clone.position.copy(asset.position);
return clone;
});
selectionGroup.current.add(...newClones);
setDuplicatedObjects(newClones);
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
const position = new THREE.Vector3();
boundingBoxRef.current?.getWorldPosition(position)
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
}
}
};
const addDuplicatedAssets = () => {
if (duplicatedObjects.length === 0) return;
duplicatedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
obj.getWorldPosition(worldPosition);
obj.position.copy(worldPosition);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modeluuid: obj.uuid,
modelname: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
isLocked: false,
isVisible: true
};
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
let eventData: SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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) {
if (eventData.type === 'Conveyor' && eventData) {
const createConveyorPoint = (index: number) => {
const pointUUID = THREE.MathUtils.generateUUID();
const hasActions = (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].actions.length > 0;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
type: 'Inherit',
material: 'Inherit',
delay: 'Inherit',
spawnInterval: 'Inherit',
isUsed: true
};
return {
uuid: pointUUID,
position: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].position,
rotation: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].rotation,
actions: hasActions
? (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].actions.map(action => ({
...action,
uuid: THREE.MathUtils.generateUUID()
}))
: [defaultAction],
triggers: (eventData as SimulationTypes.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({
...trigger,
uuid: THREE.MathUtils.generateUUID()
})),
connections: {
source: { modelUUID: newFloorItem.modeluuid, pointUUID },
targets: []
}
};
};
const backendEventData = {
type: 'Conveyor',
points: [
createConveyorPoint(0),
createConveyorPoint(1),
createConveyorPoint(2)
],
speed: (eventData as SimulationTypes.ConveyorEventsSchema)?.speed
};
//REST
// 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, speed: backendEventData.speed }
// );
//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, speed: backendEventData.speed },
socketId: socket.id,
};
const newEventData: any = { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed };
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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as SimulationTypes.ConveyorEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'Vehicle' && eventData) {
const createVehiclePoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as SimulationTypes.VehicleEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
type: 'Inherit',
start: {},
hitCount: 0,
end: {},
buffer: 0
};
return {
uuid: pointUUID,
position: vehiclePoint?.position,
rotation: vehiclePoint?.rotation,
actions: hasActions
? {
...vehiclePoint.actions,
uuid: THREE.MathUtils.generateUUID()
}
: defaultAction,
connections: {
source: { modelUUID: obj.uuid, pointUUID },
targets: []
},
speed: vehiclePoint?.speed || 2
};
};
const backendEventData = {
type: 'Vehicle',
points: createVehiclePoint()
};
// API
// 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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as SimulationTypes.VehicleEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'StaticMachine' && eventData) {
const createStaticMachinePoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const staticMachinePoint = (eventData as SimulationTypes.StaticMachineEventsSchema)?.points;
const hasActions = staticMachinePoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
buffer: 0,
material: 'Inherit',
};
return {
uuid: pointUUID,
position: staticMachinePoint?.position,
rotation: staticMachinePoint?.rotation,
actions: hasActions
? {
...staticMachinePoint.actions,
uuid: THREE.MathUtils.generateUUID()
}
: defaultAction,
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
connections: {
source: { modelUUID: obj.uuid, pointUUID },
targets: []
}
};
};
const backendEventData = {
type: 'StaticMachine',
points: createStaticMachinePoint()
};
// API
// 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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as SimulationTypes.StaticMachineEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'ArmBot' && eventData) {
const createArmBotPoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const armBotPoint = (eventData as SimulationTypes.ArmBotEventsSchema)?.points;
const hasActions = armBotPoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
buffer: 0,
material: 'Inherit',
};
return {
uuid: pointUUID,
position: armBotPoint?.position,
rotation: armBotPoint?.rotation,
actions: hasActions
? {
...armBotPoint.actions,
uuid: THREE.MathUtils.generateUUID(),
processes: []
}
: defaultAction,
triggers: {
uuid: THREE.MathUtils.generateUUID(),
name: armBotPoint.triggers.name,
type: armBotPoint.triggers.type,
},
connections: {
source: { modelUUID: obj.uuid, pointUUID },
targets: []
}
};
};
const backendEventData = {
type: 'ArmBot',
points: createArmBotPoint()
};
// API
// 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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as SimulationTypes.ArmBotEventsSchema
]);
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 {
//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);
}
obj.userData.modeluuid = newFloorItem.modeluuid;
itemsGroupRef.current.add(obj);
}
});
toast.success("Object duplicated!");
clearSelection();
}
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setMovedObjects([]);
setpastedObjects([]);
setDuplicatedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
}
return null; // This component does not render any UI
};
export default DuplicationControls;

View File

@@ -1,463 +0,0 @@
import * as THREE from "three";
import { useEffect, useMemo, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, useToggleView } from "../../../../store/store";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { toast } from "react-toastify";
import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulationTypes";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
useEffect(() => {
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && movedObjects.length > 0 && event.button === 0) {
event.preventDefault();
placeMovedAssets();
}
if (!isMoving && movedObjects.length > 0 && event.button === 2) {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) return;
if (keyCombination === "G") {
if (selectedAssets.length > 0) {
moveAssets();
itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
}
}
if (keyCombination === "ESCAPE") {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects]);
const gridSize = 0.25;
const moveSpeed = 0.25;
const isGridSnap = false;
useFrame(() => {
if (movedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
let targetX = point.x;
let targetZ = point.z;
if (isGridSnap) {
targetX = Math.round(point.x / gridSize) * gridSize;
targetZ = Math.round(point.z / gridSize) * gridSize;
}
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current.getWorldPosition(position);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (position.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (position.z - selectionGroup.current.position.z)
),
moveSpeed
);
} else {
const box = new THREE.Box3();
movedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (center.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (center.z - selectionGroup.current.position.z)
),
moveSpeed
);
}
}
}
});
const moveAssets = () => {
const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
setFloorItems(updatedItems);
setMovedObjects(selectedAssets);
selectedAssets.forEach((asset: any) => { selectionGroup.current.attach(asset); });
}
const placeMovedAssets = () => {
if (movedObjects.length === 0) return;
movedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
obj.getWorldPosition(worldPosition);
selectionGroup.current.remove(obj);
obj.position.copy(worldPosition);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modeluuid: obj.uuid,
modelname: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
isLocked: false,
isVisible: true
};
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
let eventData: SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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) {
if (eventData.type === 'Conveyor' && eventData) {
const backendEventData = {
type: 'Conveyor',
points: eventData.points,
speed: (eventData as SimulationTypes.ConveyorEventsSchema)?.speed
};
//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,
// { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed }
// );
//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, speed: backendEventData.speed },
socketId: socket.id,
};
const newEventData: any = { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed };
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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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 === 'Vehicle' && eventData) {
const backendEventData = {
type: 'Vehicle',
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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }
: event
);
return updatedEvents;
});
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);
}
itemsGroupRef.current.add(obj);
}
});
toast.success("Object moved!");
itemsData.current = [];
clearSelection();
}
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
}
return null; // No need to return anything, as this component is used for its side effects
}
export default MoveControls

View File

@@ -1,464 +0,0 @@
import * as THREE from "three";
import { useEffect, useMemo, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, useToggleView } from "../../../../store/store";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { toast } from "react-toastify";
import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulationTypes";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
useEffect(() => {
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && rotatedObjects.length > 0 && event.button === 0) {
event.preventDefault();
placeRotatedAssets();
}
if (!isMoving && rotatedObjects.length > 0 && event.button === 2) {
event.preventDefault();
clearSelection();
rotatedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setRotatedObjects([]);
itemsData.current = [];
}
};
const onKeyDown = (event: KeyboardEvent) => {
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return;
if (event.key.toLowerCase() === "r") {
if (selectedAssets.length > 0) {
rotateAssets();
itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
}
}
if (event.key.toLowerCase() === "escape") {
event.preventDefault();
clearSelection();
rotatedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setRotatedObjects([]);
itemsData.current = [];
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]);
useFrame(() => {
if (rotatedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point && prevPointerPosition.current) {
const box = new THREE.Box3();
rotatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
const center = new THREE.Vector3();
box.getCenter(center);
const delta = new THREE.Vector3().subVectors(point, center);
const prevPointerPosition3D = new THREE.Vector3(prevPointerPosition.current.x, 0, prevPointerPosition.current.y);
const angle = Math.atan2(delta.z, delta.x) - Math.atan2(prevPointerPosition3D.z - center.z, prevPointerPosition3D.x - center.x);
selectionGroup.current.rotation.y += -angle;
selectionGroup.current.position.sub(center);
selectionGroup.current.position.applyAxisAngle(new THREE.Vector3(0, 1, 0), -angle);
selectionGroup.current.position.add(center);
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
}
}
});
const rotateAssets = () => {
const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
setFloorItems(updatedItems);
const box = new THREE.Box3();
selectedAssets.forEach((asset: any) => box.expandByObject(asset));
const center = new THREE.Vector3();
box.getCenter(center);
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
}
selectedAssets.forEach((asset: any) => {
selectionGroup.current.attach(asset);
});
setRotatedObjects(selectedAssets);
};
const placeRotatedAssets = () => {
if (rotatedObjects.length === 0) return;
rotatedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
const worldQuaternion = new THREE.Quaternion();
obj.getWorldPosition(worldPosition);
obj.getWorldQuaternion(worldQuaternion);
selectionGroup.current.remove(obj);
obj.position.copy(worldPosition);
obj.quaternion.copy(worldQuaternion);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modeluuid: obj.uuid,
modelname: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
isLocked: false,
isVisible: true
};
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
let eventData: SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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) {
if (eventData.type === 'Conveyor' && eventData) {
const backendEventData = {
type: 'Conveyor',
points: eventData.points,
speed: (eventData as SimulationTypes.ConveyorEventsSchema)?.speed
};
// 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, speed: backendEventData.speed }
// );
//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, speed: backendEventData.speed },
socketId: socket.id,
};
const newEventData: any = { type: backendEventData.type, points: backendEventData.points, speed: backendEventData.speed };
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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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 === 'Vehicle' && eventData) {
const backendEventData = {
type: 'Vehicle',
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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.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: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).map(event =>
event.modeluuid === newFloorItem.modeluuid
? { ...event, ...newEventData }
: event
);
return updatedEvents;
});
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);
}
itemsGroupRef.current.add(obj);
}
});
toast.success("Object rotated!");
itemsData.current = [];
clearSelection();
}
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
}
return null; // No need to return anything, as this component is used for its side effects
}
export default RotateControls

View File

@@ -1,64 +1,64 @@
import { Line } from "@react-three/drei"; import { Line } from "@react-three/drei";
import { useMemo } from "react"; import { useMemo } from "react";
import * as THREE from "three"; import * as THREE from "three";
import { useSelectedAssets } from "../../../../store/store"; import { useSelectedAssets } from "../../../../store/store";
const BoundingBox = ({ boundingBoxRef }: any) => { const BoundingBox = ({ boundingBoxRef }: any) => {
const { selectedAssets } = useSelectedAssets(); const { selectedAssets } = useSelectedAssets();
const { points, boxProps } = useMemo(() => { const { points, boxProps } = useMemo(() => {
if (selectedAssets.length === 0) return { points: [], boxProps: {} }; if (selectedAssets.length === 0) return { points: [], boxProps: {} };
const box = new THREE.Box3(); const box = new THREE.Box3();
selectedAssets.forEach((obj: any) => box.expandByObject(obj.clone())); selectedAssets.forEach((obj: any) => box.expandByObject(obj.clone()));
const size = new THREE.Vector3(); const size = new THREE.Vector3();
box.getSize(size); box.getSize(size);
const center = new THREE.Vector3(); const center = new THREE.Vector3();
box.getCenter(center); box.getCenter(center);
const halfSize = size.clone().multiplyScalar(0.5); const halfSize = size.clone().multiplyScalar(0.5);
const min = center.clone().sub(halfSize); const min = center.clone().sub(halfSize);
const max = center.clone().add(halfSize); const max = center.clone().add(halfSize);
const points: any = [ const points: any = [
[min.x, min.y, min.z], [max.x, min.y, min.z], [min.x, min.y, min.z], [max.x, min.y, min.z],
[max.x, min.y, min.z], [max.x, max.y, min.z], [max.x, min.y, min.z], [max.x, max.y, min.z],
[max.x, max.y, min.z], [min.x, max.y, min.z], [max.x, max.y, min.z], [min.x, max.y, min.z],
[min.x, max.y, min.z], [min.x, min.y, min.z], [min.x, max.y, min.z], [min.x, min.y, min.z],
[min.x, min.y, max.z], [max.x, min.y, max.z], [min.x, min.y, max.z], [max.x, min.y, max.z],
[max.x, min.y, max.z], [max.x, max.y, max.z], [max.x, min.y, max.z], [max.x, max.y, max.z],
[max.x, max.y, max.z], [min.x, max.y, max.z], [max.x, max.y, max.z], [min.x, max.y, max.z],
[min.x, max.y, max.z], [min.x, min.y, max.z], [min.x, max.y, max.z], [min.x, min.y, max.z],
[min.x, min.y, min.z], [min.x, min.y, max.z], [min.x, min.y, min.z], [min.x, min.y, max.z],
[max.x, min.y, min.z], [max.x, min.y, max.z], [max.x, min.y, min.z], [max.x, min.y, max.z],
[max.x, max.y, min.z], [max.x, max.y, max.z], [max.x, max.y, min.z], [max.x, max.y, max.z],
[min.x, max.y, min.z], [min.x, max.y, max.z], [min.x, max.y, min.z], [min.x, max.y, max.z],
]; ];
return { return {
points, points,
boxProps: { position: center.toArray(), args: size.toArray() } boxProps: { position: center.toArray(), args: size.toArray() }
}; };
}, [selectedAssets]); }, [selectedAssets]);
const savedTheme: string | null = localStorage.getItem("theme") || "light"; const savedTheme: string | null = localStorage.getItem("theme") || "light";
return ( return (
<> <>
{points.length > 0 && ( {points.length > 0 && (
<> <>
<Line points={points} color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} segments /> <Line points={points} color={savedTheme === "dark" ? "#c4abf1" : "#6f42c1"} lineWidth={2.7} segments />
<mesh ref={boundingBoxRef} visible={false} position={boxProps.position}> <mesh ref={boundingBoxRef} visible={false} position={boxProps.position}>
<boxGeometry args={boxProps.args} /> <boxGeometry args={boxProps.args} />
<meshBasicMaterial /> <meshBasicMaterial />
</mesh> </mesh>
</> </>
)} )}
</> </>
); );
}; };
export default BoundingBox; export default BoundingBox;

View File

@@ -0,0 +1,211 @@
import * as THREE from "three";
import { useEffect, useMemo } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store";
import { toast } from "react-toastify";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import * as Types from "../../../../types/world/worldTypes";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, selectionGroup, setDuplicatedObjects, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore()
useEffect(() => {
if (!camera || !scene || toggleView) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && pastedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
event.preventDefault();
addPastedObjects();
}
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (keyCombination === "Ctrl+C" && movedObjects.length === 0 && rotatedObjects.length === 0) {
copySelection();
}
if (keyCombination === "Ctrl+V" && copiedObjects.length > 0 && pastedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
pasteCopiedObjects();
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, movedObjects, socket, floorItems, rotatedObjects]);
useFrame(() => {
if (pastedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current?.getWorldPosition(position)
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
} else {
const box = new THREE.Box3();
pastedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone()));
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z));
}
}
}
});
const copySelection = () => {
if (selectedAssets.length > 0) {
const newClones = selectedAssets.map((asset: any) => {
const clone = asset.clone();
clone.position.copy(asset.position);
return clone;
});
setCopiedObjects(newClones);
toast.info("Objects copied!");
}
};
const pasteCopiedObjects = () => {
if (copiedObjects.length > 0 && pastedObjects.length === 0) {
const newClones = copiedObjects.map((obj: THREE.Object3D) => {
const clone = obj.clone();
clone.position.copy(obj.position);
return clone;
});
selectionGroup.current.add(...newClones);
setpastedObjects([...newClones]);
setSelectedAssets([...newClones]);
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current?.getWorldPosition(position)
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
} else {
const box = new THREE.Box3();
newClones.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone()));
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z));
}
}
}
};
const addPastedObjects = () => {
if (pastedObjects.length === 0) return;
pastedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
obj.getWorldPosition(worldPosition);
obj.position.copy(worldPosition);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modeluuid: obj.uuid,
modelname: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
isLocked: false,
isVisible: true
};
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
//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);
obj.userData.modeluuid = newFloorItem.modeluuid;
itemsGroupRef.current.add(obj);
}
});
toast.success("Object added!");
clearSelection();
};
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setMovedObjects([]);
setpastedObjects([]);
setDuplicatedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
}
return null; // No visible output, but the component handles copy-paste functionality
};
export default CopyPasteControls;

View File

@@ -0,0 +1,189 @@
import * as THREE from "three";
import { useEffect, useMemo } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store";
import { toast } from "react-toastify";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import * as Types from "../../../../types/world/worldTypes";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedObjects, setpastedObjects, selectionGroup, movedObjects, setMovedObjects, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) => {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
useEffect(() => {
if (!camera || !scene || toggleView) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && duplicatedObjects.length > 0 && event.button === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
event.preventDefault();
addDuplicatedAssets();
}
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (keyCombination === "Ctrl+D" && selectedAssets.length > 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
duplicateSelection();
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
}, [camera, controls, scene, toggleView, selectedAssets, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects]);
useFrame(() => {
if (duplicatedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current?.getWorldPosition(position)
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
} else {
const box = new THREE.Box3();
duplicatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj.clone()));
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.set(point.x - (center.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (center.z - selectionGroup.current.position.z));
}
}
}
});
const duplicateSelection = () => {
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
const newClones = selectedAssets.map((asset: any) => {
const clone = asset.clone();
clone.position.copy(asset.position);
return clone;
});
selectionGroup.current.add(...newClones);
setDuplicatedObjects(newClones);
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
const position = new THREE.Vector3();
boundingBoxRef.current?.getWorldPosition(position)
selectionGroup.current.position.set(point.x - (position.x - selectionGroup.current.position.x), selectionGroup.current.position.y, point.z - (position.z - selectionGroup.current.position.z));
}
}
};
const addDuplicatedAssets = () => {
if (duplicatedObjects.length === 0) return;
duplicatedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
obj.getWorldPosition(worldPosition);
obj.position.copy(worldPosition);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modeluuid: obj.uuid,
modelname: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
isLocked: false,
isVisible: true
};
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
//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);
obj.userData.modeluuid = newFloorItem.modeluuid;
itemsGroupRef.current.add(obj);
}
});
toast.success("Object duplicated!");
clearSelection();
}
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setMovedObjects([]);
setpastedObjects([]);
setDuplicatedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
}
return null; // This component does not render any UI
};
export default DuplicationControls;

View File

@@ -0,0 +1,240 @@
import * as THREE from "three";
import { useEffect, useMemo, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { toast } from "react-toastify";
import * as Types from "../../../../types/world/worldTypes";
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
useEffect(() => {
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && movedObjects.length > 0 && event.button === 0) {
event.preventDefault();
placeMovedAssets();
}
if (!isMoving && movedObjects.length > 0 && event.button === 2) {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) return;
if (keyCombination === "G") {
if (selectedAssets.length > 0) {
moveAssets();
itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
}
}
if (keyCombination === "ESCAPE") {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects]);
const gridSize = 0.25;
const moveSpeed = 0.25;
const isGridSnap = false;
useFrame(() => {
if (movedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
let targetX = point.x;
let targetZ = point.z;
if (isGridSnap) {
targetX = Math.round(point.x / gridSize) * gridSize;
targetZ = Math.round(point.z / gridSize) * gridSize;
}
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current.getWorldPosition(position);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (position.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (position.z - selectionGroup.current.position.z)
),
moveSpeed
);
} else {
const box = new THREE.Box3();
movedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (center.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (center.z - selectionGroup.current.position.z)
),
moveSpeed
);
}
}
}
});
const moveAssets = () => {
const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
setFloorItems(updatedItems);
setMovedObjects(selectedAssets);
selectedAssets.forEach((asset: any) => { selectionGroup.current.attach(asset); });
}
const placeMovedAssets = () => {
if (movedObjects.length === 0) return;
movedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
obj.getWorldPosition(worldPosition);
selectionGroup.current.remove(obj);
obj.position.copy(worldPosition);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modeluuid: obj.uuid,
modelname: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
isLocked: false,
isVisible: true
};
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
//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);
itemsGroupRef.current.add(obj);
}
});
toast.success("Object moved!");
itemsData.current = [];
clearSelection();
}
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
}
return null; // No need to return anything, as this component is used for its side effects
}
export default MoveControls

View File

@@ -0,0 +1,241 @@
import * as THREE from "three";
import { useEffect, useMemo, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView } from "../../../../store/store";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { toast } from "react-toastify";
import * as Types from "../../../../types/world/worldTypes";
import { setFloorItemApi } from "../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi";
function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, boundingBoxRef }: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
useEffect(() => {
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && rotatedObjects.length > 0 && event.button === 0) {
event.preventDefault();
placeRotatedAssets();
}
if (!isMoving && rotatedObjects.length > 0 && event.button === 2) {
event.preventDefault();
clearSelection();
rotatedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setRotatedObjects([]);
itemsData.current = [];
}
};
const onKeyDown = (event: KeyboardEvent) => {
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || movedObjects.length > 0) return;
if (event.key.toLowerCase() === "r") {
if (selectedAssets.length > 0) {
rotateAssets();
itemsData.current = floorItems.filter((item: { modeluuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
}
}
if (event.key.toLowerCase() === "escape") {
event.preventDefault();
clearSelection();
rotatedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setRotatedObjects([]);
itemsData.current = [];
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
};
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, rotatedObjects, movedObjects]);
useFrame(() => {
if (rotatedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point && prevPointerPosition.current) {
const box = new THREE.Box3();
rotatedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
const center = new THREE.Vector3();
box.getCenter(center);
const delta = new THREE.Vector3().subVectors(point, center);
const prevPointerPosition3D = new THREE.Vector3(prevPointerPosition.current.x, 0, prevPointerPosition.current.y);
const angle = Math.atan2(delta.z, delta.x) - Math.atan2(prevPointerPosition3D.z - center.z, prevPointerPosition3D.x - center.x);
selectionGroup.current.rotation.y += -angle;
selectionGroup.current.position.sub(center);
selectionGroup.current.position.applyAxisAngle(new THREE.Vector3(0, 1, 0), -angle);
selectionGroup.current.position.add(center);
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
}
}
});
const rotateAssets = () => {
const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modeluuid));
setFloorItems(updatedItems);
const box = new THREE.Box3();
selectedAssets.forEach((asset: any) => box.expandByObject(asset));
const center = new THREE.Vector3();
box.getCenter(center);
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
prevPointerPosition.current = new THREE.Vector2(point.x, point.z);
}
selectedAssets.forEach((asset: any) => {
selectionGroup.current.attach(asset);
});
setRotatedObjects(selectedAssets);
};
const placeRotatedAssets = () => {
if (rotatedObjects.length === 0) return;
rotatedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
const worldQuaternion = new THREE.Quaternion();
obj.getWorldPosition(worldPosition);
obj.getWorldQuaternion(worldQuaternion);
selectionGroup.current.remove(obj);
obj.position.copy(worldPosition);
obj.quaternion.copy(worldQuaternion);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modeluuid: obj.uuid,
modelname: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
isLocked: false,
isVisible: true
};
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "default";
//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);
itemsGroupRef.current.add(obj);
}
});
toast.success("Object rotated!");
itemsData.current = [];
clearSelection();
}
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
}
return null; // No need to return anything, as this component is used for its side effects
}
export default RotateControls

View File

@@ -1,479 +1,272 @@
import * as THREE from "three"; import * as THREE from "three";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox"; import { SelectionBox } from "three/examples/jsm/interactive/SelectionBox";
import { SelectionHelper } from "./selectionHelper"; import { SelectionHelper } from "./selectionHelper";
import { useFrame, useThree } from "@react-three/fiber"; import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSimulationStates, useSocketStore, useToggleView, } from "../../../../store/store"; import { useFloorItems, useSelectedAssets, useSocketStore, useToggleView, } from "../../../../store/store";
import BoundingBox from "./boundingBoxHelper"; import BoundingBox from "./boundingBoxHelper";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
// import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi'; // import { deleteFloorItem } from '../../../../services/factoryBuilder/assest/floorAsset/deleteFloorItemApi';
import * as Types from "../../../../types/world/worldTypes"; import * as Types from "../../../../types/world/worldTypes";
import * as SimulationTypes from "../../../../types/simulationTypes";
import DuplicationControls from "./duplicationControls";
import DuplicationControls from "./duplicationControls"; import CopyPasteControls from "./copyPasteControls";
import CopyPasteControls from "./copyPasteControls"; import MoveControls from "./moveControls";
import MoveControls from "./moveControls"; import RotateControls from "./rotateControls";
import RotateControls from "./rotateControls"; import useModuleStore from "../../../../store/useModuleStore";
import useModuleStore from "../../../../store/useModuleStore";
const SelectionControls: React.FC = () => {
const SelectionControls: React.FC = () => { const { camera, controls, gl, scene, pointer } = useThree();
const { camera, controls, gl, scene, pointer } = useThree(); const itemsGroupRef = useRef<THREE.Group | undefined>(undefined);
const itemsGroupRef = useRef<THREE.Group | undefined>(undefined); const selectionGroup = useRef() as Types.RefGroup;
const selectionGroup = useRef() as Types.RefGroup; const { toggleView } = useToggleView();
const { toggleView } = useToggleView(); const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { simulationStates, setSimulationStates } = useSimulationStates(); const [movedObjects, setMovedObjects] = useState<THREE.Object3D[]>([]);
const { selectedAssets, setSelectedAssets } = useSelectedAssets(); const [rotatedObjects, setRotatedObjects] = useState<THREE.Object3D[]>([]);
const [movedObjects, setMovedObjects] = useState<THREE.Object3D[]>([]); const [copiedObjects, setCopiedObjects] = useState<THREE.Object3D[]>([]);
const [rotatedObjects, setRotatedObjects] = useState<THREE.Object3D[]>([]); const [pastedObjects, setpastedObjects] = useState<THREE.Object3D[]>([]);
const [copiedObjects, setCopiedObjects] = useState<THREE.Object3D[]>([]); const [duplicatedObjects, setDuplicatedObjects] = useState<THREE.Object3D[]>([]);
const [pastedObjects, setpastedObjects] = useState<THREE.Object3D[]>([]); const boundingBoxRef = useRef<THREE.Mesh>();
const [duplicatedObjects, setDuplicatedObjects] = useState<THREE.Object3D[]>([]); const { floorItems, setFloorItems } = useFloorItems();
const boundingBoxRef = useRef<THREE.Mesh>(); const { activeModule } = useModuleStore();
const { floorItems, setFloorItems } = useFloorItems(); const { socket } = useSocketStore();
const { activeModule } = useModuleStore(); const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
const { socket } = useSocketStore();
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]); useEffect(() => {
if (!camera || !scene || toggleView) return;
useEffect(() => {
if (!camera || !scene || toggleView) return; const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0; const itemsGroup: any = scene.getObjectByName("itemsGroup");
itemsGroupRef.current = itemsGroup;
const itemsGroup: any = scene.getObjectByName("itemsGroup");
itemsGroupRef.current = itemsGroup; let isSelecting = false;
let isRightClick = false;
let isSelecting = false; let rightClickMoved = false;
let isRightClick = false; let isCtrlSelecting = false;
let rightClickMoved = false;
let isCtrlSelecting = false; const helper = new SelectionHelper(gl);
const helper = new SelectionHelper(gl); if (!itemsGroup) {
toast.warn("itemsGroup not found in the scene.");
if (!itemsGroup) { return;
toast.warn("itemsGroup not found in the scene."); }
return;
} const onPointerDown = (event: PointerEvent) => {
if (event.button === 2) {
const onPointerDown = (event: PointerEvent) => { isRightClick = true;
if (event.button === 2) { rightClickMoved = false;
isRightClick = true; } else if (event.button === 0) {
rightClickMoved = false; isSelecting = false;
} else if (event.button === 0) { isCtrlSelecting = event.ctrlKey;
isSelecting = false; if (event.ctrlKey && duplicatedObjects.length === 0) {
isCtrlSelecting = event.ctrlKey; if (controls) (controls as any).enabled = false;
if (event.ctrlKey && duplicatedObjects.length === 0) { selectionBox.startPoint.set(pointer.x, pointer.y, 0);
if (controls) (controls as any).enabled = false; }
selectionBox.startPoint.set(pointer.x, pointer.y, 0); }
} };
}
}; const onPointerMove = (event: PointerEvent) => {
if (isRightClick) {
const onPointerMove = (event: PointerEvent) => { rightClickMoved = true;
if (isRightClick) { }
rightClickMoved = true; isSelecting = true;
} if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) {
isSelecting = true; selectionBox.endPoint.set(pointer.x, pointer.y, 0);
if (helper.isDown && event.ctrlKey && duplicatedObjects.length === 0 && isCtrlSelecting) { }
selectionBox.endPoint.set(pointer.x, pointer.y, 0); };
}
}; const onPointerUp = (event: PointerEvent) => {
if (event.button === 2) {
const onPointerUp = (event: PointerEvent) => { isRightClick = false;
if (event.button === 2) { if (!rightClickMoved) {
isRightClick = false; clearSelection();
if (!rightClickMoved) { }
clearSelection(); return;
} }
return;
} if (isSelecting && isCtrlSelecting) {
isCtrlSelecting = false;
if (isSelecting && isCtrlSelecting) { isSelecting = false;
isCtrlSelecting = false; if (event.ctrlKey && duplicatedObjects.length === 0) {
isSelecting = false; selectAssets();
if (event.ctrlKey && duplicatedObjects.length === 0) { }
selectAssets(); } else if (!isSelecting && selectedAssets.length > 0 && ((pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) || event.button !== 0)) {
} clearSelection();
} else if (!isSelecting && selectedAssets.length > 0 && ((pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) || event.button !== 0)) { helper.enabled = true;
clearSelection(); isCtrlSelecting = false;
helper.enabled = true; }
isCtrlSelecting = false; };
}
}; const onKeyDown = (event: KeyboardEvent) => {
if (movedObjects.length > 0 || rotatedObjects.length > 0) return;
const onKeyDown = (event: KeyboardEvent) => { if (event.key.toLowerCase() === "escape") {
if (movedObjects.length > 0 || rotatedObjects.length > 0) return; event.preventDefault();
if (event.key.toLowerCase() === "escape") { clearSelection();
event.preventDefault(); }
clearSelection(); if (event.key.toLowerCase() === "delete") {
} event.preventDefault();
if (event.key.toLowerCase() === "delete") { deleteSelection();
event.preventDefault(); }
deleteSelection(); };
}
}; const onContextMenu = (event: MouseEvent) => {
event.preventDefault();
const onContextMenu = (event: MouseEvent) => { if (!rightClickMoved) {
event.preventDefault(); clearSelection();
if (!rightClickMoved) { }
clearSelection(); };
}
}; if (!toggleView && activeModule === "builder") {
helper.enabled = true;
if (!toggleView && activeModule === "builder") { if (duplicatedObjects.length === 0 && pastedObjects.length === 0) {
helper.enabled = true; canvasElement.addEventListener("pointerdown", onPointerDown);
if (duplicatedObjects.length === 0 && pastedObjects.length === 0) { canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerdown", onPointerDown); canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("pointermove", onPointerMove); } else {
canvasElement.addEventListener("pointerup", onPointerUp); helper.enabled = false;
} else { helper.dispose();
helper.enabled = false; }
helper.dispose(); canvasElement.addEventListener("contextmenu", onContextMenu);
} canvasElement.addEventListener("keydown", onKeyDown);
canvasElement.addEventListener("contextmenu", onContextMenu); } else {
canvasElement.addEventListener("keydown", onKeyDown); helper.enabled = false;
} else { helper.dispose();
helper.enabled = false; }
helper.dispose();
} return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
return () => { canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerdown", onPointerDown); canvasElement.removeEventListener("contextmenu", onContextMenu);
canvasElement.removeEventListener("pointermove", onPointerMove); canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("contextmenu", onContextMenu); canvasElement.removeEventListener("keydown", onKeyDown);
canvasElement.removeEventListener("pointerup", onPointerUp); helper.enabled = false;
canvasElement.removeEventListener("keydown", onKeyDown); helper.dispose();
helper.enabled = false; };
helper.dispose(); }, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule,]);
};
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, floorItems, rotatedObjects, activeModule,]); useEffect(() => {
if (activeModule !== "builder") {
useEffect(() => { clearSelection();
if (activeModule !== "builder") { }
clearSelection(); }, [activeModule]);
}
}, [activeModule]); useFrame(() => {
if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) {
useFrame(() => { selectionGroup.current.position.set(0, 0, 0);
if (pastedObjects.length === 0 && duplicatedObjects.length === 0 && movedObjects.length === 0 && rotatedObjects.length === 0) { }
selectionGroup.current.position.set(0, 0, 0); });
}
}); const selectAssets = () => {
selectionBox.endPoint.set(pointer.x, pointer.y, 0);
const selectAssets = () => { if (controls) (controls as any).enabled = true;
selectionBox.endPoint.set(pointer.x, pointer.y, 0);
if (controls) (controls as any).enabled = true; let selectedObjects = selectionBox.select();
let Objects = new Set<THREE.Object3D>();
let selectedObjects = selectionBox.select();
let Objects = new Set<THREE.Object3D>(); selectedObjects.map((object) => {
let currentObject: THREE.Object3D | null = object;
selectedObjects.map((object) => { while (currentObject) {
let currentObject: THREE.Object3D | null = object; if (currentObject.userData.modelId) {
while (currentObject) { Objects.add(currentObject);
if (currentObject.userData.modelId) { break;
Objects.add(currentObject); }
break; currentObject = currentObject.parent || null;
} }
currentObject = currentObject.parent || null; });
}
}); if (Objects.size === 0) {
clearSelection();
if (Objects.size === 0) { return;
clearSelection(); }
return;
} const updatedSelections = new Set(selectedAssets);
Objects.forEach((obj) => { updatedSelections.has(obj) ? updatedSelections.delete(obj) : updatedSelections.add(obj); });
const updatedSelections = new Set(selectedAssets);
Objects.forEach((obj) => { updatedSelections.has(obj) ? updatedSelections.delete(obj) : updatedSelections.add(obj); }); const selected = Array.from(updatedSelections);
const selected = Array.from(updatedSelections); setSelectedAssets(selected);
};
setSelectedAssets(selected);
}; const clearSelection = () => {
selectionGroup.current.children = [];
const clearSelection = () => { selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.children = []; selectionGroup.current.rotation.set(0, 0, 0);
selectionGroup.current.position.set(0, 0, 0); setpastedObjects([]);
selectionGroup.current.rotation.set(0, 0, 0); setDuplicatedObjects([]);
setpastedObjects([]); setSelectedAssets([]);
setDuplicatedObjects([]); };
setSelectedAssets([]);
}; const deleteSelection = () => {
const updateBackend = async (updatedPaths: (SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => { if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
if (updatedPaths.length === 0) return; const email = localStorage.getItem("email");
const email = localStorage.getItem("email"); const organization = email!.split("@")[1].split(".")[0];
const organization = email ? email.split("@")[1].split(".")[0] : "";
const storedItems = JSON.parse(localStorage.getItem("FloorItems") || "[]");
updatedPaths.forEach(async (updatedPath) => { const selectedUUIDs = selectedAssets.map((mesh: THREE.Object3D) => mesh.uuid);
if (updatedPath.type === "Conveyor") {
// await setEventApi( const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid));
// organization, localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems));
// updatedPath.modeluuid,
// { type: "Conveyor", points: updatedPath.points, speed: updatedPath.speed } selectedAssets.forEach((selectedMesh: THREE.Object3D) => {
// ); //REST
const data = { // const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name);
organization: organization,
modeluuid: updatedPath.modeluuid, //SOCKET
eventData: {
type: "Conveyor", const data = {
points: updatedPath.points, organization: organization,
speed: updatedPath.speed, modeluuid: selectedMesh.uuid,
}, modelname: selectedMesh.userData.name,
}; socketId: socket.id,
};
socket.emit("v2:model-asset:updateEventData", data);
} else if (updatedPath.type === "Vehicle") { socket.emit("v2:model-asset:delete", data);
// await setEventApi(
// organization, selectedMesh.traverse((child: THREE.Object3D) => {
// updatedPath.modeluuid, if (child instanceof THREE.Mesh) {
// { type: "Vehicle", points: updatedPath.points } if (child.geometry) child.geometry.dispose();
// ); if (Array.isArray(child.material)) {
child.material.forEach((material) => {
const data = { if (material.map) material.map.dispose();
organization: organization, material.dispose();
modeluuid: updatedPath.modeluuid, });
eventData: { type: "Vehicle", points: updatedPath.points }, } else if (child.material) {
}; if (child.material.map) child.material.map.dispose();
child.material.dispose();
socket.emit("v2:model-asset:updateEventData", data); }
} else if (updatedPath.type === "StaticMachine") { }
// await setEventApi( });
// organization,
// updatedPath.modeluuid, itemsGroupRef.current?.remove(selectedMesh);
// { type: "StaticMachine", points: updatedPath.points } });
// );
const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid));
const data = { setFloorItems(updatedItems);
organization: organization, }
modeluuid: updatedPath.modeluuid, toast.success("Selected models removed!");
eventData: { type: "StaticMachine", points: updatedPath.points }, clearSelection();
}; };
socket.emit("v2:model-asset:updateEventData", data); return (
} else if (updatedPath.type === "ArmBot") { <>
// await setEventApi( <group name="SelectionGroup">
// organization, <group ref={selectionGroup} name="selectionAssetGroup">
// updatedPath.modeluuid, <BoundingBox boundingBoxRef={boundingBoxRef} />
// { type: "ArmBot", points: updatedPath.points } </group>
// ); </group>
const data = { <MoveControls movedObjects={movedObjects} setMovedObjects={setMovedObjects} itemsGroupRef={itemsGroupRef} copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} selectionGroup={selectionGroup} boundingBoxRef={boundingBoxRef} />
organization: organization,
modeluuid: updatedPath.modeluuid, <RotateControls rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} itemsGroupRef={itemsGroupRef} copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} selectionGroup={selectionGroup} boundingBoxRef={boundingBoxRef} />
eventData: { type: "ArmBot", points: updatedPath.points },
}; <DuplicationControls itemsGroupRef={itemsGroupRef} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
socket.emit("v2:model-asset:updateEventData", data); <CopyPasteControls itemsGroupRef={itemsGroupRef} copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
} </>
}); );
}; };
const removeConnections = (deletedModelUUIDs: string[]) => { export default SelectionControls;
const deletedPointUUIDs = new Set<string>();
simulationStates.forEach(state => {
if (deletedModelUUIDs.includes(state.modeluuid)) {
if (state.type === "Conveyor" && state.points) {
state.points.forEach(point => {
deletedPointUUIDs.add(point.uuid);
});
} else if (state.points && 'uuid' in state.points) {
deletedPointUUIDs.add(state.points.uuid);
}
}
});
const updatedStates = simulationStates.map((state) => {
// Handle Conveyor
if (state.type === "Conveyor") {
const updatedConveyor: SimulationTypes.ConveyorEventsSchema = {
...state,
points: state.points.map((point) => {
return {
...point,
connections: {
...point.connections,
targets: point.connections.targets.filter(
(target) => !deletedModelUUIDs.includes(target.modelUUID)
),
},
};
}),
};
return updatedConveyor;
}
// Handle Vehicle
else if (state.type === "Vehicle") {
const updatedVehicle: SimulationTypes.VehicleEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(
(target) => !deletedModelUUIDs.includes(target.modelUUID)
),
},
},
};
return updatedVehicle;
}
// Handle StaticMachine
else if (state.type === "StaticMachine") {
const updatedStaticMachine: SimulationTypes.StaticMachineEventsSchema =
{
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(
(target) => !deletedModelUUIDs.includes(target.modelUUID)
),
},
},
};
return updatedStaticMachine;
}
// Handle ArmBot
else if (state.type === "ArmBot") {
const updatedArmBot: SimulationTypes.ArmBotEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(
(target: any) => !deletedModelUUIDs.includes(target.modelUUID)
),
},
actions: {
...state.points.actions,
processes: state.points.actions.processes?.filter((process) => {
// Check if trigger is from deleted model
const matchedStates = simulationStates.filter((s) => deletedModelUUIDs.includes(s.modeluuid));
if (matchedStates.length > 0) {
if (matchedStates[0]?.type === "StaticMachine") {
const trigPoints = matchedStates[0]?.points;
if (process.triggerId === trigPoints?.triggers?.uuid) {
return false;
}
} else if (matchedStates[0]?.type === "Conveyor") {
const trigPoints = matchedStates[0]?.points;
if (Array.isArray(trigPoints)) {
const nonEmptyTriggers = trigPoints.filter((point) => point && point.triggers && point.triggers.length > 0);
const allTriggerUUIDs = nonEmptyTriggers.flatMap((point) => point.triggers).map((trigger) => trigger.uuid);
if (allTriggerUUIDs.includes(process.triggerId)) {
return false;
}
}
}
}
// Check if startPoint or endPoint is from deleted model
if (deletedPointUUIDs.has(process.startPoint) || deletedPointUUIDs.has(process.endPoint)) {
return false;
}
return true;
}),
},
},
};
return updatedArmBot;
}
return state;
});
const filteredStates = updatedStates.filter((state) => !deletedModelUUIDs.includes(state.modeluuid));
updateBackend(filteredStates);
setSimulationStates(filteredStates);
};
const deleteSelection = () => {
if (selectedAssets.length > 0 && duplicatedObjects.length === 0) {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
const storedItems = JSON.parse(localStorage.getItem("FloorItems") || "[]");
const selectedUUIDs = selectedAssets.map((mesh: THREE.Object3D) => mesh.uuid);
const updatedStoredItems = storedItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid));
localStorage.setItem("FloorItems", JSON.stringify(updatedStoredItems));
selectedAssets.forEach((selectedMesh: THREE.Object3D) => {
//REST
// const response = await deleteFloorItem(organization, selectedMesh.uuid, selectedMesh.userData.name);
//SOCKET
const data = {
organization: organization,
modeluuid: selectedMesh.uuid,
modelname: selectedMesh.userData.name,
socketId: socket.id,
};
socket.emit("v2:model-asset:delete", data);
selectedMesh.traverse((child: THREE.Object3D) => {
if (child instanceof THREE.Mesh) {
if (child.geometry) child.geometry.dispose();
if (Array.isArray(child.material)) {
child.material.forEach((material) => {
if (material.map) material.map.dispose();
material.dispose();
});
} else if (child.material) {
if (child.material.map) child.material.map.dispose();
child.material.dispose();
}
}
});
setSimulationStates((prevEvents: (| SimulationTypes.ConveyorEventsSchema | SimulationTypes.VehicleEventsSchema | SimulationTypes.StaticMachineEventsSchema | SimulationTypes.ArmBotEventsSchema)[]) => {
const updatedEvents = (prevEvents || []).filter((event) => event.modeluuid !== selectedMesh.uuid);
return updatedEvents;
});
itemsGroupRef.current?.remove(selectedMesh);
});
const allUUIDs = selectedAssets.map((val: any) => val.uuid);
removeConnections(allUUIDs);
const updatedItems = floorItems.filter((item: { modeluuid: string }) => !selectedUUIDs.includes(item.modeluuid));
setFloorItems(updatedItems);
}
toast.success("Selected models removed!");
clearSelection();
};
return (
<>
<group name="SelectionGroup">
<group ref={selectionGroup} name="selectionAssetGroup">
<BoundingBox boundingBoxRef={boundingBoxRef} />
</group>
</group>
<MoveControls movedObjects={movedObjects} setMovedObjects={setMovedObjects} itemsGroupRef={itemsGroupRef} copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} selectionGroup={selectionGroup} boundingBoxRef={boundingBoxRef} />
<RotateControls rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} itemsGroupRef={itemsGroupRef} copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} selectionGroup={selectionGroup} boundingBoxRef={boundingBoxRef} />
<DuplicationControls itemsGroupRef={itemsGroupRef} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
<CopyPasteControls itemsGroupRef={itemsGroupRef} copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} selectionGroup={selectionGroup} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
</>
);
};
export default SelectionControls;

View File

@@ -1,115 +1,115 @@
import { Vector2, WebGLRenderer } from 'three'; import { Vector2, WebGLRenderer } from 'three';
class SelectionHelper { class SelectionHelper {
element: HTMLDivElement; element: HTMLDivElement;
renderer: WebGLRenderer; renderer: WebGLRenderer;
startPoint: Vector2; startPoint: Vector2;
pointTopLeft: Vector2; pointTopLeft: Vector2;
pointBottomRight: Vector2; pointBottomRight: Vector2;
isDown: boolean; isDown: boolean;
enabled: boolean; enabled: boolean;
constructor(renderer: WebGLRenderer) { constructor(renderer: WebGLRenderer) {
this.element = document.createElement('div'); this.element = document.createElement('div');
this.element.style.position = 'fixed'; this.element.style.position = 'fixed';
this.element.style.border = '1px solid #55aaff'; this.element.style.border = '1px solid #55aaff';
this.element.style.backgroundColor = 'rgba(75, 160, 255, 0.3)'; this.element.style.backgroundColor = 'rgba(75, 160, 255, 0.3)';
this.element.style.pointerEvents = 'none'; this.element.style.pointerEvents = 'none';
this.element.style.display = 'none'; this.element.style.display = 'none';
this.renderer = renderer; this.renderer = renderer;
this.startPoint = new Vector2(); this.startPoint = new Vector2();
this.pointTopLeft = new Vector2(); this.pointTopLeft = new Vector2();
this.pointBottomRight = new Vector2(); this.pointBottomRight = new Vector2();
this.isDown = false; this.isDown = false;
this.enabled = true; this.enabled = true;
this.onPointerDown = this.onPointerDown.bind(this); this.onPointerDown = this.onPointerDown.bind(this);
this.onPointerMove = this.onPointerMove.bind(this); this.onPointerMove = this.onPointerMove.bind(this);
this.onPointerUp = this.onPointerUp.bind(this); this.onPointerUp = this.onPointerUp.bind(this);
this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown); this.renderer.domElement.addEventListener('pointerdown', this.onPointerDown);
this.renderer.domElement.addEventListener('pointermove', this.onPointerMove); this.renderer.domElement.addEventListener('pointermove', this.onPointerMove);
this.renderer.domElement.addEventListener('pointerup', this.onPointerUp); this.renderer.domElement.addEventListener('pointerup', this.onPointerUp);
window.addEventListener("blur", this.cleanup.bind(this)); window.addEventListener("blur", this.cleanup.bind(this));
} }
dispose() { dispose() {
this.enabled = false; this.enabled = false;
this.isDown = false; this.isDown = false;
this.cleanup(); this.cleanup();
this.renderer.domElement.removeEventListener("pointerdown", this.onPointerDown); this.renderer.domElement.removeEventListener("pointerdown", this.onPointerDown);
this.renderer.domElement.removeEventListener("pointermove", this.onPointerMove); this.renderer.domElement.removeEventListener("pointermove", this.onPointerMove);
this.renderer.domElement.removeEventListener("pointerup", this.onPointerUp); this.renderer.domElement.removeEventListener("pointerup", this.onPointerUp);
window.removeEventListener("blur", this.cleanup); window.removeEventListener("blur", this.cleanup);
} }
private cleanup() { private cleanup() {
this.isDown = false; this.isDown = false;
this.element.style.display = 'none'; this.element.style.display = 'none';
if (this.element.parentElement) { if (this.element.parentElement) {
this.element.parentElement.removeChild(this.element); this.element.parentElement.removeChild(this.element);
} }
} }
onPointerDown(event: PointerEvent) { onPointerDown(event: PointerEvent) {
if (!this.enabled || !event.ctrlKey || event.button !== 0) return; if (!this.enabled || !event.ctrlKey || event.button !== 0) return;
this.isDown = true; this.isDown = true;
this.onSelectStart(event); this.onSelectStart(event);
} }
onPointerMove(event: PointerEvent) { onPointerMove(event: PointerEvent) {
if (!this.enabled || !this.isDown || !event.ctrlKey) return; if (!this.enabled || !this.isDown || !event.ctrlKey) return;
this.onSelectMove(event); this.onSelectMove(event);
} }
onPointerUp() { onPointerUp() {
if (!this.enabled) return; if (!this.enabled) return;
this.isDown = false; this.isDown = false;
this.onSelectOver(); this.onSelectOver();
} }
onSelectStart(event: PointerEvent) { onSelectStart(event: PointerEvent) {
this.element.style.display = 'none'; this.element.style.display = 'none';
this.renderer.domElement.parentElement?.appendChild(this.element); this.renderer.domElement.parentElement?.appendChild(this.element);
this.element.style.left = `${event.clientX}px`; this.element.style.left = `${event.clientX}px`;
this.element.style.top = `${event.clientY}px`; this.element.style.top = `${event.clientY}px`;
this.element.style.width = '0px'; this.element.style.width = '0px';
this.element.style.height = '0px'; this.element.style.height = '0px';
this.startPoint.x = event.clientX; this.startPoint.x = event.clientX;
this.startPoint.y = event.clientY; this.startPoint.y = event.clientY;
} }
onSelectMove(event: PointerEvent) { onSelectMove(event: PointerEvent) {
if (!this.isDown) return; if (!this.isDown) return;
this.element.style.display = 'block'; this.element.style.display = 'block';
this.pointBottomRight.x = Math.max(this.startPoint.x, event.clientX); this.pointBottomRight.x = Math.max(this.startPoint.x, event.clientX);
this.pointBottomRight.y = Math.max(this.startPoint.y, event.clientY); this.pointBottomRight.y = Math.max(this.startPoint.y, event.clientY);
this.pointTopLeft.x = Math.min(this.startPoint.x, event.clientX); this.pointTopLeft.x = Math.min(this.startPoint.x, event.clientX);
this.pointTopLeft.y = Math.min(this.startPoint.y, event.clientY); this.pointTopLeft.y = Math.min(this.startPoint.y, event.clientY);
this.element.style.left = `${this.pointTopLeft.x}px`; this.element.style.left = `${this.pointTopLeft.x}px`;
this.element.style.top = `${this.pointTopLeft.y}px`; this.element.style.top = `${this.pointTopLeft.y}px`;
this.element.style.width = `${this.pointBottomRight.x - this.pointTopLeft.x}px`; this.element.style.width = `${this.pointBottomRight.x - this.pointTopLeft.x}px`;
this.element.style.height = `${this.pointBottomRight.y - this.pointTopLeft.y}px`; this.element.style.height = `${this.pointBottomRight.y - this.pointTopLeft.y}px`;
} }
onSelectOver() { onSelectOver() {
this.element.style.display = 'none'; this.element.style.display = 'none';
if (this.element.parentElement) { if (this.element.parentElement) {
this.element.parentElement.removeChild(this.element); this.element.parentElement.removeChild(this.element);
} }
} }
} }
export { SelectionHelper }; export { SelectionHelper };

View File

@@ -1,120 +0,0 @@
import { TransformControls } from "@react-three/drei";
import * as THREE from "three";
import { useSelectedFloorItem, useObjectPosition, useObjectScale, useObjectRotation, useTransformMode, useFloorItems, useSocketStore, useActiveTool } from "../../../store/store";
import { useThree } from "@react-three/fiber";
import * as Types from '../../../types/world/worldTypes';
import { useEffect } from "react";
export default function TransformControl() {
const state = useThree();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { objectPosition, setObjectPosition } = useObjectPosition();
const { objectScale, setObjectScale } = useObjectScale();
const { objectRotation, setObjectRotation } = useObjectRotation();
const { transformMode, setTransformMode } = useTransformMode();
const { floorItems, setFloorItems } = useFloorItems();
const { activeTool, setActiveTool } = useActiveTool();
const { socket } = useSocketStore();
function handleObjectChange() {
if (selectedFloorItem && transformMode) {
setObjectPosition(selectedFloorItem.position);
setObjectScale(selectedFloorItem.scale);
setObjectRotation({
x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z),
});
}
}
function handleMouseUp() {
if (selectedFloorItem) {
setObjectPosition(selectedFloorItem.position);
setObjectScale(selectedFloorItem.scale);
setObjectRotation({
x: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.x),
y: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.y),
z: THREE.MathUtils.radToDeg(selectedFloorItem.rotation.z),
});
}
setFloorItems((prevItems: Types.FloorItems) => {
if (!prevItems) {
return
}
let updatedItem: any = null;
const updatedItems = prevItems.map((item) => {
if (item.modeluuid === selectedFloorItem?.uuid) {
updatedItem = {
...item,
position: [selectedFloorItem.position.x, selectedFloorItem.position.y, selectedFloorItem.position.z,] as [number, number, number],
rotation: { x: selectedFloorItem.rotation.x, y: selectedFloorItem.rotation.y, z: selectedFloorItem.rotation.z, },
};
return updatedItem;
}
return item;
});
if (updatedItem && selectedFloorItem) {
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
//REST
// setFloorItemApi(
// organization,
// updatedItem.modeluuid,
// updatedItem.modelname,
// [selectedFloorItem.position.x, selectedFloorItem.position.y, selectedFloorItem.position.z,],
// { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z },
// false,
// true,
// );
//SOCKET
const data = {
organization: organization,
modeluuid: updatedItem.modeluuid,
modelname: updatedItem.modelname,
position: [selectedFloorItem.position.x, selectedFloorItem.position.y, selectedFloorItem.position.z],
rotation: { "x": selectedFloorItem.rotation.x, "y": selectedFloorItem.rotation.y, "z": selectedFloorItem.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id
}
socket.emit('v2:model-asset:add', data);
}
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
}
useEffect(() => {
if (activeTool === "Add pillar" || activeTool === "Delete") {
if (state.controls) {
const target = (state.controls as any).getTarget(new THREE.Vector3());
(state.controls as any).setTarget(target.x, 0, target.z, true);
}
setSelectedFloorItem(null);
{
setObjectPosition({ x: undefined, y: undefined, z: undefined });
setObjectScale({ x: undefined, y: undefined, z: undefined });
setObjectRotation({ x: undefined, y: undefined, z: undefined });
}
}
}, [activeTool]);
return (
<>
{(selectedFloorItem && transformMode) &&
<TransformControls
object={selectedFloorItem}
mode={transformMode}
onObjectChange={handleObjectChange}
onMouseUp={handleMouseUp}
/>
}
</>
);
}

View File

@@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from "react";
import * as CONSTANTS from '../../../types/world/worldConstants'; import * as CONSTANTS from '../../../types/world/worldConstants';
export default function Sun() { export default function Sun() {
const savedTheme: string | null = localStorage.getItem("theme");
const { elevation, setElevation } = useElevation(); const { elevation, setElevation } = useElevation();
const { sunPosition, setSunPosition } = useSunPosition(); const { sunPosition, setSunPosition } = useSunPosition();
const { azimuth, setAzimuth } = useAzimuth(); const { azimuth, setAzimuth } = useAzimuth();
@@ -28,7 +29,7 @@ export default function Sun() {
return ( return (
<> <>
{(azimuth !== undefined && elevation !== undefined) && ( {(azimuth !== undefined && elevation !== undefined && savedTheme !== "dark") && (
<> <>
<Sky <Sky
distance={CONSTANTS.skyConfig.skyDistance} distance={CONSTANTS.skyConfig.skyDistance}

View File

@@ -3,8 +3,6 @@ import { EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
import { BlendFunction } from "postprocessing"; import { BlendFunction } from "postprocessing";
import { import {
useDeletableFloorItem, useDeletableFloorItem,
useSelectedActionSphere,
useSelectedPath,
useSelectedWallItem, useSelectedWallItem,
useSelectedFloorItem, useSelectedFloorItem,
} from "../../../store/store"; } from "../../../store/store";
@@ -16,8 +14,6 @@ export default function PostProcessing() {
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem(); const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem(); const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem(); const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { selectedActionSphere } = useSelectedActionSphere();
const { selectedPath } = useSelectedPath();
function flattenChildren(children: any[]) { function flattenChildren(children: any[]) {
const allChildren: any[] = []; const allChildren: any[] = [];
@@ -89,36 +85,6 @@ export default function PostProcessing() {
xRay={true} xRay={true}
/> />
)} )}
{selectedActionSphere && (
<Outline
selection={[selectedActionSphere.points]}
selectionLayer={10}
width={1000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={10}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={0x6f42c1}
hiddenEdgeColor={0x6f42c1}
blur={true}
xRay={true}
/>
)}
{selectedPath && (
<Outline
selection={flattenChildren(selectedPath.group.children)}
selectionLayer={10}
width={1000}
blendFunction={BlendFunction.ALPHA}
edgeStrength={10}
resolutionScale={2}
pulseSpeed={0}
visibleEdgeColor={0x6f42c1}
hiddenEdgeColor={0x6f42c1}
blur={true}
xRay={true}
/>
)}
</EffectComposer> </EffectComposer>
</> </>
); );

View File

@@ -1,63 +1,33 @@
import { useMemo } from "react"; import { useMemo } from "react";
import { Canvas } from "@react-three/fiber"; import { Canvas } from "@react-three/fiber";
import { Environment, KeyboardControls, Stars } from "@react-three/drei"; import { KeyboardControls } from "@react-three/drei";
import World from "./world/world"; import Builder from "../builder/builder";
import Controls from "./controls/controls"; import Visualization from "../visualization/visualization";
import TransformControl from "./controls/transformControls"; import Setup from "./setup/setup";
import PostProcessing from "./postProcessing/postProcessing";
import Sun from "./environment/sky";
import CamModelsGroup from "../collaboration/collabCams";
import Shadows from "./environment/shadow";
import MqttEvents from "../../services/factoryBuilder/mqtt/mqttEvents";
import background from "../../assets/textures/hdr/mudroadpuresky2k.hdr";
import SelectionControls from "./controls/selection/selectionControls";
import MeasurementTool from "./tools/measurementTool";
import Simulation from "../simulation/simulation"; import Simulation from "../simulation/simulation";
// import Simulation from "./simulationtemp/simulation";
import ZoneCentreTarget from "../visualization/functions/zoneCameraTarget";
import Dropped3dWidgets from "../../modules/visualization/widgets/3d/Dropped3dWidget";
import ZoneAssets from "../visualization/zoneAssets";
export default function Scene() { export default function Scene() {
const map = useMemo( const map = useMemo(() => [
() => [ { name: "forward", keys: ["ArrowUp", "w", "W"] },
{ name: "forward", keys: ["ArrowUp", "w", "W"] }, { name: "backward", keys: ["ArrowDown", "s", "S"] },
{ name: "backward", keys: ["ArrowDown", "s", "S"] }, { name: "left", keys: ["ArrowLeft", "a", "A"] },
{ name: "left", keys: ["ArrowLeft", "a", "A"] }, { name: "right", keys: ["ArrowRight", "d", "D"] },],
{ name: "right", keys: ["ArrowRight", "d", "D"] }, []);
],
[]
);
const savedTheme: string | null = localStorage.getItem("theme");
return ( return (
<KeyboardControls map={map}> <KeyboardControls map={map}>
<Canvas <Canvas eventPrefix="client" gl={{ powerPreference: "high-performance", antialias: true }} onContextMenu={(e) => { e.preventDefault(); }}>
eventPrefix="client"
gl={{ powerPreference: "high-performance", antialias: true }} <Setup />
onContextMenu={(e) => {
e.preventDefault(); <Builder />
}}
> <Simulation />
<Dropped3dWidgets />
<Controls /> <Visualization />
<TransformControl />
<SelectionControls /> </Canvas>
<MeasurementTool /> </KeyboardControls>
<World /> );
<ZoneCentreTarget />
<ZoneAssets />
<Simulation />
<PostProcessing />
{savedTheme !== "dark" ? <Sun /> : <></>}
<Shadows />
<CamModelsGroup />
<MqttEvents />
<Environment files={background} environmentIntensity={1.5} />
</Canvas>
</KeyboardControls>
);
} }

View File

@@ -0,0 +1,25 @@
import Sun from '../environment/sky'
import Shadows from '../environment/shadow'
import PostProcessing from '../postProcessing/postProcessing'
import Controls from '../controls/controls';
import { Environment } from '@react-three/drei'
import background from "../../../assets/hdr/mudroadpuresky2k.hdr";
function Setup() {
return (
<>
<Controls />
<Sun />
<Shadows />
<PostProcessing />
<Environment files={background} environmentIntensity={1.5} />
</>
)
}
export default Setup

View File

@@ -1,87 +0,0 @@
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/simulationTypes';
import { ArmbotInstances } from "./ArmBotInstances";
import { useResetButtonStore } from "../../../store/usePlayButtonStore";
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
interface ArmBotProps {
armBots: ArmBotState[];
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
}
const ArmBot = ({ armBots, setArmBots, setStaticMachines }: ArmBotProps) => {
const { activeModule } = useModuleStore();
const { scene } = useThree();
const { simulationStates } = useSimulationStates();
const { isReset } = useResetButtonStore();
useEffect(() => {
const filtered = simulationStates.filter((s): s is SimulationTypes.ArmBotEventsSchema => s.type === "ArmBot");
const initialStates: ArmBotState[] = filtered
.filter(bot => bot.points.connections.targets.length > 0)
.map(bot => ({
uuid: bot.modeluuid,
position: bot.position,
rotation: bot.rotation,
status: "idle",
material: "default",
triggerId: '',
actions: bot.points.actions,
connections: bot.points.connections,
isActive: false
}));
setArmBots(initialStates);
}, [simulationStates, isReset]);
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}
setStaticMachines={setStaticMachines}
/>
))}
</>
);
};
export default ArmBot;

View File

@@ -1,90 +0,0 @@
import IkInstances from "./IkInstances";
import armModel from "../../../assets/gltf-glb/rigged/ik_arm_4.glb";
import { useEffect, useState } from "react";
import { useThree } from "@react-three/fiber";
import { Vector3 } from "three";
interface Process {
triggerId: string;
startPoint?: Vector3;
endPoint?: Vector3;
speed: number;
}
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
interface ArmbotInstancesProps {
index: number;
armBot: ArmBotState;
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
}
export const ArmbotInstances: React.FC<ArmbotInstancesProps> = ({ index, armBot, setArmBots, setStaticMachines }) => {
const { scene } = useThree();
const [processes, setProcesses] = useState<Process[]>([]);
useEffect(() => {
if (armBot.actions.processes.length > 0) {
const mappedProcesses = armBot.actions.processes.map((process) => {
return {
triggerId: process.triggerId,
startPoint: scene.getObjectByProperty('uuid', process.startPoint)?.getWorldPosition(new Vector3()),
endPoint: scene.getObjectByProperty('uuid', process.endPoint)?.getWorldPosition(new Vector3()),
speed: armBot.actions.speed
};
});
setProcesses(mappedProcesses);
} else {
setProcesses([]);
}
}, [armBot, scene]);
const updateArmBotStatus = (status: string) => {
setArmBots((prevArmBots) => {
return prevArmBots.map(bot => {
if (bot.uuid === armBot.uuid) {
return { ...bot, status, triggerId: status === 'idle' ? '' : armBot.triggerId };
}
return bot;
});
});
};
return (
<IkInstances
key={index}
uuid={armBot.uuid}
selectedTrigger={armBot.triggerId}
modelUrl={armModel}
position={armBot.position}
rotation={armBot.rotation}
processes={processes}
armBot={armBot}
setArmBots={setArmBots}
setStaticMachines={setStaticMachines}
updateArmBotStatus={updateArmBotStatus}
/>
);
};

View File

@@ -1,379 +0,0 @@
import { useEffect, useMemo, useState, useRef } from "react";
import { useFrame } from "@react-three/fiber";
import * as THREE from "three";
import { usePlayButtonStore, useResetButtonStore } from "../../../store/usePlayButtonStore";
import { useSimulationStates } from "../../../store/store";
import MaterialInstances from "./MaterialInstances";
import { Line } from "react-chartjs-2";
import { QuadraticBezierLine } from "@react-three/drei";
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
type IKAnimationControllerProps = {
ikSolver: any;
processes: {
triggerId: string;
startPoint: THREE.Vector3;
endPoint: THREE.Vector3;
speed: number;
}[];
selectedTrigger: string;
targetBoneName: string;
uuid: string;
logStatus: (status: string) => void;
groupRef: React.RefObject<THREE.Group>;
armBot: ArmBotState;
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
updateArmBotStatus: (status: string) => void;
}
const IKAnimationController = ({
ikSolver,
processes,
selectedTrigger,
targetBoneName,
uuid,
logStatus,
groupRef,
armBot,
setArmBots,
setStaticMachines,
updateArmBotStatus
}: IKAnimationControllerProps) => {
const [progress, setProgress] = useState(0);
const [initialProgress, setInitialProgress] = useState(0);
const [needsInitialMovement, setNeedsInitialMovement] = useState(true);
const [isInitializing, setIsInitializing] = useState(true);
const restSpeed = 0.1;
const restPosition = new THREE.Vector3(0, 2, 1.6);
const { isPlaying } = usePlayButtonStore();;
const statusRef = useRef("idle");
const { simulationStates } = useSimulationStates();
const { isReset } = useResetButtonStore();
const initialCurveRef = useRef<THREE.CatmullRomCurve3 | null>(null);
const initialStartPositionRef = useRef<THREE.Vector3 | null>(null);
useEffect(() => {
setProgress(0);
}, [selectedTrigger]);
useEffect(() => {
setProgress(0);
setNeedsInitialMovement(true);
setInitialProgress(0);
setIsInitializing(true);
}, [isReset]);
useEffect(() => {
if (ikSolver) {
const targetBone = ikSolver.mesh.skeleton.bones.find(
(b: any) => b.name === targetBoneName
);
if (targetBone) {
initialStartPositionRef.current = targetBone.position.clone();
calculateInitialCurve(targetBone.position);
logStatus(`[Arm ${uuid}] Initializing IK system, starting position: ${targetBone.position.toArray()}`);
}
}
}, [ikSolver]);
const calculateInitialCurve = (startPosition: THREE.Vector3) => {
const direction = new THREE.Vector3().subVectors(restPosition, startPosition);
const distance = direction.length();
direction.normalize();
const perpendicular = new THREE.Vector3(-direction.z, 0, direction.x).normalize();
const midHeight = 0.5;
const tiltAmount = 1;
const mid = new THREE.Vector3()
.addVectors(startPosition, restPosition)
.multiplyScalar(0.5)
.add(perpendicular.clone().multiplyScalar(distance * 0.3 * tiltAmount))
.add(new THREE.Vector3(0, midHeight, 0));
initialCurveRef.current = new THREE.CatmullRomCurve3([
startPosition,
new THREE.Vector3().lerpVectors(startPosition, mid, 0.33),
mid,
new THREE.Vector3().lerpVectors(mid, restPosition, 0.66),
restPosition
]);
};
const processedCurves = useMemo(() => {
if (!isPlaying) return [];
return processes.map(process => {
const localStart = groupRef.current?.worldToLocal(process.startPoint.clone());
const localEnd = groupRef.current?.worldToLocal(process.endPoint.clone());
if (!localStart || !localEnd) return null;
const midPoint = new THREE.Vector3(
(localStart.x + localEnd.x) / 2,
Math.max(localStart.y, localEnd.y) + 1,
(localStart.z + localEnd.z) / 2
);
const restToStartCurve = new THREE.CatmullRomCurve3([
restPosition,
new THREE.Vector3().lerpVectors(restPosition, localStart, 0.5),
localStart
]);
const processCurve = new THREE.CatmullRomCurve3([
localStart,
midPoint,
localEnd
]);
const endToRestCurve = new THREE.CatmullRomCurve3([
localEnd,
new THREE.Vector3().lerpVectors(localEnd, restPosition, 0.5),
restPosition
]);
return {
triggerId: process.triggerId,
restToStartCurve,
processCurve,
endToRestCurve,
speed: process.speed,
totalDistance:
restPosition.distanceTo(localStart) +
localStart.distanceTo(localEnd) +
localEnd.distanceTo(restPosition)
};
}).filter(Boolean);
}, [processes, isPlaying]);
const activeProcess = useMemo(() => {
if (!selectedTrigger) return null;
return processedCurves.find(p => p?.triggerId === selectedTrigger);
}, [processedCurves, selectedTrigger]);
// Initial movement to rest position
useFrame((_, delta) => {
if (!ikSolver || !needsInitialMovement || !isInitializing || !initialCurveRef.current) return;
const targetBone = ikSolver.mesh.skeleton.bones.find(
(b: any) => b.name === targetBoneName
);
if (!targetBone) return;
setInitialProgress((prev) => {
const next = prev + delta * 0.5;
if (next >= 1) {
targetBone.position.copy(restPosition);
setNeedsInitialMovement(false);
setIsInitializing(false);
return 1;
}
targetBone.position.copy(initialCurveRef.current!.getPoint(next));
return next;
});
ikSolver.update();
});
// Main animation loop
useFrame((_, delta) => {
if (isInitializing || !isPlaying || !selectedTrigger || !activeProcess || !ikSolver) return;
const bone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBoneName);
if (!bone) return;
const {
restToStartCurve,
processCurve,
endToRestCurve,
speed,
totalDistance
} = activeProcess;
// Calculate current segment and progress
const restToStartDist = restPosition.distanceTo(restToStartCurve.points[2]);
const processDist = processCurve.getLength();
const endToRestDist = endToRestCurve.getLength();
const restToStartEnd = restToStartDist / totalDistance;
const processEnd = (restToStartDist + processDist) / totalDistance;
setProgress(prev => {
let currentStatus = statusRef.current;
let currentPosition: THREE.Vector3;
const newProgress = Math.min(prev + delta * ((currentStatus === 'returning to rest') ? restSpeed : speed), 1);
if (newProgress < restToStartEnd) {
// Moving from rest to start position
currentStatus = "moving to start";
const segmentProgress = newProgress / restToStartEnd;
currentPosition = restToStartCurve.getPoint(segmentProgress);
} else if (newProgress < processEnd) {
// Processing - moving from start to end
currentStatus = "processing";
const segmentProgress = (newProgress - restToStartEnd) / (processEnd - restToStartEnd);
currentPosition = processCurve.getPoint(segmentProgress);
if (statusRef.current !== "processing") {
updateConveyorOrStaticMachineStatusOnStart(selectedTrigger);
}
} else {
// Returning to rest position
currentStatus = "returning to rest";
const segmentProgress = (newProgress - processEnd) / (1 - processEnd);
currentPosition = endToRestCurve.getPoint(segmentProgress);
}
// Update status if changed
if (currentStatus !== statusRef.current) {
statusRef.current = currentStatus;
// updateArmBotStatus(currentStatus);
logStatus(`[Arm ${uuid}] Status: ${currentStatus}`);
}
// Only trigger when the entire animation is complete (newProgress === 1)
if (newProgress === 1 && currentStatus === "returning to rest") {
updateConveyorOrStaticMachineStatusOnEnd(selectedTrigger);
}
bone.position.copy(currentPosition);
ikSolver.update();
return newProgress;
});
});
const updateConveyorOrStaticMachineStatusOnStart = (selectedTrigger: string) => {
const currentProcess = processes.find(p => p.triggerId === selectedTrigger);
if (currentProcess) {
const triggerId = currentProcess.triggerId;
const startPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.startPoint;
const matchedMachine = simulationStates.find((state) => {
if (state.type === "Conveyor") {
return (state).points.some(
(point) => point.uuid === startPoint
);
} else if (state.type === "StaticMachine") {
return state.points.uuid === startPoint;
}
return false;
});
if (matchedMachine) {
if (matchedMachine.type === "Conveyor") {
logStatus(`[Arm ${uuid}] start point which is a conveyor (${matchedMachine.modelName})`);
} else {
logStatus(`[Arm ${uuid}] started form start point which is a static machine (${matchedMachine.modelName})`);
}
setTimeout(() => {
if (matchedMachine.type === "StaticMachine") {
updateArmBotStatus('dropping');
}
if (matchedMachine.type === "Conveyor") {
updateArmBotStatus('picking');
}
}, 0);
}
}
}
const updateConveyorOrStaticMachineStatusOnEnd = (selectedTrigger: string) => {
const currentProcess = processes.find(p => p.triggerId === selectedTrigger);
if (currentProcess) {
const triggerId = currentProcess.triggerId;
const endPoint = armBot.actions.processes.find((process) => process.triggerId === triggerId)?.endPoint;
const matchedMachine = simulationStates.find((state) => {
if (state.type === "Conveyor") {
return (state).points.some(
(point) => point.uuid === endPoint
);
} else if (state.type === "StaticMachine") {
return state.points.uuid === endPoint;
}
return false;
});
if (matchedMachine) {
if (matchedMachine.type === "Conveyor") {
logStatus(`[Arm ${uuid}] Reached end point which is a conveyor (${matchedMachine.modelName})`);
} else {
logStatus(`[Arm ${uuid}] Reached end point which is a static machine (${matchedMachine.modelName})`);
}
setTimeout(() => {
if (matchedMachine.type === "StaticMachine") {
setStaticMachines((machines) => {
return machines.map((machine) => {
if (machine.uuid === matchedMachine.modeluuid) {
return { ...machine, status: "running" };
} else {
return machine;
}
});
});
updateArmBotStatus('idle');
}
if (matchedMachine.type === "Conveyor") {
setArmBots((prev) =>
prev.map((arm) => {
if (arm.uuid === uuid && arm.isActive === true) {
return {
...arm,
isActive: false,
status: "idle",
};
}
else {
return arm;
}
})
);
}
}, 0);
}
}
}
return (
<>
<MaterialInstances
statusRef={statusRef}
ikSolver={ikSolver}
targetBoneName={targetBoneName}
/>
</>
);
};
export default IKAnimationController;

View File

@@ -1,150 +0,0 @@
import * as THREE from "three";
import { useEffect, useMemo, useRef, useState } from "react";
import { useFrame, 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";
import { TransformControls } from "@react-three/drei";
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
const IkInstances = ({
uuid,
selectedTrigger,
modelUrl,
processes,
position,
rotation,
armBot,
setArmBots,
setStaticMachines,
updateArmBotStatus
}: {
uuid: string;
selectedTrigger: string;
modelUrl: string;
processes: any;
position: [number, number, number];
rotation: [number, number, number];
armBot: ArmBotState;
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
updateArmBotStatus: (status: string) => void;
}) => {
const [ikSolver, setIkSolver] = useState<any>(null);
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 targetBoneName = "Target";
const skinnedMeshName = "link_0";
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]);
const logStatus = (status: string) => {
// console.log(status);
}
return (
<>
<group
ref={groupRef}
position={position}
rotation={rotation}
>
<primitive
object={cloned}
scale={[1, 1, 1]}
name={`arm-bot`}
/>
</group>
<IKAnimationController
ikSolver={ikSolver}
processes={processes}
selectedTrigger={selectedTrigger}
targetBoneName={targetBoneName}
uuid={uuid}
logStatus={logStatus}
groupRef={groupRef}
armBot={armBot}
setArmBots={setArmBots}
setStaticMachines={setStaticMachines}
updateArmBotStatus={updateArmBotStatus}
/>
</>
);
};
export default IkInstances;

View File

@@ -1,31 +0,0 @@
import React from 'react';
import * as THREE from 'three';
import { Box } from '@react-three/drei';
type MaterialInstancesProps = {
statusRef: React.RefObject<string>;
ikSolver: any;
targetBoneName: string;
};
function MaterialInstances({
statusRef,
ikSolver,
targetBoneName
}: MaterialInstancesProps) {
if (!ikSolver) return null;
const targetBone = ikSolver.mesh.skeleton.bones.find((b: any) => b.name === targetBoneName);
if (!targetBone) return null;
const worldPos = new THREE.Vector3();
targetBone.getWorldPosition(worldPos);
return (
<Box args={[0.5, 0.5, 0.5]} position={worldPos} visible={statusRef.current === 'processing'}>
<meshStandardMaterial color="orange" />
</Box>
);
}
export default MaterialInstances;

File diff suppressed because it is too large Load Diff

View File

@@ -1,415 +0,0 @@
import * as THREE from "three";
import * as SimulationTypes from "../../../types/simulationTypes";
import { useRef, useState, useEffect, useMemo } from "react";
import { Sphere, TransformControls } from "@react-three/drei";
import {
useEditingPoint,
useEyeDropMode,
useIsConnecting,
usePreviewPosition,
useRenderDistance,
useSelectedActionSphere,
useSelectedPath,
useSimulationStates,
} from "../../../store/store";
import { useFrame, useThree } from "@react-three/fiber";
import { useSubModuleStore } from "../../../store/useModuleStore";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import { setEventApi } from "../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys";
function PathCreation({ pathsGroupRef, }: { pathsGroupRef: React.MutableRefObject<THREE.Group>; }) {
const { isPlaying } = usePlayButtonStore();
const { renderDistance } = useRenderDistance();
const { setSubModule } = useSubModuleStore();
const { setSelectedActionSphere, selectedActionSphere } = useSelectedActionSphere();
const { eyeDropMode, setEyeDropMode } = useEyeDropMode();
const { editingPoint, setEditingPoint } = useEditingPoint();
const { previewPosition, setPreviewPosition } = usePreviewPosition();
const { raycaster, camera, pointer, gl } = useThree();
const { setSelectedPath } = useSelectedPath();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { isConnecting } = useIsConnecting();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const groupRefs = useRef<{ [key: string]: THREE.Group }>({});
const sphereRefs = useRef<{ [key: string]: THREE.Mesh }>({});
const isMovingRef = useRef(false);
const transformRef = useRef<any>(null);
const [transformMode, setTransformMode] = useState<"translate" | "rotate" | null>(null);
useEffect(() => {
setTransformMode(null);
const handleKeyDown = (e: KeyboardEvent) => {
const keyCombination = detectModifierKeys(e);
if (!selectedActionSphere) return;
if (keyCombination === "G") {
setTransformMode((prev) => (prev === "translate" ? null : "translate"));
}
if (keyCombination === "R") {
setTransformMode((prev) => (prev === "rotate" ? null : "rotate"));
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, [selectedActionSphere]);
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 (eyeDropMode) {
raycaster.setFromCamera(pointer, camera);
const intersectionPoint = new THREE.Vector3();
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
setPreviewPosition({ x: point.x, y: point.z });
}
} else {
setPreviewPosition(null);
}
});
useEffect(() => {
if (!camera) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
const onPointerDown = () => {
isMovingRef.current = false;
};
const onPointerMove = () => {
isMovingRef.current = true;
};
const onPointerUp = (event: PointerEvent) => {
if (
!isMovingRef.current &&
eyeDropMode &&
event.button === 0 &&
previewPosition
) {
event.preventDefault();
if (editingPoint) {
handlePointUpdate(editingPoint, previewPosition.x, previewPosition.y);
setEditingPoint(null);
setEyeDropMode(false);
}
}
};
if (eyeDropMode) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
};
}, [eyeDropMode, editingPoint, previewPosition]);
const updateBackend = async (updatedPath: SimulationTypes.VehicleEventsSchema | undefined) => {
if (!updatedPath) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
await setEventApi(
organization,
updatedPath.modeluuid,
{ type: "Vehicle", points: updatedPath.points }
);
}
const handlePointUpdate = (pointType: "start" | "end", x: number, z: number) => {
if (!selectedActionSphere?.points?.uuid) return;
const updatedPaths = simulationStates.map((path) => {
if (path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid) {
return {
...path,
points: {
...path.points,
actions: {
...path.points.actions,
[pointType]: { ...path.points.actions[pointType], x: x, y: z, },
},
},
};
}
return path;
});
const updatedPath = updatedPaths.find((path): path is SimulationTypes.VehicleEventsSchema => path.type === "Vehicle" && path.points.uuid === selectedActionSphere.points.uuid);
updateBackend(updatedPath);
setSimulationStates(updatedPaths);
};
return (
<group visible={!isPlaying} name="simulation-simulationStates-group" ref={pathsGroupRef}>
{simulationStates.map((path) => {
if (path.type === "Conveyor") {
const points = path.points.map(
(point) => new THREE.Vector3(...point.position)
);
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");
}}
>
{path.points.map((point, index) => (
<Sphere
key={point.uuid}
uuid={point.uuid}
position={point.position}
args={[0.15, 32, 32]}
name="events-sphere"
ref={(el) => (sphereRefs.current[point.uuid] = el!)}
onClick={(e) => {
if (isConnecting || eyeDropMode) return;
e.stopPropagation();
setSelectedActionSphere({
path,
points: sphereRefs.current[point.uuid],
});
setSubModule("mechanics");
setSelectedPath(null);
}}
userData={{ points, path }}
onPointerMissed={() => {
if (eyeDropMode) return;
setSubModule("properties");
setSelectedActionSphere(null);
}}
>
<meshStandardMaterial
color={index === 0 ? "orange" : index === path.points.length - 1 ? "blue" : "green"}
/>
</Sphere>
))}
{points.slice(0, -1).map((point, index) => {
const nextPoint = points[index + 1];
const segmentCurve = new THREE.CatmullRomCurve3([point, nextPoint,]);
const tubeGeometry = new THREE.TubeGeometry(segmentCurve, 20, 0.1, 16, false);
return (
<mesh name="event-connection-tube" key={`tube-${index}`} geometry={tubeGeometry}>
<meshStandardMaterial transparent opacity={0.9} color="red" />
</mesh>
);
})}
</group>
);
} 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}
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="purple" />
</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;
})}
{selectedActionSphere && transformMode && (
<TransformControls
ref={transformRef}
object={selectedActionSphere.points}
mode={transformMode}
/>
)}
</group>
);
}
export default PathCreation;

View File

@@ -1,611 +0,0 @@
import React, { useRef, useEffect, useMemo, useCallback } from "react";
import { useLoader, useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three-stdlib";
import * as THREE from "three";
import { GLTF } from "three-stdlib";
import crate from "../../../assets/gltf-glb/crate_box.glb";
import { useProcessAnimation } from "./useProcessAnimations";
import ProcessObject from "./processObject";
import { ProcessData } from "./types";
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: {
uuid: string;
name: string;
speed: number;
processes: { triggerId: string; startPoint: string; endPoint: string }[];
};
isActive?: boolean;
}
interface ProcessContainerProps {
processes: ProcessData[];
setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
agvRef: any;
MaterialRef: any;
armBots: ArmBotState[];
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
}
const ProcessAnimator: React.FC<ProcessContainerProps> = ({
processes,
setProcesses,
agvRef,
MaterialRef,
armBots,
setArmBots,
}) => {
const gltf = useLoader(GLTFLoader, crate) as GLTF;
const groupRef = useRef<THREE.Group>(null);
const tempStackedObjectsRef = useRef<Record<string, boolean>>({});
const {
animationStates,
setAnimationStates,
clockRef,
elapsedBeforePauseRef,
speedRef,
debugRef,
findSpawnPoint,
createSpawnedObject,
handlePointActions,
hasNonInheritActions,
getPointDataForAnimationIndex,
processes: processedProcesses,
checkAndCountTriggers,
} = useProcessAnimation(processes, setProcesses, agvRef, armBots, setArmBots);
const baseMaterials = useMemo(
() => ({
Box: new THREE.MeshStandardMaterial({ color: 0x8b4513 }),
Crate: new THREE.MeshStandardMaterial({ color: 0x00ff00 }),
Default: new THREE.MeshStandardMaterial(),
}),
[]
);
useEffect(() => {
// Update material references for all spawned objects
Object.entries(animationStates).forEach(([processId, processState]) => {
Object.keys(processState.spawnedObjects).forEach((objectId) => {
const entry = { processId, objectId };
const materialType =
processState.spawnedObjects[objectId]?.currentMaterialType;
if (!materialType) {
return;
}
const matRefArray = MaterialRef.current;
// Find existing material group
const existing = matRefArray.find(
(entryGroup: { material: string; objects: any[] }) =>
entryGroup.material === materialType
);
if (existing) {
// Check if this processId + objectId already exists
const alreadyExists = existing.objects.some(
(o: any) =>
o.processId === entry.processId && o.objectId === entry.objectId
);
if (!alreadyExists) {
existing.objects.push(entry);
}
} else {
// Create new group for this material type
matRefArray.push({
material: materialType,
objects: [entry],
});
}
});
});
}, [animationStates, MaterialRef, agvRef]);
// In processAnimator.tsx - only the relevant spawn logic part that needs fixes
// Add this function to ProcessAnimator component
const isConnectedToActiveArmBot = useCallback(
(processId: any) => {
// Check if any active armbot is connected to this process
return armBots.some((armbot) => {
if (!armbot.isActive) return false;
// Check if this armbot is connected to the process
return armbot.connections?.targets?.some((connection) => {
// Find the process that owns this modelUUID
const connectedProcess = processes.find((p) =>
p.paths?.some((path) => path.modeluuid === connection.modelUUID)
);
return connectedProcess?.id === processId;
});
});
},
[armBots, processes]
);
// First useFrame for spawn logic
useFrame(() => {
// Spawn logic frame
const currentTime =
clockRef.current.getElapsedTime() - elapsedBeforePauseRef.current;
setAnimationStates((prev) => {
const newStates = { ...prev };
processedProcesses.forEach((process) => {
const processState = newStates[process.id];
if (!processState) return;
// Check connection status
const isConnected = isConnectedToActiveArmBot(process.id);
if (processState.isProcessDelaying) {
// Existing delay handling logic...
return;
}
if (isConnected) {
newStates[process.id] = {
...processState,
nextSpawnTime: Infinity, // Prevent future spawns
};
return;
}
const spawnPoint = findSpawnPoint(process);
if (!spawnPoint || !spawnPoint.actions) {
// console.log(
// `Process ${process.id} has no valid spawn point or actions`
// );
return;
}
const spawnAction = spawnPoint.actions.find(
(a) => a.isUsed && a.type === "Spawn"
);
if (!spawnAction) {
return;
}
const spawnInterval =
typeof spawnAction.spawnInterval === "number"
? spawnAction.spawnInterval
: parseFloat(spawnAction.spawnInterval || "0") || 0;
// Check if this is a zero interval spawn and we already spawned an object
if (
spawnInterval === 0 &&
processState.hasSpawnedZeroIntervalObject === true
) {
return; // Don't spawn more objects for zero interval
}
const effectiveSpawnInterval = spawnInterval / speedRef.current;
if (currentTime >= processState.nextSpawnTime) {
const objectId = `obj-${process.id}-${processState.objectIdCounter}`;
const newObject = createSpawnedObject(
process,
currentTime,
spawnAction.material || "Default",
spawnPoint,
baseMaterials
);
// Initialize state properly to ensure animation
newObject.state = {
...newObject.state,
isAnimating: true,
isDelaying: false,
delayComplete: false,
progress: 0.005, // Start with tiny progress to ensure animation begins
};
// Update state with the new object and flag for zero interval
newStates[process.id] = {
...processState,
spawnedObjects: {
...processState.spawnedObjects,
[objectId]: newObject,
},
objectIdCounter: processState.objectIdCounter + 1,
nextSpawnTime: currentTime + effectiveSpawnInterval,
// Mark that we've spawned an object for zero interval case
hasSpawnedZeroIntervalObject:
spawnInterval === 0
? true
: processState.hasSpawnedZeroIntervalObject,
};
}
});
return newStates;
});
});
// Second useFrame for animation logic
useFrame((_, delta) => {
// Animation logic frame
const currentTime =
clockRef.current.getElapsedTime() - elapsedBeforePauseRef.current;
setAnimationStates((prev) => {
const newStates = { ...prev };
processedProcesses.forEach((process) => {
const processState = newStates[process.id];
if (!processState) {
return;
}
// Check connection status with debugging
const isConnected = isConnectedToActiveArmBot(process.id);
// console.log(
// `Process ${process.id} animation - connected:`,
// isConnected
// );
if (isConnected) {
// Stop all animations when connected to active arm bot
newStates[process.id] = {
...processState,
spawnedObjects: Object.entries(processState.spawnedObjects).reduce(
(acc, [id, obj]) => ({
...acc,
[id]: {
...obj,
state: {
...obj.state,
isAnimating: false, // Stop animation
isDelaying: false, // Clear delays
delayComplete: false, // Reset delays
progress: 0, // Reset progress
},
},
}),
{}
),
};
return;
}
// Process delay handling
if (processState.isProcessDelaying) {
const effectiveDelayTime =
processState.processDelayDuration / speedRef.current;
if (
currentTime - processState.processDelayStartTime >=
effectiveDelayTime
) {
// console.log(
// `Process ${process.id} delay completed, resuming animation`
// );
newStates[process.id] = {
...processState,
isProcessDelaying: false,
spawnedObjects: Object.entries(
processState.spawnedObjects
).reduce(
(acc, [id, obj]) => ({
...acc,
[id]: {
...obj,
state: {
...obj.state,
isDelaying: false,
delayComplete: true,
isAnimating: true,
progress:
obj.state.progress === 0 ? 0.005 : obj.state.progress,
},
},
}),
{}
),
};
return;
} else {
return;
}
}
// Ensure we have a valid path to follow
const path =
process.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) ||
[];
if (path.length < 2) {
// console.log(
// `Process ${process.id} has insufficient path points: ${path.length}`
// );
return;
}
const updatedObjects = { ...processState.spawnedObjects };
let animationOccurring = false; // Track if any animation is happening
Object.entries(processState.spawnedObjects).forEach(
([objectId, obj]) => {
if (!obj.visible) {
return;
}
const currentRef = gltf?.scene ? obj.ref.current : obj.ref.current;
if (!currentRef) {
// console.log(
// `No reference for object ${objectId}, skipping animation`
// );
return;
}
// Initialize position for new objects
if (
obj.position &&
obj.state.currentIndex === 0 &&
obj.state.progress === 0
) {
currentRef.position.copy(obj.position);
}
const stateRef = obj.state;
// Ensure animation state is properly set for objects
if (!stateRef.isAnimating && !stateRef.isDelaying && !isConnected) {
stateRef.isAnimating = true;
stateRef.progress =
stateRef.progress > 0 ? stateRef.progress : 0.005;
}
// Handle delay logic
if (stateRef.isDelaying) {
const effectiveDelayTime =
stateRef.currentDelayDuration / speedRef.current;
if (currentTime - stateRef.delayStartTime >= effectiveDelayTime) {
// console.log(
// `Delay complete for object ${objectId}, resuming animation`
// );
stateRef.isDelaying = false;
stateRef.delayComplete = true;
stateRef.isAnimating = true;
if (stateRef.progress === 0) {
stateRef.progress = 0.005;
}
const nextPointIdx = stateRef.currentIndex + 1;
if (nextPointIdx < path.length) {
const slightProgress = Math.max(stateRef.progress, 0.005);
currentRef.position.lerpVectors(
path[stateRef.currentIndex],
nextPointIdx < path.length
? path[nextPointIdx]
: path[stateRef.currentIndex],
slightProgress
);
}
} else {
updatedObjects[objectId] = { ...obj, state: { ...stateRef } };
return;
}
}
// Skip non-animating objects
if (!stateRef.isAnimating) {
// console.log(
// `Object ${objectId} not animating, skipping animation updates`
// );
return;
}
animationOccurring = true; // Mark that animation is happening
// Handle point actions
const currentPointData = getPointDataForAnimationIndex(
process,
stateRef.currentIndex
);
// Handle point actions when first arriving at point
if (stateRef.progress === 0 && currentPointData?.actions) {
const shouldStop = handlePointActions(
process.id,
objectId,
currentPointData.actions,
currentTime,
processedProcesses,
baseMaterials
);
if (shouldStop) {
updatedObjects[objectId] = { ...obj, state: { ...stateRef } };
return;
}
}
const nextPointIdx = stateRef.currentIndex + 1;
const isLastPoint = nextPointIdx >= path.length;
// Handle objects at the last point
if (isLastPoint) {
const isAgvPicking = agvRef.current.some(
(agv: any) =>
agv.processId === process.id && agv.status === "picking"
);
const shouldHide =
!currentPointData?.actions ||
!hasNonInheritActions(currentPointData.actions);
if (shouldHide) {
if (isAgvPicking) {
// console.log(
// `AGV picking at last point for object ${objectId}, hiding object`
// );
updatedObjects[objectId] = {
...obj,
visible: false,
state: {
...stateRef,
isAnimating: false,
},
};
} else {
tempStackedObjectsRef.current[objectId] = true;
updatedObjects[objectId] = {
...obj,
visible: true,
state: {
...stateRef,
isAnimating: true,
},
};
}
return;
}
}
// Handle stacked objects when AGV picks
if (tempStackedObjectsRef.current[objectId]) {
const isAgvPicking = agvRef.current.some(
(agv: any) =>
agv.processId === process.id && agv.status === "picking"
);
if (isAgvPicking) {
delete tempStackedObjectsRef.current[objectId];
updatedObjects[objectId] = {
...obj,
visible: false,
state: {
...stateRef,
isAnimating: false,
},
};
return;
}
}
// Handle normal animation progress for objects not at last point
if (!isLastPoint) {
const nextPoint = path[nextPointIdx];
const distance =
path[stateRef.currentIndex].distanceTo(nextPoint);
const effectiveSpeed = stateRef.speed * speedRef.current;
const movement = effectiveSpeed * delta;
// Ensure progress is always moving forward
if (stateRef.delayComplete && stateRef.progress < 0.01) {
stateRef.progress = 0.05;
stateRef.delayComplete = false;
// console.log(
// `Boosting progress for object ${objectId} after delay`
// );
} else {
stateRef.progress += movement / distance;
// console.log(
// `Object ${objectId} progress: ${stateRef.progress.toFixed(3)}`
// );
}
// Handle point transition
if (stateRef.progress >= 1) {
stateRef.currentIndex = nextPointIdx;
stateRef.progress = 0;
currentRef.position.copy(nextPoint);
// TRIGGER CHECK - When object arrives at new point
checkAndCountTriggers(
process.id,
objectId,
stateRef.currentIndex, // The new point index
processedProcesses,
currentTime
);
const newPointData = getPointDataForAnimationIndex(
process,
stateRef.currentIndex
);
// No action needed with newPointData here - will be handled in next frame
} else {
// Update position with lerp
currentRef.position.lerpVectors(
path[stateRef.currentIndex],
nextPoint,
stateRef.progress
);
}
}
updatedObjects[objectId] = { ...obj, state: { ...stateRef } };
}
);
// Log if no animation is occurring when it should
if (!animationOccurring && !isConnected) {
// console.log(
// `Warning: No animation occurring for process ${process.id} despite not being connected`
// );
}
newStates[process.id] = {
...processState,
spawnedObjects: updatedObjects,
};
});
return newStates;
});
});
if (!processedProcesses || processedProcesses.length === 0) {
return null;
}
return (
<group ref={groupRef}>
{Object.entries(animationStates).flatMap(([processId, processState]) =>
Object.entries(processState.spawnedObjects)
.filter(([_, obj]) => obj.visible)
.map(([objectId, obj]) => {
const process = processedProcesses.find((p) => p.id === processId);
const renderAs = process?.renderAs || "custom";
return (
<ProcessObject
key={objectId}
objectId={objectId}
obj={obj}
renderAs={renderAs}
gltf={gltf}
/>
);
})
)}
</group>
);
};
export default ProcessAnimator;

View File

@@ -1,52 +0,0 @@
import React, { useState } from "react";
import ProcessCreator from "./processCreator";
import ProcessAnimator from "./processAnimator";
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface ProcessContainerProps {
processes: any[];
setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
agvRef: any;
MaterialRef: any;
armBots: ArmBotState[];
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
}
const ProcessContainer: React.FC<ProcessContainerProps> = ({
processes,
setProcesses,
agvRef,
MaterialRef,
armBots,
setArmBots
}) => {
return (
<>
<ProcessCreator onProcessesCreated={setProcesses} />
<ProcessAnimator
processes={processes}
setProcesses={setProcesses}
agvRef={agvRef}
MaterialRef={MaterialRef}
armBots={armBots}
setArmBots={setArmBots}
/>
</>
);
};
export default ProcessContainer;

View File

@@ -1,492 +0,0 @@
import React, {
useEffect,
useMemo,
useState,
useCallback,
useRef,
} from "react";
import { useSimulationStates } from "../../../store/store";
import * as THREE from "three";
import { useThree } from "@react-three/fiber";
import {
ArmBotEventsSchema,
ConveyorEventsSchema,
VehicleEventsSchema,
} from "../../../types/simulationTypes";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
// Type definitions
export interface PointAction {
uuid: string;
name: string;
type: string;
material: string;
delay: number | string;
spawnInterval: string | number;
isUsed: boolean;
}
export interface PointTrigger {
uuid: string;
bufferTime: number;
name: string;
type: string;
isUsed: boolean;
}
// Update the connections type in your interfaces
export interface PathPoint {
uuid: string;
position: [number, number, number];
actions: PointAction[];
triggers: PointTrigger[];
connections: {
targets: Array<{ modelUUID: string; pointUUID?: string }>;
};
}
export interface SimulationPath {
type: string;
modeluuid: string;
points: PathPoint[];
pathPosition: [number, number, number];
speed?: number;
isActive: boolean;
}
export interface ArmBot {
type: string;
modeluuid: string;
points: PathPoint[];
pathPosition: [number, number, number];
speed?: number;
isActive: boolean;
}
export interface Process {
id: string;
paths: SimulationPath[];
animationPath: THREE.Vector3[];
pointActions: PointAction[][];
pointTriggers: PointTrigger[][];
speed: number;
isActive: boolean;
}
interface ProcessCreatorProps {
onProcessesCreated: (processes: Process[]) => void;
}
// Convert event schemas to SimulationPath
function convertToSimulationPath(
path: ConveyorEventsSchema | VehicleEventsSchema | ArmBotEventsSchema
): SimulationPath {
const { modeluuid } = path;
// Normalized action handler
const normalizeAction = (action: any): PointAction => {
return { ...action }; // Return exact copy with no modifications
};
// Normalized trigger handler
const normalizeTrigger = (trigger: any): PointTrigger => {
return { ...trigger }; // Return exact copy with no modifications
};
if (path.type === "Conveyor") {
return {
type: path.type,
modeluuid,
points: path.points.map((point) => ({
uuid: point.uuid,
position: point.position,
actions: Array.isArray(point.actions)
? point.actions.map(normalizeAction)
: point.actions
? [normalizeAction(point.actions)]
: [],
triggers: Array.isArray(point.triggers)
? point.triggers.map(normalizeTrigger)
: point.triggers
? [normalizeTrigger(point.triggers)]
: [],
connections: {
targets: point.connections.targets.map((target) => ({
modelUUID: target.modelUUID,
})),
},
})),
pathPosition: path.position,
speed:
typeof path.speed === "string"
? parseFloat(path.speed) || 1
: path.speed || 1,
isActive: false, // Added missing property
};
} else if (path.type === "ArmBot") {
return {
type: path.type,
modeluuid,
points: [
{
uuid: path.points.uuid,
position: path.points.position,
actions: Array.isArray(path.points.actions)
? path.points.actions.map(normalizeAction)
: path.points.actions
? [normalizeAction(path.points.actions)]
: [],
triggers: Array.isArray(path.points.triggers)
? path.points.triggers.map(normalizeTrigger)
: path.points.triggers
? [normalizeTrigger(path.points.triggers)]
: [],
connections: {
targets: path.points.connections.targets.map((target) => ({
modelUUID: target.modelUUID,
pointUUID: target.pointUUID, // Include if available
})),
},
},
],
pathPosition: path.position,
speed: path.points.actions?.speed || 1,
isActive: false,
};
} else {
// For vehicle paths, handle the case where triggers might not exist
return {
type: path.type,
modeluuid,
points: [
{
uuid: path.points.uuid,
position: path.points.position,
actions: Array.isArray(path.points.actions)
? path.points.actions.map(normalizeAction)
: path.points.actions
? [normalizeAction(path.points.actions)]
: [],
triggers: [],
connections: {
targets: path.points.connections.targets.map((target) => ({
modelUUID: target.modelUUID,
})),
},
},
],
pathPosition: path.position,
speed: path.points.speed || 1,
isActive: false,
};
}
}
// Helper function to create an empty process
const createEmptyProcess = (): Process => ({
id: `process-${Math.random().toString(36).substring(2, 11)}`,
paths: [],
animationPath: [],
pointActions: [],
pointTriggers: [], // Added point triggers array
speed: 1,
isActive: false,
});
// Enhanced connection checking function
function shouldReverseNextPath(
currentPath: SimulationPath,
nextPath: SimulationPath
): boolean {
if (nextPath.points.length !== 3) return false;
const currentLastPoint = currentPath.points[currentPath.points.length - 1];
const nextFirstPoint = nextPath.points[0];
const nextLastPoint = nextPath.points[nextPath.points.length - 1];
// Check if current last connects to next last (requires reversal)
const connectsToLast = currentLastPoint.connections.targets.some(
(target) =>
target.modelUUID === nextPath.modeluuid &&
nextLastPoint.connections.targets.some(
(t) => t.modelUUID === currentPath.modeluuid
)
);
// Check if current last connects to next first (no reversal needed)
const connectsToFirst = currentLastPoint.connections.targets.some(
(target) =>
target.modelUUID === nextPath.modeluuid &&
nextFirstPoint.connections.targets.some(
(t) => t.modelUUID === currentPath.modeluuid
)
);
// Only reverse if connected to last point and not to first point
return connectsToLast && !connectsToFirst;
}
// Check if a point has a spawn action
function hasSpawnAction(point: PathPoint): boolean {
return point.actions.some((action) => action.type.toLowerCase() === "spawn");
}
// Ensure spawn point is always at the beginning of the path
function ensureSpawnPointIsFirst(path: SimulationPath): SimulationPath {
if (path.points.length !== 3) return path;
// If the third point has spawn action and first doesn't, reverse the array
if (hasSpawnAction(path.points[2]) && !hasSpawnAction(path.points[0])) {
return {
...path,
points: [...path.points].reverse(),
};
}
return path;
}
// Updated path adjustment function
function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] {
if (paths.length < 1) return paths;
const adjustedPaths = [...paths];
// First ensure all paths have spawn points at the beginning
for (let i = 0; i < adjustedPaths.length; i++) {
adjustedPaths[i] = ensureSpawnPointIsFirst(adjustedPaths[i]);
}
// Then handle connections between paths
for (let i = 0; i < adjustedPaths.length - 1; i++) {
const currentPath = adjustedPaths[i];
const nextPath = adjustedPaths[i + 1];
if (shouldReverseNextPath(currentPath, nextPath)) {
const reversedPoints = [
nextPath.points[2],
nextPath.points[1],
nextPath.points[0],
];
adjustedPaths[i + 1] = {
...nextPath,
points: reversedPoints,
};
}
}
return adjustedPaths;
}
// Main hook for process creation
export function useProcessCreation() {
const { scene } = useThree();
const [processes, setProcesses] = useState<Process[]>([]);
const hasSpawnAction = useCallback((path: SimulationPath): boolean => {
if (path.type !== "Conveyor") return false;
return path.points.some((point) =>
point.actions.some((action) => action.type.toLowerCase() === "spawn")
);
}, []);
const createProcess = useCallback(
(paths: SimulationPath[]): Process => {
if (!paths || paths.length === 0) {
return createEmptyProcess();
}
const animationPath: THREE.Vector3[] = [];
const pointActions: PointAction[][] = [];
const pointTriggers: PointTrigger[][] = []; // Added point triggers collection
const processSpeed = paths[0]?.speed || 1;
for (const path of paths) {
for (const point of path.points) {
if (path.type === "Conveyor") {
const obj = scene.getObjectByProperty("uuid", point.uuid);
if (!obj) {
console.warn(`Object with UUID ${point.uuid} not found in scene`);
continue;
}
const position = obj.getWorldPosition(new THREE.Vector3());
animationPath.push(position.clone());
pointActions.push(point.actions);
pointTriggers.push(point.triggers); // Collect triggers for each point
}
}
}
return {
id: `process-${Math.random().toString(36).substring(2, 11)}`,
paths,
animationPath,
pointActions,
pointTriggers,
speed: processSpeed,
isActive: false,
};
},
[scene]
);
const getAllConnectedPaths = useCallback(
(
initialPath: SimulationPath,
allPaths: SimulationPath[],
visited: Set<string> = new Set()
): SimulationPath[] => {
const connectedPaths: SimulationPath[] = [];
const queue: SimulationPath[] = [initialPath];
visited.add(initialPath.modeluuid);
const pathMap = new Map<string, SimulationPath>();
allPaths.forEach((path) => pathMap.set(path.modeluuid, path));
while (queue.length > 0) {
const currentPath = queue.shift()!;
connectedPaths.push(currentPath);
// Process outgoing connections
for (const point of currentPath.points) {
for (const target of point.connections.targets) {
if (!visited.has(target.modelUUID)) {
const targetPath = pathMap.get(target.modelUUID);
if (targetPath) {
visited.add(target.modelUUID);
queue.push(targetPath);
}
}
}
}
// Process incoming connections
for (const [uuid, path] of pathMap) {
if (!visited.has(uuid)) {
const hasConnectionToCurrent = path.points.some((point) =>
point.connections.targets.some(
(t) => t.modelUUID === currentPath.modeluuid
)
);
if (hasConnectionToCurrent) {
visited.add(uuid);
queue.push(path);
}
}
}
}
return connectedPaths;
},
[]
);
const createProcessesFromPaths = useCallback(
(paths: SimulationPath[]): Process[] => {
if (!paths || paths.length === 0) return [];
const visited = new Set<string>();
const processes: Process[] = [];
const pathMap = new Map<string, SimulationPath>();
paths.forEach((path) => pathMap.set(path.modeluuid, path));
for (const path of paths) {
if (!visited.has(path.modeluuid) && hasSpawnAction(path)) {
const connectedPaths = getAllConnectedPaths(path, paths, visited);
const adjustedPaths = adjustPathPointsOrder(connectedPaths);
const process = createProcess(adjustedPaths);
processes.push(process);
}
}
return processes;
},
[createProcess, getAllConnectedPaths, hasSpawnAction]
);
return {
processes,
createProcessesFromPaths,
setProcesses,
};
}
const ProcessCreator: React.FC<ProcessCreatorProps> = React.memo(
({ onProcessesCreated }) => {
const { simulationStates } = useSimulationStates();
const { createProcessesFromPaths } = useProcessCreation();
const prevPathsRef = useRef<SimulationPath[]>([]);
const prevProcessesRef = useRef<Process[]>([]);
const { isPlaying } = usePlayButtonStore();
const convertedPaths = useMemo((): SimulationPath[] => {
if (!simulationStates) return [];
return simulationStates.map((path) =>
convertToSimulationPath(
path as
| ConveyorEventsSchema
| VehicleEventsSchema
| ArmBotEventsSchema
)
);
}, [simulationStates]);
// Enhanced dependency tracking that includes action and trigger types
const pathsDependency = useMemo(() => {
if (!convertedPaths) return null;
return convertedPaths.map((path) => ({
id: path.modeluuid,
// Track all action types for each point
actionSignature: path.points
.map((point, index) =>
point.actions.map((action) => `${index}-${action.type}`).join("|")
)
.join(","),
// Track all trigger types for each point
triggerSignature: path.points
.map((point, index) =>
point.triggers
.map((trigger) => `${index}-${trigger.type}`)
.join("|")
)
.join(","),
connections: path.points
.flatMap((p: PathPoint) =>
p.connections.targets.map((t: { modelUUID: string }) => t.modelUUID)
)
.join(","),
isActive: false,
}));
}, [convertedPaths]);
// Force process recreation when paths change
useEffect(() => {
if (!convertedPaths || convertedPaths.length === 0) {
if (prevProcessesRef.current.length > 0) {
onProcessesCreated([]);
prevProcessesRef.current = [];
}
return;
}
// Always regenerate processes if the pathsDependency has changed
// This ensures action and trigger type changes will be detected
const newProcesses = createProcessesFromPaths(convertedPaths);
prevPathsRef.current = convertedPaths;
// Always update processes when action or trigger types change
onProcessesCreated(newProcesses);
prevProcessesRef.current = newProcesses;
}, [
pathsDependency, // This now includes action and trigger types
onProcessesCreated,
convertedPaths,
createProcessesFromPaths,
]);
return null;
}
);
export default ProcessCreator;

View File

@@ -1,58 +0,0 @@
import React, { useMemo } from "react";
import * as THREE from "three";
import { GLTF } from "three-stdlib";
import { SpawnedObject } from "./types";
interface ProcessObjectProps {
objectId: string;
obj: SpawnedObject;
renderAs?: "box" | "custom";
gltf?: GLTF;
}
const ProcessObject: React.FC<ProcessObjectProps> = ({
objectId,
obj,
renderAs = "custom",
gltf,
}) => {
const renderedObject = useMemo(() => {
if (renderAs === "box") {
return (
<mesh
key={objectId}
ref={obj.ref as React.RefObject<THREE.Mesh>}
material={obj.material}
position={obj.position}
>
<boxGeometry args={[1, 1, 1]} />
</mesh>
);
}
if (gltf?.scene) {
const clonedScene = gltf.scene.clone();
clonedScene.traverse((child) => {
if (child instanceof THREE.Mesh) {
child.material = obj.material;
}
});
return (
<group
key={objectId}
ref={obj.ref as React.RefObject<THREE.Group>}
position={obj.position}
>
<primitive object={clonedScene} />
</group>
);
}
return null;
}, [objectId, obj, renderAs, gltf]);
return renderedObject;
};
export default ProcessObject;

View File

@@ -1,86 +0,0 @@
import * as THREE from "three";
export interface Trigger {
uuid: string;
name: string;
type: string;
bufferTime: number;
isUsed: boolean;
}
export interface PointAction {
uuid: string;
name: string;
type: "Inherit" | "Spawn" | "Despawn" | "Delay" | "Swap";
objectType: string;
material: string;
delay: string | number;
spawnInterval: string | number;
isUsed: boolean;
hitCount?: number;
}
export interface ProcessPoint {
uuid: string;
position: number[];
rotation: number[];
actions: PointAction[];
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
triggers?: Trigger[];
}
export interface ProcessPath {
modeluuid: string;
modelName: string;
points: ProcessPoint[];
pathPosition: number[];
pathRotation: number[];
speed: number;
type: "Conveyor" | "Vehicle" | "ArmBot";
isActive: boolean
}
export interface ProcessData {
id: string;
paths: ProcessPath[];
animationPath: { x: number; y: number; z: number }[];
pointActions: PointAction[][];
speed: number;
customMaterials?: Record<string, THREE.Material>;
renderAs?: "box" | "custom";
pointTriggers: [];
}
export interface AnimationState {
currentIndex: number;
progress: number;
isAnimating: boolean;
speed: number;
isDelaying: boolean;
delayStartTime: number;
currentDelayDuration: number;
delayComplete: boolean;
currentPathIndex: number;
}
export interface SpawnedObject {
ref: React.RefObject<THREE.Group | THREE.Mesh>;
state: AnimationState;
visible: boolean;
material: THREE.Material;
spawnTime: number;
currentMaterialType: string;
position: THREE.Vector3;
}
export interface ProcessAnimationState {
spawnedObjects: { [objectId: string]: SpawnedObject };
nextSpawnTime: number;
objectIdCounter: number;
isProcessDelaying: boolean;
processDelayStartTime: number;
processDelayDuration: number;
hasSpawnedZeroIntervalObject?: boolean;
}

View File

@@ -1,671 +0,0 @@
import { useCallback, useEffect, useRef, useState } from "react";
import * as THREE from "three";
import {
ProcessData,
ProcessAnimationState,
SpawnedObject,
AnimationState,
ProcessPoint,
PointAction,
Trigger,
} from "./types";
import {
useAnimationPlaySpeed,
usePauseButtonStore,
usePlayButtonStore,
useResetButtonStore,
} from "../../../store/usePlayButtonStore";
import { usePlayAgv, useSimulationStates } from "../../../store/store";
interface ArmBotProcess {
triggerId: string;
startPoint: string;
endPoint: string;
}
// Enhanced ProcessAnimationState with trigger tracking
interface EnhancedProcessAnimationState extends ProcessAnimationState {
triggerCounts: Record<string, number>;
triggerLogs: Array<{
timestamp: number;
pointId: string;
objectId: string;
triggerId: string;
}>;
}
interface ProcessContainerProps {
processes: ProcessData[];
setProcesses: React.Dispatch<React.SetStateAction<any[]>>;
agvRef: any;
}
interface PlayAgvState {
playAgv: Record<string, any>;
setPlayAgv: (data: any) => void;
}
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
export const useProcessAnimation = (
processes: ProcessData[],
setProcesses: React.Dispatch<React.SetStateAction<any[]>>,
agvRef: any,
armBots: ArmBotState[],
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>
) => {
// State and refs initialization
const { isPlaying, setIsPlaying } = usePlayButtonStore();
const { isPaused, setIsPaused } = usePauseButtonStore();
const { isReset, setReset } = useResetButtonStore();
const debugRef = useRef<boolean>(false);
const clockRef = useRef<THREE.Clock>(new THREE.Clock());
const pauseTimeRef = useRef<number>(0);
const elapsedBeforePauseRef = useRef<number>(0);
const animationStatesRef = useRef<Record<string, EnhancedProcessAnimationState>>({});
const { speed } = useAnimationPlaySpeed();
const prevIsPlaying = useRef<boolean | null>(null);
const [internalResetFlag, setInternalResetFlag] = useState(false);
const [animationStates, setAnimationStates] = useState<Record<string, EnhancedProcessAnimationState>>({});
const speedRef = useRef<number>(speed);
const { PlayAgv, setPlayAgv } = usePlayAgv();
const { simulationStates } = useSimulationStates();
// Effect hooks
useEffect(() => {
speedRef.current = speed;
}, [speed]);
useEffect(() => {
if (prevIsPlaying.current !== null || !isPlaying) {
setAnimationStates({});
}
prevIsPlaying.current = isPlaying;
}, [isPlaying]);
useEffect(() => {
animationStatesRef.current = animationStates;
}, [animationStates]);
// Reset handler
useEffect(() => {
if (isReset) {
setInternalResetFlag(true);
setIsPlaying(false);
setIsPaused(false);
setAnimationStates({});
animationStatesRef.current = {};
clockRef.current = new THREE.Clock();
elapsedBeforePauseRef.current = 0;
pauseTimeRef.current = 0;
setReset(false);
setTimeout(() => {
setInternalResetFlag(false);
setIsPlaying(true);
}, 0);
}
}, [isReset, setReset, setIsPlaying, setIsPaused]);
// Pause handler
useEffect(() => {
if (isPaused) {
pauseTimeRef.current = clockRef.current.getElapsedTime();
} else if (pauseTimeRef.current > 0) {
const pausedDuration = clockRef.current.getElapsedTime() - pauseTimeRef.current;
elapsedBeforePauseRef.current += pausedDuration;
}
}, [isPaused]);
// Initialize animation states with trigger tracking
useEffect(() => {
if (isPlaying && !internalResetFlag) {
const newStates: Record<string, EnhancedProcessAnimationState> = {};
processes.forEach((process) => {
const triggerCounts: Record<string, number> = {};
// Initialize trigger counts for all On-Hit triggers
process.paths?.forEach((path) => {
path.points?.forEach((point) => {
point.triggers?.forEach((trigger: Trigger) => {
if (trigger.type === "On-Hit" && trigger.isUsed) {
triggerCounts[`${point.uuid}-${trigger.uuid}`] = 0;
}
});
});
});
newStates[process.id] = {
spawnedObjects: {},
nextSpawnTime: 0,
objectIdCounter: 0,
isProcessDelaying: false,
processDelayStartTime: 0,
processDelayDuration: 0,
triggerCounts,
triggerLogs: [],
};
});
setAnimationStates(newStates);
animationStatesRef.current = newStates;
clockRef.current.start();
}
}, [isPlaying, processes, internalResetFlag]);
useEffect(() => {
if (isPlaying && !internalResetFlag) {
const newStates: Record<string, EnhancedProcessAnimationState> = {};
// Initialize AGVs for each process first
processes.forEach((process) => {
// Find all vehicle paths for this process
const vehiclePaths = process.paths?.filter(
(path) => path.type === "Vehicle"
) || [];
// Initialize AGVs for each vehicle path
vehiclePaths.forEach((vehiclePath) => {
if (vehiclePath.points?.length > 0) {
const vehiclePoint = vehiclePath.points[0];
const action = vehiclePoint.actions?.[0];
const maxHitCount = action?.hitCount;
const vehicleId = vehiclePath.modeluuid;
const processId = process.id;
// Check if this AGV already exists
const existingAgv = agvRef.current.find(
(v: any) => v.vehicleId === vehicleId && v.processId === processId
);
if (!existingAgv) {
// Initialize the AGV in a stationed state
agvRef.current.push({
processId,
vehicleId,
maxHitCount: maxHitCount || 0,
isActive: false,
hitCount: 0,
status: 'stationed',
lastUpdated: 0
});
}
}
});
// Then initialize trigger counts as before
const triggerCounts: Record<string, number> = {};
process.paths?.forEach((path) => {
path.points?.forEach((point) => {
point.triggers?.forEach((trigger: Trigger) => {
if (trigger.type === "On-Hit" && trigger.isUsed) {
triggerCounts[`${point.uuid}-${trigger.uuid}`] = 0;
}
});
});
});
newStates[process.id] = {
spawnedObjects: {},
nextSpawnTime: 0,
objectIdCounter: 0,
isProcessDelaying: false,
processDelayStartTime: 0,
processDelayDuration: 0,
triggerCounts,
triggerLogs: [],
};
});
setAnimationStates(newStates);
animationStatesRef.current = newStates;
clockRef.current.start();
}
}, [isPlaying, processes, internalResetFlag]);
// Helper functions
const findSpawnPoint = (process: ProcessData): ProcessPoint | null => {
for (const path of process.paths || []) {
for (const point of path.points || []) {
const spawnAction = point.actions?.find(
(a) => a.isUsed && a.type === "Spawn"
);
if (spawnAction) {
return point;
}
}
}
return null;
};
const findAnimationPathPoint = (
process: ProcessData,
spawnPoint: ProcessPoint
): THREE.Vector3 => {
if (process.animationPath && process.animationPath.length > 0) {
let pointIndex = 0;
for (const path of process.paths || []) {
for (let i = 0; i < (path.points?.length || 0); i++) {
const point = path.points?.[i];
if (point && point.uuid === spawnPoint.uuid) {
if (process.animationPath[pointIndex]) {
const p = process.animationPath[pointIndex];
return new THREE.Vector3(p.x, p.y, p.z);
}
}
pointIndex++;
}
}
}
return new THREE.Vector3(
spawnPoint.position[0],
spawnPoint.position[1],
spawnPoint.position[2]
);
};
// Optimized object creation
const createSpawnedObject = useCallback(
(
process: ProcessData,
currentTime: number,
materialType: string,
spawnPoint: ProcessPoint,
baseMaterials: Record<string, THREE.Material>
): SpawnedObject => {
const processMaterials = {
...baseMaterials,
...(process.customMaterials || {}),
};
const spawnPosition = findAnimationPathPoint(process, spawnPoint);
const material =
processMaterials[materialType as keyof typeof processMaterials] ||
baseMaterials.Default;
return {
ref: { current: null },
state: {
currentIndex: 0,
progress: 0,
isAnimating: true,
speed: process.speed || 1,
isDelaying: false,
delayStartTime: 0,
currentDelayDuration: 0,
delayComplete: false,
currentPathIndex: 0,
},
visible: true,
material: material,
currentMaterialType: materialType,
spawnTime: currentTime,
position: spawnPosition,
};
},
[]
);
// Material handling
const handleMaterialSwap = useCallback(
(
processId: string,
objectId: string,
materialType: string,
processes: ProcessData[],
baseMaterials: Record<string, THREE.Material>
) => {
setAnimationStates((prev) => {
const processState = prev[processId];
if (!processState || !processState.spawnedObjects[objectId])
return prev;
const process = processes.find((p) => p.id === processId);
if (!process) return prev;
const processMaterials = {
...baseMaterials,
...(process.customMaterials || {}),
};
const newMaterial =
processMaterials[materialType as keyof typeof processMaterials];
if (!newMaterial) return prev;
return {
...prev,
[processId]: {
...processState,
spawnedObjects: {
...processState.spawnedObjects,
[objectId]: {
...processState.spawnedObjects[objectId],
material: newMaterial,
currentMaterialType: materialType,
},
},
},
};
});
},
[]
);
// Point action handler with trigger counting
const handlePointActions = useCallback(
(
processId: string,
objectId: string,
actions: PointAction[] = [],
currentTime: number,
processes: ProcessData[],
baseMaterials: Record<string, THREE.Material>
): boolean => {
let shouldStopAnimation = false;
actions.forEach((action) => {
if (!action.isUsed) return;
switch (action.type) {
case "Delay":
setAnimationStates((prev) => {
const processState = prev[processId];
if (!processState || processState.isProcessDelaying) return prev;
const delayDuration =
typeof action.delay === "number"
? action.delay
: parseFloat(action.delay as string) || 0;
if (delayDuration > 0) {
return {
...prev,
[processId]: {
...processState,
isProcessDelaying: true,
processDelayStartTime: currentTime,
processDelayDuration: delayDuration,
spawnedObjects: {
...processState.spawnedObjects,
[objectId]: {
...processState.spawnedObjects[objectId],
state: {
...processState.spawnedObjects[objectId].state,
isAnimating: false,
isDelaying: true,
delayStartTime: currentTime,
currentDelayDuration: delayDuration,
delayComplete: false,
},
},
},
},
};
}
return prev;
});
shouldStopAnimation = true;
break;
case "Despawn":
setAnimationStates((prev) => {
const processState = prev[processId];
if (!processState) return prev;
const newSpawnedObjects = { ...processState.spawnedObjects };
delete newSpawnedObjects[objectId];
return {
...prev,
[processId]: {
...processState,
spawnedObjects: newSpawnedObjects,
},
};
});
shouldStopAnimation = true;
break;
case "Swap":
if (action.material) {
handleMaterialSwap(
processId,
objectId,
action.material,
processes,
baseMaterials
);
}
break;
default:
break;
}
});
return shouldStopAnimation;
},
[handleMaterialSwap]
);
const deferredArmBotUpdates = useRef<{ uuid: string; triggerId: string }[]>([]);
// Trigger counting system
const checkAndCountTriggers = useCallback(
(
processId: string,
objectId: string,
currentPointIndex: number,
processes: ProcessData[],
currentTime: number
) => {
setAnimationStates((prev) => {
const processState = prev[processId];
if (!processState) return prev;
const process = processes.find((p) => p.id === processId);
if (!process) return prev;
const point = getPointDataForAnimationIndex(process, currentPointIndex);
if (!point?.triggers) return prev;
const onHitTriggers = point.triggers.filter((t: Trigger) => t.type === "On-Hit" && t.isUsed);
if (onHitTriggers.length === 0) return prev;
let newTriggerCounts = { ...processState.triggerCounts };
const newTriggerLogs = [...processState.triggerLogs];
let shouldLog = false;
const vehiclePaths = process.paths.filter((path) => path.type === "Vehicle");
const armBotPaths = process.paths.filter((path) => path.type === "ArmBot");
const activeVehicles = vehiclePaths.filter((path) => {
const vehicleId = path.modeluuid;
const vehicleEntry = agvRef.current.find((v: any) => v.vehicleId === vehicleId && v.processId === processId);
return vehicleEntry?.isActive;
});
// Check if any ArmBot is active for this process
// const activeArmBots = armBotPaths.filter((path) => {
// const armBotId = path.modeluuid;
// const armBotEntry = armBots.find((a: any) => a.uuid === armBotId);
// return armBotEntry;
// });
// Only count triggers if no vehicles and no ArmBots are active for this process
if (activeVehicles.length === 0) {
onHitTriggers.forEach((trigger: Trigger) => {
const triggerKey = `${point.uuid}-${trigger.uuid}`;
newTriggerCounts[triggerKey] = (newTriggerCounts[triggerKey] || 0) + 1;
newTriggerLogs.push({ timestamp: currentTime, pointId: point.uuid, objectId, triggerId: trigger.uuid, });
const connections = point.connections?.targets || [];
connections.forEach((connection) => {
const connectedModelUUID = connection.modelUUID;
const isConveyor = simulationStates.find((state) => state.modeluuid === connectedModelUUID && state.type === "Conveyor");
if (!isConveyor) {
const matchingArmPath = armBotPaths.find((path) => path.modeluuid === connectedModelUUID);
if (matchingArmPath) {
deferredArmBotUpdates.current.push({
uuid: connectedModelUUID,
triggerId: trigger.uuid,
});
} else {
shouldLog = true;
}
}
});
});
}
let processTotalHits = Object.values(newTriggerCounts).reduce((a, b) => a + b, 0);
// Handle logic for vehicles when a trigger is hit
if (shouldLog) {
vehiclePaths.forEach((vehiclePath) => {
if (vehiclePath.points?.length > 0) {
const vehiclePoint = vehiclePath.points[0];
const action = vehiclePoint.actions?.[0];
const maxHitCount = action?.hitCount;
if (maxHitCount !== undefined) {
const vehicleId = vehiclePath.modeluuid;
let vehicleEntry = agvRef.current.find(
(v: any) =>
v.vehicleId === vehicleId && v.processId === processId
);
if (!vehicleEntry) {
vehicleEntry = {
processId,
vehicleId,
maxHitCount: maxHitCount,
isActive: false,
hitCount: 0,
status: "stationed",
};
agvRef.current.push(vehicleEntry);
}
if (!vehicleEntry.isActive) {
vehicleEntry.hitCount++;
vehicleEntry.lastUpdated = currentTime;
if (vehicleEntry.hitCount >= vehicleEntry.maxHitCount) {
vehicleEntry.isActive = true;
newTriggerCounts = {};
processTotalHits = 0;
}
}
}
}
});
}
return {
...prev,
[processId]: {
...processState,
triggerCounts: newTriggerCounts,
triggerLogs: newTriggerLogs,
totalHits: processTotalHits,
},
};
});
}, []);
useEffect(() => {
// console.log('deferredArmBotUpdates: ', deferredArmBotUpdates);
if (deferredArmBotUpdates.current.length > 0) {
const updates = [...deferredArmBotUpdates.current];
deferredArmBotUpdates.current = [];
setArmBots((prev) =>
prev.map((bot) => {
const update = updates.find((u) => u.uuid === bot.uuid);
return update
? { ...bot, triggerId: update.triggerId, isActive: true }
: bot;
})
);
}
}, [animationStates]);
// Utility functions
const hasNonInheritActions = useCallback(
(actions: PointAction[] = []): boolean => {
return actions.some(
(action) => action.isUsed && action.type !== "Inherit"
);
}, []);
const getPointDataForAnimationIndex = useCallback(
(process: ProcessData, index: number): ProcessPoint | null => {
if (!process.paths) return null;
let cumulativePoints = 0;
for (const path of process.paths) {
const pointCount = path.points?.length || 0;
if (index < cumulativePoints + pointCount) {
const pointIndex = index - cumulativePoints;
return path.points?.[pointIndex] || null;
}
cumulativePoints += pointCount;
}
return null;
},
[]
);
const getTriggerCounts = useCallback((processId: string) => {
return animationStatesRef.current[processId]?.triggerCounts || {};
}, []);
const getTriggerLogs = useCallback((processId: string) => {
return animationStatesRef.current[processId]?.triggerLogs || [];
}, []);
return {
animationStates,
setAnimationStates,
clockRef,
elapsedBeforePauseRef,
speedRef,
debugRef,
findSpawnPoint,
createSpawnedObject,
handlePointActions,
hasNonInheritActions,
getPointDataForAnimationIndex,
checkAndCountTriggers,
getTriggerCounts,
getTriggerLogs,
processes,
};
};

View File

@@ -1,74 +1,10 @@
import { useState, useRef } from "react"; import React from 'react'
import * as THREE from "three";
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";
import StaticMachine from "./staticMachine/staticMachine";
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
function Simulation() { function Simulation() {
const { activeModule } = useModuleStore(); return (
const pathsGroupRef = useRef() as React.MutableRefObject<THREE.Group>; <>
const [armBots, setArmBots] = useState<ArmBotState[]>([]); </>
const [staticMachines, setStaticMachines] = useState<StaticMachineState[]>([]); )
const [processes, setProcesses] = useState<any[]>([]);
const agvRef = useRef([]);
const MaterialRef = useRef([]);
return (
<>
{activeModule === "simulation" && (
<>
<PathCreation pathsGroupRef={pathsGroupRef} />
<PathConnector pathsGroupRef={pathsGroupRef} />
<ProcessContainer
processes={processes}
setProcesses={setProcesses}
agvRef={agvRef}
MaterialRef={MaterialRef}
armBots={armBots}
setArmBots={setArmBots}
/>
<Agv
processes={processes}
agvRef={agvRef}
MaterialRef={MaterialRef}
/>
</>
)}
<StaticMachine setArmBots={setArmBots} staticMachines={staticMachines} setStaticMachines={setStaticMachines} />
<ArmBot armBots={armBots} setArmBots={setArmBots} setStaticMachines={setStaticMachines} />
</>
);
} }
export default Simulation; export default Simulation

View File

@@ -1,409 +0,0 @@
// import { useMemo, useState } from 'react';
// import { useSelectedActionSphere, useToggleView, useSimulationStates, useSelectedPath, useStartSimulation, useDrawMaterialPath } from '../../store/store';
// import * as THREE from 'three';
// import useModuleStore from '../../store/useModuleStore';
// function SimulationUI() {
// const { ToggleView } = useToggleView();
// const { activeModule } = useModuleStore();
// const { startSimulation, setStartSimulation } = useStartSimulation();
// const { selectedActionSphere } = useSelectedActionSphere();
// const { selectedPath, setSelectedPath } = useSelectedPath();
// const { simulationStates, setSimulationStates } = useSimulationStates();
// const { drawMaterialPath, setDrawMaterialPath } = useDrawMaterialPath();
// const [activeButton, setActiveButton] = useState<string | null>(null);
// const handleAddAction = () => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) => {
// if (point.uuid === selectedActionSphere.points.uuid) {
// const actionIndex = point.actions.length;
// const newAction = {
// uuid: THREE.MathUtils.generateUUID(),
// name: `Action ${actionIndex + 1}`, // Assign action name based on index
// type: 'Inherit',
// material: 'Inherit',
// delay: 'Inherit',
// spawnInterval: 'Inherit',
// isUsed: false
// };
// return { ...point, actions: [...point.actions, newAction] };
// }
// return point;
// }),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleDeleteAction = (uuid: string) => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.points.uuid
// ? { ...point, actions: point.actions.filter(action => action.uuid !== uuid) }
// : point
// ),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleActionSelect = (uuid: string, actionType: string) => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.points.uuid
// ? {
// ...point,
// actions: point.actions.map((action) =>
// action.uuid === uuid ? { ...action, type: actionType } : action
// ),
// }
// : point
// ),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleMaterialSelect = (uuid: string, material: string) => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.points.uuid
// ? {
// ...point,
// actions: point.actions.map((action) =>
// action.uuid === uuid ? { ...action, material } : action
// ),
// }
// : point
// ),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleDelayChange = (uuid: string, delay: number | string) => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.points.uuid
// ? {
// ...point,
// actions: point.actions.map((action) =>
// action.uuid === uuid ? { ...action, delay } : action
// ),
// }
// : point
// ),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleSpawnIntervalChange = (uuid: string, spawnInterval: number | string) => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.points.uuid
// ? {
// ...point,
// actions: point.actions.map((action) =>
// action.uuid === uuid ? { ...action, spawnInterval } : action
// ),
// }
// : point
// ),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleSpeedChange = (speed: number) => {
// if (!selectedPath) return;
// const updatedPaths = simulationStates.map((path) =>
// path.modeluuid === selectedPath.path.modeluuid ? { ...path, speed } : path
// );
// setSimulationStates(updatedPaths);
// setSelectedPath({ ...selectedPath, path: { ...selectedPath.path, speed } });
// };
// const handleAddTrigger = () => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) => {
// if (point.uuid === selectedActionSphere.points.uuid) {
// const triggerIndex = point.triggers.length;
// const newTrigger = {
// uuid: THREE.MathUtils.generateUUID(),
// name: `Trigger ${triggerIndex + 1}`, // Assign name based on index
// type: '',
// isUsed: false
// };
// return { ...point, triggers: [...point.triggers, newTrigger] };
// }
// return point;
// }),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleDeleteTrigger = (uuid: string) => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.points.uuid
// ? { ...point, triggers: point.triggers.filter(trigger => trigger.uuid !== uuid) }
// : point
// ),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleTriggerSelect = (uuid: string, triggerType: string) => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.points.uuid
// ? {
// ...point,
// triggers: point.triggers.map((trigger) =>
// trigger.uuid === uuid ? { ...trigger, type: triggerType } : trigger
// ),
// }
// : point
// ),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleResetPath = () => {
// if (!selectedPath) return;
// };
// const handleActionToggle = (uuid: string) => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.points.uuid
// ? {
// ...point,
// actions: point.actions.map((action) => ({
// ...action,
// isUsed: action.uuid === uuid ? !action.isUsed : false,
// })),
// }
// : point
// ),
// }));
// setSimulationStates(updatedPaths);
// };
// const handleTriggerToggle = (uuid: string) => {
// if (!selectedActionSphere) return;
// const updatedPaths = simulationStates.map((path) => ({
// ...path,
// points: path.points.map((point) =>
// point.uuid === selectedActionSphere.points.uuid
// ? {
// ...point,
// triggers: point.triggers.map((trigger) => ({
// ...trigger,
// isUsed: trigger.uuid === uuid ? !trigger.isUsed : false,
// })),
// }
// : point
// ),
// }));
// setSimulationStates(updatedPaths);
// };
// const selectedPoint = useMemo(() => {
// if (!selectedActionSphere) return null;
// return simulationStates.flatMap((path) => path.points).find((point) => point.uuid === selectedActionSphere.points.uuid);
// }, [selectedActionSphere, simulationStates]);
// const createPath = () => {
// setActiveButton(activeButton !== 'addMaterialPath' ? 'addMaterialPath' : null);
// setDrawMaterialPath(!drawMaterialPath);
// }
// return (
// <>
// {activeModule === "simulation" && (
// <div style={{ zIndex: 10, position: "fixed", width: '260px' }}>
// {!ToggleView && (
// <>
// <button
// onClick={() => setStartSimulation(!startSimulation)}
// style={{
// marginTop: "10px",
// background: startSimulation ? '#ff320e' : '',
// padding: "10px",
// borderRadius: "5px"
// }}
// >
// {startSimulation ? 'Stop Simulation' : 'Start Simulation'}
// </button>
// <div style={{ zIndex: "10", position: "relative" }}>
// {!ToggleView && <button onClick={createPath} style={{ marginTop: "10px", background: activeButton === 'addMaterialPath' ? '#ff320e' : '' }}> Add Material Path</button>}
// </div>
// {selectedPath && (
// <div style={{ marginTop: "10px" }}>
// <label>Path Speed:</label>
// <input
// style={{ width: '50px' }}
// type="number"
// value={selectedPath.path.speed}
// min="0.1"
// step="0.1"
// onChange={(e) => handleSpeedChange(parseFloat(e.target.value))}
// />
// </div>
// )}
// {selectedActionSphere && (
// <div style={{ marginTop: "10px" }}>
// <button onClick={handleAddAction}>Add Action</button>
// <button onClick={handleAddTrigger}>Add Trigger</button>
// {selectedPoint?.actions.map((action) => (
// <div key={action.uuid} style={{ marginTop: "10px" }}>
// <select value={action.type} onChange={(e) => handleActionSelect(action.uuid, e.target.value)}>
// <option value="Inherit">Inherit</option>
// <option value="Spawn">Spawn Point</option>
// <option value="Swap">Swap Material</option>
// <option value="Despawn">Despawn Point</option>
// <option value="Delay">Delay</option>
// </select>
// <button onClick={() => handleDeleteAction(action.uuid)}>Delete Action</button>
// <label>
// <input
// type="checkbox"
// checked={action.isUsed}
// onChange={() => handleActionToggle(action.uuid)}
// />
// </label>
// {(action.type === 'Spawn' || action.type === 'Swap') && (
// <div style={{ marginTop: "10px" }}>
// <select value={action.material} onChange={(e) => handleMaterialSelect(action.uuid, e.target.value)}>
// <option value="Inherit">Inherit</option>
// <option value="Crate">Crate</option>
// <option value="Box">Box</option>
// </select>
// </div>
// )}
// {action.type === 'Delay' && (
// <div style={{ marginTop: "10px" }}>
// <label>Delay Time:</label>
// <input
// style={{ width: '50px' }}
// type="text"
// value={isNaN(Number(action.delay)) || action.delay === "Inherit" ? "Inherit" : action.delay}
// min="1"
// onChange={(e) => handleDelayChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
// />
// </div>
// )}
// {action.type === 'Spawn' && (
// <div style={{ marginTop: "10px" }}>
// <label>Spawn Interval:</label>
// <input
// style={{ width: '50px' }}
// type="text"
// value={isNaN(Number(action.spawnInterval)) || action.spawnInterval === "Inherit" ? "Inherit" : action.spawnInterval}
// min="1"
// onChange={(e) => handleSpawnIntervalChange(action.uuid, parseInt(e.target.value) || 'Inherit')}
// />
// </div>
// )}
// <hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
// </div>
// ))}
// <hr style={{ margin: "10px 0", border: "1px solid black" }} />
// {selectedPoint?.triggers.map((trigger) => (
// <div key={trigger.uuid} style={{ marginTop: "10px" }}>
// <select value={trigger.type} onChange={(e) => handleTriggerSelect(trigger.uuid, e.target.value)}>
// <option value="">Select Trigger Type</option>
// <option value="On-Hit">On Hit</option>
// <option value="Buffer">Buffer</option>
// </select>
// <button onClick={() => handleDeleteTrigger(trigger.uuid)}>Delete Trigger</button>
// <label>
// <input
// type="checkbox"
// checked={trigger.isUsed}
// onChange={() => handleTriggerToggle(trigger.uuid)}
// />
// </label>
// <hr style={{ margin: "10px 0", borderColor: "#ccc" }} />
// </div>
// ))}
// </div>
// )}
// {selectedPath && (
// <div style={{ marginTop: "10px" }}>
// <button
// onClick={handleResetPath}
// style={{ padding: "10px", borderRadius: "5px", background: "#ff0000", color: "#fff" }}
// >
// Reset Path
// </button>
// </div>
// )}
// </>
// )}
// </div>
// )}
// </>
// );
// }
// export default SimulationUI;

View File

@@ -1,9 +0,0 @@
import React from 'react'
function ColliderCreator() {
return (
<></>
)
}
export default ColliderCreator

View File

@@ -1,407 +0,0 @@
import { useEffect, useState } from 'react';
import * as THREE from 'three';
import { useThree, useFrame } from '@react-three/fiber';
import { Line, TransformControls } from '@react-three/drei';
import { useDrawMaterialPath } from '../../../../store/store';
type PathPoint = {
position: THREE.Vector3;
rotation: THREE.Quaternion;
uuid: string;
};
type PathCreatorProps = {
simulationStates: PathPoint[][];
setSimulationStates: React.Dispatch<React.SetStateAction<PathPoint[][]>>;
connections: { start: PathPoint; end: PathPoint }[];
setConnections: React.Dispatch<React.SetStateAction<{ start: PathPoint; end: PathPoint }[]>>
};
const PathCreator = ({ simulationStates, setSimulationStates, connections, setConnections }: PathCreatorProps) => {
const { camera, scene, raycaster, pointer, gl } = useThree();
const { drawMaterialPath } = useDrawMaterialPath();
const [currentPath, setCurrentPath] = useState<{ position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string }[]>([]);
const [temporaryPoint, setTemporaryPoint] = useState<THREE.Vector3 | null>(null);
const [selectedPoint, setSelectedPoint] = useState<{ position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string } | null>(null);
const [selectedConnectionPoint, setSelectedConnectionPoint] = useState<{ point: PathPoint; pathIndex: number } | null>(null);
const [previewConnection, setPreviewConnection] = useState<{ start: PathPoint; end?: THREE.Vector3 } | null>(null);
const [transformMode, setTransformMode] = useState<'translate' | 'rotate'>('translate');
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (selectedPoint) {
if (event.key === 'g') {
setTransformMode('translate');
} else if (event.key === 'r') {
setTransformMode('rotate');
}
}
};
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [selectedPoint]);
useEffect(() => {
const canvasElement = gl.domElement;
let drag = false;
let MouseDown = false;
const onMouseDown = () => {
MouseDown = true;
drag = false;
};
const onMouseUp = () => {
MouseDown = false;
};
const onMouseMove = () => {
if (MouseDown) {
drag = true;
}
};
const onContextMenu = (e: any) => {
e.preventDefault();
if (drag || e.button === 0) return;
if (currentPath.length > 1) {
setSimulationStates((prevPaths) => [...prevPaths, currentPath]);
}
setCurrentPath([]);
setTemporaryPoint(null);
setPreviewConnection(null);
setSelectedConnectionPoint(null);
};
const onMouseClick = (evt: any) => {
if (drag || evt.button !== 0) return;
evt.preventDefault();
raycaster.setFromCamera(pointer, camera);
let intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.some((intersect) => intersect.object.name.includes("path-point"))) {
intersects = [];
} else {
intersects = intersects.filter(
(intersect) =>
!intersect.object.name.includes("Roof") &&
!intersect.object.name.includes("agv-collider") &&
!intersect.object.name.includes("MeasurementReference") &&
!intersect.object.userData.isPathObject &&
!(intersect.object.type === "GridHelper")
);
}
if (intersects.length > 0 && selectedPoint === null) {
let point = intersects[0].point;
if (point.y < 0.05) {
point = new THREE.Vector3(point.x, 0.05, point.z);
}
const newPoint = {
position: point,
rotation: new THREE.Quaternion(),
uuid: THREE.MathUtils.generateUUID(),
};
setCurrentPath((prevPath) => [...prevPath, newPoint]);
setTemporaryPoint(null);
} else {
setSelectedPoint(null);
}
};
if (drawMaterialPath) {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
canvasElement.addEventListener("click", onMouseClick);
canvasElement.addEventListener("contextmenu", onContextMenu);
} else {
if (currentPath.length > 1) {
setSimulationStates((prevPaths) => [...prevPaths, currentPath]);
}
setCurrentPath([]);
setTemporaryPoint(null);
}
return () => {
canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("click", onMouseClick);
canvasElement.removeEventListener("contextmenu", onContextMenu);
};
}, [camera, scene, raycaster, currentPath, drawMaterialPath, selectedPoint]);
useFrame(() => {
if (drawMaterialPath && currentPath.length > 0) {
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(scene.children, true).filter(
(intersect) =>
!intersect.object.name.includes("Roof") &&
!intersect.object.name.includes("agv-collider") &&
!intersect.object.name.includes("MeasurementReference") &&
!intersect.object.userData.isPathObject &&
!(intersect.object.type === "GridHelper")
);
if (intersects.length > 0) {
let point = intersects[0].point;
if (point.y < 0.05) {
point = new THREE.Vector3(point.x, 0.05, point.z);
}
setTemporaryPoint(point);
} else {
setTemporaryPoint(null);
}
} else {
setTemporaryPoint(null);
}
});
const handlePointClick = (point: { position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string }) => {
if (currentPath.length === 0 && drawMaterialPath) {
setSelectedPoint(point);
} else {
setSelectedPoint(null);
}
};
const handleTransform = (e: any) => {
if (selectedPoint) {
const updatedPosition = e.target.object.position.clone();
const updatedRotation = e.target.object.quaternion.clone();
const updatedPaths = simulationStates.map((path) =>
path.map((p) =>
p.uuid === selectedPoint.uuid ? { ...p, position: updatedPosition, rotation: updatedRotation } : p
)
);
setSimulationStates(updatedPaths);
}
};
const meshContext = (uuid: string) => {
const pathIndex = simulationStates.findIndex(path => path.some(point => point.uuid === uuid));
if (pathIndex === -1) return;
const clickedPoint = simulationStates[pathIndex].find(point => point.uuid === uuid);
if (!clickedPoint) return;
const isStart = simulationStates[pathIndex][0].uuid === uuid;
const isEnd = simulationStates[pathIndex][simulationStates[pathIndex].length - 1].uuid === uuid;
if (pathIndex === 0 && isStart) {
console.log("The first-ever point is not connectable.");
setSelectedConnectionPoint(null);
setPreviewConnection(null);
return;
}
if (!isStart && !isEnd) {
console.log("Selected point is not a valid connection point (not start or end)");
setSelectedConnectionPoint(null);
setPreviewConnection(null);
return;
}
if (connections.some(conn => conn.start.uuid === uuid || conn.end.uuid === uuid)) {
console.log("The selected point is already connected.");
setSelectedConnectionPoint(null);
setPreviewConnection(null);
return;
}
if (!selectedConnectionPoint) {
setSelectedConnectionPoint({ point: clickedPoint, pathIndex });
setPreviewConnection({ start: clickedPoint });
console.log("First point selected for connection:", clickedPoint);
return;
}
if (selectedConnectionPoint.pathIndex === pathIndex) {
console.log("Cannot connect points within the same path.");
setSelectedConnectionPoint(null);
setPreviewConnection(null);
return;
}
if (connections.some(conn => conn.start.uuid === clickedPoint.uuid || conn.end.uuid === clickedPoint.uuid)) {
console.log("The target point is already connected.");
setSelectedConnectionPoint(null);
setPreviewConnection(null);
return;
}
setConnections(prevConnections => [
...prevConnections,
{ start: selectedConnectionPoint.point, end: clickedPoint },
]);
setSelectedConnectionPoint(null);
setPreviewConnection(null);
};
useEffect(() => {
if (!selectedConnectionPoint) {
setPreviewConnection(null);
}
}, [selectedConnectionPoint, connections]);
useFrame(() => {
if (selectedConnectionPoint) {
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObjects(scene.children, true).filter(
(intersect) =>
!intersect.object.name.includes("Roof") &&
!intersect.object.name.includes("agv-collider") &&
!intersect.object.name.includes("MeasurementReference") &&
!intersect.object.userData.isPathObject &&
!(intersect.object.type === "GridHelper")
);
if (intersects.length > 0) {
let point = intersects[0].point;
if (point.y < 0.05) {
point = new THREE.Vector3(point.x, 0.05, point.z);
}
setPreviewConnection({ start: selectedConnectionPoint.point, end: point });
} else {
setPreviewConnection(null);
}
}
});
return (
<>
<group name='pathObjects'>
{/* Render finalized simulationStates */}
{simulationStates.map((path, pathIndex) => (
<group key={`path-line-${pathIndex}`}>
<Line
name={`path-line-${pathIndex}`}
points={path.map((point) => point.position)}
color="yellow"
lineWidth={5}
userData={{ isPathObject: true }}
/>
</group>
))}
{/* Render finalized points */}
{simulationStates.map((path) =>
path.map((point) => (
<mesh
key={`path-point-${point.uuid}`}
name={`path-point-${point.uuid}`}
uuid={`${point.uuid}`}
position={point.position}
userData={{ isPathObject: true }}
onClick={() => handlePointClick(point)}
onPointerMissed={() => { setSelectedPoint(null) }}
onContextMenu={() => { meshContext(point.uuid); }}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="blue" wireframe />
</mesh>
))
)}
{connections.map((conn, index) => (
<Line
key={`connection-${index}`}
points={[conn.start.position, conn.end.position]}
color="white"
dashed
lineWidth={4}
dashSize={1}
dashScale={15}
userData={{ isPathObject: true }}
/>
))}
</group>
{/* Render current path */}
{currentPath.length > 1 && (
<group>
<Line
points={currentPath.map((point) => point.position)}
color="red"
lineWidth={5}
userData={{ isPathObject: true }}
/>
</group>
)}
{/* Render current path points */}
{currentPath.map((point) => (
<mesh
key={`current-point-${point.uuid}`}
position={point.position}
userData={{ isPathObject: true }}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="red" />
</mesh>
))}
{/* Render temporary indicator line */}
{temporaryPoint && currentPath.length > 0 && (
<group>
<Line
points={[currentPath[currentPath.length - 1].position, temporaryPoint]}
color="white"
lineWidth={2}
userData={{ isPathObject: true }}
/>
</group>
)}
{/* Render dashed preview connection */}
{previewConnection && previewConnection.end && (
<Line
points={[previewConnection.start.position, previewConnection.end]}
color="white"
dashed
lineWidth={4}
dashSize={1}
dashScale={15}
userData={{ isPathObject: true }}
/>
)}
{/* Render temporary point */}
{temporaryPoint && (
<mesh
position={temporaryPoint}
userData={{ isPathObject: true }}
>
<sphereGeometry args={[0.1, 16, 16]} />
<meshStandardMaterial color="white" />
</mesh>
)}
{/* Attach TransformControls to the selected point */}
{selectedPoint && (
<TransformControls
object={scene.getObjectByProperty('uuid', selectedPoint.uuid)}
mode={transformMode}
onObjectChange={handleTransform}
/>
)}
</>
);
};
export default PathCreator;

View File

@@ -1,164 +0,0 @@
import * as THREE from 'three';
import { useState, useEffect, useRef, useMemo } from "react";
import { useLoader, useFrame } from "@react-three/fiber";
import { GLTFLoader } from "three-stdlib";
import crate from "../../../../assets/gltf-glb/crate_box.glb";
import { useOrganization } from '../../../../store/store';
import { useControls } from 'leva';
type PathPoint = {
position: THREE.Vector3;
rotation: THREE.Quaternion;
uuid: string;
};
type PathFlowProps = {
path: PathPoint[];
connections: { start: PathPoint; end: PathPoint }[];
};
export default function PathFlow({ path, connections }: PathFlowProps) {
const { organization } = useOrganization();
const [isPaused, setIsPaused] = useState(false);
const [isStopped, setIsStopped] = useState(false);
const { spawnInterval, speed, pauseResume, startStop } = useControls({
spawnInterval: { value: 1000, min: 500, max: 5000, step: 100 },
speed: { value: 2, min: 1, max: 20, step: 0.5 },
pauseResume: { value: false, label: "Pause/Resume" },
startStop: { value: false, label: "Start/Stop" },
});
const [meshes, setMeshes] = useState<{ id: number }[]>([]);
const gltf = useLoader(GLTFLoader, crate);
const meshIdRef = useRef(0);
const lastSpawnTime = useRef(performance.now());
const totalPausedTime = useRef(0);
const pauseStartTime = useRef<number | null>(null);
useEffect(() => {
setIsPaused(pauseResume);
setIsStopped(startStop);
}, [pauseResume, startStop]);
const removeMesh = (id: number) => {
setMeshes((prev) => prev.filter((m) => m.id !== id));
};
useFrame(() => {
if (isStopped || !path) return;
const now = performance.now();
if (isPaused) {
if (pauseStartTime.current === null) {
pauseStartTime.current = now;
}
return;
}
if (pauseStartTime.current !== null) {
totalPausedTime.current += now - pauseStartTime.current;
pauseStartTime.current = null;
}
const adjustedTime = now - totalPausedTime.current;
if (adjustedTime - lastSpawnTime.current >= spawnInterval) {
setMeshes((prev) => [...prev, { id: meshIdRef.current++ }]);
lastSpawnTime.current = adjustedTime;
}
});
return (
<>
{meshes.map((mesh) => (
<MovingMesh
key={mesh.id}
meshId={mesh.id}
points={path}
speed={speed}
gltf={gltf}
removeMesh={removeMesh}
isPaused={isPaused}
/>
))}
</>
);
}
function MovingMesh({ meshId, points, speed, gltf, removeMesh, isPaused }: any) {
const meshRef = useRef<any>();
const startTime = useRef<number | null>(null); // Initialize as null
const pausedTime = useRef(0);
const pauseStartTime = useRef<number | null>(null);
const distances = useMemo(() => {
if (!points || points.length < 2) return [];
return points.slice(1).map((point: any, i: number) => points[i].position.distanceTo(point.position));
}, [points]);
useFrame(() => {
if (!points || points.length < 2) return;
if (startTime.current === null && points.length > 0) {
startTime.current = performance.now();
}
if (!meshRef.current) return;
if (isPaused) {
if (pauseStartTime.current === null) {
pauseStartTime.current = performance.now();
}
return;
}
if (pauseStartTime.current !== null) {
pausedTime.current += performance.now() - pauseStartTime.current;
pauseStartTime.current = null;
}
if (startTime.current === null) return;
const elapsed = performance.now() - startTime.current - pausedTime.current;
const distanceTraveled = elapsed / 1000 * speed;
let remainingDistance = distanceTraveled;
let currentSegmentIndex = 0;
while (currentSegmentIndex < distances.length && remainingDistance > distances[currentSegmentIndex]) {
remainingDistance -= distances[currentSegmentIndex];
currentSegmentIndex++;
}
if (currentSegmentIndex >= distances.length) {
removeMesh(meshId);
return;
}
const progress = remainingDistance / distances[currentSegmentIndex];
const start = points[currentSegmentIndex].position;
const end = points[currentSegmentIndex + 1].position;
meshRef.current.position.lerpVectors(start, end, Math.min(progress, 1));
const startRotation = points[currentSegmentIndex].rotation;
const endRotation = points[currentSegmentIndex + 1].rotation;
const interpolatedRotation = new THREE.Quaternion().slerpQuaternions(startRotation, endRotation, Math.min(progress, 1));
meshRef.current.quaternion.copy(interpolatedRotation);
});
return (
<>
{points && points.length > 0 &&
<mesh ref={meshRef}>
<primitive object={gltf.scene.clone()} />
</mesh>
}
</>
);
}

View File

@@ -1,9 +0,0 @@
import React from 'react'
function ProcessCreator() {
return (
<></>
)
}
export default ProcessCreator

View File

@@ -1,26 +0,0 @@
import React, { useState } from 'react';
import * as THREE from 'three';
import PathCreator from './path/pathCreator';
import PathFlow from './path/pathFlow';
type PathPoint = {
position: THREE.Vector3;
rotation: THREE.Quaternion;
uuid: string;
};
function Simulation() {
const [simulationStates, setSimulationStates] = useState<{ position: THREE.Vector3; rotation: THREE.Quaternion; uuid: string }[][]>([]);
const [connections, setConnections] = useState<{ start: PathPoint; end: PathPoint }[]>([]);
return (
<>
<PathCreator simulationStates={simulationStates} setSimulationStates={setSimulationStates} connections={connections} setConnections={setConnections} />
{simulationStates.map((path, index) => (
<PathFlow key={index} path={path} connections={connections} />
))}
</>
);
}
export default Simulation;

View File

@@ -1,83 +0,0 @@
import React, { useEffect } from 'react'
import * as SimulationTypes from '../../../types/simulationTypes';
import { useSimulationStates } from '../../../store/store';
import StaticMachineInstances from './staticMachineInstances';
import { useResetButtonStore } from '../../../store/usePlayButtonStore';
interface ArmBotState {
uuid: string;
position: [number, number, number];
rotation: [number, number, number];
status: string;
material: string;
triggerId: string;
connections: {
source: { modelUUID: string; pointUUID: string };
targets: { modelUUID: string; pointUUID: string }[];
};
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[]; };
isActive?: boolean;
}
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
type StaticMachineProps = {
setArmBots: React.Dispatch<React.SetStateAction<ArmBotState[]>>;
staticMachines: StaticMachineState[];
setStaticMachines: React.Dispatch<React.SetStateAction<StaticMachineState[]>>;
}
function StaticMachine({ setArmBots, staticMachines, setStaticMachines }: StaticMachineProps) {
const { simulationStates } = useSimulationStates();
const { isReset } = useResetButtonStore();
useEffect(() => {
const filtered = simulationStates.filter((s): s is SimulationTypes.StaticMachineEventsSchema => s.type === "StaticMachine");
const initialStates: StaticMachineState[] = filtered
.filter(machine => machine.points.connections.targets.length > 0)
.map(machine => ({
uuid: machine.modeluuid,
status: "idle",
actions: machine.points.actions,
machineTriggerId: machine.points.triggers.uuid,
connectedArmBot: machine.points.connections.targets[0].modelUUID
}));
setStaticMachines(initialStates);
}, [simulationStates, isReset]);
const updateArmBotTriggerAndMachineStatus = (armBotUuid: string, triggerId: string, machineId: string) => {
setArmBots((prevArmBots) => {
return prevArmBots.map(bot => {
if (bot.uuid === armBotUuid) {
return { ...bot, triggerId: triggerId };
}
return bot;
});
});
setStaticMachines((prevStaticMachines) => {
return prevStaticMachines.map(machine => {
if (machine.uuid === machineId) {
return { ...machine, status: "idle" };
} else {
return machine;
}
});
});
}
return (
<>
{staticMachines.map((machine, index) => (
<StaticMachineInstances key={index} machine={machine} updateArmBotTriggerAndMachineStatus={updateArmBotTriggerAndMachineStatus} />
))}
</>
)
}
export default StaticMachine;

View File

@@ -1,33 +0,0 @@
import React, { useEffect } from 'react'
import { useAnimationPlaySpeed } from '../../../store/usePlayButtonStore';
interface StaticMachineState {
uuid: string;
status: string;
actions: { uuid: string; name: string; buffer: number; material: string; };
machineTriggerId: string;
connectedArmBot: string;
}
type StaticMachineInstancesProps = {
machine: StaticMachineState,
updateArmBotTriggerAndMachineStatus: (armBotUuid: string, triggerId: string, machineId: string) => void;
}
function StaticMachineInstances({ machine, updateArmBotTriggerAndMachineStatus }: StaticMachineInstancesProps) {
const { speed } = useAnimationPlaySpeed();
useEffect(() => {
if (machine.status === 'running') {
setTimeout(() => {
updateArmBotTriggerAndMachineStatus(machine.connectedArmBot, machine.machineTriggerId, machine.uuid);
}, machine.actions.buffer * 1000 * speed);
}
}, [machine])
return (
<></>
)
}
export default StaticMachineInstances

View File

@@ -3,7 +3,7 @@ import { usePlayButtonStore } from "../../store/usePlayButtonStore";
import Panel from "./widgets/panel/Panel"; import Panel from "./widgets/panel/Panel";
import AddButtons from "./widgets/panel/AddButtons"; import AddButtons from "./widgets/panel/AddButtons";
import { useSelectedZoneStore } from "../../store/useZoneStore"; import { useSelectedZoneStore } from "../../store/useZoneStore";
import DisplayZone from "./DisplayZone"; import DisplayZone from "./zone/DisplayZone";
import Scene from "../scene/scene"; import Scene from "../scene/scene";
import useModuleStore from "../../store/useModuleStore"; import useModuleStore from "../../store/useModuleStore";
@@ -17,10 +17,10 @@ import {
useWidgetSubOption, useWidgetSubOption,
useZones, useZones,
} from "../../store/store"; } from "../../store/store";
import { getZone2dData } from "../../services/realTimeVisulization/zoneData/getZoneData"; import { getZone2dData } from "../../services/visulization/zone/getZoneData";
import { generateUniqueId } from "../../functions/generateUniqueId"; import { generateUniqueId } from "../../functions/generateUniqueId";
import { determinePosition } from "./functions/determinePosition"; import { determinePosition } from "./functions/determinePosition";
import { addingFloatingWidgets } from "../../services/realTimeVisulization/zoneData/addFloatingWidgets"; import { addingFloatingWidgets } from "../../services/visulization/zone/addFloatingWidgets";
import SocketRealTimeViz from "./socket/realTimeVizSocket.dev"; import SocketRealTimeViz from "./socket/realTimeVizSocket.dev";
import RenderOverlay from "../../components/templates/Overlay"; import RenderOverlay from "../../components/templates/Overlay";
import ConfirmationPopup from "../../components/layout/confirmationPopup/ConfirmationPopup"; import ConfirmationPopup from "../../components/layout/confirmationPopup/ConfirmationPopup";
@@ -31,7 +31,6 @@ import {
useRightClickSelected, useRightClickSelected,
useRightSelected, useRightSelected,
} from "../../store/useZone3DWidgetStore"; } from "../../store/useZone3DWidgetStore";
import Dropped3dWidgets from "./widgets/3d/Dropped3dWidget";
import OuterClick from "../../utils/outerClick"; import OuterClick from "../../utils/outerClick";
import { useWidgetStore } from "../../store/useWidgetStore"; import { useWidgetStore } from "../../store/useWidgetStore";
import { getActiveProperties } from "./functions/getActiveProperties"; import { getActiveProperties } from "./functions/getActiveProperties";

View File

@@ -1,109 +1,109 @@
import { Html } from "@react-three/drei"; import { Html } from "@react-three/drei";
import * as THREE from "three"; import * as THREE from "three";
import * as Types from "../../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
import { useDrieTemp, useDrieUIValue } from "../../../store/store" import { useDrieTemp, useDrieUIValue } from "../../../store/store"
import UI from "./ui"; import UI from "./ui";
import { useEffect } from "react"; import { useEffect } from "react";
import { useThree } from "@react-three/fiber"; import { useThree } from "@react-three/fiber";
export default function DrieHtmlTemp({ itemsGroup }: { itemsGroup: Types.RefGroup }) { export default function DrieHtmlTemp({ itemsGroup }: { itemsGroup: Types.RefGroup }) {
const { drieTemp, setDrieTemp } = useDrieTemp(); const { drieTemp, setDrieTemp } = useDrieTemp();
const { drieUIValue, setDrieUIValue } = useDrieUIValue(); const { drieUIValue, setDrieUIValue } = useDrieUIValue();
const state = useThree(); const state = useThree();
const { camera, raycaster } = state; const { camera, raycaster } = state;
useEffect(() => { useEffect(() => {
const canvasElement = state.gl.domElement; const canvasElement = state.gl.domElement;
let drag = false; let drag = false;
let isLeftMouseDown = false; let isLeftMouseDown = false;
const onMouseDown = (evt: any) => { const onMouseDown = (evt: any) => {
if (evt.button === 0) { if (evt.button === 0) {
isLeftMouseDown = true; isLeftMouseDown = true;
drag = false; drag = false;
} }
}; };
const onMouseMove = () => { const onMouseMove = () => {
if (isLeftMouseDown) { if (isLeftMouseDown) {
drag = true; drag = true;
} }
}; };
const onMouseUp = (evt: any) => { const onMouseUp = (evt: any) => {
if (evt.button === 0) { if (evt.button === 0) {
isLeftMouseDown = false; isLeftMouseDown = false;
if (drag) return; if (drag) return;
if (!itemsGroup.current) return if (!itemsGroup.current) return
let intersects = raycaster.intersectObjects(itemsGroup.current.children, true); let intersects = raycaster.intersectObjects(itemsGroup.current.children, true);
if (intersects.length > 0) { if (intersects.length > 0) {
let currentObject = intersects[0].object; let currentObject = intersects[0].object;
while (currentObject) { while (currentObject) {
if (currentObject.name === "Scene") { if (currentObject.name === "Scene") {
break; break;
} }
currentObject = currentObject.parent as THREE.Object3D; currentObject = currentObject.parent as THREE.Object3D;
} }
if (currentObject && (currentObject.userData.name === "SV2 Controll pannel" || currentObject.userData.name === "forklift")) { if (currentObject && (currentObject.userData.name === "SV2 Controll pannel" || currentObject.userData.name === "forklift")) {
const worldPos = new THREE.Vector3(); const worldPos = new THREE.Vector3();
currentObject.getWorldPosition(worldPos); currentObject.getWorldPosition(worldPos);
const rightOffset = new THREE.Vector3(1, 0, 0); const rightOffset = new THREE.Vector3(1, 0, 0);
const upOffset = new THREE.Vector3(0, 1, 0); const upOffset = new THREE.Vector3(0, 1, 0);
currentObject.localToWorld(rightOffset); currentObject.localToWorld(rightOffset);
currentObject.localToWorld(upOffset); currentObject.localToWorld(upOffset);
const finalPosition = worldPos.clone().addScaledVector(rightOffset.sub(currentObject.position).normalize(), 2.5).addScaledVector(upOffset.sub(currentObject.position).normalize(), 2.3); const finalPosition = worldPos.clone().addScaledVector(rightOffset.sub(currentObject.position).normalize(), 2.5).addScaledVector(upOffset.sub(currentObject.position).normalize(), 2.3);
setDrieTemp(finalPosition); setDrieTemp(finalPosition);
} else { } else {
setDrieTemp(undefined); setDrieTemp(undefined);
} }
} }
else { else {
setDrieTemp(undefined); setDrieTemp(undefined);
} }
} }
}; };
canvasElement.addEventListener("mousedown", onMouseDown); canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp); canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove); canvasElement.addEventListener("mousemove", onMouseMove);
return () => { return () => {
canvasElement.removeEventListener("mousedown", onMouseDown); canvasElement.removeEventListener("mousedown", onMouseDown);
canvasElement.removeEventListener("mouseup", onMouseUp); canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove); canvasElement.removeEventListener("mousemove", onMouseMove);
}; };
}, []) }, [])
return ( return (
<> <>
{drieTemp && {drieTemp &&
<mesh position={[drieTemp.x, drieTemp.y, drieTemp.z]}> <mesh position={[drieTemp.x, drieTemp.y, drieTemp.z]}>
<Html <Html
as="div" as="div"
center center
zIndexRange={[1, 0]} zIndexRange={[1, 0]}
transform transform
sprite sprite
style={{ style={{
padding: "10px", padding: "10px",
color: "white", color: "white",
borderRadius: "8px", borderRadius: "8px",
textAlign: "center", textAlign: "center",
fontFamily: "Arial, sans-serif", fontFamily: "Arial, sans-serif",
}} }}
scale={[0.3, 0.3, 0.3]} scale={[0.3, 0.3, 0.3]}
// occlude // occlude
> >
<UI temperature={drieUIValue.temperature} humidity={drieUIValue.humidity} touch={drieUIValue.touch} header={""} /> <UI temperature={drieUIValue.temperature} humidity={drieUIValue.humidity} touch={drieUIValue.touch} header={""} />
</Html> </Html>
</mesh> </mesh>
} }
</> </>
) )
} }

View File

@@ -1,141 +1,141 @@
export default function UI({ temperature, humidity, touch, header }) { export default function UI({ temperature, humidity, touch, header }) {
return ( return (
<div <div
className="temp-visualization-wrapper" className="temp-visualization-wrapper"
style={{ style={{
padding: "24px", padding: "24px",
width: "fit-content", width: "fit-content",
background: "white", background: "white",
borderRadius: "20px", borderRadius: "20px",
color: "#282829", color: "#282829",
// transform: "translate(0, -100%)" // transform: "translate(0, -100%)"
}} }}
> >
<div <div
className="header" className="header"
style={{ paddingBottom: "22px", fontWeight: "600" }} style={{ paddingBottom: "22px", fontWeight: "600" }}
> >
{header ? header : "Sensor Details"} {header ? header : "Sensor Details"}
</div> </div>
<div className="container-1" style={{ display: "flex", gap: "24px" }}> <div className="container-1" style={{ display: "flex", gap: "24px" }}>
<div <div
className="temperature-container" className="temperature-container"
style={{ style={{
padding: "12px", padding: "12px",
borderRadius: "12px", borderRadius: "12px",
background: "white", background: "white",
boxShadow: "7px 7px 14px #e3e3e3, -7px -7px 14px #f4f4f4", boxShadow: "7px 7px 14px #e3e3e3, -7px -7px 14px #f4f4f4",
display: "flex", display: "flex",
gap: "6px", gap: "6px",
flexDirection: "column", flexDirection: "column",
width: "92px", width: "92px",
}} }}
> >
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "12px" }}> <div style={{ display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "12px" }}>
<svg <svg
width="24" width="24"
height="24" height="24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M8.73109 11.6758L9 11.5357V11.2324V4C9 2.61929 10.1193 1.5 11.5 1.5C12.8807 1.5 14 2.61929 14 4V11.2324V11.5357L14.2689 11.6758C16.1901 12.6771 17.5 14.6861 17.5 17.0002C17.5 20.3139 14.8137 23.0002 11.5 23.0002C8.18629 23.0002 5.5 20.3139 5.5 17.0002C5.5 14.6861 6.80994 12.6771 8.73109 11.6758Z" d="M8.73109 11.6758L9 11.5357V11.2324V4C9 2.61929 10.1193 1.5 11.5 1.5C12.8807 1.5 14 2.61929 14 4V11.2324V11.5357L14.2689 11.6758C16.1901 12.6771 17.5 14.6861 17.5 17.0002C17.5 20.3139 14.8137 23.0002 11.5 23.0002C8.18629 23.0002 5.5 20.3139 5.5 17.0002C5.5 14.6861 6.80994 12.6771 8.73109 11.6758Z"
stroke="#FE4519" stroke="#FE4519"
/> />
<path d="M11.5 7V16" stroke="#FE4519" strokeLinecap="round" /> <path d="M11.5 7V16" stroke="#FE4519" strokeLinecap="round" />
<circle cx="11.5" cy="17" r="3" fill="#FE4519" /> <circle cx="11.5" cy="17" r="3" fill="#FE4519" />
</svg> </svg>
</div> </div>
<div className="key" style={{ fontSize: "12px" }}> <div className="key" style={{ fontSize: "12px" }}>
Temperature Temperature
</div> </div>
<div <div
className="value" className="value"
style={{ fontSize: "18px", fontWeight: "600" }} style={{ fontSize: "18px", fontWeight: "600" }}
> >
{temperature} {temperature}
</div> </div>
</div> </div>
<div <div
className="humidity-container" className="humidity-container"
style={{ style={{
padding: "12px", padding: "12px",
borderRadius: "12px", borderRadius: "12px",
background: "white", background: "white",
boxShadow: "7px 7px 14px #e3e3e3, -7px -7px 14px #f4f4f4", boxShadow: "7px 7px 14px #e3e3e3, -7px -7px 14px #f4f4f4",
display: "flex", display: "flex",
gap: "6px", gap: "6px",
flexDirection: "column", flexDirection: "column",
width: "92px", width: "92px",
}} }}
> >
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "12px" }}> <div style={{ display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "12px" }}>
<svg <svg
width="24" width="24"
height="24" height="24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M14.8041 19.1765C15.2714 17.4843 14.6826 15.891 12.6962 13.7257C12.3217 13.3175 11.6786 13.3192 11.305 13.7284C9.1738 16.0628 8.77326 17.5784 9.16555 19.0737C9.32805 19.6931 9.79837 20.1765 10.3593 20.4854C11.742 21.2468 12.2655 21.3361 13.7514 20.4639C14.2463 20.1734 14.6514 19.7296 14.8041 19.1765Z" d="M14.8041 19.1765C15.2714 17.4843 14.6826 15.891 12.6962 13.7257C12.3217 13.3175 11.6786 13.3192 11.305 13.7284C9.1738 16.0628 8.77326 17.5784 9.16555 19.0737C9.32805 19.6931 9.79837 20.1765 10.3593 20.4854C11.742 21.2468 12.2655 21.3361 13.7514 20.4639C14.2463 20.1734 14.6514 19.7296 14.8041 19.1765Z"
stroke="#0F96F5" stroke="#0F96F5"
strokeWidth="1.5" strokeWidth="1.5"
/> />
<path <path
d="M20.8104 9.0293C21.2043 7.39129 20.5932 5.82808 18.6645 3.72574C18.2899 3.31747 17.6469 3.3192 17.2733 3.72838C15.1959 6.00386 14.7629 7.50129 15.1056 8.96027C15.2684 9.65314 15.8159 10.18 16.4679 10.4655C17.7279 11.0173 18.291 11.0385 19.5446 10.4598C20.1511 10.1799 20.6542 9.6787 20.8104 9.0293Z" d="M20.8104 9.0293C21.2043 7.39129 20.5932 5.82808 18.6645 3.72574C18.2899 3.31747 17.6469 3.3192 17.2733 3.72838C15.1959 6.00386 14.7629 7.50129 15.1056 8.96027C15.2684 9.65314 15.8159 10.18 16.4679 10.4655C17.7279 11.0173 18.291 11.0385 19.5446 10.4598C20.1511 10.1799 20.6542 9.6787 20.8104 9.0293Z"
stroke="#0F96F5" stroke="#0F96F5"
strokeWidth="1.5" strokeWidth="1.5"
/> />
<path <path
d="M8.81041 9.0293C9.20431 7.39129 8.59319 5.82808 6.66448 3.72574C6.28992 3.31747 5.64687 3.3192 5.27331 3.72838C3.19591 6.00386 2.76287 7.50129 3.1056 8.96027C3.26837 9.65314 3.81593 10.18 4.46789 10.4655C5.72785 11.0173 6.29105 11.0385 7.54464 10.4598C8.15106 10.1799 8.65424 9.6787 8.81041 9.0293Z" d="M8.81041 9.0293C9.20431 7.39129 8.59319 5.82808 6.66448 3.72574C6.28992 3.31747 5.64687 3.3192 5.27331 3.72838C3.19591 6.00386 2.76287 7.50129 3.1056 8.96027C3.26837 9.65314 3.81593 10.18 4.46789 10.4655C5.72785 11.0173 6.29105 11.0385 7.54464 10.4598C8.15106 10.1799 8.65424 9.6787 8.81041 9.0293Z"
stroke="#0F96F5" stroke="#0F96F5"
strokeWidth="1.5" strokeWidth="1.5"
/> />
</svg> </svg>
</div> </div>
<div className="key" style={{ fontSize: "12px" }}> <div className="key" style={{ fontSize: "12px" }}>
Humidity Humidity
</div> </div>
<div <div
className="value" className="value"
style={{ fontSize: "18px", fontWeight: "600" }} style={{ fontSize: "18px", fontWeight: "600" }}
> >
{humidity} {humidity}
</div> </div>
</div> </div>
</div> </div>
<div className="container-2"> <div className="container-2">
<div <div
className="touch-container" className="touch-container"
style={{ style={{
display: "flex", display: "flex",
borderRadius: "12px", borderRadius: "12px",
background: "white", background: "white",
boxShadow: "7px 7px 14px #e3e3e3, -7px -7px 14px #f4f4f4", boxShadow: "7px 7px 14px #e3e3e3, -7px -7px 14px #f4f4f4",
padding: "16px", padding: "16px",
marginTop: "16px", marginTop: "16px",
gap: "18px", gap: "18px",
alignItems: "center", alignItems: "center",
fontWeight: "600", fontWeight: "600",
}} }}
> >
<div className="key" style={{ fontSize: "14px" }}> <div className="key" style={{ fontSize: "14px" }}>
Touch Sensor Touch Sensor
</div> </div>
<div <div
className="value" className="value"
style={ style={
touch === "True" touch === "True"
? { color: "#2AA553", fontWeight: 500 } ? { color: "#2AA553", fontWeight: 500 }
: { color: "#FE4519", fontWeight: 500 } : { color: "#FE4519", fontWeight: 500 }
} }
> >
{touch === "True" ? "Active" : "In active"} {touch === "True" ? "Active" : "In active"}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
); );
} }

View File

@@ -2,7 +2,7 @@ import { useEffect } from "react";
import useTemplateStore from "../../../store/useTemplateStore"; import useTemplateStore from "../../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../../store/useZoneStore"; import { useSelectedZoneStore } from "../../../store/useZoneStore";
import { useSocketStore } from "../../../store/store"; import { useSocketStore } from "../../../store/store";
import { getTemplateData } from "../../../services/realTimeVisulization/zoneData/getTemplate"; import { getTemplateData } from "../../../services/visulization/zone/getTemplate";
import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore"; import { useDroppedObjectsStore } from "../../../store/useDroppedObjectsStore";
import RenameInput from "../../../components/ui/inputs/RenameInput"; import RenameInput from "../../../components/ui/inputs/RenameInput";

View File

@@ -0,0 +1,22 @@
import React from 'react'
import Dropped3dWidgets from './widgets/3d/Dropped3dWidget'
import ZoneCentreTarget from './zone/zoneCameraTarget'
import ZoneAssets from './zone/zoneAssets'
// import MqttEvents from '../../services/factoryBuilder/mqtt/mqttEvents'
const Visualization = () => {
return (
<>
<Dropped3dWidgets />
{/* <ZoneCentreTarget />
<ZoneAssets />
<MqttEvents /> */}
</>
)
}
export default Visualization;

View File

@@ -13,8 +13,6 @@ import {
KebabIcon, KebabIcon,
} from "../../../../components/icons/ExportCommonIcons"; } from "../../../../components/icons/ExportCommonIcons";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { duplicateWidgetApi } from "../../../../services/realTimeVisulization/zoneData/duplicateWidget";
import { deleteWidgetApi } from "../../../../services/realTimeVisulization/zoneData/deleteWidgetApi";
import { useClickOutside } from "../../functions/handleWidgetsOuterClick"; import { useClickOutside } from "../../functions/handleWidgetsOuterClick";
import { useSocketStore } from "../../../../store/store"; import { useSocketStore } from "../../../../store/store";
import { usePlayButtonStore } from "../../../../store/usePlayButtonStore"; import { usePlayButtonStore } from "../../../../store/usePlayButtonStore";

View File

@@ -7,7 +7,7 @@ import { ThreeState } from "../../../../types/world/worldTypes";
import { useSelectedZoneStore } from "../../../../store/useZoneStore"; import { useSelectedZoneStore } from "../../../../store/useZoneStore";
import { useEditWidgetOptionsStore, useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../../store/useZone3DWidgetStore"; import { useEditWidgetOptionsStore, useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../../store/useZone3DWidgetStore";
import { use3DWidget } from "../../../../store/useDroppedObjectsStore"; import { use3DWidget } from "../../../../store/useDroppedObjectsStore";
import { get3dWidgetZoneData } from "../../../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { get3dWidgetZoneData } from "../../../../services/visulization/zone/get3dWidgetData";
import { generateUniqueId } from "../../../../functions/generateUniqueId"; import { generateUniqueId } from "../../../../functions/generateUniqueId";
import ProductionCapacity from "./cards/ProductionCapacity"; import ProductionCapacity from "./cards/ProductionCapacity";
import ReturnOfInvestment from "./cards/ReturnOfInvestment"; import ReturnOfInvestment from "./cards/ReturnOfInvestment";
@@ -16,10 +16,6 @@ import Throughput from "./cards/Throughput";
import { useWidgetStore } from "../../../../store/useWidgetStore"; import { useWidgetStore } from "../../../../store/useWidgetStore";
import useChartStore from "../../../../store/useChartStore"; import useChartStore from "../../../../store/useChartStore";
type WidgetData = { type WidgetData = {
id: string; id: string;
type: string; type: string;

View File

@@ -7,14 +7,14 @@ import {
import useModuleStore from "../../../../store/useModuleStore"; import useModuleStore from "../../../../store/useModuleStore";
import { determinePosition } from "../../functions/determinePosition"; import { determinePosition } from "../../functions/determinePosition";
import { getActiveProperties } from "../../functions/getActiveProperties"; import { getActiveProperties } from "../../functions/getActiveProperties";
import { addingFloatingWidgets } from "../../../../services/realTimeVisulization/zoneData/addFloatingWidgets"; import { addingFloatingWidgets } from "../../../../services/visulization/zone/addFloatingWidgets";
import { import {
DublicateIcon, DublicateIcon,
KebabIcon, KebabIcon,
DeleteIcon, DeleteIcon,
} from "../../../../components/icons/ExportCommonIcons"; } from "../../../../components/icons/ExportCommonIcons";
import DistanceLines from "./DistanceLines"; // Import the DistanceLines component import DistanceLines from "./DistanceLines"; // Import the DistanceLines component
import { deleteFloatingWidgetApi } from "../../../../services/realTimeVisulization/zoneData/deleteFloatingWidget"; import { deleteFloatingWidgetApi } from "../../../../services/visulization/zone/deleteFloatingWidget";
import TotalCardComponent from "./cards/TotalCardComponent"; import TotalCardComponent from "./cards/TotalCardComponent";
import WarehouseThroughputComponent from "./cards/WarehouseThroughputComponent"; import WarehouseThroughputComponent from "./cards/WarehouseThroughputComponent";

View File

@@ -6,8 +6,8 @@ import {
} from "../../../../components/icons/RealTimeVisulationIcons"; } from "../../../../components/icons/RealTimeVisulationIcons";
import { AddIcon } from "../../../../components/icons/ExportCommonIcons"; import { AddIcon } from "../../../../components/icons/ExportCommonIcons";
import { useSocketStore } from "../../../../store/store"; import { useSocketStore } from "../../../../store/store";
import { clearPanel } from "../../../../services/realTimeVisulization/zoneData/clearPanel"; import { clearPanel } from "../../../../services/visulization/zone/clearPanel";
import { lockPanel } from "../../../../services/realTimeVisulization/zoneData/lockPanel"; import { lockPanel } from "../../../../services/visulization/zone/lockPanel";
// Define the type for `Side` // Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";

View File

@@ -1,18 +1,18 @@
import React, { useEffect, useRef, useState, useCallback } from "react"; import React, { useEffect, useRef, useState, useCallback } from "react";
import { useWidgetStore, Widget } from "../../store/useWidgetStore"; import { useWidgetStore, Widget } from "../../../store/useWidgetStore";
import { import {
useDroppedObjectsStore, useDroppedObjectsStore,
useFloatingWidget, useFloatingWidget,
} from "../../store/useDroppedObjectsStore"; } from "../../../store/useDroppedObjectsStore";
import { getSelect2dZoneData } from "../../services/realTimeVisulization/zoneData/getSelect2dZoneData"; import { getSelect2dZoneData } from "../../../services/visulization/zone/getSelect2dZoneData";
import { getFloatingZoneData } from "../../services/realTimeVisulization/zoneData/getFloatingData"; import { getFloatingZoneData } from "../../../services/visulization/zone/getFloatingData";
import { get3dWidgetZoneData } from "../../services/realTimeVisulization/zoneData/get3dWidgetData"; import { get3dWidgetZoneData } from "../../../services/visulization/zone/get3dWidgetData";
import { import {
MoveArrowLeft, MoveArrowLeft,
MoveArrowRight, MoveArrowRight,
} from "../../components/icons/SimulationIcons"; } from "../../../components/icons/SimulationIcons";
import { InfoIcon } from "../../components/icons/ExportCommonIcons"; import { InfoIcon } from "../../../components/icons/ExportCommonIcons";
// Define the type for `Side` // Define the type for `Side`
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";

View File

@@ -1,8 +1,8 @@
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
import { useSelectedFloorItem, useZoneAssetId } from '../../store/store'; import { useSelectedFloorItem, useZoneAssetId } from '../../../store/store';
import * as THREE from "three"; import * as THREE from "three";
import { useThree } from '@react-three/fiber'; import { useThree } from '@react-three/fiber';
import * as Types from "../../types/world/worldTypes"; import * as Types from "../../../types/world/worldTypes";
export default function ZoneAssets() { export default function ZoneAssets() {
const { zoneAssetId, setZoneAssetId } = useZoneAssetId(); const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
const { setSelectedFloorItem } = useSelectedFloorItem(); const { setSelectedFloorItem } = useSelectedFloorItem();
@@ -10,7 +10,6 @@ export default function ZoneAssets() {
useEffect(() => { useEffect(() => {
// console.log('zoneAssetId: ', zoneAssetId); // console.log('zoneAssetId: ', zoneAssetId);
if (!zoneAssetId) return if (!zoneAssetId) return
console.log('zoneAssetId: ', zoneAssetId);
let AssetMesh = scene.getObjectByProperty("uuid", zoneAssetId.id); let AssetMesh = scene.getObjectByProperty("uuid", zoneAssetId.id);
if (AssetMesh) { if (AssetMesh) {
const bbox = new THREE.Box3().setFromObject(AssetMesh); const bbox = new THREE.Box3().setFromObject(AssetMesh);
@@ -30,20 +29,17 @@ export default function ZoneAssets() {
setSelectedFloorItem(AssetMesh); setSelectedFloorItem(AssetMesh);
} else { } else {
console.log('zoneAssetId: ', zoneAssetId)
if (Array.isArray(zoneAssetId.position) && zoneAssetId.position.length >= 3) { if (Array.isArray(zoneAssetId.position) && zoneAssetId.position.length >= 3) {
let selectedAssetPosition = [ let selectedAssetPosition = [
zoneAssetId.position[0], zoneAssetId.position[0],
10, 10,
zoneAssetId.position[2] zoneAssetId.position[2]
]; ];
console.log('selectedAssetPosition: ', selectedAssetPosition);
let selectedAssetTarget = [ let selectedAssetTarget = [
zoneAssetId.position[0], zoneAssetId.position[0],
zoneAssetId.position[1], zoneAssetId.position[1],
zoneAssetId.position[2] zoneAssetId.position[2]
]; ];
console.log('selectedAssetTarget: ', selectedAssetTarget);
const setCam = async () => { const setCam = async () => {
await controls?.setLookAt(...selectedAssetPosition, ...selectedAssetTarget, true); await controls?.setLookAt(...selectedAssetPosition, ...selectedAssetTarget, true);
setTimeout(() => { setTimeout(() => {

View File

@@ -1,32 +0,0 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const setEventApi = async (
organization: string,
modeluuid: string,
eventData: any
) => {
try {
const body: any = { organization, modeluuid, eventData };
const response = await fetch(`${url_Backend_dwinzo}/api/v2/eventDataUpdate`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!response.ok) {
throw new Error("Failed to set or Update Event Data");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

View File

@@ -8,13 +8,9 @@ export const setFloorItemApi = async (
rotation?: Object, rotation?: Object,
isLocked?: boolean, isLocked?: boolean,
isVisible?: boolean, isVisible?: boolean,
eventData?: any
) => { ) => {
try { try {
const body: any = { organization, modeluuid, modelname, position, rotation, modelfileID, isLocked, isVisible }; const body: any = { organization, modeluuid, modelname, position, rotation, modelfileID, isLocked, isVisible };
if (eventData) {
body.eventData = eventData;
}
const response = await fetch(`${url_Backend_dwinzo}/api/v2/setasset`, { const response = await fetch(`${url_Backend_dwinzo}/api/v2/setasset`, {
method: "POST", method: "POST",

View File

@@ -1,25 +0,0 @@
let url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}`;
export const getAssetEventType = async (modelId: string, organization: string) => {
try {
const response = await fetch(`${url_Backend_dwinzo}/api/v2/pointData/${modelId}/${organization}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error("Failed to fetch model event type");
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message);
} else {
throw new Error("An unknown error occurred");
}
}
};

Some files were not shown because too many files have changed in this diff Show More