Merge pull request 'simulation' (#52) from simulation into main

Reviewed-on: http://185.100.212.76:7776/Dwinzo-Beta/Dwinzo_dev/pulls/52
This commit is contained in:
Vishnu 2025-04-08 13:48:22 +00:00
commit 43184a7e25
48 changed files with 1917 additions and 841 deletions

View File

@ -35,19 +35,32 @@ interface ProductionCapacityProps {
// onPointerDown:any
}
const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, position, rotation, onContextMenu }) => {
const ProductionCapacity: React.FC<ProductionCapacityProps> = ({
id,
type,
position,
rotation,
onContextMenu,
}) => {
const { selectedChartId, setSelectedChartId } = useWidgetStore();
const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore();
const {
measurements: chartMeasurements,
duration: chartDuration,
name: widgetName,
} = useChartStore();
const [measurements, setmeasurements] = useState<any>({});
const [duration, setDuration] = useState("1h")
const [name, setName] = useState("Widget")
const [chartData, setChartData] = useState<{ labels: string[]; datasets: any[] }>({
const [duration, setDuration] = useState("1h");
const [name, setName] = useState("Widget");
const [chartData, setChartData] = useState<{
labels: string[];
datasets: any[];
}>({
labels: [],
datasets: [],
});
const iotApiUrl = process.env.REACT_APP_IOT_SOCKET_SERVER_URL;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0]
const organization = email?.split("@")[1]?.split(".")[0];
// Chart data for a week
const defaultChartData = {
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], // Days of the week
@ -101,7 +114,8 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
};
useEffect(() => {
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0) return;
if (!iotApiUrl || !measurements || Object.keys(measurements).length === 0)
return;
const socket = io(`http://${iotApiUrl}`);
@ -111,7 +125,6 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
interval: 1000,
};
const startStream = () => {
socket.emit("lineInput", inputData);
};
@ -148,22 +161,20 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
}, [measurements, duration, iotApiUrl]);
const fetchSavedInputes = async () => {
if (id !== "") {
try {
const response = await axios.get(`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`);
const response = await axios.get(
`http://${process.env.REACT_APP_SERVER_REST_API_BASE_URL}/api/v2/widget3D/${id}/${organization}`
);
if (response.status === 200) {
setmeasurements(response.data.Data.measurements)
setDuration(response.data.Data.duration)
setName(response.data.widgetName)
setmeasurements(response.data.Data.measurements);
setDuration(response.data.Data.duration);
setName(response.data.widgetName);
} else {
}
} catch (error) {
}
} catch (error) { }
}
}
};
useEffect(() => {
fetchSavedInputes();
@ -173,13 +184,9 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
if (selectedChartId?.id === id) {
fetchSavedInputes();
}
}
, [chartMeasurements, chartDuration, widgetName])
}, [chartMeasurements, chartDuration, widgetName]);
useEffect(() => {
}, [rotation])
useEffect(() => { }, [rotation]);
const rotationDegrees = {
x: (rotation[0] * 180) / Math.PI,
y: (rotation[1] * 180) / Math.PI,
@ -187,30 +194,44 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
};
const transformStyle = {
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`,
transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg) translate(-50%, -50%)`,
};
return (
<Html position={position}
<Html
position={position}
scale={[0.5, 0.5, 0.5]}
transform
sprite
zIndexRange={[1,0]}
// center
// distanceFactor={10} // Adjusted for visual balance
zIndexRange={[1, 0]}
style={{
transform: transformStyle.transform,
transformStyle: 'preserve-3d',
transition: 'transform 0.1s ease-out'
}}>
<div className="productionCapacity-wrapper card"
transformStyle: "preserve-3d",
transition: "transform 0.1s ease-out",
}}
onDragOver={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onDrop={(e) => {
e.preventDefault();
// e.stopPropagation();
}}
wrapperClass="pointer-none"
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
>
<div
className={`productionCapacity-wrapper card ${selectedChartId?.id === id ? "activeChart" : ""}`}
onClick={() => setSelectedChartId({ id: id, type: type })}
onContextMenu={onContextMenu}
style={{
width: '300px', // Original width
height: '300px', // Original height
width: "300px", // Original width
height: "300px", // Original height
transform: transformStyle.transform,
transformStyle: 'preserve-3d'
transformStyle: "preserve-3d",
position: "absolute",
}}
>
<div className="headeproductionCapacityr-wrapper">
@ -233,10 +254,18 @@ const ProductionCapacity: React.FC<ProductionCapacityProps> = ({ id, type, posit
</div>{" "}
<div className="bar-chart charts">
{/* Bar Chart */}
<Bar data={Object.keys(measurements).length > 0 ? chartData : defaultChartData} options={chartOptions} />
<Bar
data={
Object.keys(measurements).length > 0
? chartData
: defaultChartData
}
options={chartOptions}
/>
</div>
</div>
</Html>
);
};

View File

@ -226,6 +226,7 @@ const ReturnOfInvestment: React.FC<ReturnOfInvestmentProps> = ({ id, type, posit
transition: 'transform 0.1s ease-out'
}}
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
>
<div className="returnOfInvestment card"
onClick={() => setSelectedChartId({ id: id, type: type })}

View File

@ -111,6 +111,7 @@ const StateWorking: React.FC<StateWorkingProps> = ({ id, type, position, rotatio
transition: 'transform 0.1s ease-out'
}}
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
>
<div className="stateWorking-wrapper card"
onClick={() => setSelectedChartId({ id: id, type: type })}

View File

@ -206,6 +206,7 @@ const Throughput: React.FC<ThroughputProps> = ({ id, type, position, rotation, o
transition: 'transform 0.1s ease-out'
}}
className={`${selectedChartId?.id === id ? "activeChart" : ""}`}
>
<div className="throughput-wrapper"
onClick={() => setSelectedChartId({ id: id, type: type })}

View File

@ -3,13 +3,10 @@ import { useDroppedObjectsStore } from "../../../../store/useDroppedObjectsStore
import useTemplateStore from "../../../../store/useTemplateStore";
import { useSelectedZoneStore } from "../../../../store/useZoneStore";
import { getTemplateData } from "../../../../services/realTimeVisulization/zoneData/getTemplate";
import { deleteTemplateApi } from "../../../../services/realTimeVisulization/zoneData/deleteTemplate";
import { loadTempleteApi } from "../../../../services/realTimeVisulization/zoneData/loadTemplate";
import { useSocketStore } from "../../../../store/store";
const Templates = () => {
const { templates, removeTemplate } = useTemplateStore();
const { setTemplates } = useTemplateStore();
const { templates, removeTemplate, setTemplates } = useTemplateStore();
const { setSelectedZone, selectedZone } = useSelectedZoneStore();
const { visualizationSocket } = useSocketStore();
@ -35,15 +32,11 @@ const Templates = () => {
let deleteTemplate = {
organization: organization,
templateID: id,
}
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate)
visualizationSocket.emit("v2:viz-template:deleteTemplate", deleteTemplate);
}
removeTemplate(id);
// let response = await deleteTemplateApi(id, organization);
// if (response.message === "Template deleted successfully") {
// }
} catch (error) {
console.error("Error deleting template:", error);
}
@ -60,114 +53,54 @@ const Templates = () => {
organization: organization,
zoneId: selectedZone.zoneId,
templateID: template.id,
}
console.log('template: ', template);
console.log('loadingTemplate: ', loadingTemplate);
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate)
visualizationSocket.emit("v2:viz-template:addToZone", loadingTemplate);
}
// let response = await loadTempleteApi(template.id, selectedZone.zoneId, organization);
// if (response.message === "Template placed in Zone") {
setSelectedZone({
panelOrder: template.panelOrder,
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
widgets: template.widgets,
setSelectedZone({
panelOrder: template.panelOrder,
activeSides: Array.from(new Set(template.panelOrder)), // No merging with previous `activeSides`
widgets: template.widgets,
});
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
if (Array.isArray(template.floatingWidget)) {
template.floatingWidget.forEach((val: any) => {
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val);
});
useDroppedObjectsStore.getState().setZone(selectedZone.zoneName, selectedZone.zoneId);
if (Array.isArray(template.floatingWidget)) {
template.floatingWidget.forEach((val: any) => {
useDroppedObjectsStore.getState().addObject(selectedZone.zoneName, val);
});
}
// }
}
} catch (error) {
console.error("Error loading template:", error);
}
};
return (
<div
className="template-list"
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
gap: "1rem",
padding: "1rem",
}}
>
<div className="template-list">
{templates.map((template) => (
<div
key={template.id}
className="template-item"
style={{
border: "1px solid #e0e0e0",
borderRadius: "8px",
padding: "1rem",
transition: "box-shadow 0.3s ease",
}}
>
<div key={template.id} className="template-item">
{template?.snapshot && (
<div style={{ position: "relative", paddingBottom: "56.25%" }}>
{" "}
{/* 16:9 aspect ratio */}
<div className="template-image-container">
<img
src={template.snapshot} // Corrected from template.image to template.snapshot
src={template.snapshot}
alt={`${template.name} preview`}
style={{
position: "absolute",
width: "100%",
height: "100%",
objectFit: "contain",
borderRadius: "4px",
cursor: "pointer",
transition: "transform 0.3s ease",
// ':hover': {
// transform: 'scale(1.05)'
// }
}}
className="template-image"
onClick={() => handleLoadTemplate(template)}
/>
</div>
)}
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginTop: "0.5rem",
}}
>
<div className="template-details">
<div
onClick={() => handleLoadTemplate(template)}
style={{
cursor: "pointer",
fontWeight: "500",
// ':hover': {
// textDecoration: 'underline'
// }
}}
className="template-name"
>
{template.name}
</div>
<button
onClick={() => handleDeleteTemplate(template.id)}
style={{
padding: "0.25rem 0.5rem",
background: "#ff4444",
color: "white",
border: "none",
borderRadius: "4px",
cursor: "pointer",
transition: "opacity 0.3s ease",
// ':hover': {
// opacity: 0.8
// }
}}
className="delete-button"
aria-label="Delete template"
>
Delete
@ -176,14 +109,7 @@ const Templates = () => {
</div>
))}
{templates.length === 0 && (
<div
style={{
textAlign: "center",
color: "#666",
padding: "2rem",
gridColumn: "1 / -1",
}}
>
<div className="no-templates">
No saved templates yet. Create one in the visualization view!
</div>
)}
@ -192,4 +118,3 @@ const Templates = () => {
};
export default Templates;

View File

@ -21,11 +21,13 @@ const Widgets3D = () => {
className="widget-item"
draggable
onDragStart={(e) => {
let name = widget.name
let crt = e.target
if (crt instanceof HTMLElement) {
const widget = crt.cloneNode(true) as HTMLElement;
e.dataTransfer.setDragImage(widget, 0, 0)
e.dataTransfer.effectAllowed = "move"
e.dataTransfer.setData("text/plain", "ui-" + name)
}
}}
onPointerDown={() => {
@ -40,7 +42,7 @@ const Widgets3D = () => {
className="widget-image"
src={widget.img}
alt={widget.name}
// draggable={false}
draggable={false}
/>
</div>
))}

View File

@ -46,7 +46,7 @@ const WidgetsFloating = () => {
))} */}
{/* Floating 1 */}
<SimpleCard
header={"Todays Money"}
header={"Todays Earnings"}
icon={WalletIcon}
value={"$53,000"}
per={"+55%"}

View File

