folder structure change
This commit is contained in:
@@ -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 = () => {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
@@ -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;
|
|
||||||
@@ -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);
|
|
||||||
@@ -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);
|
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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} /> */}
|
||||||
|
</>
|
||||||
</>
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
231
app/src/modules/collaboration/camera/collabCams.tsx
Normal file
231
app/src/modules/collaboration/camera/collabCams.tsx
Normal 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;
|
||||||
@@ -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;
|
|
||||||
14
app/src/modules/collaboration/collaboration.tsx
Normal file
14
app/src/modules/collaboration/collaboration.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import CamModelsGroup from './camera/collabCams'
|
||||||
|
|
||||||
|
const Collaboration = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
|
||||||
|
<CamModelsGroup />
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Collaboration
|
||||||
@@ -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 />
|
||||||
|
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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;
|
|
||||||
@@ -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 };
|
||||||
@@ -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}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
25
app/src/modules/scene/setup/setup.tsx
Normal file
25
app/src/modules/scene/setup/setup.tsx
Normal 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
|
||||||
@@ -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;
|
|
||||||
@@ -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}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -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
|
||||||
@@ -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;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
function ColliderCreator() {
|
|
||||||
return (
|
|
||||||
<></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ColliderCreator
|
|
||||||
@@ -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;
|
|
||||||
@@ -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>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
|
|
||||||
function ProcessCreator() {
|
|
||||||
return (
|
|
||||||
<></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ProcessCreator
|
|
||||||
@@ -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;
|
|
||||||
@@ -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;
|
|
||||||
@@ -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
|
|
||||||
@@ -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";
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
22
app/src/modules/visualization/visualization.tsx
Normal file
22
app/src/modules/visualization/visualization.tsx
Normal 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;
|
||||||
@@ -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";
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -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(() => {
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user