zone based assets added and 3d widgets dnd updated

This commit is contained in:
Gomathi 2025-04-08 18:14:59 +05:30
parent daa507e464
commit d76d09019e
12 changed files with 493 additions and 282 deletions

View File

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

View File

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

View File

@ -101,7 +101,7 @@ const SideBarRight: React.FC = () => {
)} )}
{toggleUI && {toggleUI &&
subModule === "zoneProperties" && subModule === "zoneProperties" &&
activeModule === "builder" && ( (activeModule === "builder" || activeModule === "simulation") && (
<div className="sidebar-right-container"> <div className="sidebar-right-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
<ZoneProperties /> <ZoneProperties />
@ -135,7 +135,7 @@ const SideBarRight: React.FC = () => {
selectedActionSphere.path.type === "StaticMachine" && ( selectedActionSphere.path.type === "StaticMachine" && (
<div className="sidebar-right-container"> <div className="sidebar-right-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
<StaticMachineMechanics /> <StaticMachineMechanics />
</div> </div>
</div> </div>
)} )}
@ -144,7 +144,7 @@ const SideBarRight: React.FC = () => {
selectedActionSphere.path.type === "ArmBot" && ( selectedActionSphere.path.type === "ArmBot" && (
<div className="sidebar-right-container"> <div className="sidebar-right-container">
<div className="sidebar-right-content-container"> <div className="sidebar-right-content-container">
<ArmBotMechanics /> <ArmBotMechanics />
</div> </div>
</div> </div>
)} )}

View File

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

@ -28,6 +28,7 @@ import {
useRightClickSelected, useRightClickSelected,
useRightSelected, useRightSelected,
} from "../../../store/useZone3DWidgetStore"; } from "../../../store/useZone3DWidgetStore";
import Dropped3dWidgets from "./Dropped3dWidget";
type Side = "top" | "bottom" | "left" | "right"; type Side = "top" | "bottom" | "left" | "right";
@ -127,6 +128,8 @@ const RealTimeVisulization: React.FC = () => {
// useEffect(() => {}, [floatingWidgets]); // useEffect(() => {}, [floatingWidgets]);
const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => { const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
try { try {
event.preventDefault(); event.preventDefault();
const email = localStorage.getItem("email") || ""; const email = localStorage.getItem("email") || "";
@ -172,7 +175,6 @@ const RealTimeVisulization: React.FC = () => {
if (visualizationSocket) { if (visualizationSocket) {
visualizationSocket.emit("v2:viz-float:add", addFloatingWidget); visualizationSocket.emit("v2:viz-float:add", addFloatingWidget);
} }
// let response = await addingFloatingWidgets( // let response = await addingFloatingWidgets(
// selectedZone.zoneId, // selectedZone.zoneId,
// organization, // organization,
@ -198,7 +200,9 @@ const RealTimeVisulization: React.FC = () => {
}, },
})); }));
} catch (error) { } } catch (error) { }
}; };
useEffect(() => { useEffect(() => {
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent) => {
const editWidgetOptions = document.querySelector( const editWidgetOptions = document.querySelector(
@ -219,31 +223,10 @@ const RealTimeVisulization: React.FC = () => {
}; };
}, [setRightClickSelected]); }, [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 ( return (
<> <>
<div <div
ref={containerRef} ref={containerRef}
id="real-time-vis-canvas" id="real-time-vis-canvas"
@ -274,6 +257,7 @@ const RealTimeVisulization: React.FC = () => {
}} }}
onDrop={(event) => handleDrop(event)} onDrop={(event) => handleDrop(event)}
onDragOver={(event) => event.preventDefault()} onDragOver={(event) => event.preventDefault()}
> >
<Scene /> <Scene />
</div> </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 List from "./List";
import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons"; import { AddIcon, ArrowIcon, FocusIcon } from "../../icons/ExportCommonIcons";
import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect"; import KebabMenuListMultiSelect from "./KebebMenuListMultiSelect";
import { useZones } from "../../../store/store"; import { useFloorItems, useZones } from "../../../store/store";
import { useSelectedZoneStore } from "../../../store/useZoneStore"; import { useSelectedZoneStore } from "../../../store/useZoneStore";
interface DropDownListProps { interface DropDownListProps {
@ -38,92 +38,66 @@ const DropDownList: React.FC<DropDownListProps> = ({
const handleToggle = () => { const handleToggle = () => {
setIsOpen((prev) => !prev); // Toggle the state setIsOpen((prev) => !prev); // Toggle the state
}; };
interface Asset { interface Asset {
id: string; id: string;
name: string; name: string;
position: [number, number, number]; // x, y, z
} }
const [zoneDataList, setZoneDataList] = useState< interface Zone {
{ id: string; name: string; assets: Asset[] }[] zoneId: string;
>([]); zoneName: string;
const [zonePoints3D, setZonePoints3D] = useState<[]>([]); 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 intersect = ((zi > point[1]) !== (zj > point[1])) &&
// const value = (zones || []).map( (point[0] < (xj - xi) * (point[1] - zi) / (zj - zi + 0.000001) + xi);
// (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",
// },
// ],
// },
// ]);
}, [zones]); if (intersect) inside = !inside;
}
return inside;
};
useEffect(() => { useEffect(() => {
// console.log('zonePoints3D: ', zonePoints3D); const updatedZoneList: ZoneData[] = zones.map((zone: Zone) => {
}, [zonePoints3D]) 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 ( return (
<div className="dropdown-list-container"> <div className="dropdown-list-container">

View File

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

View File

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

View File

@ -446,3 +446,23 @@ export const useTileDistance = create<any>((set: any) => ({
planeValue: { ...state.planeValue, ...value }, 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[]>; zoneWidgetData: Record<string, WidgetData[]>;
setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void; setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void;
addWidget: (zoneId: string, widget: WidgetData) => void; addWidget: (zoneId: string, widget: WidgetData) => void;
tempWidget: (zoneId: string, widget: WidgetData) => void;
updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => 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; 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] }], [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]) => updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) =>
set((state: ZoneWidgetStore) => { 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]) => updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) =>
set((state: ZoneWidgetStore) => { 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 { interface RightClickStore {
rightClickSelected: string | null; rightClickSelected: string | null;

View File

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