@ -16,7 +16,7 @@ import Analysis from "./analysis/Analysis";
import Simulations from "./simulation/Simulations";
import {
useSelectedActionSphere,
useselectedFloorItem,
useSelectedFloorItem,
} from "../../../store/store";
import GlobalProperties from "./properties/GlobalProperties";
import AsstePropertiies from "./properties/AssetProperties";
@ -30,7 +30,7 @@ const SideBarRight: React.FC = () => {
const { toggleUI } = useToggleStore();
const { selectedActionSphere } = useSelectedActionSphere();
const { subModule, setSubModule } = useSubModuleStore();
const { selectedFloorItem } = useselectedFloorItem();
const { selectedFloorItem } = useSelectedFloorItem();
// Reset activeList whenever activeModule changes
useEffect(() => {
if (activeModule !== "simulation") setSubModule("properties");
@ -101,7 +101,7 @@ const SideBarRight: React.FC = () => {
)}
{toggleUI &&
subModule === "zoneProperties" &&
activeModule === "builder" && (
(activeModule === "builder" || activeModule === "simulation") && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ZoneProperties />
@ -135,7 +135,7 @@ const SideBarRight: React.FC = () => {
selectedActionSphere.path.type === "StaticMachine" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<StaticMachineMechanics />
<StaticMachineMechanics />
</div>
</div>
)}
@ -144,7 +144,7 @@ const SideBarRight: React.FC = () => {
selectedActionSphere.path.type === "ArmBot" && (
<div className="sidebar-right-container">
<div className="sidebar-right-content-container">
<ArmBotMechanics />
<ArmBotMechanics />
</div>
</div>
)}

View File

@ -1,65 +1,121 @@
import React, { useRef, useMemo } from "react";
import React, { useRef, useMemo, useCallback, useState } from "react";
import { InfoIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import {
useSelectedActionSphere,
useSimulationStates,
useSocketStore
} from "../../../../store/store";
import * as Types from '../../../../types/world/worldTypes';
import PositionInput from "../customInput/PositionInputs";
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
import LabeledButton from "../../../ui/inputs/LabledButton";
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
const ArmBotMechanics: React.FC = () => {
const { selectedActionSphere } = useSelectedActionSphere();
const { simulationStates, setSimulationStates } = useSimulationStates();
const { socket } = useSocketStore();
const [selectedTrigger, setSelectedTrigger] = useState<string | null>(null);
const propertiesContainerRef = useRef<HTMLDivElement>(null);
const { selectedPoint, connectedPointUuids } = useMemo(() => {
if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null, connectedPointUuids: [] };
// Get connected models for dropdowns
const connectedModels = useMemo(() => {
if (!selectedActionSphere?.points?.uuid) return [];
const vehiclePaths = simulationStates.filter(
}, [selectedActionSphere, simulationStates]);
// Get triggers only from connected models
const connectedTriggers = useMemo(() => {
}, [connectedModels, simulationStates]);
const { selectedPoint } = useMemo(() => {
if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null };
const armBotPaths = simulationStates.filter(
(path): path is Types.ArmBotEventsSchema => path.type === "ArmBot"
);
const points = vehiclePaths.find(
const points = armBotPaths.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
selectedPoint: points || null
};
}, [selectedActionSphere, simulationStates]);
const updateBackend = async (updatedPath: Types.ArmBotEventsSchema | undefined) => {
if (!updatedPath) return;
const email = localStorage.getItem("email");
const organization = email ? email.split("@")[1].split(".")[0] : "";
// if (!updatedPath) return;
// const email = localStorage.getItem("email");
// const organization = email ? email.split("@")[1].split(".")[0] : "";
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "ArmBot", points: updatedPath.points }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "ArmBot", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
// 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<Types.ArmBotEventsSchema['points']['actions']>) => {
// if (!selectedActionSphere?.points?.uuid) return;
// const updatedPaths = simulationStates.map((path) => {
// return path;
// });
// const updatedPath = updatedPaths.find(
// (path): path is Types.ArmBotEventsSchema =>
// path.type === "ArmBot" &&
// path.points.uuid === selectedActionSphere.points.uuid
// );
// updateBackend(updatedPath);
// setSimulationStates(updatedPaths);
// }, [selectedActionSphere?.points?.uuid, simulationStates, setSimulationStates]);
// const handleSpeedChange = useCallback((speed: number) => {
// handleActionUpdate({ speed });
// }, [handleActionUpdate]);
// const handleProcessChange = useCallback((processes: Types.ArmBotEventsSchema['points']['actions']['processes']) => {
// handleActionUpdate({ processes });
// }, [handleActionUpdate]);
// const handleTriggerSelect = useCallback((displayName: string) => {
// const selected = connectedTriggers.find(t => t.displayName === displayName);
// setSelectedTrigger(selected?.uuid || null);
// }, [connectedTriggers]);
// const handleStartPointSelect = useCallback((pointUUID: string) => {
// if (!selectedTrigger || !selectedPoint) return;
// const updatedProcesses = selectedPoint.actions.processes?.map(process =>
// process.triggerId === selectedTrigger
// ? { ...process, startPoint: pointUUID }
// : process
// ) || [];
// handleProcessChange(updatedProcesses);
// }, [selectedTrigger, selectedPoint, handleProcessChange]);
// const handleEndPointSelect = useCallback((pointUUID: string) => {
// if (!selectedTrigger || !selectedPoint) return;
// const updatedProcesses = selectedPoint.actions.processes?.map(process =>
// process.triggerId === selectedTrigger
// ? { ...process, endPoint: pointUUID }
// : process
// ) || [];
// handleProcessChange(updatedProcesses);
// }, [selectedTrigger, selectedPoint, handleProcessChange]);
// const getCurrentProcess = useCallback(() => {
// if (!selectedTrigger || !selectedPoint) return null;
// return selectedPoint.actions.processes?.find(p => p.triggerId === selectedTrigger);
// }, [selectedTrigger, selectedPoint]);
return (
<div className="machine-mechanics-container" key={selectedPoint?.uuid}>
@ -71,16 +127,50 @@ const ArmBotMechanics: React.FC = () => {
<div className="selected-properties-container" ref={propertiesContainerRef}>
<div className="properties-header">ArmBot Properties</div>
{selectedPoint && (
{/* {selectedPoint && (
<>
<InputWithDropDown
key={`speed-${selectedPoint.uuid}`}
label="ArmBot Speed"
value={selectedPoint.actions.speed.toString()}
onChange={(value) => handleSpeedChange(parseInt(value))}
/>
<LabledDropdown
key={`trigger-select-${selectedPoint.uuid}`}
label="Select Trigger"
defaultOption={connectedTriggers.find(t => t.uuid === selectedTrigger)?.displayName || ''}
onSelect={handleTriggerSelect}
options={connectedTriggers.map(trigger => trigger.displayName)}
/>
{selectedTrigger && (
<>
<LabledDropdown
key={`start-point-${selectedTrigger}`}
label="Start Point"
defaultOption={getCurrentProcess()?.startPoint || ''}
onSelect={handleStartPointSelect}
options={connectedModels.map((model, index) => `${model.modelName} [${index}]`)}
/>
<LabledDropdown
key={`end-point-${selectedTrigger}`}
label="End Point"
defaultOption={getCurrentProcess()?.endPoint || ''}
onSelect={handleEndPointSelect}
options={connectedModels.map((model, index) => `${model.modelName} [${index}]`)}
/>
</>
)}
</>
)}
)} */}
</div>
<div className="footer">
<InfoIcon />
Configure armbot properties.
Configure ArmBot properties and trigger-based processes.
</div>
</div>
</div>

View File

@ -1,9 +1,9 @@
import React, { useRef, useMemo } from "react";
import React, { useRef, useMemo, useCallback } from "react";
import { InfoIcon } from "../../../icons/ExportCommonIcons";
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import { useSelectedActionSphere, useSimulationStates, useSocketStore } from "../../../../store/store";
import * as Types from '../../../../types/world/worldTypes';
import PositionInput from "../customInput/PositionInputs";
import LabledDropdown from "../../../ui/inputs/LabledDropdown";
import { setEventApi } from "../../../../services/factoryBuilder/assest/floorAsset/setEventsApt";
const StaticMachineMechanics: React.FC = () => {
@ -16,11 +16,11 @@ const StaticMachineMechanics: React.FC = () => {
const { selectedPoint, connectedPointUuids } = useMemo(() => {
if (!selectedActionSphere?.points?.uuid) return { selectedPoint: null, connectedPointUuids: [] };
const vehiclePaths = simulationStates.filter(
const staticMachinePaths = simulationStates.filter(
(path): path is Types.StaticMachineEventsSchema => path.type === "StaticMachine"
);
const points = vehiclePaths.find(
const points = staticMachinePaths.find(
(path) => path.points.uuid === selectedActionSphere.points.uuid
)?.points;
@ -47,7 +47,7 @@ const StaticMachineMechanics: React.FC = () => {
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "StaticMachine", points: updatedPath.points }
// { type: "Vehicle", points: updatedPath.points }
// );
const data = {
@ -57,9 +57,77 @@ const StaticMachineMechanics: React.FC = () => {
}
socket.emit('v2:model-asset:updateEventData', data);
}
const handleActionUpdate = useCallback((updatedAction: Partial<Types.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 Types.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<Types.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 Types.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}>
@ -67,20 +135,50 @@ const StaticMachineMechanics: React.FC = () => {
{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 properties.
Configure machine interaction properties and triggers.
</div>
</div>
</div>

View File

@ -5,6 +5,7 @@ import { useEditingPoint, useEyeDropMode, usePreviewPosition, useSelectedActionS
import * as Types from '../../../../types/world/worldTypes';
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();
@ -126,6 +127,33 @@ const VehicleMechanics: React.FC = () => {
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 Types.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);
@ -193,6 +221,14 @@ const VehicleMechanics: React.FC = () => {
handleEyeDropClick={handleEndEyeDropClick}
/>
<LabeledButton
label="Reset"
value="Clear Points"
onClick={() => {
ResetVehicleState();
}}
/>
<InputWithDropDown
key={`hitcount-${selectedPoint.uuid}`}
label="Hit Count"

View File

@ -4,7 +4,7 @@ import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
import { RemoveIcon } from "../../../icons/ExportCommonIcons";
import PositionInput from "../customInput/PositionInputs";
import RotationInput from "../customInput/RotationInput";
import { useselectedFloorItem } from "../../../../store/store";
import { useSelectedFloorItem } from "../../../../store/store";
import * as THREE from "three";
interface UserData {
@ -16,7 +16,7 @@ interface UserData {
const AssetProperties: React.FC = () => {
const [userData, setUserData] = useState<UserData[]>([]); // State to track user data
const [nextId, setNextId] = useState(1); // Unique ID for new entries
const { selectedFloorItem } = useselectedFloorItem();
const { selectedFloorItem } = useSelectedFloorItem();
// Function to handle adding new user data
const handleAddUserData = () => {
const newUserData: UserData = {

View File

@ -22,7 +22,7 @@ import { useSelectedZoneStore } from "../../store/useZoneStore";
import {
useActiveTool,
useAddAction,
useDeleteModels,
useDeleteTool,
useDeletePointOrLine,
useMovePoint,
useRefTextUpdate,
@ -35,6 +35,7 @@ import {
import useToggleStore from "../../store/useUIToggleStore";
import {
use3DWidget,
useDroppedObjectsStore,
useFloatingWidget,
} from "../../store/useDroppedObjectsStore";
@ -52,11 +53,15 @@ const Tools: React.FC = () => {
const { addTemplate } = useTemplateStore();
const { selectedZone } = useSelectedZoneStore();
const { floatingWidget } = useFloatingWidget();
const { widgets3D } = use3DWidget();
const zones = useDroppedObjectsStore((state) => state.zones);
// wall options
const { toggleView, setToggleView } = useToggleView();
const { setDeleteModels } = useDeleteModels();
const { setDeleteTool } = useDeleteTool();
const { setAddAction } = useAddAction();
const { setSelectedWallItem } = useSelectedWallItem();
@ -84,7 +89,7 @@ const Tools: React.FC = () => {
const toggleSwitch = () => {
if (toggleThreeD) {
setSelectedWallItem(null);
setDeleteModels(false);
setDeleteTool(false);
setAddAction(null);
setToggleView(true);
// localStorage.setItem("navBarUi", JSON.stringify(!toggleThreeD));
@ -131,7 +136,7 @@ const Tools: React.FC = () => {
useEffect(() => {
setToolMode(null);
setDeleteModels(false);
setDeleteTool(false);
setAddAction(null);
setTransformMode(null);
setMovePoint(false);
@ -197,7 +202,7 @@ const Tools: React.FC = () => {
if (toggleView) {
setDeletePointOrLine(true);
} else {
setDeleteModels(true);
setDeleteTool(true);
}
break;
@ -409,10 +414,9 @@ const Tools: React.FC = () => {
widgets3D,
selectedZone,
templates,
visualizationSocket
})
}
}
visualizationSocket,
});
}}
>
<SaveTemplateIcon isActive={false} />
</div>

View File

@ -74,6 +74,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
const [showLeftArrow, setShowLeftArrow] = useState(false);
const [showRightArrow, setShowRightArrow] = useState(false);
const { floatingWidget, setFloatingWidget } = useFloatingWidget()
const{setSelectedChartId}=useWidgetStore()
// Function to calculate overflow state
@ -178,7 +179,7 @@ const DisplayZone: React.FC<DisplayZoneProps> = ({
zoneViewPortPosition: response.viewPortposition || {},
});
} catch (error) {
console.error(error);
}
}

View File

@ -18,6 +18,7 @@ import { deleteWidgetApi } from "../../../services/realTimeVisulization/zoneData
import { useClickOutside } from "./functions/handleWidgetsOuterClick";
import { useSocketStore } from "../../../store/store";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";
import OuterClick from "../../../utils/outerClick";
type Side = "top" | "bottom" | "left" | "right";
@ -89,6 +90,11 @@ export const DraggableWidget = ({
const isPanelHidden = hiddenPanels.includes(widget.panel);
// OuterClick({
// contextClassName: ["chart-container", "floating", "sidebar-right-wrapper"],
// setMenuVisible: () => setSelectedChartId(null),
// });
const deleteSelectedChart = async () => {
try {
const email = localStorage.getItem("email") || "";
@ -251,36 +257,35 @@ export const DraggableWidget = ({
});
// Track canvas dimensions
// Current: Two identical useEffect hooks for canvas dimensions
// Remove the duplicate and keep only one
useEffect(() => {
const canvas = document.getElementById("real-time-vis-canvas");
if (!canvas) return;
// Current: Two identical useEffect hooks for canvas dimensions
// Remove the duplicate and keep only one
useEffect(() => {
const canvas = document.getElementById("real-time-vis-canvas");
if (!canvas) return;
const updateCanvasDimensions = () => {
const rect = canvas.getBoundingClientRect();
setCanvasDimensions({
width: rect.width,
height: rect.height,
});
};
const updateCanvasDimensions = () => {
const rect = canvas.getBoundingClientRect();
setCanvasDimensions({
width: rect.width,
height: rect.height,
});
};
updateCanvasDimensions();
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
resizeObserver.observe(canvas);
updateCanvasDimensions();
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
resizeObserver.observe(canvas);
return () => resizeObserver.unobserve(canvas);
}, []);
return () => resizeObserver.unobserve(canvas);
}, []);
return (
<>
<style>
{`
:root {
--realTimeViz-container-width: ${canvasDimensions.width * 0.25}px;
--realTimeViz-container-width: ${canvasDimensions.width}px;
--realTimeViz-container-height: ${canvasDimensions.height}px;
}
`}
</style>
@ -296,13 +301,12 @@ useEffect(() => {
onDragOver={handleDragOver}
onDrop={handleDrop}
style={{
// Apply styles based on panel position
width: ["top", "bottom"].includes(widget.panel)
? `calc(${canvasDimensions.width * 0.16}px - 2px)` // For top/bottom panels, set width
: undefined, // Don't set width if it's left or right
? `calc(${canvasDimensions.width}px / 6)`
: undefined,
height: ["left", "right"].includes(widget.panel)
? `calc(${canvasDimensions.height * 0.25}px - 2px)` // For left/right panels, set height
: undefined, // Don't set height if it's top or bottom
? `calc(${canvasDimensions.height - 10}px / 4)`
: undefined,
}}
ref={chartWidget}
onClick={() => setSelectedChartId(widget)}
@ -393,4 +397,4 @@ useEffect(() => {
);
};
// while resize calculate --realTimeViz-container-height pprperly

View File

@ -1,6 +1,10 @@
import { useThree } from "@react-three/fiber";
import React, { useEffect, useRef } from "react";
import { useAsset3dWidget, useSocketStore, useWidgetSubOption } from "../../../store/store";
import {
useAsset3dWidget,
useSocketStore,
useWidgetSubOption,
} from "../../../store/store";
import useModuleStore from "../../../store/useModuleStore";
import { ThreeState } from "../../../types/world/worldTypes";
import * as THREE from "three";
@ -13,11 +17,19 @@ import { generateUniqueId } from "../../../functions/generateUniqueId";
import { adding3dWidgets } from "../../../services/realTimeVisulization/zoneData/add3dWidget";
import { get3dWidgetZoneData } from "../../../services/realTimeVisulization/zoneData/get3dWidgetData";
import { use3DWidget } from "../../../store/useDroppedObjectsStore";
import { useEditWidgetOptionsStore, useLeftData, useRightClickSelected, useRightSelected, useTopData, useZoneWidgetStore } from "../../../store/useZone3DWidgetStore";
import { useWidgetStore } from "../../../store/useWidgetStore";
import EditWidgetOption from "../menu/EditWidgetOption";
import {
useEditWidgetOptionsStore,
useLeftData,
useRightClickSelected,
useRightSelected,
useTopData,
useZoneWidgetStore,
} from "../../../store/useZone3DWidgetStore";
import { delete3dWidgetApi } from "../../../services/realTimeVisulization/zoneData/delete3dWidget";
import { update3dWidget, update3dWidgetRotation } from "../../../services/realTimeVisulization/zoneData/update3dWidget";
import {
update3dWidget,
update3dWidgetRotation,
} from "../../../services/realTimeVisulization/zoneData/update3dWidget";
type WidgetData = {
id: string;
type: string;
@ -26,7 +38,6 @@ type WidgetData = {
tempPosition?: [number, number, number];
};
export default function Dropped3dWidgets() {
const { widgetSelect } = useAsset3dWidget();
const { activeModule } = useModuleStore();
@ -36,19 +47,18 @@ export default function Dropped3dWidgets() {
const { top, setTop } = useTopData();
const { left, setLeft } = useLeftData();
const { rightSelect, setRightSelect } = useRightSelected();
const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore()
const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition, updateWidgetRotation } = useZoneWidgetStore();
const { editWidgetOptions, setEditWidgetOptions } = useEditWidgetOptionsStore();
const { zoneWidgetData, setZoneWidgetData, addWidget, updateWidgetPosition, updateWidgetRotation, tempWidget, tempWidgetPosition } = useZoneWidgetStore();
const { setWidgets3D } = use3DWidget();
const { visualizationSocket } = useSocketStore();
const { rightClickSelected, setRightClickSelected } = useRightClickSelected();
const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // Floor plane for horizontal move
const verticalPlane = useRef(new THREE.Plane(new THREE.Vector3(0, 0, 1), 0)); // Vertical plane for vertical move
const planeIntersect = useRef(new THREE.Vector3());
const rotationStartRef = useRef<[number, number, number]>([0, 0, 0]);
const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || [];
useEffect(() => {
if (activeModule !== "visualization") return;
if (!selectedZone.zoneId) return;
@ -57,7 +67,10 @@ export default function Dropped3dWidgets() {
const organization = email?.split("@")[1]?.split(".")[0];
async function get3dWidgetData() {
const result = await get3dWidgetZoneData(selectedZone.zoneId, organization);
const result = await get3dWidgetZoneData(
selectedZone.zoneId,
organization
);
setWidgets3D(result);
@ -68,66 +81,50 @@ export default function Dropped3dWidgets() {
rotation: widget.rotation || [0, 0, 0],
}));
setZoneWidgetData(selectedZone.zoneId, formattedWidgets);
}
get3dWidgetData();
}, [selectedZone.zoneId, activeModule]);
const createdWidgetRef = useRef<WidgetData | null>(null);
useEffect(() => {
if (activeModule !== "visualization") return;
if (widgetSubOption === "Floating" || widgetSubOption === "2D") return;
if (selectedZone.zoneName === "") return;
const canvasElement = gl.domElement;
const canvasElement = document.getElementById("real-time-vis-canvas");
if (!canvasElement) return;
const hasEntered = { current: false };
const handleDragEnter = (event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
console.log("Drag enter");
};
if (hasEntered.current || !widgetSelect.startsWith("ui")) return;
hasEntered.current = true;
const handleDragOver = (event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
};
const handleDragLeave = (event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
console.log("Drag leave");
// Remove visual feedback
canvasElement.style.cursor = "";
};
const onDrop = async (event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
canvasElement.style.cursor = ""; // Reset cursor
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
if (!widgetSelect.startsWith("ui")) return;
const group1 = scene.getObjectByName("itemsGroup");
if (!group1) return;
// Update raycaster with current mouse position
const rect = canvasElement.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
raycaster.setFromCamera(mouse, 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")
);
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) {
const { x, y, z } = intersects[0].point;
@ -138,36 +135,100 @@ export default function Dropped3dWidgets() {
rotation: [0, 0, 0],
};
const add3dWidget = {
organization: organization,
widget: newWidget,
zoneId: selectedZone.zoneId
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget);
}
addWidget(selectedZone.zoneId, newWidget);
createdWidgetRef.current = newWidget;
tempWidget(selectedZone.zoneId, newWidget); // temp add in UI
}
};
// Add all event listeners
// canvasElement.addEventListener("dragenter", handleDragEnter);
// canvasElement.addEventListener("dragover", handleDragOver);
// canvasElement.addEventListener("dragleave", handleDragLeave);
const handleDragOver = (event: DragEvent) => {
event.preventDefault();
event.stopPropagation();
event.dataTransfer!.dropEffect = "move"; // ✅ Add this line
const widget = createdWidgetRef.current;
if (!widget) return;
const rect = canvasElement.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
raycaster.setFromCamera(mouse, 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")
);
// Update widget's position in memory
if (intersects.length > 0) {
const { x, y, z } = intersects[0].point;
tempWidgetPosition(selectedZone.zoneId, widget.id, [x, y, z]);
widget.position = [x, y, z];
}
};
const onDrop = (event: any) => {
console.log("onDrop called. hasEntered: ", hasEntered.current);
event.preventDefault();
event.stopPropagation();
hasEntered.current = false;
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const newWidget = createdWidgetRef.current;
if (!newWidget || !widgetSelect.startsWith("ui")) return;
// ✅ Manual removal of the temp widget (same ID)
const prevWidgets = useZoneWidgetStore.getState().zoneWidgetData[selectedZone.zoneId] || [];
const cleanedWidgets = prevWidgets.filter(w => w.id !== newWidget.id);
useZoneWidgetStore.setState((state) => ({
zoneWidgetData: {
...state.zoneWidgetData,
[selectedZone.zoneId]: cleanedWidgets,
},
}));
// ✅ Now re-add it as final
addWidget(selectedZone.zoneId, newWidget);
const add3dWidget = {
organization,
widget: newWidget,
zoneId: selectedZone.zoneId,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget);
}
setTimeout(() => {
let pointerDivs = document.getElementsByClassName("pointer-none");
Array.from(pointerDivs).forEach((el) => {
el.classList.remove("pointer-none");
});
}, 1000);
createdWidgetRef.current = null;
};
canvasElement.addEventListener("dragenter", handleDragEnter);
canvasElement.addEventListener("dragover", handleDragOver);
canvasElement.addEventListener("drop", onDrop);
return () => {
// // Clean up all event listeners
// canvasElement.removeEventListener("dragenter", handleDragEnter);
// canvasElement.removeEventListener("dragover", handleDragOver);
// canvasElement.removeEventListener("dragleave", handleDragLeave);
canvasElement.removeEventListener("dragenter", handleDragEnter);
canvasElement.removeEventListener("dragover", handleDragOver);
canvasElement.removeEventListener("drop", onDrop);
canvasElement.style.cursor = ""; // Ensure cursor is reset
};
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption, gl.domElement, scene, raycaster, camera]);
const activeZoneWidgets = zoneWidgetData[selectedZone.zoneId] || [];
}, [widgetSelect, activeModule, selectedZone.zoneId, widgetSubOption, camera,]);
useEffect(() => {
if (!rightClickSelected) return;
@ -176,7 +237,9 @@ export default function Dropped3dWidgets() {
if (rightSelect === "Duplicate") {
async function duplicateWidget() {
const widgetToDuplicate = activeZoneWidgets.find((w: WidgetData) => w.id === rightClickSelected);
const widgetToDuplicate = activeZoneWidgets.find(
(w: WidgetData) => w.id === rightClickSelected
);
if (!widgetToDuplicate) return;
const newWidget: WidgetData = {
id: generateUniqueId(),
@ -191,19 +254,19 @@ export default function Dropped3dWidgets() {
const adding3dWidget = {
organization: organization,
widget: newWidget,
zoneId: selectedZone.zoneId
zoneId: selectedZone.zoneId,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-3D-widget:add", adding3dWidget);
}
// let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget)
//
//
addWidget(selectedZone.zoneId, newWidget);
setRightSelect(null);
setRightClickSelected(null);
}
duplicateWidget()
duplicateWidget();
}
if (rightSelect === "Delete") {
@ -215,7 +278,6 @@ export default function Dropped3dWidgets() {
zoneId: selectedZone.zoneId,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-3D-widget:delete", deleteWidget);
}
@ -223,10 +285,11 @@ export default function Dropped3dWidgets() {
// const response = await delete3dWidgetApi(selectedZone.zoneId, organization, rightClickSelected);
setZoneWidgetData(
selectedZone.zoneId,
activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected)
activeZoneWidgets.filter(
(w: WidgetData) => w.id !== rightClickSelected
)
);
} catch (error) {
} finally {
setRightClickSelected(null);
setRightSelect(null);
@ -238,7 +301,6 @@ export default function Dropped3dWidgets() {
}, [rightSelect, rightClickSelected]);
useEffect(() => {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const handleMouseDown = (event: MouseEvent) => {
@ -247,13 +309,18 @@ export default function Dropped3dWidgets() {
if (rightSelect === "RotateX" || rightSelect === "RotateY") {
mouseStartRef.current = { x: event.clientX, y: event.clientY };
const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) =>
zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected)
const selectedZone = Object.keys(zoneWidgetData).find(
(zoneId: string) =>
zoneWidgetData[zoneId].some(
(widget: WidgetData) => widget.id === rightClickSelected
)
);
if (!selectedZone) return;
const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected);
const selectedWidget = zoneWidgetData[selectedZone].find(
(widget: WidgetData) => widget.id === rightClickSelected
);
if (selectedWidget) {
rotationStartRef.current = selectedWidget.rotation || [0, 0, 0];
}
@ -263,11 +330,15 @@ export default function Dropped3dWidgets() {
const handleMouseMove = (event: MouseEvent) => {
if (!rightClickSelected || !rightSelect) return;
const selectedZone = Object.keys(zoneWidgetData).find((zoneId: string) =>
zoneWidgetData[zoneId].some((widget: WidgetData) => widget.id === rightClickSelected)
zoneWidgetData[zoneId].some(
(widget: WidgetData) => widget.id === rightClickSelected
)
);
if (!selectedZone) return;
const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected);
const selectedWidget = zoneWidgetData[selectedZone].find(
(widget: WidgetData) => widget.id === rightClickSelected
);
if (!selectedWidget) return;
const rect = gl.domElement.getBoundingClientRect();
@ -276,22 +347,29 @@ export default function Dropped3dWidgets() {
raycaster.setFromCamera(mouse, camera);
if (rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current)) {
if (
rightSelect === "Horizontal Move" &&
raycaster.ray.intersectPlane(plane.current, planeIntersect.current)
) {
const newPosition: [number, number, number] = [
planeIntersect.current.x,
selectedWidget.position[1],
planeIntersect.current.z
planeIntersect.current.z,
];
updateWidgetPosition(selectedZone, rightClickSelected, newPosition);
}
if (rightSelect === "Vertical Move") {
if (raycaster.ray.intersectPlane(verticalPlane.current, planeIntersect.current)) {
if (
raycaster.ray.intersectPlane(
verticalPlane.current,
planeIntersect.current
)
) {
updateWidgetPosition(selectedZone, rightClickSelected, [
selectedWidget.position[0],
planeIntersect.current.y,
selectedWidget.position[2]
selectedWidget.position[2],
]);
}
}
@ -302,7 +380,7 @@ export default function Dropped3dWidgets() {
const newRotation: [number, number, number] = [
rotationStartRef.current[0] + deltaX * rotationSpeed,
rotationStartRef.current[1],
rotationStartRef.current[2]
rotationStartRef.current[2],
];
updateWidgetRotation(selectedZone, rightClickSelected, newRotation);
}
@ -313,7 +391,7 @@ export default function Dropped3dWidgets() {
const newRotation: [number, number, number] = [
rotationStartRef.current[0],
rotationStartRef.current[1] + deltaY * rotationSpeed,
rotationStartRef.current[2]
rotationStartRef.current[2],
];
updateWidgetRotation(selectedZone, rightClickSelected, newRotation);
}
@ -324,31 +402,42 @@ export default function Dropped3dWidgets() {
const newRotation: [number, number, number] = [
currentRotation[0],
currentRotation[1],
currentRotation[2] + deltaX * rotationSpeed
currentRotation[2] + deltaX * rotationSpeed,
];
updateWidgetRotation(selectedZone, rightClickSelected, newRotation);
}
};
const handleMouseUp = () => {
if (!rightClickSelected || !rightSelect) return;
const selectedZone = Object.keys(zoneWidgetData).find(zoneId =>
zoneWidgetData[zoneId].some(widget => widget.id === rightClickSelected)
const selectedZone = Object.keys(zoneWidgetData).find((zoneId) =>
zoneWidgetData[zoneId].some(
(widget) => widget.id === rightClickSelected
)
);
if (!selectedZone) return;
const selectedWidget = zoneWidgetData[selectedZone].find(widget => widget.id === rightClickSelected);
const selectedWidget = zoneWidgetData[selectedZone].find(
(widget) => widget.id === rightClickSelected
);
if (!selectedWidget) return;
// Format values to 2 decimal places
const formatValues = (vals: number[]) => vals.map(val => parseFloat(val.toFixed(2)));
if (rightSelect === "Horizontal Move" || rightSelect === "Vertical Move") {
let lastPosition = formatValues(selectedWidget.position) as [number, number, number];
const formatValues = (vals: number[]) =>
vals.map((val) => parseFloat(val.toFixed(2)));
if (
rightSelect === "Horizontal Move" ||
rightSelect === "Vertical Move"
) {
let lastPosition = formatValues(selectedWidget.position) as [
number,
number,
number
];
// (async () => {
// let response = await update3dWidget(selectedZone, organization, rightClickSelected, lastPosition);
//
//
// if (response) {
//
//
// }
// })();
let updatingPosition = {
@ -356,21 +445,22 @@ export default function Dropped3dWidgets() {
zoneId: selectedZone,
id: rightClickSelected,
position: lastPosition,
}
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-3D-widget:modifyPositionRotation", updatingPosition);
visualizationSocket.emit(
"v2:viz-3D-widget:modifyPositionRotation",
updatingPosition
);
}
}
else if (rightSelect.includes("Rotate")) {
} else if (rightSelect.includes("Rotate")) {
const rotation = selectedWidget.rotation || [0, 0, 0];
let lastRotation = formatValues(rotation) as [number, number, number];
// (async () => {
// let response = await update3dWidgetRotation(selectedZone, organization, rightClickSelected, lastRotation);
//
//
// if (response) {
//
//
// }
// })();
let updatingRotation = {
@ -378,9 +468,12 @@ export default function Dropped3dWidgets() {
zoneId: selectedZone,
id: rightClickSelected,
rotation: lastRotation,
}
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-3D-widget:modifyPositionRotation", updatingRotation);
visualizationSocket.emit(
"v2:viz-3D-widget:modifyPositionRotation",
updatingRotation
);
}
}
@ -403,33 +496,73 @@ export default function Dropped3dWidgets() {
return (
<>
{activeZoneWidgets.map(({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => {
const handleRightClick = (event: React.MouseEvent, id: string) => {
event.preventDefault();
const canvasElement = document.getElementById("real-time-vis-canvas");
if (!canvasElement) throw new Error("Canvas element not found");
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = event.clientX - canvasRect.left;
const relativeY = event.clientY - canvasRect.top;
setEditWidgetOptions(true);
setRightClickSelected(id);
setTop(relativeY);
setLeft(relativeX);
};
{activeZoneWidgets.map(
({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => {
const handleRightClick = (event: React.MouseEvent, id: string) => {
event.preventDefault();
const canvasElement = document.getElementById(
"real-time-vis-canvas"
);
if (!canvasElement) throw new Error("Canvas element not found");
const canvasRect = canvasElement.getBoundingClientRect();
const relativeX = event.clientX - canvasRect.left;
const relativeY = event.clientY - canvasRect.top;
setEditWidgetOptions(true);
setRightClickSelected(id);
setTop(relativeY);
setLeft(relativeX);
};
switch (type) {
case "ui-Widget 1":
return (<ProductionCapacity key={id} id={id} type={type} position={position} rotation={rotation} onContextMenu={(e) => handleRightClick(e, id)} />);
case "ui-Widget 2":
return (<ReturnOfInvestment key={id} id={id} type={type} position={position} rotation={rotation} onContextMenu={(e) => handleRightClick(e, id)} />);
case "ui-Widget 3":
return (<StateWorking key={id} id={id} type={type} position={position} rotation={rotation} onContextMenu={(e) => handleRightClick(e, id)} />);
case "ui-Widget 4":
return (<Throughput key={id} id={id} type={type} position={position} rotation={rotation} onContextMenu={(e) => handleRightClick(e, id)} />);
default:
return null;
switch (type) {
case "ui-Widget 1":
return (
<ProductionCapacity
key={id}
id={id}
type={type}
position={position}
rotation={rotation}
onContextMenu={(e) => handleRightClick(e, id)}
/>
);
case "ui-Widget 2":
return (
<ReturnOfInvestment
key={id}
id={id}
type={type}
position={position}
rotation={rotation}
onContextMenu={(e) => handleRightClick(e, id)}
/>
);
case "ui-Widget 3":
return (
<StateWorking
key={id}
id={id}
type={type}
position={position}
rotation={rotation}
onContextMenu={(e) => handleRightClick(e, id)}
/>
);
case "ui-Widget 4":
return (
<Throughput
key={id}
id={id}
type={type}
position={position}
rotation={rotation}
onContextMenu={(e) => handleRightClick(e, id)}
/>
);
default:
return null;
}
}
})}
)}
</>
);
}
}

View File

@ -126,12 +126,11 @@ const DroppedObjects: React.FC = () => {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
let deleteFloatingWidget = {
floatWidgetID: id,
organization: organization,
zoneId: zone.zoneId
}
zoneId: zone.zoneId,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-float:delete", deleteFloatingWidget);
@ -144,9 +143,7 @@ const DroppedObjects: React.FC = () => {
// if (res.message === "FloatingWidget deleted successfully") {
// deleteObject(zoneName, id, index); // Call the deleteObject method from the store
// }
} catch (error) {
}
} catch (error) {}
}
const handlePointerDown = (event: React.PointerEvent, index: number) => {
@ -461,15 +458,14 @@ const DroppedObjects: React.FC = () => {
position: boundedPosition,
},
index: draggingIndex.index,
zoneId: zone.zoneId
}
zoneId: zone.zoneId,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-float:add", updateFloatingWidget);
}
// if (response.message === "Widget updated successfully") {
console.log('boundedPosition: ', boundedPosition);
updateObjectPosition(zoneName, draggingIndex.index, boundedPosition);
// }
@ -503,106 +499,130 @@ const DroppedObjects: React.FC = () => {
setOpenKebabId((prevId) => (prevId === id ? null : id));
};
const containerHeight = getComputedStyle(
document.documentElement
).getPropertyValue("--realTimeViz-container-height");
const containerWidth = getComputedStyle(
document.documentElement
).getPropertyValue("--realTimeViz-container-width");
const heightMultiplier = parseFloat(containerHeight) * 0.14;
const widthMultiplier = parseFloat(containerWidth) * 0.13;
console.log("zone.objects: ", zone.objects);
return (
<div
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
className="floating-wrapper"
>
{zone.objects.map((obj, index) => (
<div
key={`${zoneName}-${index}`}
className={`${obj.className} ${selectedChartId?.id === obj.id && "activeChart"}`}
ref={chartWidget}
style={{
position: "absolute",
top:
typeof obj.position.top === "number"
? `calc(${obj.position.top}px + ${isPlaying && selectedZone.activeSides.includes("top")
? 90
: 0
}px)`
: "auto",
left:
typeof obj.position.left === "number"
? `calc(${obj.position.left}px + ${isPlaying && selectedZone.activeSides.includes("left")
? 90
: 0
}px)`
: "auto",
right:
typeof obj.position.right === "number"
? `calc(${obj.position.right}px + ${isPlaying && selectedZone.activeSides.includes("right")
? 90
: 0
}px)`
: "auto",
bottom:
typeof obj.position.bottom === "number"
? `calc(${obj.position.bottom}px + ${isPlaying && selectedZone.activeSides.includes("bottom")
? 90
: 0
}px)`
: "auto",
}}
onPointerDown={(event) => {
setSelectedChartId(obj);
handlePointerDown(event, index);
}}
>
{obj.className === "floating total-card" ? (
<>
<TotalCardComponent object={obj} />
</>
) : obj.className === "warehouseThroughput floating" ? (
<>
<WarehouseThroughputComponent object={obj} />
</>
) : obj.className === "fleetEfficiency floating" ? (
<>
<FleetEfficiencyComponent object={obj} />
</>
) : null}
{zone.objects.map((obj, index) => {
const topPosition =
typeof obj.position.top === "number"
? `calc(${obj.position.top}px + ${
isPlaying && selectedZone.activeSides.includes("top")
? `${heightMultiplier - 55}px`
: "0px"
})`
: "auto";
const leftPosition =
typeof obj.position.left === "number"
? `calc(${obj.position.left}px + ${
isPlaying && selectedZone.activeSides.includes("left")
? `${widthMultiplier - 100}px`
: "0px"
})`
: "auto";
const rightPosition =
typeof obj.position.right === "number"
? `calc(${obj.position.right}px + ${
isPlaying && selectedZone.activeSides.includes("right")
? `${widthMultiplier - 100}px`
: "0px"
})`
: "auto";
const bottomPosition =
typeof obj.position.bottom === "number"
? `calc(${obj.position.bottom}px + ${
isPlaying && selectedZone.activeSides.includes("bottom")
? `${heightMultiplier - 55}px`
: "0px"
})`
: "auto";
return (
<div
className="icon kebab"
ref={kebabRef}
onClick={(event) => {
event.stopPropagation();
handleKebabClick(obj.id, event);
key={`${zoneName}-${index}`}
className={`${obj.className} ${
selectedChartId?.id === obj.id && "activeChart"
}`}
ref={chartWidget}
style={{
position: "absolute",
top: topPosition,
left: leftPosition,
right: rightPosition,
bottom: bottomPosition,
}}
onPointerDown={(event) => {
setSelectedChartId(obj);
handlePointerDown(event, index);
}}
>
<KebabIcon />
</div>
{openKebabId === obj.id && (
<div className="kebab-options" ref={kebabRef}>
<div
className="dublicate btn"
onClick={(event) => {
event.stopPropagation();
handleDuplicate(zoneName, index); // Call the duplicate handler
}}
>
<div className="icon">
<DublicateIcon />
</div>
<div className="label">Duplicate</div>
</div>
<div
className="edit btn"
onClick={(event) => {
event.stopPropagation();
handleDelete(zoneName, obj.id); // Call the delete handler
}}
>
<div className="icon">
<DeleteIcon />
</div>
<div className="label">Delete</div>
</div>
{obj.className === "floating total-card" ? (
<TotalCardComponent object={obj} />
) : obj.className === "warehouseThroughput floating" ? (
<WarehouseThroughputComponent object={obj} />
) : obj.className === "fleetEfficiency floating" ? (
<FleetEfficiencyComponent object={obj} />
) : null}
<div
className="icon kebab"
ref={kebabRef}
onClick={(event) => {
event.stopPropagation();
handleKebabClick(obj.id, event);
}}
>
<KebabIcon />
</div>
)}
</div>
))}
{openKebabId === obj.id && (
<div className="kebab-options" ref={kebabRef}>
<div
className="dublicate btn"
onClick={(event) => {
event.stopPropagation();
handleDuplicate(zoneName, index); // Call the duplicate handler
}}
>
<div className="icon">
<DublicateIcon />
</div>
<div className="label">Duplicate</div>
</div>
<div
className="edit btn"
onClick={(event) => {
event.stopPropagation();
handleDelete(zoneName, obj.id); // Call the delete handler
}}
>
<div className="icon">
<DeleteIcon />
</div>
<div className="label">Delete</div>
</div>
</div>
)}
</div>
);
})}
{/* Render DistanceLines component during drag */}
{isPlaying === false &&

View File

@ -32,7 +32,6 @@ interface PanelProps {
zoneName: string;
activeSides: Side[];
panelOrder: Side[];
lockedPanels: Side[];
zoneId: string;
zoneViewPortTarget: number[];
@ -41,7 +40,7 @@ interface PanelProps {
}>
>;
hiddenPanels: string[];
setZonesData: React.Dispatch<React.SetStateAction<any>>; // Add this line
setZonesData: React.Dispatch<React.SetStateAction<any>>;
}
const generateUniqueId = () =>
@ -59,14 +58,13 @@ const Panel: React.FC<PanelProps> = ({
[side in Side]?: { width: number; height: number };
}>({});
const [openKebabId, setOpenKebabId] = useState<string | null>(null);
const { isPlaying } = usePlayButtonStore();
const { visualizationSocket } = useSocketStore();
const [canvasDimensions, setCanvasDimensions] = useState({
width: 0,
height: 0,
});
// Track canvas dimensions
useEffect(() => {
const canvas = document.getElementById("real-time-vis-canvas");
@ -80,42 +78,20 @@ const Panel: React.FC<PanelProps> = ({
});
};
// Initial measurement
updateCanvasDimensions();
// Set up ResizeObserver to track changes
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
resizeObserver.observe(canvas);
return () => {
resizeObserver.unobserve(canvas);
};
return () => resizeObserver.unobserve(canvas);
}, []);
useEffect(() => {
const canvas = document.getElementById("real-time-vis-canvas");
if (!canvas) return;
const updateCanvasDimensions = () => {
const rect = canvas.getBoundingClientRect();
setCanvasDimensions({
width: rect.width,
height: rect.height,
});
};
// Initial measurement
updateCanvasDimensions();
// Set up ResizeObserver to track changes
const resizeObserver = new ResizeObserver(updateCanvasDimensions);
resizeObserver.observe(canvas);
return () => {
resizeObserver.unobserve(canvas);
};
}, []);
// Calculate panel size
const panelSize = Math.max(
Math.min(canvasDimensions.width * 0.25, canvasDimensions.height * 0.25),
170 // Min 170px
);
// Define getPanelStyle
const getPanelStyle = useMemo(
() => (side: Side) => {
const currentIndex = selectedZone.panelOrder.indexOf(side);
@ -125,123 +101,105 @@ const Panel: React.FC<PanelProps> = ({
const topActive = previousPanels.includes("top");
const bottomActive = previousPanels.includes("bottom");
// Dynamic panel sizes based on canvas width
const panelSizeWidth = isPlaying ? Math.max(canvasDimensions.width * 0.14, 200) : Math.max(canvasDimensions.width * 0.165, 200); // Ensure minimum width of 200px
const panelSizeHeight = isPlaying ? Math.max(canvasDimensions.width * 0.13, 200) : Math.max(canvasDimensions.width * 0.13, 200); // Ensure minimum height of 200px
switch (side) {
case "top":
case "bottom":
return {
// minWidth: "200px", // Minimum width constraint
width: `calc(100% - ${(leftActive ? panelSizeWidth : 0) +
(rightActive ? panelSizeWidth : 0)
}px)`,
minHeight: "200px", // Minimum height constraint
height: `${panelSizeHeight - 2}px`, // Subtracting for border or margin
left: leftActive ? `${panelSizeWidth}px` : "0",
right: rightActive ? `${panelSizeWidth}px` : "0",
minWidth: "170px",
width: `calc(100% - ${
(leftActive ? panelSize : 0) + (rightActive ? panelSize : 0)
}px)`,
minHeight: "170px",
height: `${panelSize}px`,
left: leftActive ? `${panelSize}px` : "0",
right: rightActive ? `${panelSize}px` : "0",
[side]: "0",
};
case "left":
case "right":
return {
minWidth: "200px", // Minimum width constraint
width: `${panelSizeWidth - 2}px`, // Subtracting for border or margin
// minHeight: "200px", // Minimum height constraint
height: `calc(100% - ${(topActive ? panelSizeHeight : 0) +
(bottomActive ? panelSizeHeight : 0)
}px)`,
top: topActive ? `${panelSizeHeight}px` : "0",
bottom: bottomActive ? `${panelSizeHeight}px` : "0",
minWidth: "170px",
width: `${panelSize}px`,
minHeight: "170px",
height: `calc(100% - ${
(topActive ? panelSize : 0) + (bottomActive ? panelSize : 0)
}px)`,
top: topActive ? `${panelSize}px` : "0",
bottom: bottomActive ? `${panelSize}px` : "0",
[side]: "0",
};
default:
return {};
}
},
[
selectedZone.panelOrder,
isPlaying,
canvasDimensions.width,
canvasDimensions.height,
]
[selectedZone.panelOrder, panelSize]
);
// Handle drop event
const handleDrop = (e: React.DragEvent, panel: Side) => {
e.preventDefault();
const { draggedAsset } = useWidgetStore.getState();
if (!draggedAsset) return;
if (isPanelLocked(panel)) return;
if (!draggedAsset || isPanelLocked(panel)) return;
const currentWidgetsCount = getCurrentWidgetCount(panel);
const maxCapacity = calculatePanelCapacity(panel);
if (currentWidgetsCount >= maxCapacity) return;
addWidgetToPanel(draggedAsset, panel);
if (currentWidgetsCount < maxCapacity) {
addWidgetToPanel(draggedAsset, panel);
}
};
// Check if panel is locked
const isPanelLocked = (panel: Side) =>
selectedZone.lockedPanels.includes(panel);
// Get current widget count in a panel
const getCurrentWidgetCount = (panel: Side) =>
selectedZone.widgets.filter((w) => w.panel === panel).length;
// Calculate panel capacity
const calculatePanelCapacity = (panel: Side) => {
const CHART_WIDTH = 170;
const CHART_HEIGHT = 170;
const FALLBACK_HORIZONTAL_CAPACITY = 5;
const FALLBACK_VERTICAL_CAPACITY = 3;
const CHART_WIDTH = panelSize;
const CHART_HEIGHT = panelSize;
const dimensions = panelDimensions[panel];
if (!dimensions) {
return panel === "top" || panel === "bottom"
? FALLBACK_HORIZONTAL_CAPACITY
: FALLBACK_VERTICAL_CAPACITY;
return panel === "top" || panel === "bottom" ? 5 : 3; // Fallback capacities
}
return panel === "top" || panel === "bottom"
? Math.floor(dimensions.width / CHART_WIDTH)
: Math.floor(dimensions.height / CHART_HEIGHT);
? Math.max(1, Math.floor(dimensions.width / CHART_WIDTH))
: Math.max(1, Math.floor(dimensions.height / CHART_HEIGHT));
};
// while dublicate check this and add
// Add widget to panel
const addWidgetToPanel = async (asset: any, panel: Side) => {
const email = localStorage.getItem("email") || "";
const organization = email?.split("@")[1]?.split(".")[0];
const newWidget = {
...asset,
id: generateUniqueId(),
panel,
};
let addWidget = {
organization: organization,
zoneId: selectedZone.zoneId,
widget: newWidget,
};
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-widget:add", addWidget);
}
setSelectedZone((prev) => ({
...prev,
widgets: [...prev.widgets, newWidget],
}));
try {
// let response = await addingWidgets(selectedZone.zoneId, organization, newWidget);
// if (response.message === "Widget created successfully") {
// setSelectedZone((prev) => ({
// ...prev,
// widgets: [...prev.widgets, newWidget],
// }));
// }
} catch (error) {
console.error("Error adding widget:", error);
}
};
// Observe panel dimensions
useEffect(() => {
const observers: ResizeObserver[] = [];
const currentPanelRefs = panelRefs.current;
@ -258,6 +216,7 @@ const Panel: React.FC<PanelProps> = ({
}));
}
});
observer.observe(element);
observers.push(observer);
}
@ -268,22 +227,15 @@ const Panel: React.FC<PanelProps> = ({
};
}, [selectedZone.activeSides]);
// Handle widget reordering
const handleReorder = (fromIndex: number, toIndex: number, panel: Side) => {
if (!selectedZone) return; // Ensure selectedZone is not null
setSelectedZone((prev) => {
if (!prev) return prev; // Ensure prev is not null
// Filter widgets for the specified panel
const widgetsInPanel = prev.widgets.filter((w) => w.panel === panel);
// Reorder widgets within the same panel
const reorderedWidgets = arrayMove(widgetsInPanel, fromIndex, toIndex);
// Merge the reordered widgets back into the full list while preserving the order
const updatedWidgets = prev.widgets
.filter((widget) => widget.panel !== panel) // Keep widgets from other panels
.concat(reorderedWidgets); // Add the reordered widgets for the specified panel
.filter((widget) => widget.panel !== panel)
.concat(reorderedWidgets);
return {
...prev,
@ -292,13 +244,42 @@ const Panel: React.FC<PanelProps> = ({
});
};
// Calculate capacities and dimensions
const topWidth = getPanelStyle("top").width;
const bottomWidth = getPanelStyle("bottom").width;
const leftHeight = getPanelStyle("left").height;
const rightHeight = getPanelStyle("right").height;
const topCapacity = calculatePanelCapacity("top");
const bottomCapacity = calculatePanelCapacity("bottom");
const leftCapacity = calculatePanelCapacity("left");
const rightCapacity = calculatePanelCapacity("right");
return (
<>
<style>
{`
:root {
--topWidth: ${topWidth};
--bottomWidth: ${bottomWidth};
--leftHeight: ${leftHeight};
--rightHeight: ${rightHeight};
--topCapacity: ${topCapacity};
--bottomCapacity: ${bottomCapacity};
--leftCapacity: ${leftCapacity};
--rightCapacity: ${rightCapacity};
}
`}
</style>
{selectedZone.activeSides.map((side) => (
<div
key={side}
className={`panel ${side}-panel absolute ${isPlaying ? "" : ""} ${hiddenPanels.includes(side) ? "hidePanel" : ""
}`}
id="panel-wrapper"
className={`panel ${side}-panel absolute ${
hiddenPanels.includes(side) ? "hidePanel" : ""
}`}
style={getPanelStyle(side)}
onDrop={(e) => handleDrop(e, side)}
onDragOver={(e) => e.preventDefault()}
@ -344,5 +325,3 @@ const Panel: React.FC<PanelProps> = ({
};
export default Panel;
// canvasDimensions.width as percent

View File

@ -28,6 +28,7 @@ import {
useRightClickSelected,
useRightSelected,
} from "../../../store/useZone3DWidgetStore";
import Dropped3dWidgets from "./Dropped3dWidget";
type Side = "top" | "bottom" | "left" | "right";
@ -127,6 +128,8 @@ const RealTimeVisulization: React.FC = () => {
// useEffect(() => {}, [floatingWidgets]);
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
try {
event.preventDefault();
const email = localStorage.getItem("email") || "";
@ -172,7 +175,6 @@ const RealTimeVisulization: React.FC = () => {
if (visualizationSocket) {
visualizationSocket.emit("v2:viz-float:add", addFloatingWidget);
}
// let response = await addingFloatingWidgets(
// selectedZone.zoneId,
// organization,
@ -198,7 +200,9 @@ const RealTimeVisulization: React.FC = () => {
},
}));
} catch (error) { }
};
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const editWidgetOptions = document.querySelector(
@ -219,31 +223,10 @@ const RealTimeVisulization: React.FC = () => {
};
}, [setRightClickSelected]);
// Add this useEffect hook to your component
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const editWidgetOptions = document.querySelector(
".editWidgetOptions-wrapper"
);
if (
editWidgetOptions &&
!editWidgetOptions.contains(event.target as Node)
) {
setRightClickSelected(null);
setRightSelect(null);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, [setRightClickSelected]);
return (
<>
<div
ref={containerRef}
id="real-time-vis-canvas"
@ -274,6 +257,7 @@ const RealTimeVisulization: React.FC = () => {
}}
onDrop={(event) => handleDrop(event)}
onDragOver={(event) => event.preventDefault()}
>
<Scene />
</div>

View File

@ -0,0 +1,42 @@
import React, { useEffect, useRef } from 'react'
import { useSelectedFloorItem, useZoneAssetId } from '../../../store/store';
import * as THREE from "three";
import { useThree } from '@react-three/fiber';
import * as Types from "../../../types/world/worldTypes";
export default function ZoneAssets() {
const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
const { setSelectedFloorItem } = useSelectedFloorItem();
const { raycaster, controls, scene }: any = useThree();
useEffect(() => {
// console.log('zoneAssetId: ', zoneAssetId);
if (!zoneAssetId) return
console.log('zoneAssetId: ', zoneAssetId);
let AssetMesh = scene.getObjectByProperty("uuid", zoneAssetId.id);
if (!AssetMesh) return;
const bbox = new THREE.Box3().setFromObject(AssetMesh);
const size = bbox.getSize(new THREE.Vector3());
const center = bbox.getCenter(new THREE.Vector3());
const front = new THREE.Vector3(0, 0, 1);
AssetMesh.localToWorld(front);
front.sub(AssetMesh.position).normalize();
const distance = Math.max(size.x, size.y, size.z) * 2;
const newPosition = center.clone().addScaledVector(front, distance);
controls.setPosition(newPosition.x, newPosition.y, newPosition.z, true);
controls.setTarget(center.x, center.y, center.z, true);
controls.fitToBox(AssetMesh, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, });
setSelectedFloorItem(AssetMesh);
}, [zoneAssetId, scene, controls])
return (
<>
</>
)
}

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import List from "./List";
import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons";
import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect";
import { useZones } from "../../../store/store";
import { useFloorItems, useZones } from "../../../store/store";
import { useSelectedZoneStore } from "../../../store/useZoneStore";
interface DropDownListProps {
@ -38,92 +38,66 @@ const DropDownList: React.FC<DropDownListProps> = ({
const handleToggle = () => {
setIsOpen((prev) => !prev); // Toggle the state
};
interface Asset {
id: string;
name: string;
position: [number, number, number]; // x, y, z
}
const [zoneDataList, setZoneDataList] = useState<
{ id: string; name: string; assets: Asset[] }[]
>([]);
const [zonePoints3D, setZonePoints3D] = useState<[]>([]);
interface Zone {
zoneId: string;
zoneName: string;
points: [number, number, number][]; // polygon vertices
}
interface ZoneData {
id: string;
name: string;
assets: { id: string; name: string; position?: [] ;rotation?:{}}[];
}
const [zoneDataList, setZoneDataList] = useState<ZoneData[]>([]);
const { floorItems, setFloorItems } = useFloorItems();
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const isPointInsidePolygon = (point: [number, number], polygon: [number, number][]) => {
let inside = false;
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const xi = polygon[i][0], zi = polygon[i][1];
const xj = polygon[j][0], zj = polygon[j][1];
useEffect(() => {
// const value = (zones || []).map(
// (val: { zoneId: string; zoneName: string }) => ({
// id: val.zoneId,
// name: val.zoneName,
// })
// );
// console.log('zones: ', zones);
const value = (zones || []).map((val: { zoneId: string; zoneName: string }) => ({
id: val.zoneId,
name: val.zoneName
}));
setZoneDataList(prev => (JSON.stringify(prev) !== JSON.stringify(value) ? value : prev));
const allPoints = zones.flatMap((zone: any) => zone.points);
setZonePoints3D(allPoints);
// setZoneDataList([
// {
// id: "zone1",
// name: "Zone 1",
// assets: [
// {
// id: "asset1",
// name: "Asset 1",
// },
// {
// id: "asset2",
// name: "Asset 2",
// },
// {
// id: "asset3",
// name: "Asset 3",
// },
// ],
// },
// {
// id: "zone2",
// name: "Zone 2",
// assets: [
// {
// id: "asset4",
// name: "Asset 4",
// },
// {
// id: "asset5",
// name: "Asset 5",
// },
// {
// id: "asset6",
// name: "Asset 6",
// },
// ],
// },
// {
// id: "zone3",
// name: "Zone 3",
// assets: [
// {
// id: "asset7",
// name: "Asset 7",
// },
// {
// id: "asset8",
// name: "Asset 8",
// },
// ],
// },
// ]);
const intersect = ((zi > point[1]) !== (zj > point[1])) &&
(point[0] < (xj - xi) * (point[1] - zi) / (zj - zi + 0.000001) + xi);
}, [zones]);
if (intersect) inside = !inside;
}
return inside;
};
useEffect(() => {
// console.log('zonePoints3D: ', zonePoints3D);
}, [zonePoints3D])
const updatedZoneList: ZoneData[] = zones.map((zone: Zone) => {
const polygon2D = zone.points.map((p: [number, number, number]) => [p[0], p[2]]) as [number, number][];
const assetsInZone = floorItems
.filter((item: any) => {
const [x, , z] = item.position;
return isPointInsidePolygon([x, z], polygon2D);
})
.map((item: any) => ({
id: item.modeluuid,
name: item.modelname,
position: item.position,
rotation:item.rotation
}));
return {
id: zone.zoneId,
name: zone.zoneName,
assets: assetsInZone,
};
});
setZoneDataList(updatedZoneList);
}, [zones, floorItems]);
return (
<div className="dropdown-list-container">

View File

@ -12,10 +12,14 @@ import {
LockIcon,
RmoveIcon,
} from "../../icons/ExportCommonIcons";
import { useThree } from "@react-three/fiber";
import { useZoneAssetId } from "../../../store/store";
interface Asset {
id: string;
name: string;
position?: [number, number, number]; // Proper 3D vector
rotation?: { x: number; y: number; z: number }; // Proper rotation format
}
interface ZoneItem {
@ -33,11 +37,13 @@ interface ListProps {
const List: React.FC<ListProps> = ({ items = [], remove }) => {
const { activeModule, setActiveModule } = useModuleStore();
const { selectedZone, setSelectedZone } = useSelectedZoneStore();
const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
const { setSubModule } = useSubModuleStore();
const [expandedZones, setExpandedZones] = useState<Record<string, boolean>>(
{}
);
useEffect(() => {
useSelectedZoneStore.getState().setSelectedZone({
zoneName: "",
@ -88,7 +94,9 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
console.error("Error selecting zone:", error);
}
}
function handleAssetClick(asset: Asset) {
setZoneAssetId(asset)
}
return (
<>
{items.length > 0 ? (
@ -139,7 +147,7 @@ const List: React.FC<ListProps> = ({ items = [], remove }) => {
className="list-container asset-item"
>
<div className="list-item">
<div className="value">
<div className="value" onClick={() => handleAssetClick(asset)}>
<RenameInput value={asset.name} />
</div>
<div className="options-container">

View File

@ -5,7 +5,7 @@ interface SimpleCardProps {
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>; // React component for SVG icon
value: string;
per: string; // Percentage change
position?: [number, number]
position?: [number, number];
}
const SimpleCard: React.FC<SimpleCardProps> = ({
@ -15,7 +15,6 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
per,
position = [0, 0],
}) => {
const handleDragStart = (event: React.DragEvent<HTMLDivElement>) => {
const rect = event.currentTarget.getBoundingClientRect(); // Get position
const cardData = JSON.stringify({
@ -23,7 +22,7 @@ const SimpleCard: React.FC<SimpleCardProps> = ({
value,
per,
icon: Icon,
className: event.currentTarget.className,
position: [rect.top, rect.left], // ✅ Store position
});

View File

@ -1,6 +1,6 @@
import * as THREE from "three";
import { Geometry, Base, Subtraction } from "@react-three/csg";
import { useDeleteModels } from "../../../store/store";
import { useDeleteTool } from "../../../store/store";
import { useRef } from "react";
export interface CsgProps {
@ -11,19 +11,19 @@ export interface CsgProps {
}
export const Csg: React.FC<CsgProps> = (props) => {
const { deleteModels } = useDeleteModels();
const { deleteTool } = useDeleteTool();
const modelRef = useRef<THREE.Object3D>();
const originalMaterials = useRef<Map<THREE.Mesh, THREE.Material>>(new Map());
const handleHover = (hovered: boolean, object: THREE.Mesh | null) => {
if (modelRef.current && deleteModels) {
if (modelRef.current && deleteTool) {
modelRef.current.traverse((child) => {
if (child instanceof THREE.Mesh) {
if (!originalMaterials.current.has(child)) {
originalMaterials.current.set(child, child.material);
}
child.material = child.material.clone();
child.material.color.set(hovered && deleteModels ? 0xff0000 : (originalMaterials.current.get(child) as any).color);
child.material.color.set(hovered && deleteTool ? 0xff0000 : (originalMaterials.current.get(child) as any).color);
}
});
}

View File

@ -219,7 +219,7 @@ async function handleModelLoad(
eventData.position = newFloorItem.position;
eventData.rotation = [model.rotation.x, model.rotation.y, model.rotation.z];
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
eventData as Types.ConveyorEventsSchema
]);
@ -281,13 +281,139 @@ async function handleModelLoad(
return updatedItems;
});
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
eventData as Types.VehicleEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (res.type === "StaticMachine") {
const pointUUID = THREE.MathUtils.generateUUID();
const backendEventData: Extract<Types.EventData['eventData'], { type: 'StaticMachine' }> = {
type: "StaticMachine",
points: {
uuid: pointUUID,
position: res.points.position 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: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
eventData as Types.StaticMachineEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (res.type === "ArmBot") {
const pointUUID = THREE.MathUtils.generateUUID();
const backendEventData: Extract<Types.EventData['eventData'], { type: 'ArmBot' }> = {
type: "ArmBot",
points: {
uuid: pointUUID,
position: res.points.position as [number, number, number],
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 1, 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: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
eventData as Types.ArmBotEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else {
// API

View File

@ -1,5 +1,5 @@
import { useFrame, useThree } from "@react-three/fiber";
import { useAddAction, useDeleteModels, useRoofVisibility, useToggleView, useWallVisibility, useUpdateScene } from "../../../store/store";
import { useAddAction, useDeleteTool, useRoofVisibility, useToggleView, useWallVisibility, useUpdateScene } from "../../../store/store";
import hideRoof from "../geomentries/roofs/hideRoof";
import hideWalls from "../geomentries/walls/hideWalls";
import addAndUpdateReferencePillar from "../geomentries/pillars/addAndUpdateReferencePillar";
@ -16,7 +16,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar }
const { toggleView, setToggleView } = useToggleView();
const { scene, camera, pointer, raycaster, gl } = useThree();
const { addAction, setAddAction } = useAddAction();
const { deleteModels, setDeleteModels } = useDeleteModels();
const { deleteTool, setDeleteTool } = useDeleteTool();
const { updateScene, setUpdateScene } = useUpdateScene();
useEffect(() => {
@ -56,7 +56,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar }
if (addAction === "pillar") {
addPillar(referencePole, floorGroup);
}
if (deleteModels) {
if (deleteTool) {
DeletePillar(hoveredDeletablePillar, floorGroup);
}
}
@ -78,7 +78,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar }
canvasElement.removeEventListener("mouseup", onMouseUp);
canvasElement.removeEventListener("mousemove", onMouseMove);
};
}, [deleteModels, addAction])
}, [deleteTool, addAction])
useFrame(() => {
hideRoof(roofVisibility, floorGroup, camera);
@ -87,7 +87,7 @@ const FloorGroup = ({ floorGroup, lines, referencePole, hoveredDeletablePillar }
if (addAction === "pillar") {
addAndUpdateReferencePillar(raycaster, floorGroup, referencePole);
}
if (deleteModels) {
if (deleteTool) {
DeletableHoveredPillar(state, floorGroup, hoveredDeletablePillar);
}
})

View File

@ -1,5 +1,5 @@
import { useFrame, useThree } from "@react-three/fiber";
import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteModels, useFloorItems, useLoadingProgress, useRenderDistance, useselectedFloorItem, useSelectedItem, useSimulationStates, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store";
import { useActiveTool, useAsset3dWidget, useCamMode, useDeletableFloorItem, useDeleteTool, useFloorItems, useLoadingProgress, useRenderDistance, useSelectedFloorItem, useSelectedItem, useSimulationStates, useSocketStore, useToggleView, useTransformMode, } from "../../../store/store";
import assetVisibility from "../geomentries/assets/assetVisibility";
import { useEffect } from "react";
import * as THREE from "three";
@ -26,10 +26,10 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
const { toggleView } = useToggleView();
const { floorItems, setFloorItems } = useFloorItems();
const { camMode } = useCamMode();
const { deleteModels } = useDeleteModels();
const { deleteTool } = useDeleteTool();
const { setDeletableFloorItem } = useDeletableFloorItem();
const { transformMode } = useTransformMode();
const { setselectedFloorItem } = useselectedFloorItem();
const { setSelectedFloorItem } = useSelectedFloorItem();
const { activeTool } = useActiveTool();
const { selectedItem, setSelectedItem } = useSelectedItem();
const { simulationStates, setSimulationStates } = useSimulationStates();
@ -191,8 +191,10 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
isLeftMouseDown = false;
if (drag) return;
if (deleteModels) {
if (deleteTool) {
DeleteFloorItems(itemsGroup, hoveredDeletableFloorItem, setFloorItems, setSimulationStates, socket);
// Remove EventData if there are any in the asset.
}
const Mode = transformMode;
@ -212,12 +214,12 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
// }
// if (currentObject) {
// AttachedObject.current = currentObject as any;
// setselectedFloorItem(AttachedObject.current!);
// setSelectedFloorItem(AttachedObject.current!);
// }
} else {
const target = controls.getTarget(new THREE.Vector3());
await controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
setSelectedFloorItem(null);
}
}
}
@ -261,12 +263,12 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
controls.setTarget(center.x, center.y, center.z, true);
controls.fitToBox(AttachedObject.current!, true, { cover: true, paddingTop: 5, paddingLeft: 5, paddingBottom: 5, paddingRight: 5, });
setselectedFloorItem(AttachedObject.current!);
setSelectedFloorItem(AttachedObject.current!);
}
} else {
const target = controls.getTarget(new THREE.Vector3());
await controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
setSelectedFloorItem(null);
}
}
}
@ -295,7 +297,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
if (controls) {
const target = controls.getTarget(new THREE.Vector3());
controls.setTarget(target.x, 0, target.z, true);
setselectedFloorItem(null);
setSelectedFloorItem(null);
}
}
@ -307,14 +309,14 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]);
}, [deleteTool, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule,]);
useFrame(() => {
if (controls)
assetVisibility(itemsGroup, state.camera.position, renderDistance);
if (deleteModels && activeModule === "builder") {
if (deleteTool && activeModule === "builder") {
DeletableHoveredFloorItems(state, itemsGroup, hoveredDeletableFloorItem, setDeletableFloorItem);
} else if (!deleteModels) {
} else if (!deleteTool) {
if (hoveredDeletableFloorItem.current) {
hoveredDeletableFloorItem.current = undefined;
setDeletableFloorItem(null);

View File

@ -1,5 +1,5 @@
import { useEffect } from "react";
import { useDeleteModels, useDeletePointOrLine, useObjectPosition, useObjectRotation, useObjectScale, useSelectedWallItem, useSocketStore, useWallItems } from "../../../store/store";
import { useDeleteTool, useDeletePointOrLine, useObjectPosition, useObjectRotation, useObjectScale, useSelectedWallItem, useSocketStore, useWallItems } from "../../../store/store";
import { Csg } from "../csg/csg";
import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from "../../../types/world/worldConstants";
@ -16,7 +16,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
const state = useThree();
const { socket } = useSocketStore();
const { pointer, camera, raycaster } = state;
const { deleteModels, setDeleteModels } = useDeleteModels();
const { deleteTool, setDeleteTool } = useDeleteTool();
const { wallItems, setWallItems } = useWallItems();
const { objectPosition, setObjectPosition } = useObjectPosition();
const { objectScale, setObjectScale } = useObjectScale();
@ -210,7 +210,7 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
const onMouseUp = (evt: any) => {
if (evt.button === 0) {
isLeftMouseDown = false;
if (!drag && deleteModels && activeModule === "builder") {
if (!drag && deleteTool && activeModule === "builder") {
DeleteWallItems(hoveredDeletableWallItem, setWallItems, wallItems, socket);
}
}
@ -257,15 +257,15 @@ const WallItemsGroup = ({ currentWallItem, AssetConfigurations, hoveredDeletable
canvasElement.removeEventListener("drop", onDrop);
canvasElement.removeEventListener("dragover", onDragOver);
};
}, [deleteModels, wallItems])
}, [deleteTool, wallItems])
useEffect(() => {
if (deleteModels && activeModule === "builder") {
if (deleteTool && activeModule === "builder") {
handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex);
setSelectedWallItem(null);
setSelectedItemsIndex(null);
}
}, [deleteModels])
}, [deleteTool])
return (
<>

View File

@ -1,5 +1,5 @@
import { Geometry } from "@react-three/csg";
import { useDeleteModels, useSelectedWallItem, useToggleView, useTransformMode, useWallItems, useWalls } from "../../../store/store";
import { useDeleteTool, useSelectedWallItem, useToggleView, useTransformMode, useWallItems, useWalls } from "../../../store/store";
import handleMeshDown from "../eventFunctions/handleMeshDown";
import handleMeshMissed from "../eventFunctions/handleMeshMissed";
import WallsMesh from "./wallsMesh";
@ -11,13 +11,13 @@ const WallsAndWallItems = ({ CSGGroup, AssetConfigurations, setSelectedItemsInde
const { walls, setWalls } = useWalls();
const { wallItems, setWallItems } = useWallItems();
const { toggleView, setToggleView } = useToggleView();
const { deleteModels, setDeleteModels } = useDeleteModels();
const { deleteTool, setDeleteTool } = useDeleteTool();
const { transformMode, setTransformMode } = useTransformMode();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
useEffect(() => {
if (transformMode === null) {
if (!deleteModels) {
if (!deleteTool) {
handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex);
setSelectedWallItem(null);
setSelectedItemsIndex(null);
@ -33,12 +33,12 @@ const WallsAndWallItems = ({ CSGGroup, AssetConfigurations, setSelectedItemsInde
receiveShadow
visible={!toggleView}
onClick={(event) => {
if (!deleteModels && transformMode !== null) {
if (!deleteTool && transformMode !== null) {
handleMeshDown(event, currentWallItem, setSelectedWallItem, setSelectedItemsIndex, wallItems, toggleView);
}
}}
onPointerMissed={() => {
if (!deleteModels) {
if (!deleteTool) {
handleMeshMissed(currentWallItem, setSelectedWallItem, setSelectedItemsIndex);
setSelectedWallItem(null);
setSelectedItemsIndex(null);

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect, useMemo, useRef } from "react";
import { Line, Sphere } from "@react-three/drei";
import { useThree, useFrame } from "@react-three/fiber";
import * as THREE from "three";
import { useActiveLayer, useDeleteModels, useDeletePointOrLine, useMovePoint, useSocketStore, useToggleView, useToolMode, useRemovedLayer, useZones, useZonePoints } from "../../../store/store";
import { useActiveLayer, useDeleteTool, useDeletePointOrLine, useMovePoint, useSocketStore, useToggleView, useToolMode, useRemovedLayer, useZones, useZonePoints } from "../../../store/store";
// import { setZonesApi } from "../../../services/factoryBuilder/zones/setZonesApi";
// import { deleteZonesApi } from "../../../services/factoryBuilder/zones/deleteZoneApi";
import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi";
@ -23,7 +23,7 @@ const ZoneGroup: React.FC = () => {
const { removedLayer, setRemovedLayer } = useRemovedLayer();
const { toolMode, setToolMode } = useToolMode();
const { movePoint, setMovePoint } = useMovePoint();
const { deleteModels, setDeleteModels } = useDeleteModels();
const { deleteTool, setDeleteTool } = useDeleteTool();
const { activeLayer, setActiveLayer } = useActiveLayer();
const { socket } = useSocketStore();
@ -115,7 +115,7 @@ const ZoneGroup: React.FC = () => {
} else {
setDeletePointOrLine(false);
setMovePoint(false);
setDeleteModels(false);
setDeleteTool(false);
}
if (!toggleView) {
setStartPoint(null);

View File

@ -2,7 +2,7 @@ import { useEffect } from "react";
import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import * as CONSTANTS from "../../../types/world/worldConstants";
import { useActiveLayer, useSocketStore, useDeleteModels, useDeletePointOrLine, useMovePoint, useToggleView, useUpdateScene, useNewLines, useToolMode } from "../../../store/store";
import { useActiveLayer, useSocketStore, useDeleteTool, useDeletePointOrLine, useMovePoint, useToggleView, useUpdateScene, useNewLines, useToolMode } from "../../../store/store";
import { useThree } from "@react-three/fiber";
import arrayLineToObject from "../geomentries/lines/lineConvertions/arrayLineToObject";
import addPointToScene from "../geomentries/points/addPointToScene";
@ -14,7 +14,7 @@ import loadZones from "../geomentries/zones/loadZones";
const ZoneGroup = ({ zoneGroup, plane, floorPlanGroupLine, floorPlanGroupPoint, line, lines, currentLayerPoint, dragPointControls, floorPlanGroup, ReferenceLineMesh, LineCreated, isSnapped, ispreSnapped, snappedPoint, isSnappedUUID, isAngleSnapped, anglesnappedPoint }: any) => {
const { toggleView, setToggleView } = useToggleView();
const { deleteModels, setDeleteModels } = useDeleteModels();
const { deleteTool, setDeleteTool } = useDeleteTool();
const { deletePointOrLine, setDeletePointOrLine } = useDeletePointOrLine();
const { toolMode, setToolMode } = useToolMode();
const { movePoint, setMovePoint } = useMovePoint();
@ -35,7 +35,7 @@ const ZoneGroup = ({ zoneGroup, plane, floorPlanGroupLine, floorPlanGroupPoint,
if (toolMode === "Zone") {
setDeletePointOrLine(false);
setMovePoint(false);
setDeleteModels(false);
setDeleteTool(false);
} else {
removeSoloPoint(line, floorPlanGroupLine, floorPlanGroupPoint);
removeReferenceLine(floorPlanGroup, ReferenceLineMesh, LineCreated, line);

View File

@ -192,7 +192,7 @@ function processLoadedModel(
},
]);
if (item.eventData || item.modelfileID === '67e3db5ac2e8f37134526f40' || item.modelfileID === '67eb7904c2e8f37134527eae') {
if (item.eventData || item.modelfileID === '67e3db5ac2e8f37134526f40') {
processEventData(item, setSimulationStates);
}
@ -210,7 +210,7 @@ function processEventData(item: Types.EventData, setSimulationStates: any) {
data.position = item.position;
data.rotation = [item.rotation.x, item.rotation.y, item.rotation.z];
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
data as Types.ConveyorEventsSchema
]);
@ -222,58 +222,35 @@ function processEventData(item: Types.EventData, setSimulationStates: any) {
data.modelName = item.modelname;
data.position = item.position;
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
data as Types.VehicleEventsSchema
]);
} else if (item.modelfileID === '67e3db5ac2e8f37134526f40') {
} else if (item.eventData?.type === 'StaticMachine') {
const pointUUID = THREE.MathUtils.generateUUID();
const pointPosition = new THREE.Vector3(0, 1.5, -0.5);
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];
const staticMachine: Types.StaticMachineEventsSchema = {
modeluuid: item.modeluuid,
modelName: item.modelname,
type: "StaticMachine",
points: {
uuid: pointUUID,
position: [pointPosition.x, pointPosition.y, pointPosition.z],
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', buffer: 'Inherit', material: 'Inherit', isUsed: false },
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
connections: { source: { modelUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
},
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
};
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
staticMachine as Types.StaticMachineEventsSchema
data as Types.StaticMachineEventsSchema
]);
} else if (item.modelfileID === '67eb7904c2e8f37134527eae') {
const pointUUID = THREE.MathUtils.generateUUID();
const pointPosition = new THREE.Vector3(0, 2.75, -0.5);
} else if (item.eventData?.type === 'ArmBot') {
const armBot: Types.ArmBotEventsSchema = {
modeluuid: item.modeluuid,
modelName: item.modelname,
type: "ArmBot",
points: {
uuid: pointUUID,
position: [pointPosition.x, pointPosition.y, pointPosition.z],
actions: { uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', speed: 1, processes: [] },
triggers: { uuid: THREE.MathUtils.generateUUID(), name: 'Trigger 1', type: 'OnComplete' },
connections: { source: { modelUUID: item.modeluuid, pointUUID: pointUUID }, targets: [] },
},
position: item.position,
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
}
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: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema)[]) => [
setSimulationStates((prevEvents: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
armBot as Types.ArmBotEventsSchema
data as Types.ArmBotEventsSchema
]);
}

View File

@ -181,7 +181,10 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
uuid: THREE.MathUtils.generateUUID()
}))
: [defaultAction],
triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers,
triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({
...trigger,
uuid: THREE.MathUtils.generateUUID()
})),
connections: {
source: { modelUUID: obj.uuid, pointUUID },
targets: []
@ -320,6 +323,163 @@ const CopyPasteControls = ({ itemsGroupRef, copiedObjects, setCopiedObjects, pas
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'StaticMachine' && eventData) {
const createStaticMachinePoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as Types.StaticMachineEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
buffer: 0,
material: 'Inherit',
};
return {
uuid: pointUUID,
position: vehiclePoint?.position,
actions: hasActions
? {
...vehiclePoint.actions,
uuid: THREE.MathUtils.generateUUID()
}
: defaultAction,
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: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as Types.StaticMachineEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'ArmBot' && eventData) {
const createArmBotPoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as Types.ArmBotEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
buffer: 0,
material: 'Inherit',
};
return {
uuid: pointUUID,
position: vehiclePoint?.position,
actions: hasActions
? {
...vehiclePoint.actions,
uuid: THREE.MathUtils.generateUUID()
}
: defaultAction,
triggers: {
uuid: THREE.MathUtils.generateUUID(),
name: vehiclePoint.triggers.name,
type: vehiclePoint.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: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as Types.ArmBotEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else {
//REST

View File

@ -163,7 +163,10 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
uuid: THREE.MathUtils.generateUUID()
}))
: [defaultAction],
triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers,
triggers: (eventData as Types.ConveyorEventsSchema)?.points[index].triggers.map(trigger => ({
...trigger,
uuid: THREE.MathUtils.generateUUID()
})),
connections: {
source: { modelUUID: newFloorItem.modeluuid, pointUUID },
targets: []
@ -174,9 +177,9 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
const backendEventData = {
type: 'Conveyor',
points: [
createConveyorPoint(0), // point1
createConveyorPoint(1), // middlePoint
createConveyorPoint(2) // point2
createConveyorPoint(0),
createConveyorPoint(1),
createConveyorPoint(2)
],
speed: (eventData as Types.ConveyorEventsSchema)?.speed
};
@ -302,6 +305,163 @@ const DuplicationControls = ({ itemsGroupRef, duplicatedObjects, setDuplicatedOb
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'StaticMachine' && eventData) {
const createStaticMachinePoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as Types.StaticMachineEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
buffer: 0,
material: 'Inherit',
};
return {
uuid: pointUUID,
position: vehiclePoint?.position,
actions: hasActions
? {
...vehiclePoint.actions,
uuid: THREE.MathUtils.generateUUID()
}
: defaultAction,
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: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as Types.StaticMachineEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else if (eventData.type === 'ArmBot' && eventData) {
const createArmBotPoint = () => {
const pointUUID = THREE.MathUtils.generateUUID();
const vehiclePoint = (eventData as Types.ArmBotEventsSchema)?.points;
const hasActions = vehiclePoint?.actions !== undefined;
const defaultAction = {
uuid: THREE.MathUtils.generateUUID(),
name: 'Action 1',
buffer: 0,
material: 'Inherit',
};
return {
uuid: pointUUID,
position: vehiclePoint?.position,
actions: hasActions
? {
...vehiclePoint.actions,
uuid: THREE.MathUtils.generateUUID()
}
: defaultAction,
triggers: {
uuid: THREE.MathUtils.generateUUID(),
name: vehiclePoint.triggers.name,
type: vehiclePoint.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: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => [
...(prevEvents || []),
newEventData as Types.ArmBotEventsSchema
]);
socket.emit("v2:model-asset:add", data);
} else {
//REST

View File

@ -323,7 +323,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
// eventData: { type: backendEventData.type, points: backendEventData.points },
eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id,
};
@ -376,7 +376,7 @@ function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObje
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
// eventData: { type: backendEventData.type, points: backendEventData.points },
eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id,
};

View File

@ -328,7 +328,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
// eventData: { type: backendEventData.type, points: backendEventData.points },
eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id,
};
@ -381,7 +381,7 @@ function RotateControls({ rotatedObjects, setRotatedObjects, movedObjects, setMo
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
// eventData: { type: backendEventData.type, points: backendEventData.points },
eventData: { type: backendEventData.type, points: backendEventData.points },
socketId: socket.id,
};

View File

@ -1,6 +1,6 @@
import { TransformControls } from "@react-three/drei";
import * as THREE from "three";
import { useselectedFloorItem, useObjectPosition, useObjectScale, useObjectRotation, useTransformMode, useFloorItems, useSocketStore, useActiveTool } from "../../../store/store";
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';
@ -8,7 +8,7 @@ import { useEffect } from "react";
export default function TransformControl() {
const state = useThree();
const { selectedFloorItem, setselectedFloorItem } = useselectedFloorItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { objectPosition, setObjectPosition } = useObjectPosition();
const { objectScale, setObjectScale } = useObjectScale();
const { objectRotation, setObjectRotation } = useObjectRotation();
@ -96,7 +96,7 @@ export default function TransformControl() {
const target = (state.controls as any).getTarget(new THREE.Vector3());
(state.controls as any).setTarget(target.x, 0, target.z, true);
}
setselectedFloorItem(null);
setSelectedFloorItem(null);
{
setObjectPosition({ x: undefined, y: undefined, z: undefined });
setObjectScale({ x: undefined, y: undefined, z: undefined });

View File

@ -6,7 +6,7 @@ import {
useSelectedActionSphere,
useSelectedPath,
useSelectedWallItem,
useselectedFloorItem,
useSelectedFloorItem,
} from "../../../store/store";
import * as Types from "../../../types/world/worldTypes";
import * as CONSTANTS from "../../../types/world/worldConstants";
@ -15,7 +15,7 @@ import { useEffect } from "react";
export default function PostProcessing() {
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
const { selectedWallItem, setSelectedWallItem } = useSelectedWallItem();
const { selectedFloorItem, setselectedFloorItem } = useselectedFloorItem();
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
const { selectedActionSphere } = useSelectedActionSphere();
const { selectedPath } = useSelectedPath();

View File

@ -19,6 +19,7 @@ import Simulation from "../simulation/simulation";
// import Simulation from "./simulationtemp/simulation";
import ZoneCentreTarget from "../../components/ui/componets/zoneCameraTarget";
import Dropped3dWidgets from "../../components/ui/componets/Dropped3dWidget";
import ZoneAssets from "../../components/ui/componets/zoneAssets";
export default function Scene() {
const map = useMemo(
@ -48,6 +49,7 @@ export default function Scene() {
<MeasurementTool />
<World />
<ZoneCentreTarget />
<ZoneAssets />
<Simulation />
<PostProcessing />
{savedTheme !== "dark" ? <Sun /> : <></>}

View File

@ -3,7 +3,7 @@ import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import * as Types from '../../../types/world/worldTypes';
import { QuadraticBezierLine } from '@react-three/drei';
import { useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store';
import { useDeleteTool, useIsConnecting, useRenderDistance, useSimulationStates, useSocketStore } from '../../../store/store';
import useModuleStore from '../../../store/useModuleStore';
import { usePlayButtonStore } from '../../../store/usePlayButtonStore';
import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/setEventsApt';
@ -11,6 +11,7 @@ import { setEventApi } from '../../../services/factoryBuilder/assest/floorAsset/
function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObject<THREE.Group> }) {
const { activeModule } = useModuleStore();
const { gl, raycaster, scene, pointer, camera } = useThree();
const { deleteTool } = useDeleteTool();
const { renderDistance } = useRenderDistance();
const { setIsConnecting } = useIsConnecting();
const { simulationStates, setSimulationStates } = useSimulationStates();
@ -21,6 +22,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const [firstSelected, setFirstSelected] = useState<{ modelUUID: string; sphereUUID: string; position: THREE.Vector3; isCorner: boolean; } | null>(null);
const [currentLine, setCurrentLine] = useState<{ start: THREE.Vector3, end: THREE.Vector3, mid: THREE.Vector3 } | null>(null);
const [helperlineColor, setHelperLineColor] = useState<string>('red');
const [hoveredLineKey, setHoveredLineKey] = useState<string | null>(null);
const updatePathConnections = (fromModelUUID: string, fromPointUUID: string, toModelUUID: string, toPointUUID: string) => {
const updatedPaths = simulationStates.map(path => {
@ -334,7 +336,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
path.modeluuid === fromModelUUID || path.modeluuid === toModelUUID
);
// updateBackend(updatedPathDetails);
updateBackend(updatedPathDetails);
};
const updateBackend = async (updatedPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema | Types.StaticMachineEventsSchema | Types.ArmBotEventsSchema)[]) => {
@ -375,6 +377,38 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
socket.emit('v2:model-asset:updateEventData', data);
} else if (updatedPath.type === 'StaticMachine') {
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "StaticMachine", points: updatedPath.points }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "StaticMachine", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
} else if (updatedPath.type === 'ArmBot') {
// await setEventApi(
// organization,
// updatedPath.modeluuid,
// { type: "ArmBot", points: updatedPath.points }
// );
const data = {
organization: organization,
modeluuid: updatedPath.modeluuid,
eventData: { type: "ArmBot", points: updatedPath.points }
}
socket.emit('v2:model-asset:updateEventData', data);
}
})
@ -479,7 +513,6 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
// For Vehicles, check if they're already connected to anything
if (intersected.userData.path.type === 'Vehicle') {
console.log('intersected: ', intersected);
const vehicleConnections = intersected.userData.path.points.connections.targets.length;
if (vehicleConnections >= 1) {
console.log("Vehicle can only have one connection");
@ -589,20 +622,10 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
// All checks passed - make the connection
handleAddConnection(
firstSelected.modelUUID,
firstSelected.sphereUUID,
modelUUID,
sphereUUID
);
handleAddConnection(firstSelected.modelUUID, firstSelected.sphereUUID, modelUUID, sphereUUID);
} else {
// First selection - just store it
setFirstSelected({
modelUUID,
sphereUUID,
position: worldPosition,
isCorner: isStartOrEnd
});
setFirstSelected({ modelUUID, sphereUUID, position: worldPosition, isCorner: isStartOrEnd });
setIsConnecting(true);
}
}
@ -615,7 +638,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
};
if (activeModule === 'simulation') {
if (activeModule === 'simulation' && !deleteTool) {
canvasElement.addEventListener("mousedown", onMouseDown);
canvasElement.addEventListener("mouseup", onMouseUp);
canvasElement.addEventListener("mousemove", onMouseMove);
@ -632,7 +655,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
canvasElement.removeEventListener("mousemove", onMouseMove);
canvasElement.removeEventListener("contextmenu", onContextMenu);
};
}, [camera, scene, raycaster, firstSelected, simulationStates]);
}, [camera, scene, raycaster, firstSelected, simulationStates, deleteTool]);
useFrame(() => {
Object.values(groupRefs.current).forEach((group) => {
@ -665,9 +688,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
}
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) =>
obj.object.name.includes("events-sphere")
);
const sphereIntersects = raycaster.intersectObjects(pathsGroupRef.current.children, true).filter((obj) => obj.object.name.includes("events-sphere"));
if (sphereIntersects.length > 0) {
const sphere = sphereIntersects[0].object;
@ -682,15 +703,21 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const isVehicleToVehicle = firstPath?.type === 'Vehicle' && secondPath?.type === 'Vehicle';
// Inside the useFrame hook, where we check for snapped spheres:
const isConnectable = (pathData.type === 'Vehicle' ||
const isConnectable = (
pathData.type === 'Vehicle' ||
pathData.type === 'ArmBot' ||
(pathData.points.length > 0 && (
sphereUUID === pathData.points[0].uuid ||
sphereUUID === pathData.points[pathData.points.length - 1].uuid
))) &&
sphereUUID === pathData.points[pathData.points.length - 1].uuid ||
(pathData.type === 'Conveyor' && firstPath?.type === 'ArmBot') // Allow ArmBot to connect to middle points
))
) &&
!isVehicleToVehicle &&
!(firstPath?.type === 'Conveyor' &&
!(
firstPath?.type === 'Conveyor' &&
pathData.type === 'Conveyor' &&
!firstSelected.isCorner);
!firstSelected.isCorner
);
// Check for duplicate connection (regardless of path type)
const isDuplicateConnection = simulationStates.some(path => {
@ -825,6 +852,127 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
}
});
const removeConnections = (connection1: { model: string; point: string }, connection2: { model: string; point: string }) => {
const updatedStates = simulationStates.map(state => {
// Handle Conveyor (which has multiple points)
if (state.type === 'Conveyor') {
const updatedConveyor: Types.ConveyorEventsSchema = {
...state,
points: state.points.map(point => {
// Check if this point is either connection1 or connection2
if ((state.modeluuid === connection1.model && point.uuid === connection1.point) ||
(state.modeluuid === connection2.model && point.uuid === connection2.point)) {
return {
...point,
connections: {
...point.connections,
targets: point.connections.targets.filter(target => {
// Remove the target that matches the other connection
return !(
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
);
})
}
};
}
return point;
})
};
return updatedConveyor;
}
// Handle Vehicle
else if (state.type === 'Vehicle') {
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
const updatedVehicle: Types.VehicleEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(target => {
return !(
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
);
})
},
// Ensure all required Vehicle point properties are included
speed: state.points.speed,
actions: state.points.actions
}
};
return updatedVehicle;
}
}
// Handle StaticMachine
else if (state.type === 'StaticMachine') {
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
const updatedStaticMachine: Types.StaticMachineEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(target => {
return !(
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
);
})
},
// Ensure all required StaticMachine point properties are included
actions: state.points.actions,
triggers: state.points.triggers
}
};
return updatedStaticMachine;
}
}
// Handle ArmBot
else if (state.type === 'ArmBot') {
if ((state.modeluuid === connection1.model && state.points.uuid === connection1.point) ||
(state.modeluuid === connection2.model && state.points.uuid === connection2.point)) {
const updatedArmBot: Types.ArmBotEventsSchema = {
...state,
points: {
...state.points,
connections: {
...state.points.connections,
targets: state.points.connections.targets.filter(target => {
return !(
(target.modelUUID === connection1.model && target.pointUUID === connection1.point) ||
(target.modelUUID === connection2.model && target.pointUUID === connection2.point)
);
})
},
// Ensure all required ArmBot point properties are included
actions: state.points.actions,
triggers: state.points.triggers
}
};
return updatedArmBot;
}
}
return state;
});
const updatedPaths = updatedStates.filter(state =>
state.modeluuid === connection1.model || state.modeluuid === connection2.model
);
updateBackend(updatedPaths);
setSimulationStates(updatedStates);
};
return (
<group name='simulationConnectionGroup' visible={!isPlaying}>
{simulationStates.flatMap(path => {
@ -832,7 +980,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
return path.points.flatMap(point =>
point.connections.targets.map((target, index) => {
const targetPath = simulationStates.find(p => p.modeluuid === target.modelUUID);
if (targetPath?.type === 'Vehicle') return null;
if (targetPath?.type !== 'Conveyor' && targetPath?.type !== 'ArmBot') return null;
const fromSphere = pathsGroupRef.current?.getObjectByProperty('uuid', point.uuid);
const toSphere = pathsGroupRef.current?.getObjectByProperty('uuid', target.pointUUID);
@ -845,11 +993,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2);
return (
<QuadraticBezierLine
@ -858,11 +1002,30 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="white"
color={
deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}`
? 'red'
: targetPath?.type === 'ArmBot'
? '#42a5f5'
: 'white'
}
lineWidth={4}
dashed
dashed={(deleteTool && hoveredLineKey === `${point.uuid}-${target.pointUUID}-${index}`) ? false : true}
dashSize={0.75}
dashScale={20}
onPointerOver={() => setHoveredLineKey(`${point.uuid}-${target.pointUUID}-${index}`)}
onPointerOut={() => setHoveredLineKey(null)}
onClick={() => {
if (deleteTool) {
const connection1 = { model: path.modeluuid, point: point.uuid }
const connection2 = { model: target.modelUUID, point: target.pointUUID }
removeConnections(connection1, connection2)
}
}}
userData={target}
/>
);
}
@ -884,11 +1047,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2);
return (
<QuadraticBezierLine
@ -897,11 +1056,27 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="orange"
color={
deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`
? 'red'
: 'orange'
}
lineWidth={4}
dashed
dashed={(deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`) ? false : true}
dashSize={0.75}
dashScale={20}
onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)}
onPointerOut={() => setHoveredLineKey(null)}
onClick={() => {
if (deleteTool) {
const connection1 = { model: path.modeluuid, point: path.points.uuid }
const connection2 = { model: target.modelUUID, point: target.pointUUID }
removeConnections(connection1, connection2)
}
}}
userData={target}
/>
);
}
@ -925,11 +1100,7 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
const distance = fromWorldPosition.distanceTo(toWorldPosition);
const heightFactor = Math.max(0.5, distance * 0.2);
const midPoint = new THREE.Vector3(
(fromWorldPosition.x + toWorldPosition.x) / 2,
Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor,
(fromWorldPosition.z + toWorldPosition.z) / 2
);
const midPoint = new THREE.Vector3((fromWorldPosition.x + toWorldPosition.x) / 2, Math.max(fromWorldPosition.y, toWorldPosition.y) + heightFactor, (fromWorldPosition.z + toWorldPosition.z) / 2);
return (
<QuadraticBezierLine
@ -938,11 +1109,28 @@ function PathConnector({ pathsGroupRef }: { pathsGroupRef: React.MutableRefObjec
start={fromWorldPosition.toArray()}
end={toWorldPosition.toArray()}
mid={midPoint.toArray()}
color="#42a5f5"
color={
deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`
? 'red'
: '#42a5f5'
}
lineWidth={4}
dashed
dashed={(deleteTool && hoveredLineKey === `${path.points.uuid}-${target.pointUUID}-${index}`) ? false : true}
dashSize={0.75}
dashScale={20}
onPointerOver={() => setHoveredLineKey(`${path.points.uuid}-${target.pointUUID}-${index}`)}
onPointerOut={() => setHoveredLineKey(null)}
onClick={() => {
if (deleteTool) {
const connection1 = { model: path.modeluuid, point: path.points.uuid }
const connection2 = { model: target.modelUUID, point: target.pointUUID }
removeConnections(connection1, connection2)
}
}}
userData={target}
/>
);
}

View File

@ -64,7 +64,8 @@ export const handleSaveTemplate = async ({
floatingWidget,
widgets3D,
};
console.log('newTemplate: ', newTemplate);
// Extract organization from email
const email = localStorage.getItem("email") || "";
const organization = email.includes("@")

View File

@ -106,9 +106,9 @@ export const useMenuVisible = create<any>((set: any) => ({
setMenuVisible: (x: any) => set(() => ({ menuVisible: x })),
}));
export const useDeleteModels = create<any>((set: any) => ({
deleteModels: false,
setDeleteModels: (x: any) => set(() => ({ deleteModels: x })),
export const useDeleteTool = create<any>((set: any) => ({
deleteTool: false,
setDeleteTool: (x: any) => set(() => ({ deleteTool: x })),
}));
export const useToolMode = create<any>((set: any) => ({
@ -164,9 +164,9 @@ export const useSelectedWallItem = create<any>((set: any) => ({
setSelectedWallItem: (x: any) => set(() => ({ selectedWallItem: x })),
}));
export const useselectedFloorItem = create<any>((set: any) => ({
export const useSelectedFloorItem = create<any>((set: any) => ({
selectedFloorItem: null,
setselectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })),
setSelectedFloorItem: (x: any) => set(() => ({ selectedFloorItem: x })),
}));
export const useDeletableFloorItem = create<any>((set: any) => ({
@ -446,3 +446,23 @@ export const useTileDistance = create<any>((set: any) => ({
planeValue: { ...state.planeValue, ...value },
})),
}));
// Define the Asset type
type Asset = {
id: string;
name: string;
position?: [number, number, number]; // Optional: 3D position
rotation?: { x: number; y: number; z: number }; // Optional: Euler rotation
};
// Zustand store type
type ZoneAssetState = {
zoneAssetId: Asset | null;
setZoneAssetId: (asset: Asset | null) => void;
};
// Zustand store
export const useZoneAssetId = create<ZoneAssetState>((set) => ({
zoneAssetId: null,
setZoneAssetId: (asset) => set({ zoneAssetId: asset }),
}));

View File

@ -12,7 +12,9 @@ type ZoneWidgetStore = {
zoneWidgetData: Record<string, WidgetData[]>;
setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void;
addWidget: (zoneId: string, widget: WidgetData) => void;
tempWidget: (zoneId: string, widget: WidgetData) => void;
updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void;
tempWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void;
updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => void;
};
@ -31,6 +33,13 @@ export const useZoneWidgetStore = create<ZoneWidgetStore>((set) => ({
[zoneId]: [...(state.zoneWidgetData[zoneId] || []), { ...widget, rotation: widget.rotation || [0, 0, 0] }],
},
})),
tempWidget: (zoneId: string, widget: WidgetData) =>
set((state: ZoneWidgetStore) => ({
zoneWidgetData: {
...state.zoneWidgetData,
[zoneId]: [...(state.zoneWidgetData[zoneId] || []), { ...widget, rotation: widget.rotation || [0, 0, 0] }],
},
})),
updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) =>
set((state: ZoneWidgetStore) => {
@ -44,6 +53,18 @@ export const useZoneWidgetStore = create<ZoneWidgetStore>((set) => ({
},
};
}),
tempWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) =>
set((state: ZoneWidgetStore) => {
const widgets = state.zoneWidgetData[zoneId] || [];
return {
zoneWidgetData: {
...state.zoneWidgetData,
[zoneId]: widgets.map((widget: WidgetData) =>
widget.id === widgetId ? { ...widget, position: newPosition } : widget
),
},
};
}),
updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) =>
set((state: ZoneWidgetStore) => {
@ -59,13 +80,6 @@ export const useZoneWidgetStore = create<ZoneWidgetStore>((set) => ({
}),
}));
// export type WidgetData = {
// id: string;
// type: string;
// position: [number, number, number];
// rotation?: [number, number, number];
// tempPosition?: [number, number, number];
// };
interface RightClickStore {
rightClickSelected: string | null;

View File

@ -70,6 +70,67 @@
position: relative;
overflow: auto;
.template-list {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
min-height: 50vh;
max-height: 60vh;
}
.template-item {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 1rem;
transition: box-shadow 0.3s ease;
}
.template-image-container {
position: relative;
padding-bottom: 56.25%; // 16:9 aspect ratio
}
.template-image {
position: absolute;
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 4px;
cursor: pointer;
transition: transform 0.3s ease;
}
.template-details {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 0.5rem;
}
.template-name {
cursor: pointer;
font-weight: 500;
}
.delete-button {
padding: 0.25rem 0.5rem;
background: #ff4444;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: opacity 0.3s ease;
}
.no-templates {
text-align: center;
color: #666;
padding: 2rem;
grid-column: 1 / -1;
}
.widget-left-sideBar {
min-height: 50vh;
max-height: 60vh;

View File

@ -24,9 +24,17 @@
}
.floating {
width: 100%;
max-width: 250px;
min-height: 83px;
width: calc(var(--realTimeViz-container-width) * 0.2);
height: calc(var(--realTimeViz-container-width) * 0.05);
min-width: 250px;
max-width: 300px;
min-height: 83px !important;
// max-height: 100px !important;
background: var(--background-color);
border: 1.23px solid var(--border-color);
box-shadow: 0px 4.91px 4.91px 0px #0000001c;
@ -60,9 +68,8 @@
display: flex;
background-color: var(--background-color);
position: absolute;
bottom: 10px;
// bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
border-radius: 8px;
@ -70,6 +77,7 @@
overflow: auto;
max-width: calc(100% - 500px);
z-index: 3;
transform: translate(-50%, -100%);
&::-webkit-scrollbar {
display: none;
@ -116,8 +124,8 @@
}
.zone-wrapper.bottom {
bottom: calc(var(--realTimeViz-container-height) * 0.27);
bottom: 200px;
top: var(--bottomWidth);
// bottom: 200px;
}
.content-container {
@ -138,7 +146,7 @@
display: flex;
background-color: rgba(224, 223, 255, 0.5);
position: absolute;
bottom: 10px;
// bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
gap: 6px;
@ -203,9 +211,9 @@
.chart-container {
width: 100%;
min-height: 150px;
max-height: 100%;
// border: 1px dashed var(--background-color-gray);
border: 1px dashed var(--background-color-gray);
border-radius: 8px;
box-shadow: var(--box-shadow-medium);
padding: 6px 0;
@ -343,7 +351,7 @@
.chart-container {
width: 100%;
min-height: 160px;
min-height: 150px;
max-height: 100%;
border-radius: 8px;
box-shadow: var(--box-shadow-medium);
@ -362,7 +370,7 @@
.playingFlase {
.zone-wrapper.bottom {
bottom: calc(var(--realTimeViz-container-height) * 0.25);
top: var(--bottomWidth);
// bottom: 210px;
}
}
@ -742,7 +750,7 @@
}
.activeChart {
outline: 1px solid var(--accent-color);
outline: 2px solid var(--accent-color);
z-index: 2 !important;
}
@ -786,4 +794,4 @@
}
}
}
}
}

View File

@ -21,3 +21,7 @@
box-shadow: var(--box-shadow-light);
}
}
.pointer-none{
pointer-events: none;
}

View File

@ -324,7 +324,7 @@ interface StaticMachineEventsSchema {
points: {
uuid: string;
position: [number, number, number];
actions: { uuid: string; name: string; buffer: number | string; material: string; isUsed: boolean };
actions: { uuid: string; name: string; buffer: number; material: string; };
triggers: { uuid: string; name: string; type: string };
connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] };
};
@ -375,5 +375,23 @@ export type EventData = {
connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] };
speed: number;
};
} | {
type: 'StaticMachine';
points: {
uuid: string;
position: [number, number, number];
actions: { uuid: string; name: string; buffer: number; material: string; };
triggers: { uuid: string; name: string; type: string };
connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] };
};
} | {
type: 'ArmBot';
points: {
uuid: string;
position: [number, number, number];
actions: { uuid: string; name: string; speed: number; processes: { triggerId: string; startPoint: string; endPoint: string }[] };
triggers: { uuid: string; name: string; type: string };
connections: { source: { modelUUID: string; pointUUID: string }; targets: { modelUUID: string; pointUUID: string }[] };
};
};
}

View File

@ -1,7 +1,7 @@
import React from "react";
interface OuterClickProps {
contextClassName: string;
contextClassName: string[]; // Make sure this is an array of strings
setMenuVisible: React.Dispatch<React.SetStateAction<boolean>>;
}
@ -11,8 +11,12 @@ export default function OuterClick({
}: OuterClickProps) {
const handleClick = (event: MouseEvent) => {
const targets = event.target as HTMLElement;
// Check if the click is outside the selectable-dropdown-wrapper
if (!targets.closest(`.${contextClassName}`)) {
// Check if the click is outside of any of the provided class names
const isOutside = contextClassName.every(
(className) => !targets.closest(`.${className}`)
);
if (isOutside) {
setMenuVisible(false); // Close the menu by updating the state
}
};
@ -23,7 +27,7 @@ export default function OuterClick({
return () => {
document.removeEventListener("click", handleClick);
};
}, []);
}, [contextClassName]); // Add contextClassName to dependency array to handle any changes
return null; // This component doesn't render anything
}