diff --git a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx index 4ec9fdd..0d239b4 100644 --- a/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx +++ b/app/src/components/layout/3D-cards/cards/ProductionCapacity.tsx @@ -30,14 +30,13 @@ interface ProductionCapacityProps { id: string; type: string; position: [number, number, number]; + rotation: [number, number, number]; onContextMenu?: (event: React.MouseEvent) => void; // onPointerDown:any } -const ProductionCapacity: React.FC = ({ id, type, position, onContextMenu }) => { - +const ProductionCapacity: React.FC = ({ id, type, position, rotation, onContextMenu }) => { const { selectedChartId, setSelectedChartId } = useWidgetStore(); - const { measurements: chartMeasurements, duration: chartDuration, name: widgetName } = useChartStore(); const [measurements, setmeasurements] = useState({}); const [duration, setDuration] = useState("1h") @@ -177,15 +176,43 @@ const ProductionCapacity: React.FC = ({ id, type, posit } , [chartMeasurements, chartDuration, widgetName]) + useEffect(() => { + + console.log('rotation: ', rotation); + }, [rotation]) + const rotationDegrees = { + x: (rotation[0] * 180) / Math.PI, + y: (rotation[1] * 180) / Math.PI, + z: (rotation[2] * 180) / Math.PI, + }; + + const transformStyle = { + transform: `rotateX(${rotationDegrees.x}deg) rotateY(${rotationDegrees.y}deg) rotateZ(${rotationDegrees.z}deg)`, + }; + return ( - + sprite + zIndexRange={[1,0]} + // center + // distanceFactor={10} // Adjusted for visual balance + style={{ + transform: transformStyle.transform, + transformStyle: 'preserve-3d', + transition: 'transform 0.1s ease-out' + + }}>
setSelectedChartId({ id: id, type: type })} onContextMenu={onContextMenu} + style={{ + width: '300px', // Original width + height: '300px', // Original height + transform: transformStyle.transform, + transformStyle: 'preserve-3d' + }} >
Production Capacity
diff --git a/app/src/components/ui/componets/DraggableWidget.tsx b/app/src/components/ui/componets/DraggableWidget.tsx index 41727db..fa7cff8 100644 --- a/app/src/components/ui/componets/DraggableWidget.tsx +++ b/app/src/components/ui/componets/DraggableWidget.tsx @@ -239,14 +239,13 @@ export const DraggableWidget = ({ onReorder(fromIndex, toIndex); // Call the reorder function passed as a prop } }; - console.log("widget.type", widget.type); // useClickOutside(chartWidget, () => { // setSelectedChartId(null); // }); const { isPlaying } = usePlayButtonStore(); - console.log('isPanelHidden: ', isPanelHidden); + return ( <>
([0, 0, 0]); + const mouseStartRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); useEffect(() => { if (activeModule !== "visualization") return; @@ -48,14 +56,14 @@ export default function Dropped3dWidgets() { const organization = email?.split("@")[1]?.split(".")[0]; async function get3dWidgetData() { - let result = await get3dWidgetZoneData(selectedZone.zoneId, organization); - + const result = await get3dWidgetZoneData(selectedZone.zoneId, organization); setWidgets3D(result); - const formattedWidgets = result.map((widget: any) => ({ + const formattedWidgets = result.map((widget: WidgetData) => ({ id: widget.id, type: widget.type, position: widget.position, + rotation: widget.rotation || [0, 0, 0], })); setZoneWidgetData(selectedZone.zoneId, formattedWidgets); @@ -91,28 +99,24 @@ export default function Dropped3dWidgets() { if (intersects.length > 0) { const { x, y, z } = intersects[0].point; - const newWidget = { + const newWidget: WidgetData = { id: generateUniqueId(), type: widgetSelect, - position: [x, y, z] as [number, number, number], + position: [x, y, z], + rotation: [0, 0, 0], }; - let add3dWidget = { + const add3dWidget = { organization: organization, widget: newWidget, zoneId: selectedZone.zoneId - } + }; if (visualizationSocket) { - visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget) + visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); } - // let response = await adding3dWidgets(selectedZone.zoneId, organization, newWidget); - // - - // if (response.message === "Widget created successfully") { addWidget(selectedZone.zoneId, newWidget); - // } } }; @@ -128,63 +132,76 @@ export default function Dropped3dWidgets() { if (!rightClickSelected) return; const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; + if (rightSelect === "Duplicate") { - const widgetToDuplicate = activeZoneWidgets.find(w => w.id === rightClickSelected); + const widgetToDuplicate = activeZoneWidgets.find((w: WidgetData) => w.id === rightClickSelected); if (!widgetToDuplicate) return; - const newWidget = { + const newWidget: WidgetData = { id: generateUniqueId(), type: widgetToDuplicate.type, position: [ - widgetToDuplicate.position[0] + 0.5, // Slightly shift position + widgetToDuplicate.position[0] + 0.5, widgetToDuplicate.position[1], widgetToDuplicate.position[2] + 0.5, - ] as [number, number, number], + ], + rotation: widgetToDuplicate.rotation || [0, 0, 0], }; - let add3dWidget = { + + const add3dWidget = { organization, widget: newWidget, zoneId: selectedZone.zoneId }; - // if (visualizationSocket) { - // visualizationSocket.emit("v2:viz-3D-widget:add", add3dWidget); - // } + addWidget(selectedZone.zoneId, newWidget); setRightSelect(null); setRightClickSelected(null); } + if (rightSelect === "Delete") { - let deleteWidget = { + const deleteWidget = { organization, widgetId: rightClickSelected, zoneId: selectedZone.zoneId }; - // if (visualizationSocket) { - // visualizationSocket.emit("v2:viz-3D-widget:delete", deleteWidget); - // } - setZoneWidgetData(selectedZone.zoneId, activeZoneWidgets.filter(w => w.id !== rightClickSelected)); + + setZoneWidgetData( + selectedZone.zoneId, + activeZoneWidgets.filter((w: WidgetData) => w.id !== rightClickSelected) + ); setRightClickSelected(null); setRightSelect(null); } - if (rightSelect === "Horizontal Move") { - - } - if (rightSelect === "Vertical Move") { - - } - }, [rightSelect, rightClickSelected]); useEffect(() => { - const handleMouseMove = (event: MouseEvent) => { + const handleMouseDown = (event: MouseEvent) => { if (!rightClickSelected || !rightSelect) return; + if (rightSelect === "RotateX" || rightSelect === "RotateY") { + mouseStartRef.current = { x: event.clientX, y: event.clientY }; - const selectedZone = Object.keys(zoneWidgetData).find(zoneId => - zoneWidgetData[zoneId].some(widget => 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); + if (selectedWidget) { + rotationStartRef.current = selectedWidget.rotation || [0, 0, 0]; + } + } + }; + + const handleMouseMove = (event: MouseEvent) => { + if (!rightClickSelected || !rightSelect) return; + 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 => widget.id === rightClickSelected); + const selectedWidget = zoneWidgetData[selectedZone].find((widget: WidgetData) => widget.id === rightClickSelected); if (!selectedWidget) return; const rect = gl.domElement.getBoundingClientRect(); @@ -194,31 +211,67 @@ export default function Dropped3dWidgets() { raycaster.setFromCamera(mouse, camera); if (rightSelect === "Horizontal Move" && raycaster.ray.intersectPlane(plane.current, planeIntersect.current)) { - - updateWidgetPosition(selectedZone, rightClickSelected, [ + const newPosition: [number, number, number] = [ planeIntersect.current.x, selectedWidget.position[1], planeIntersect.current.z - ]); + ]; + updateWidgetPosition(selectedZone, rightClickSelected, newPosition); + console.log('Horizontal Move - Final Position:', newPosition); } if (rightSelect === "Vertical Move") { if (raycaster.ray.intersectPlane(verticalPlane.current, planeIntersect.current)) { updateWidgetPosition(selectedZone, rightClickSelected, [ selectedWidget.position[0], - planeIntersect.current.y, // Ensure Y value updates correctly + planeIntersect.current.y, selectedWidget.position[2] ]); - } else { - console.log("No Intersection with Vertical Plane"); } } + + if (rightSelect === "RotateX") { + const deltaX = event.clientX - mouseStartRef.current.x; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0] + deltaX * rotationSpeed, + rotationStartRef.current[1], + rotationStartRef.current[2] + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + + if (rightSelect === "RotateY") { + const deltaY = event.clientY - mouseStartRef.current.y; + const rotationSpeed = 0.03; + const newRotation: [number, number, number] = [ + rotationStartRef.current[0], + rotationStartRef.current[1] + deltaY * rotationSpeed, + rotationStartRef.current[2] + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + if (rightSelect === "RotateZ") { + const deltaX = event.movementX; + const rotationSpeed = 0.03; + const currentRotation = selectedWidget.rotation || [0, 0, 0]; + const newRotation: [number, number, number] = [ + currentRotation[0], + currentRotation[1], + currentRotation[2] + deltaX * rotationSpeed + ]; + updateWidgetRotation(selectedZone, rightClickSelected, newRotation); + } + }; const handleMouseUp = () => { - - if (rightClickSelected && (rightSelect === "Horizontal Move" || rightSelect === "Vertical Move")) { - + if (rightClickSelected && ( + rightSelect === "Horizontal Move" || + rightSelect === "Vertical Move" || + rightSelect === "RotateX" || + rightSelect === "RotateY" || rightSelect === "RotateZ" + )) { setTimeout(() => { setRightClickSelected(null); setRightSelect(null); @@ -226,40 +279,81 @@ export default function Dropped3dWidgets() { } }; - // Attach events to window instead of gl.domElement + window.addEventListener("mousedown", handleMouseDown); window.addEventListener("mousemove", handleMouseMove); window.addEventListener("mouseup", handleMouseUp); return () => { + window.removeEventListener("mousedown", handleMouseDown); window.removeEventListener("mousemove", handleMouseMove); window.removeEventListener("mouseup", handleMouseUp); }; }, [rightClickSelected, rightSelect, zoneWidgetData, gl]); - - - return ( <> - {activeZoneWidgets.map(({ id, type, position }) => { - const handleRightClick = (event: React.MouseEvent) => { + {activeZoneWidgets.map(({ id, type, position, rotation = [0, 0, 0] }: WidgetData) => { + const handleRightClick = (event: React.MouseEvent, id: string) => { event.preventDefault(); - setRightClickSelected(id) + 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; + setRightClickSelected(id); + setTop(relativeY); + setLeft(relativeX); }; + switch (type) { case "ui-Widget 1": - return ; + return ( + handleRightClick(e, id)} + /> + ); case "ui-Widget 2": - return ; + return ( + handleRightClick(e, id)} + /> + ); case "ui-Widget 3": - return ; + return ( + handleRightClick(e, id)} + /> + ); case "ui-Widget 4": - return ; + return ( + handleRightClick(e, id)} + /> + ); default: return null; } })} - ); -} +} \ No newline at end of file diff --git a/app/src/components/ui/componets/RealTimeVisulization.tsx b/app/src/components/ui/componets/RealTimeVisulization.tsx index 915e3b3..acbbf68 100644 --- a/app/src/components/ui/componets/RealTimeVisulization.tsx +++ b/app/src/components/ui/componets/RealTimeVisulization.tsx @@ -197,7 +197,6 @@ const RealTimeVisulization: React.FC = () => { } catch (error) { } }; - function handleRightClickSel(){} return (
{ {activeModule === "visualization" && selectedZone.zoneName !== "" && } {activeModule === "visualization" && } - {activeModule === "visualization" && widgetSubOption === "3D" && rightClickSelected && } {activeModule === "visualization" && ( diff --git a/app/src/components/ui/menu/EditWidgetOption.tsx b/app/src/components/ui/menu/EditWidgetOption.tsx index ce5261a..3978430 100644 --- a/app/src/components/ui/menu/EditWidgetOption.tsx +++ b/app/src/components/ui/menu/EditWidgetOption.tsx @@ -17,7 +17,15 @@ const EditWidgetOption: React.FC = ({ options }) => { }, [top, left]) return ( -
+
{options.map((option, index) => (
setRightSelect(option)}> diff --git a/app/src/store/useZone3DWidgetStore.ts b/app/src/store/useZone3DWidgetStore.ts index 850623f..9700878 100644 --- a/app/src/store/useZone3DWidgetStore.ts +++ b/app/src/store/useZone3DWidgetStore.ts @@ -1,10 +1,10 @@ - import { create } from "zustand"; type WidgetData = { id: string; type: string; position: [number, number, number]; + rotation?: [number, number, number]; tempPosition?: [number, number, number]; }; @@ -13,38 +13,59 @@ type ZoneWidgetStore = { setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => void; addWidget: (zoneId: string, widget: WidgetData) => void; updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => void; + updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => void; }; export const useZoneWidgetStore = create((set) => ({ zoneWidgetData: {}, - setZoneWidgetData: (zoneId, widgets) => - set((state) => ({ + setZoneWidgetData: (zoneId: string, widgets: WidgetData[]) => + set((state: ZoneWidgetStore) => ({ zoneWidgetData: { ...state.zoneWidgetData, [zoneId]: widgets }, })), - addWidget: (zoneId, widget) => - set((state) => ({ + addWidget: (zoneId: string, widget: WidgetData) => + set((state: ZoneWidgetStore) => ({ zoneWidgetData: { ...state.zoneWidgetData, - [zoneId]: [...(state.zoneWidgetData[zoneId] || []), widget], + [zoneId]: [...(state.zoneWidgetData[zoneId] || []), { ...widget, rotation: widget.rotation || [0, 0, 0] }], }, })), - updateWidgetPosition: (zoneId, widgetId, newPosition) => - set((state) => { + updateWidgetPosition: (zoneId: string, widgetId: string, newPosition: [number, number, number]) => + set((state: ZoneWidgetStore) => { const widgets = state.zoneWidgetData[zoneId] || []; return { zoneWidgetData: { ...state.zoneWidgetData, - [zoneId]: widgets.map((widget) => + [zoneId]: widgets.map((widget: WidgetData) => widget.id === widgetId ? { ...widget, position: newPosition } : widget ), }, }; }), + + updateWidgetRotation: (zoneId: string, widgetId: string, newRotation: [number, number, number]) => + set((state: ZoneWidgetStore) => { + const widgets = state.zoneWidgetData[zoneId] || []; + return { + zoneWidgetData: { + ...state.zoneWidgetData, + [zoneId]: widgets.map((widget: WidgetData) => + widget.id === widgetId ? { ...widget, rotation: newRotation } : widget + ), + }, + }; + }), })); +// export type WidgetData = { +// id: string; +// type: string; +// position: [number, number, number]; +// rotation?: [number, number, number]; +// tempPosition?: [number, number, number]; +// }; interface RightClickStore { rightClickSelected: string | null;