From a682d3dd1e8c6ac9b016757450b6e2079e817cef Mon Sep 17 00:00:00 2001 From: Vishnu Date: Tue, 1 Apr 2025 12:03:29 +0530 Subject: [PATCH 1/6] Refactor UI components and styles for improved layout and consistency --- app/src/components/icons/marketPlaceIcons.tsx | 12 +-- .../properties/GlobalProperties.tsx | 7 +- .../components/ui/componets/DistanceLine.tsx | 75 ++++++++++++++----- .../ui/componets/DroppedFloatingWidgets.tsx | 1 - app/src/modules/scene/scene.tsx | 8 +- app/src/styles/base/base.scss | 5 +- app/src/styles/components/input.scss | 6 ++ .../components/marketPlace/marketPlace.scss | 9 ++- app/src/styles/pages/realTimeViz.scss | 17 ++--- app/src/styles/pages/userAuth.scss | 4 +- app/src/types/world/worldConstants.ts | 2 +- 11 files changed, 95 insertions(+), 51 deletions(-) diff --git a/app/src/components/icons/marketPlaceIcons.tsx b/app/src/components/icons/marketPlaceIcons.tsx index 8719ef8..e69303f 100644 --- a/app/src/components/icons/marketPlaceIcons.tsx +++ b/app/src/components/icons/marketPlaceIcons.tsx @@ -29,13 +29,13 @@ export function DownloadIcon() { fillRule="evenodd" clipRule="evenodd" d="M2.5 11.875C2.84518 11.875 3.125 12.1548 3.125 12.5C3.125 13.6962 3.12633 14.5304 3.21096 15.1599C3.29317 15.7714 3.44354 16.0952 3.67418 16.3258C3.90481 16.5565 4.22863 16.7068 4.8401 16.7891C5.46956 16.8737 6.30383 16.875 7.5 16.875H12.5C13.6962 16.875 14.5304 16.8737 15.1599 16.7891C15.7714 16.7068 16.0952 16.5565 16.3258 16.3258C16.5565 16.0952 16.7068 15.7714 16.7891 15.1599C16.8737 14.5304 16.875 13.6962 16.875 12.5C16.875 12.1548 17.1548 11.875 17.5 11.875C17.8452 11.875 18.125 12.1548 18.125 12.5V12.5458C18.125 13.6854 18.125 14.604 18.0279 15.3265C17.9271 16.0766 17.7113 16.7081 17.2097 17.2097C16.7081 17.7113 16.0766 17.9271 15.3265 18.0279C14.604 18.125 13.6854 18.125 12.5458 18.125H7.45428C6.31462 18.125 5.39602 18.125 4.67354 18.0279C3.92345 17.9271 3.29189 17.7113 2.79029 17.2097C2.28869 16.7081 2.07295 16.0766 1.9721 15.3265C1.87497 14.604 1.87498 13.6854 1.875 12.5458C1.875 12.5305 1.875 12.5152 1.875 12.5C1.875 12.1548 2.15483 11.875 2.5 11.875Z" - fill="#FCFDFD" + fill="var(--primary-color)" /> ); @@ -52,13 +52,13 @@ export function EyeIconBig() { > @@ -80,7 +80,7 @@ export function CommentsIcon() { fillRule="evenodd" clipRule="evenodd" d="M8 13C7.416 13 6.852 12.932 6.31 12.8165L3.956 14.2315L3.9875 11.912C2.183 10.827 1 9.033 1 7C1 3.6865 4.134 1 8 1C11.866 1 15 3.6865 15 7C15 10.314 11.866 13 8 13ZM8 0C3.582 0 0 3.1345 0 7C0 9.2095 1.1725 11.177 3 12.4595V16L6.5045 13.8735C6.9895 13.9535 7.4885 14 8 14C12.418 14 16 10.866 16 7C16 3.1345 12.418 0 8 0ZM11.5 5.5H4.5C4.224 5.5 4 5.724 4 6C4 6.2765 4.224 6.5 4.5 6.5H11.5C11.776 6.5 12 6.2765 12 6C12 5.724 11.776 5.5 11.5 5.5ZM10.5 8.5H5.5C5.224 8.5 5 8.7235 5 9C5 9.2765 5.224 9.5 5.5 9.5H10.5C10.776 9.5 11 9.2765 11 9C11 8.7235 10.776 8.5 10.5 8.5Z" - fill="#2B3344" + fill="var(--text-color)" /> @@ -105,7 +105,7 @@ export function VerifiedIcon() { fillRule="evenodd" clipRule="evenodd" d="M4.7962 2.10014C4.67444 2.2039 4.61356 2.25579 4.54853 2.29937C4.39948 2.39927 4.23209 2.46861 4.05605 2.50336C3.97926 2.51853 3.89952 2.52489 3.74004 2.53761C3.33935 2.56959 3.139 2.58558 2.97186 2.64462C2.58526 2.78117 2.28117 3.08526 2.14462 3.47186C2.08558 3.639 2.06959 3.83935 2.03761 4.24004C2.02489 4.39952 2.01853 4.47926 2.00336 4.55605C1.96861 4.73209 1.89927 4.89948 1.79937 5.04853C1.75579 5.11356 1.70391 5.17444 1.60014 5.2962C1.33942 5.60215 1.20905 5.7551 1.13261 5.91505C0.955796 6.285 0.955796 6.715 1.13261 7.08495C1.20906 7.2449 1.33942 7.39785 1.60014 7.7038C1.70389 7.82555 1.75579 7.88645 1.79937 7.95145C1.89927 8.1005 1.96861 8.2679 2.00336 8.44395C2.01853 8.52075 2.02489 8.6005 2.03761 8.75995C2.06959 9.16065 2.08558 9.361 2.14462 9.52815C2.28117 9.91475 2.58526 10.2189 2.97186 10.3554C3.139 10.4144 3.33935 10.4304 3.74004 10.4624C3.89952 10.4751 3.97926 10.4815 4.05605 10.4966C4.23209 10.5314 4.39948 10.6007 4.54853 10.7007C4.61356 10.7442 4.67444 10.7961 4.7962 10.8998C5.10215 11.1606 5.2551 11.2909 5.41505 11.3674C5.785 11.5442 6.215 11.5442 6.58495 11.3674C6.7449 11.2909 6.89785 11.1606 7.2038 10.8998C7.32555 10.7961 7.38645 10.7442 7.45145 10.7007C7.6005 10.6007 7.7679 10.5314 7.94395 10.4966C8.02075 10.4815 8.1005 10.4751 8.25995 10.4624C8.66065 10.4304 8.861 10.4144 9.02815 10.3554C9.41475 10.2189 9.71885 9.91475 9.8554 9.52815C9.9144 9.361 9.9304 9.16065 9.9624 8.75995C9.9751 8.6005 9.9815 8.52075 9.99665 8.44395C10.0314 8.2679 10.1007 8.1005 10.2007 7.95145C10.2442 7.88645 10.2961 7.82555 10.3998 7.7038C10.6606 7.39785 10.7909 7.2449 10.8674 7.08495C11.0442 6.715 11.0442 6.285 10.8674 5.91505C10.7909 5.7551 10.6606 5.60215 10.3998 5.2962C10.2961 5.17444 10.2442 5.11356 10.2007 5.04853C10.1007 4.89948 10.0314 4.73209 9.99665 4.55605C9.9815 4.47926 9.9751 4.39952 9.9624 4.24004C9.9304 3.83935 9.9144 3.639 9.8554 3.47186C9.71885 3.08526 9.41475 2.78117 9.02815 2.64462C8.861 2.58558 8.66065 2.56959 8.25995 2.53761C8.1005 2.52489 8.02075 2.51853 7.94395 2.50336C7.7679 2.46861 7.6005 2.39927 7.45145 2.29937C7.38645 2.25579 7.32555 2.20391 7.2038 2.10014C6.89785 1.83942 6.7449 1.70906 6.58495 1.63261C6.215 1.4558 5.785 1.4558 5.41505 1.63261C5.2551 1.70905 5.10215 1.83942 4.7962 2.10014ZM8.18675 5.43157C8.34565 5.27265 8.34565 5.015 8.18675 4.85608C8.02785 4.69717 7.77015 4.69717 7.61125 4.85608L5.18615 7.2812L4.38873 6.4838C4.22982 6.3249 3.97216 6.3249 3.81325 6.4838C3.65433 6.6427 3.65433 6.90035 3.81325 7.0593L4.89839 8.14445C5.0573 8.30335 5.31495 8.30335 5.4739 8.14445L8.18675 5.43157Z" - fill="#6F42C1" + fill="var(--accent-color)" /> ); diff --git a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx index bd7e5c1..16e0f13 100644 --- a/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/GlobalProperties.tsx @@ -40,7 +40,6 @@ const GlobalProperties: React.FC = () => { } function updateDistance(value: number) { - console.log("value: ", value); setDistance(value); setRenderDistance(value); } @@ -61,7 +60,7 @@ const GlobalProperties: React.FC = () => { !roofVisibility, shadows ); - // console.log('data: ', data); + // //using Socket // const visData = { @@ -88,7 +87,7 @@ const GlobalProperties: React.FC = () => { roofVisibility, shadows ); - // console.log('data: ', data); + // //using Socket // const visData = { @@ -115,7 +114,7 @@ const GlobalProperties: React.FC = () => { roofVisibility, !shadows ); - // console.log('data: ', data); + // //using Socket // const visData = { diff --git a/app/src/components/ui/componets/DistanceLine.tsx b/app/src/components/ui/componets/DistanceLine.tsx index 8dec7b1..62cfd3b 100644 --- a/app/src/components/ui/componets/DistanceLine.tsx +++ b/app/src/components/ui/componets/DistanceLine.tsx @@ -20,21 +20,31 @@ const DistanceLines: React.FC = ({ obj, activeEdges }) => { return ( <> - {activeEdges.vertical === "top" && typeof obj.position.top === "number" && ( -
- {obj.position.top.toFixed()}px -
- )} + {activeEdges.vertical === "top" && + typeof obj.position.top === "number" && ( +
+ + {obj.position.top}px + +
+ )} {activeEdges.vertical === "bottom" && typeof obj.position.bottom === "number" && ( @@ -49,7 +59,16 @@ const DistanceLines: React.FC = ({ obj, activeEdges }) => { height: `${obj.position.bottom}px`, }} > - {obj.position.bottom.toFixed()}px + + {obj.position.bottom}px + )} @@ -66,7 +85,16 @@ const DistanceLines: React.FC = ({ obj, activeEdges }) => { width: `${obj.position.left}px`, }} > - {obj.position.left.toFixed()}px + + {obj.position.left}px + )} @@ -83,11 +111,20 @@ const DistanceLines: React.FC = ({ obj, activeEdges }) => { width: `${obj.position.right}px`, }} > - {obj.position.right.toFixed()}px + + {obj.position.right}px + )} ); }; -export default DistanceLines; \ No newline at end of file +export default DistanceLines; diff --git a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx index d7a6960..10269b9 100644 --- a/app/src/components/ui/componets/DroppedFloatingWidgets.tsx +++ b/app/src/components/ui/componets/DroppedFloatingWidgets.tsx @@ -386,7 +386,6 @@ const DroppedObjects: React.FC = () => { ) : null} - {renderObjectContent(obj)}
handleKebabClick(obj.id, event)} diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index 0a01a99..a455557 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -1,6 +1,6 @@ import { useMemo } from "react"; import { Canvas } from "@react-three/fiber"; -import { Environment, KeyboardControls } from "@react-three/drei"; +import { Environment, KeyboardControls, Stars } from "@react-three/drei"; import World from "./world/world"; import Controls from "./controls/controls"; @@ -50,7 +50,11 @@ export default function Scene() { - {savedTheme !== "dark" && } + {savedTheme !== "dark" ? ( + + ) : ( + + )} diff --git a/app/src/styles/base/base.scss b/app/src/styles/base/base.scss index 6adc1c5..0dea569 100644 --- a/app/src/styles/base/base.scss +++ b/app/src/styles/base/base.scss @@ -58,7 +58,7 @@ --border-color: #{$border-color-dark}; // Border color for dark theme // Shadow variables - --shadow-main-dark: #{$shadow-color-dark}; // Main shadow color + --shadow-main-dark: #{$shadow-color}; // Main shadow color --box-shadow-light: 0px 2px 4px var(--shadow-main-dark); // Light shadow --box-shadow-medium: 0px 4px 8px var(--shadow-main-dark); // Medium shadow --box-shadow-heavy: 0px 8px 16px var(--shadow-main-dark); // Heavy shadow @@ -75,6 +75,7 @@ height: 100vh; // Full viewport height width: 100vw; // Full viewport width overflow: hidden; // Prevent scrollbars + background-color: #232323; } // Root overlay styles @@ -123,7 +124,7 @@ body { /* Scrollbar handle color */ border-radius: 4px; /* Rounded corners */ - border: 2px solid #f4f4f4; + border: 2px solid var(--primary-color); /* Padding around the scrollbar handle */ } diff --git a/app/src/styles/components/input.scss b/app/src/styles/components/input.scss index 9f6ee48..107ecb1 100644 --- a/app/src/styles/components/input.scss +++ b/app/src/styles/components/input.scss @@ -10,6 +10,7 @@ input { border: 1px solid var(--border-color); outline: none; background: transparent; + color: var(--input-text-color); &:focus, &:active { @@ -552,6 +553,11 @@ input { .input-value { width: 40px; text-align: center; + &::-webkit-inner-spin-button, + &::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } } } } diff --git a/app/src/styles/components/marketPlace/marketPlace.scss b/app/src/styles/components/marketPlace/marketPlace.scss index 620d934..8cc367e 100644 --- a/app/src/styles/components/marketPlace/marketPlace.scss +++ b/app/src/styles/components/marketPlace/marketPlace.scss @@ -26,9 +26,6 @@ width: 100%; height: 100%; overflow: auto; - left: calc(120px / 2); - top: 100px; - padding: 14px; padding-bottom: 60px; display: flex; flex-direction: column; @@ -39,6 +36,8 @@ display: flex; align-items: center; gap: 12px; + margin-top: 2px; + padding: 0 24px; .search-wrapper { min-width: 60%; @@ -143,6 +142,7 @@ .assets-container { display: flex; justify-content: space-between; + padding: 0; .name-container { display: flex; @@ -177,6 +177,9 @@ } .vendor-icon { + display: flex; + align-items: center; + gap: 4px; font-weight: #{$bold-weight}; font-size: $regular; } diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index d58f12d..e4cbcac 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -3,7 +3,7 @@ // Main Container .realTime-viz { - background-color: var(--background-color); + background-color: #131313; border-radius: 20px; box-shadow: $box-shadow-medium; width: calc(100% - (320px + 270px + 90px)); @@ -22,7 +22,7 @@ min-height: 83px; background: var(--background-color); border: 1.23px solid var(--border-color); - box-shadow: var(--box-shadow-heavy); + box-shadow: 0px 4.91px 4.91px 0px #0000001c; border-radius: $border-radius-medium; padding: 18px; position: absolute; @@ -31,7 +31,6 @@ .scene-container { overflow: hidden; - background: #232323; } .icon { @@ -192,11 +191,11 @@ height: 25% !important; min-height: 150px; max-height: 100%; - border: 1px dashed #a9a9a9; + // border: 1px dashed var(--background-color-gray); border-radius: 8px; - box-shadow: 0px 2px 6px 0px rgba(60, 60, 67, 0.1); + box-shadow: var(--box-shadow-medium); padding: 6px 0; - background-color: white; + background-color: var(--background-color); position: relative; .kebab { @@ -534,7 +533,7 @@ .zone { padding: 10px; - border: 1px solid #ccc; + border: 1px solid var(--highlight-accent-color); border-radius: 5px; cursor: pointer; } @@ -614,10 +613,6 @@ } } - - - - .distance-line { position: absolute; border-style: dashed; diff --git a/app/src/styles/pages/userAuth.scss b/app/src/styles/pages/userAuth.scss index 1bf4c7e..6e86ac1 100644 --- a/app/src/styles/pages/userAuth.scss +++ b/app/src/styles/pages/userAuth.scss @@ -69,7 +69,7 @@ } .error-message { - color: #ff4d4f; + color: #f3453f; font-size: 12px; margin-bottom: 10px; } @@ -89,7 +89,7 @@ border-radius: #{$border-radius-extra-large}; background: var(--background-color); font-size: 14px; - color: #333; + color: var(--input-text-color); &:focus { border-color: var(--accent-color); diff --git a/app/src/types/world/worldConstants.ts b/app/src/types/world/worldConstants.ts index 2bc3835..6bd54cb 100644 --- a/app/src/types/world/worldConstants.ts +++ b/app/src/types/world/worldConstants.ts @@ -202,7 +202,7 @@ export const thirdPersonControls: ThirdPersonControls = { maxDistance: 100, // Maximum distance from the target maxPolarAngle: Math.PI / 2 - 0.05, // Maximum polar angle minZoom: 6, // Minimum zoom level - maxZoom: 21, // Maximum zoom level + maxZoom: 100, // Maximum zoom level targetOffset: 20, // Offset of the target from the camera cameraHeight: 30, // Height of the camera leftMouse: 2, // Mouse button for panning From b5833696a51a9715380d71e78c5ef6f24347749c Mon Sep 17 00:00:00 2001 From: Vishnu Date: Tue, 1 Apr 2025 12:08:54 +0530 Subject: [PATCH 2/6] Simplify conditional rendering of Sun component based on theme --- app/src/modules/scene/scene.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/modules/scene/scene.tsx b/app/src/modules/scene/scene.tsx index a455557..59273d0 100644 --- a/app/src/modules/scene/scene.tsx +++ b/app/src/modules/scene/scene.tsx @@ -50,11 +50,7 @@ export default function Scene() { - {savedTheme !== "dark" ? ( - - ) : ( - - )} + {savedTheme !== "dark" ? : <>} From e95abfb4dd1816017283f6b8bfc7ede26522d2d9 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Tue, 1 Apr 2025 12:25:01 +0530 Subject: [PATCH 3/6] Refactor DistanceLines component for improved label positioning and remove unused DistanceLine component --- .../components/ui/componets/DisplayZone.tsx | 137 ++++++++---------- .../components/ui/componets/DistanceLine.tsx | 130 ----------------- .../components/ui/componets/DistanceLines.tsx | 75 +++++++--- 3 files changed, 120 insertions(+), 222 deletions(-) delete mode 100644 app/src/components/ui/componets/DistanceLine.tsx diff --git a/app/src/components/ui/componets/DisplayZone.tsx b/app/src/components/ui/componets/DisplayZone.tsx index 3889139..1a2d545 100644 --- a/app/src/components/ui/componets/DisplayZone.tsx +++ b/app/src/components/ui/componets/DisplayZone.tsx @@ -15,7 +15,6 @@ interface DisplayZoneProps { [key: string]: { activeSides: Side[]; panelOrder: Side[]; - lockedPanels: Side[]; widgets: Widget[]; zoneId: string; @@ -27,7 +26,6 @@ interface DisplayZoneProps { zoneName: string; activeSides: Side[]; panelOrder: Side[]; - lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; @@ -45,7 +43,6 @@ interface DisplayZoneProps { zoneName: string; activeSides: Side[]; panelOrder: Side[]; - lockedPanels: Side[]; zoneId: string; zoneViewPortTarget: number[]; @@ -66,19 +63,18 @@ const DisplayZone: React.FC = ({ selectedZone, setSelectedZone, }) => { - - // Ref for the container element + // Refs const containerRef = useRef(null); + const scrollContainerRef = useRef(null); // State to track overflow visibility const [showLeftArrow, setShowLeftArrow] = useState(false); const [showRightArrow, setShowRightArrow] = useState(false); - const { floatingWidget, setFloatingWidget } = useFloatingWidget() + const { floatingWidget, setFloatingWidget } = useFloatingWidget(); // Function to calculate overflow state const updateOverflowState = useCallback(() => { - const container = containerRef.current; - + const container = scrollContainerRef.current; if (container) { const isOverflowing = container.scrollWidth > container.clientWidth; const canScrollLeft = container.scrollLeft > 0; @@ -91,59 +87,56 @@ const DisplayZone: React.FC = ({ }, []); useEffect(() => { - const container = containerRef.current; + const container = scrollContainerRef.current; + if (!container) return; - if (container) { - // Initial calculation after the DOM has been rendered - const handleInitialRender = () => { - requestAnimationFrame(updateOverflowState); - }; + // Initial calculation after the DOM has been rendered + const observer = new ResizeObserver(updateOverflowState); + observer.observe(container); - handleInitialRender(); + // Update on scroll + const handleScroll = () => updateOverflowState(); + container.addEventListener("scroll", handleScroll); - // Update on window resize or scroll - const handleResize = () => updateOverflowState(); - const handleScroll = () => updateOverflowState(); + // Add mouse wheel listener for horizontal scrolling + const handleWheel = (event: WheelEvent) => { + if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) { + event.preventDefault(); + container.scrollBy({ + left: event.deltaY * 2, + behavior: "smooth", + }); + } + }; - // Add mouse wheel listener for horizontal scrolling - const handleWheel = (event: WheelEvent) => { - event.preventDefault(); // Prevent default vertical scrolling - if (container) { - container.scrollBy({ - left: event.deltaY * 2, // Translate vertical scroll to horizontal scroll - behavior: "smooth", - }); - } - }; + container.addEventListener("wheel", handleWheel, { passive: false }); - container.addEventListener("scroll", handleScroll); - window.addEventListener("resize", handleResize); - container.addEventListener("wheel", handleWheel, { passive: false }); + // Initial check + updateOverflowState(); - return () => { - container.removeEventListener("scroll", handleScroll); - window.removeEventListener("resize", handleResize); - container.removeEventListener("wheel", handleWheel); - }; - } + return () => { + observer.disconnect(); + container.removeEventListener("scroll", handleScroll); + container.removeEventListener("wheel", handleWheel); + }; }, [updateOverflowState]); // Handle scrolling with navigation arrows const handleScrollLeft = () => { - const container = containerRef.current; + const container = scrollContainerRef.current; if (container) { container.scrollBy({ - left: -200, // Scroll left by 200px + left: -200, behavior: "smooth", }); } }; const handleScrollRight = () => { - const container = containerRef.current; + const container = scrollContainerRef.current; if (container) { container.scrollBy({ - left: 200, // Scroll right by 200px + left: 200, behavior: "smooth", }); } @@ -152,24 +145,22 @@ const DisplayZone: React.FC = ({ async function handleSelect2dZoneData(zoneId: string, zoneName: string) { try { if (selectedZone?.zoneId === zoneId) { - return; } const email = localStorage.getItem("email") || ""; const organization = email?.split("@")[1]?.split(".")[0]; - // Fetch data from backend let response = await getSelect2dZoneData(zoneId, organization); console.log('response: ', response); let res = await getFloatingZoneData(zoneId, organization); - setFloatingWidget(res) - // Set the selected zone in the store + setFloatingWidget(res); + useDroppedObjectsStore.getState().setZone(zoneName, zoneId); if (Array.isArray(res)) { res.forEach((val) => { useDroppedObjectsStore.getState().addObject(zoneName, val); }); } - // Update selected zone state + setSelectedZone({ zoneName, activeSides: response.activeSides || [], @@ -181,17 +172,14 @@ const DisplayZone: React.FC = ({ zoneViewPortPosition: response.viewPortposition || {}, }); } catch (error) { - - + console.error(error); } } - return (
{/* Left Arrow */} {showLeftArrow && ( @@ -200,28 +188,31 @@ const DisplayZone: React.FC = ({ )} - {/* Zones Wrapper */} - {Object.keys(zonesData).length !== 0 ? ( -
- {Object.keys(zonesData).map((zoneName, index) => ( -
{ - handleSelect2dZoneData(zonesData[zoneName]?.zoneId, zoneName) - }} - > - {zoneName} -
- ))} -
- ) : ( -
- - No zones? Create one! -
- )} + {/* Scrollable Zones Container */} +
+ {Object.keys(zonesData).length !== 0 ? ( + <> + {Object.keys(zonesData).map((zoneName, index) => ( +
handleSelect2dZoneData(zonesData[zoneName]?.zoneId, zoneName)} + > + {zoneName} +
+ ))} + + ) : ( +
+ + No zones? Create one! +
+ )} +
{/* Right Arrow */} {showRightArrow && ( diff --git a/app/src/components/ui/componets/DistanceLine.tsx b/app/src/components/ui/componets/DistanceLine.tsx deleted file mode 100644 index 62cfd3b..0000000 --- a/app/src/components/ui/componets/DistanceLine.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React from "react"; - -interface DistanceLinesProps { - obj: { - position: { - top?: number | "auto"; - left?: number | "auto"; - right?: number | "auto"; - bottom?: number | "auto"; - }; - }; - activeEdges: { - vertical: "top" | "bottom"; - horizontal: "left" | "right"; - } | null; -} - -const DistanceLines: React.FC = ({ obj, activeEdges }) => { - if (!activeEdges) return null; - - return ( - <> - {activeEdges.vertical === "top" && - typeof obj.position.top === "number" && ( -
- - {obj.position.top}px - -
- )} - - {activeEdges.vertical === "bottom" && - typeof obj.position.bottom === "number" && ( -
- - {obj.position.bottom}px - -
- )} - - {activeEdges.horizontal === "left" && - typeof obj.position.left === "number" && ( -
- - {obj.position.left}px - -
- )} - - {activeEdges.horizontal === "right" && - typeof obj.position.right === "number" && ( -
- - {obj.position.right}px - -
- )} - - ); -}; - -export default DistanceLines; diff --git a/app/src/components/ui/componets/DistanceLines.tsx b/app/src/components/ui/componets/DistanceLines.tsx index 07cf202..62cfd3b 100644 --- a/app/src/components/ui/componets/DistanceLines.tsx +++ b/app/src/components/ui/componets/DistanceLines.tsx @@ -20,21 +20,31 @@ const DistanceLines: React.FC = ({ obj, activeEdges }) => { return ( <> - {activeEdges.vertical === "top" && typeof obj.position.top === "number" && ( -
- {obj.position.top}px -
- )} + {activeEdges.vertical === "top" && + typeof obj.position.top === "number" && ( +
+ + {obj.position.top}px + +
+ )} {activeEdges.vertical === "bottom" && typeof obj.position.bottom === "number" && ( @@ -49,7 +59,16 @@ const DistanceLines: React.FC = ({ obj, activeEdges }) => { height: `${obj.position.bottom}px`, }} > - {obj.position.bottom}px + + {obj.position.bottom}px +
)} @@ -66,7 +85,16 @@ const DistanceLines: React.FC = ({ obj, activeEdges }) => { width: `${obj.position.left}px`, }} > - {obj.position.left}px + + {obj.position.left}px +
)} @@ -83,11 +111,20 @@ const DistanceLines: React.FC = ({ obj, activeEdges }) => { width: `${obj.position.right}px`, }} > - {obj.position.right}px + + {obj.position.right}px + )} ); }; -export default DistanceLines; \ No newline at end of file +export default DistanceLines; From 2043712f5d3df4542d9d122bd642221db5dbfc93 Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 1 Apr 2025 18:52:21 +0530 Subject: [PATCH 4/6] add backend api and socket for conveyor events --- app/.env | 7 +++- .../sidebarRight/visualization/data/Data.tsx | 2 +- .../geomentries/assets/addAssetModel.ts | 1 - .../builder/groups/floorItemsGroup.tsx | 3 +- .../scene/IntialLoad/loadInitialFloorItems.ts | 5 +-- .../simulation/behaviour/behaviour.tsx | 41 ++----------------- app/src/modules/simulation/simulation.tsx | 1 + .../assest/assets/getCategoryAsset.ts | 2 +- 8 files changed, 15 insertions(+), 47 deletions(-) diff --git a/app/.env b/app/.env index e0b0d16..5740621 100644 --- a/app/.env +++ b/app/.env @@ -7,8 +7,11 @@ REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:8000 # Base URL for the server REST API, used for HTTP requests to the backend server. REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:5000 -# REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 -REACT_APP_SERVER_MARKETPLACE_URL=192.168.0.111:3501 +# Base URL for the server marketplace, used for market place model blob. +REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 + +# Base URL for the asset library server, used for asset library images and model blob id. +REACT_APP_SERVER_ASSET_LIBRARY_URL=192.168.0.111:3501 # base url for IoT socket server REACT_APP_IOT_SOCKET_SERVER_URL =185.100.212.76:5010 diff --git a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx index 2c9b5c6..5f501a2 100644 --- a/app/src/components/layout/sidebarRight/visualization/data/Data.tsx +++ b/app/src/components/layout/sidebarRight/visualization/data/Data.tsx @@ -118,7 +118,7 @@ const Data = () => { }; }); }; - console.log("selectedChartId", selectedChartId); + // console.log("selectedChartId", selectedChartId); return (
diff --git a/app/src/modules/builder/geomentries/assets/addAssetModel.ts b/app/src/modules/builder/geomentries/assets/addAssetModel.ts index 0cf9f22..a4255bd 100644 --- a/app/src/modules/builder/geomentries/assets/addAssetModel.ts +++ b/app/src/modules/builder/geomentries/assets/addAssetModel.ts @@ -148,7 +148,6 @@ async function handleModelLoad( const organization = email ? email.split("@")[1].split(".")[0] : ""; getAssetEventType(selectedItem.id, organization).then(async (res) => { - console.log('res: ', res); if (res.type === "Conveyor") { const pointUUIDs = res.points.map(() => THREE.MathUtils.generateUUID()); diff --git a/app/src/modules/builder/groups/floorItemsGroup.tsx b/app/src/modules/builder/groups/floorItemsGroup.tsx index 7e7282b..2f2241e 100644 --- a/app/src/modules/builder/groups/floorItemsGroup.tsx +++ b/app/src/modules/builder/groups/floorItemsGroup.tsx @@ -306,8 +306,7 @@ const FloorItemsGroup = ({ itemsGroup, hoveredDeletableFloorItem, AttachedObject }, [deleteModels, transformMode, controls, selectedItem, state.camera, state.pointer, activeTool, activeModule]); useEffect(() => { - - console.log('floorItems: ', floorItems); + // console.log('floorItems: ', floorItems); }, [floorItems]) useFrame(() => { diff --git a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts index 2b1b1f3..dfff78d 100644 --- a/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts +++ b/app/src/modules/scene/IntialLoad/loadInitialFloorItems.ts @@ -19,7 +19,6 @@ async function loadInitialFloorItems( const organization = (email!.split("@")[1]).split(".")[0]; const items = await getFloorAssets(organization); - console.log('items: ', items); localStorage.setItem("FloorItems", JSON.stringify(items)); await initializeDB(); @@ -133,7 +132,7 @@ async function loadInitialFloorItems( modelfileID: item.modelfileID, isLocked: item.isLocked, isVisible: item.isVisible, - eventData: item.eventData, + // eventData: item.eventData, }, ]); } else { @@ -198,7 +197,7 @@ function processLoadedModel( modelfileID: item.modelfileID, isLocked: item.isLocked, isVisible: item.isVisible, - eventData: item.eventData, + // eventData: item.eventData, }, ]); } else { diff --git a/app/src/modules/simulation/behaviour/behaviour.tsx b/app/src/modules/simulation/behaviour/behaviour.tsx index 3cb1df1..53af231 100644 --- a/app/src/modules/simulation/behaviour/behaviour.tsx +++ b/app/src/modules/simulation/behaviour/behaviour.tsx @@ -11,48 +11,16 @@ function Behaviour() { const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] = []; floorItems.forEach((item: Types.FloorItemType) => { - if (item.modelfileID === "672a090f80d91ac979f4d0bd") { - const point1Position = new THREE.Vector3(0, 0.85, 2.2); - const middlePointPosition = new THREE.Vector3(0, 0.85, 0); - const point2Position = new THREE.Vector3(0, 0.85, -2.2); - - const point1UUID = THREE.MathUtils.generateUUID(); - const middlePointUUID = THREE.MathUtils.generateUUID(); - const point2UUID = THREE.MathUtils.generateUUID(); - + // console.log('item: ', item); + if (item.eventData && item.eventData.type === 'Conveyor') { const newPath: Types.ConveyorEventsSchema = { modeluuid: item.modeluuid, modelName: item.modelname, type: 'Conveyor', - points: [ - { - uuid: point1UUID, - position: [point1Position.x, point1Position.y, point1Position.z], - rotation: [0, 0, 0], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }], - triggers: [], - connections: { source: { pathUUID: item.modeluuid, pointUUID: point1UUID }, targets: [] }, - }, - { - uuid: middlePointUUID, - position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z], - rotation: [0, 0, 0], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }], - triggers: [], - connections: { source: { pathUUID: item.modeluuid, pointUUID: middlePointUUID }, targets: [] }, - }, - { - uuid: point2UUID, - position: [point2Position.x, point2Position.y, point2Position.z], - rotation: [0, 0, 0], - actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: false }], - triggers: [], - connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] }, - }, - ], + points: item.eventData.points, position: [...item.position], rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - speed: 'Inherit', + speed: item.eventData.speed, }; newPaths.push(newPath); @@ -80,7 +48,6 @@ function Behaviour() { setSimulationPaths(newPaths); }, [floorItems]); - return null; } diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 739a92b..2d72a40 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -13,6 +13,7 @@ function Simulation() { const [processes, setProcesses] = useState([]); useEffect(() => { + // console.log('simulationPaths: ', simulationPaths); }, [simulationPaths]); // useEffect(() => { diff --git a/app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts b/app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts index 522e54c..a1ac727 100644 --- a/app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts +++ b/app/src/services/factoryBuilder/assest/assets/getCategoryAsset.ts @@ -1,4 +1,4 @@ -let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`; +let BackEnd_url = `http://${process.env.REACT_APP_SERVER_ASSET_LIBRARY_URL}`; export const getCategoryAsset = async (categoryName: any) => { try { const response = await fetch( From 8a177478c671a23fb3fc6cdff027e13c03c48a28 Mon Sep 17 00:00:00 2001 From: SreeNath14 <153710861+SreeNath14@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:54:04 +0530 Subject: [PATCH 5/6] "updated animation" --- .../simulation/process/processAnimator.tsx | 416 ++++++++++++++++++ .../simulation/process/processContainer.tsx | 17 + .../simulation/process/processCreator.tsx | 398 +++++++++++++++++ app/src/modules/simulation/simulation.tsx | 2 + 4 files changed, 833 insertions(+) create mode 100644 app/src/modules/simulation/process/processAnimator.tsx create mode 100644 app/src/modules/simulation/process/processContainer.tsx create mode 100644 app/src/modules/simulation/process/processCreator.tsx diff --git a/app/src/modules/simulation/process/processAnimator.tsx b/app/src/modules/simulation/process/processAnimator.tsx new file mode 100644 index 0000000..123ec80 --- /dev/null +++ b/app/src/modules/simulation/process/processAnimator.tsx @@ -0,0 +1,416 @@ +import React, { useRef, useState, useEffect, useMemo } from "react"; +import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; +import { GLTFLoader } from "three-stdlib"; +import { useLoader, useFrame } from "@react-three/fiber"; +import * as THREE from "three"; +import { GLTF } from "three-stdlib"; +import boxGltb from "../../../assets/gltf-glb/crate_box.glb"; + +interface PointAction { + uuid: string; + name: string; + type: "Inherit" | "Spawn" | "Despawn" | "Delay" | "Swap"; + material: string; + delay: string | number; + spawnInterval: string | number; + isUsed: boolean; +} + +interface ProcessPoint { + uuid: string; + position: number[]; + rotation: number[]; + actions: PointAction[]; + connections: { + source: { pathUUID: string; pointUUID: string }; + targets: { pathUUID: string; pointUUID: string }[]; + }; +} + +interface ProcessPath { + modeluuid: string; + modelName: string; + points: ProcessPoint[]; + pathPosition: number[]; + pathRotation: number[]; + speed: number; +} + +interface ProcessData { + id: string; + paths: ProcessPath[]; + animationPath: { x: number; y: number; z: number }[]; + pointActions: PointAction[][]; + speed: number; +} + +interface AnimationState { + currentIndex: number; + progress: number; + isAnimating: boolean; + speed: number; + isDelaying: boolean; + delayStartTime: number; + currentDelayDuration: number; + delayComplete: boolean; + currentPathIndex: number; + spawnPoints: Record< + string, + { + position: THREE.Vector3; + interval: number; + lastSpawnTime: number; + } + >; +} + +const MAX_SPAWNED_OBJECTS = 20; + +const ProcessAnimator: React.FC<{ processes: ProcessData[] }> = ({ + processes, +}) => { + console.log("processes: ", processes); + const gltf = useLoader(GLTFLoader, boxGltb) as GLTF; + const { isPlaying, setIsPlaying } = usePlayButtonStore(); + + const groupRef = useRef(null); + const meshRef = useRef(null); + const [visible, setVisible] = useState(false); + const spawnedObjectsRef = useRef([]); + + const materials = useMemo( + () => ({ + Wood: new THREE.MeshStandardMaterial({ color: 0x8b4513 }), + Box: new THREE.MeshStandardMaterial({ + color: 0xcccccc, + metalness: 0.8, + roughness: 0.2, + }), + Crate: new THREE.MeshStandardMaterial({ + color: 0x00aaff, + metalness: 0.1, + roughness: 0.5, + }), + Default: new THREE.MeshStandardMaterial({ color: 0x00ff00 }), + }), + [] + ); + + const [currentMaterial, setCurrentMaterial] = useState( + materials.Default + ); + + const { animationPath, currentProcess } = useMemo(() => { + const defaultProcess = { + animationPath: [], + pointActions: [], + speed: 1, + paths: [], + }; + const cp = processes?.[0] || defaultProcess; + return { + animationPath: + cp.animationPath?.map((p) => new THREE.Vector3(p.x, p.y, p.z)) || [], + currentProcess: cp, + }; + }, [processes]); + + const animationStateRef = useRef({ + currentIndex: 0, + progress: 0, + isAnimating: false, + speed: currentProcess.speed, + isDelaying: false, + delayStartTime: 0, + currentDelayDuration: 0, + delayComplete: false, + currentPathIndex: 0, + spawnPoints: {}, + }); + + const getPointDataForAnimationIndex = (index: number) => { + if (!processes[0]?.paths) return null; + + if (index < 3) { + return processes[0].paths[0]?.points[index]; + } else { + const path2Index = index - 3; + return processes[0].paths[1]?.points[path2Index]; + } + }; + + useEffect(() => { + if (isPlaying) { + setVisible(true); + animationStateRef.current = { + currentIndex: 0, + progress: 0, + isAnimating: true, + speed: currentProcess.speed, + isDelaying: false, + delayStartTime: 0, + currentDelayDuration: 0, + delayComplete: false, + currentPathIndex: 0, + spawnPoints: {}, + }; + + // Clear spawned objects + if (groupRef.current) { + spawnedObjectsRef.current.forEach((obj) => { + if (groupRef.current?.children.includes(obj)) { + groupRef.current.remove(obj); + } + if (obj instanceof THREE.Mesh) { + obj.material.dispose(); + } + }); + spawnedObjectsRef.current = []; + } + + const currentRef = gltf?.scene ? groupRef.current : meshRef.current; + if (currentRef && animationPath.length > 0) { + currentRef.position.copy(animationPath[0]); + } + } else { + animationStateRef.current.isAnimating = false; + } + }, [isPlaying, currentProcess, animationPath]); + + const handleMaterialSwap = (materialType: string) => { + const newMaterial = + materials[materialType as keyof typeof materials] || materials.Default; + setCurrentMaterial(newMaterial); + + spawnedObjectsRef.current.forEach((obj) => { + if (obj instanceof THREE.Mesh) { + obj.material = newMaterial.clone(); + } + }); + }; + + const hasNonInheritActions = (actions: PointAction[] = []) => { + return actions.some((action) => action.isUsed && action.type !== "Inherit"); + }; + + const handlePointActions = ( + actions: PointAction[] = [], + currentTime: number, + currentPosition: THREE.Vector3 + ) => { + let shouldStopAnimation = false; + + actions.forEach((action) => { + if (!action.isUsed) return; + + switch (action.type) { + case "Delay": + if ( + !animationStateRef.current.isDelaying && + !animationStateRef.current.delayComplete + ) { + const delayDuration = + typeof action.delay === "number" + ? action.delay + : parseFloat(action.delay as string) || 0; + + if (delayDuration > 0) { + animationStateRef.current.isDelaying = true; + animationStateRef.current.delayStartTime = currentTime; + animationStateRef.current.currentDelayDuration = delayDuration; + shouldStopAnimation = true; + } + } + break; + + case "Despawn": + setVisible(false); + setIsPlaying(false); + animationStateRef.current.isAnimating = false; + shouldStopAnimation = true; + break; + + case "Spawn": + const spawnInterval = + typeof action.spawnInterval === "number" + ? action.spawnInterval + : parseFloat(action.spawnInterval as string) || 1; + + const positionKey = currentPosition.toArray().join(","); + + animationStateRef.current.spawnPoints[positionKey] = { + position: currentPosition.clone(), + interval: spawnInterval, + lastSpawnTime: currentTime - spawnInterval, // Force immediate spawn + }; + break; + + case "Swap": + if (action.material) { + handleMaterialSwap(action.material); + } + break; + + case "Inherit": + break; + } + }); + + return shouldStopAnimation; + }; + + useFrame((state, delta) => { + const currentRef = gltf?.scene ? groupRef.current : meshRef.current; + if ( + !currentRef || + !animationStateRef.current.isAnimating || + animationPath.length < 2 + ) { + return; + } + + const currentTime = state.clock.getElapsedTime(); + const path = animationPath; + const stateRef = animationStateRef.current; + + if (stateRef.currentIndex === 3 && stateRef.currentPathIndex === 0) { + stateRef.currentPathIndex = 1; + } + + const currentPointData = getPointDataForAnimationIndex( + stateRef.currentIndex + ); + + if (stateRef.progress === 0 && currentPointData?.actions) { + const shouldStop = handlePointActions( + currentPointData.actions, + currentTime, + currentRef.position + ); + if (shouldStop) return; + } + + if (stateRef.isDelaying) { + if ( + currentTime - stateRef.delayStartTime >= + stateRef.currentDelayDuration + ) { + stateRef.isDelaying = false; + stateRef.delayComplete = true; + } else { + return; + } + } + + // Handle spawning - this is the key updated part + Object.entries(stateRef.spawnPoints).forEach(([key, spawnPoint]) => { + if (currentTime - spawnPoint.lastSpawnTime >= spawnPoint.interval) { + spawnPoint.lastSpawnTime = currentTime; + + if (gltf?.scene && groupRef?.current) { + const newObject = gltf.scene.clone(); + newObject.position.copy(spawnPoint.position); + + newObject.traverse((child) => { + if (child instanceof THREE.Mesh) { + child.material = currentMaterial.clone(); + } + }); + + groupRef.current.add(newObject); + spawnedObjectsRef.current.push(newObject); + + // Clean up old objects if needed + console.log( + "spawnedObjectsRef.current.length: ", + spawnedObjectsRef.current.length + ); + if (spawnedObjectsRef.current.length > MAX_SPAWNED_OBJECTS) { + const oldest = spawnedObjectsRef.current.shift(); + if (oldest && groupRef.current.children.includes(oldest)) { + groupRef.current.remove(oldest); + if (oldest instanceof THREE.Mesh) { + oldest.material.dispose(); + } + } + } + } + } + }); + + const nextPointIdx = stateRef.currentIndex + 1; + const isLastPoint = nextPointIdx >= path.length; + + if (isLastPoint) { + if (currentPointData?.actions) { + const shouldStop = !hasNonInheritActions(currentPointData.actions); + if (shouldStop) { + currentRef.position.copy(path[stateRef.currentIndex]); + setIsPlaying(false); + stateRef.isAnimating = false; + return; + } + } + } + + if (!isLastPoint) { + const nextPoint = path[nextPointIdx]; + const distance = path[stateRef.currentIndex].distanceTo(nextPoint); + const movement = stateRef.speed * delta; + stateRef.progress += movement / distance; + + if (stateRef.progress >= 1) { + stateRef.currentIndex = nextPointIdx; + stateRef.progress = 0; + stateRef.delayComplete = false; + currentRef.position.copy(nextPoint); + } else { + currentRef.position.lerpVectors( + path[stateRef.currentIndex], + nextPoint, + stateRef.progress + ); + } + } + }); + + useEffect(() => { + return () => { + if (groupRef.current) { + spawnedObjectsRef.current.forEach((obj) => { + if (groupRef.current?.children.includes(obj)) { + groupRef.current.remove(obj); + } + if (obj instanceof THREE.Mesh) { + obj.material.dispose(); + } + }); + spawnedObjectsRef.current = []; + } + }; + }, []); + + if (!processes || processes.length === 0) { + return null; + } + + if (!gltf?.scene) { + return visible ? ( + + + + ) : null; + } + + return visible ? ( + + + + ) : null; +}; + +export default ProcessAnimator; diff --git a/app/src/modules/simulation/process/processContainer.tsx b/app/src/modules/simulation/process/processContainer.tsx new file mode 100644 index 0000000..967376c --- /dev/null +++ b/app/src/modules/simulation/process/processContainer.tsx @@ -0,0 +1,17 @@ +import React, { useState } from "react"; +import ProcessCreator from "./processCreator"; +import ProcessAnimator from "./processAnimator"; + +const ProcessContainer: React.FC = () => { + const [processes, setProcesses] = useState([]); + + + return ( + <> + + {processes.length > 0 && } + + ); +}; + +export default ProcessContainer; diff --git a/app/src/modules/simulation/process/processCreator.tsx b/app/src/modules/simulation/process/processCreator.tsx new file mode 100644 index 0000000..8e1ed1d --- /dev/null +++ b/app/src/modules/simulation/process/processCreator.tsx @@ -0,0 +1,398 @@ +import React, { + useEffect, + useMemo, + useState, + useCallback, + useRef, +} from "react"; +import { useSimulationPaths } from "../../../store/store"; +import * as THREE from "three"; +import { useThree } from "@react-three/fiber"; +import { + ConveyorEventsSchema, + VehicleEventsSchema, +} from "../../../types/world/worldTypes"; + +// Type definitions +export interface PointAction { + uuid: string; + name: string; + type: string; + material: string; + delay: number | string; + spawnInterval: string | number; + isUsed: boolean; +} + +export interface PathPoint { + uuid: string; + position: [number, number, number]; + actions: PointAction[]; + connections: { + targets: Array<{ pathUUID: string }>; + }; +} + +export interface SimulationPath { + modeluuid: string; + points: PathPoint[]; + pathPosition: [number, number, number]; + speed?: number; +} + +export interface Process { + id: string; + paths: SimulationPath[]; + animationPath: THREE.Vector3[]; + pointActions: PointAction[][]; + speed: number; +} + +interface ProcessCreatorProps { + onProcessesCreated: (processes: Process[]) => void; +} + +// Convert event schemas to SimulationPath +function convertToSimulationPath( + path: ConveyorEventsSchema | VehicleEventsSchema +): SimulationPath { + const { modeluuid } = path; + + // Simplified normalizeAction function that preserves exact original properties + const normalizeAction = (action: any): PointAction => { + return { ...action }; // Return exact copy with no modifications + }; + + if (path.type === "Conveyor") { + return { + modeluuid, + points: path.points.map((point) => ({ + uuid: point.uuid, + position: point.position, + actions: point.actions.map(normalizeAction), // Preserve exact actions + connections: { + targets: point.connections.targets.map((target) => ({ + pathUUID: target.pathUUID, + })), + }, + })), + pathPosition: path.position, + speed: + typeof path.speed === "string" + ? parseFloat(path.speed) || 1 + : path.speed || 1, + }; + } else { + return { + modeluuid, + points: [ + { + uuid: path.point.uuid, + position: path.point.position, + actions: Array.isArray(path.point.actions) + ? path.point.actions.map(normalizeAction) + : [normalizeAction(path.point.actions)], + connections: { + targets: path.point.connections.targets.map((target) => ({ + pathUUID: target.pathUUID, + })), + }, + }, + ], + pathPosition: path.position, + speed: path.point.speed || 1, + }; + } +} + +// Custom shallow comparison for arrays +const areArraysEqual = (a: any[], b: any[]) => { + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; +}; + +// Helper function to create an empty process +const createEmptyProcess = (): Process => ({ + id: `process-${Math.random().toString(36).substring(2, 11)}`, + paths: [], + animationPath: [], + pointActions: [], + speed: 1, +}); + +// Enhanced connection checking function +function shouldReverseNextPath( + currentPath: SimulationPath, + nextPath: SimulationPath +): boolean { + if (nextPath.points.length !== 3) return false; + + const currentLastPoint = currentPath.points[currentPath.points.length - 1]; + const nextFirstPoint = nextPath.points[0]; + const nextLastPoint = nextPath.points[nextPath.points.length - 1]; + + // Check if current last connects to next last (requires reversal) + const connectsToLast = currentLastPoint.connections.targets.some( + (target) => + target.pathUUID === nextPath.modeluuid && + nextLastPoint.connections.targets.some( + (t) => t.pathUUID === currentPath.modeluuid + ) + ); + + // Check if current last connects to next first (no reversal needed) + const connectsToFirst = currentLastPoint.connections.targets.some( + (target) => + target.pathUUID === nextPath.modeluuid && + nextFirstPoint.connections.targets.some( + (t) => t.pathUUID === currentPath.modeluuid + ) + ); + + // Only reverse if connected to last point and not to first point + return connectsToLast && !connectsToFirst; +} + +// Updated path adjustment function +function adjustPathPointsOrder(paths: SimulationPath[]): SimulationPath[] { + if (paths.length < 2) return paths; + + const adjustedPaths = [...paths]; + + for (let i = 0; i < adjustedPaths.length - 1; i++) { + const currentPath = adjustedPaths[i]; + const nextPath = adjustedPaths[i + 1]; + + if (shouldReverseNextPath(currentPath, nextPath)) { + const reversedPoints = [ + nextPath.points[2], + nextPath.points[1], + nextPath.points[0], + ]; + + adjustedPaths[i + 1] = { + ...nextPath, + points: reversedPoints, + }; + } + } + + return adjustedPaths; +} + +// Main hook for process creation +export function useProcessCreation() { + const { scene } = useThree(); + const [processes, setProcesses] = useState([]); + + const hasSpawnAction = useCallback((path: SimulationPath): boolean => { + return path.points.some((point) => + point.actions.some((action) => action.type.toLowerCase() === "spawn") + ); + }, []); + + const createProcess = useCallback( + (paths: SimulationPath[]): Process => { + if (!paths || paths.length === 0) { + return createEmptyProcess(); + } + + const animationPath: THREE.Vector3[] = []; + const pointActions: PointAction[][] = []; + const processSpeed = paths[0]?.speed || 1; + + for (const path of paths) { + for (const point of path.points) { + const obj = scene.getObjectByProperty("uuid", point.uuid); + if (!obj) { + console.warn(`Object with UUID ${point.uuid} not found in scene`); + continue; + } + + const position = obj.getWorldPosition(new THREE.Vector3()); + animationPath.push(position.clone()); + pointActions.push(point.actions); + } + } + + return { + id: `process-${Math.random().toString(36).substring(2, 11)}`, + paths, + animationPath, + pointActions, + speed: processSpeed, + }; + }, + [scene] + ); + + const getAllConnectedPaths = useCallback( + ( + initialPath: SimulationPath, + allPaths: SimulationPath[], + visited: Set = new Set() + ): SimulationPath[] => { + const connectedPaths: SimulationPath[] = []; + const queue: SimulationPath[] = [initialPath]; + visited.add(initialPath.modeluuid); + + const pathMap = new Map(); + allPaths.forEach((path) => pathMap.set(path.modeluuid, path)); + + while (queue.length > 0) { + const currentPath = queue.shift()!; + connectedPaths.push(currentPath); + + // Process outgoing connections + for (const point of currentPath.points) { + for (const target of point.connections.targets) { + if (!visited.has(target.pathUUID)) { + const targetPath = pathMap.get(target.pathUUID); + if (targetPath) { + visited.add(target.pathUUID); + queue.push(targetPath); + } + } + } + } + + // Process incoming connections + for (const [uuid, path] of pathMap) { + if (!visited.has(uuid)) { + const hasConnectionToCurrent = path.points.some((point) => + point.connections.targets.some( + (t) => t.pathUUID === currentPath.modeluuid + ) + ); + if (hasConnectionToCurrent) { + visited.add(uuid); + queue.push(path); + } + } + } + } + + return connectedPaths; + }, + [] + ); + + const createProcessesFromPaths = useCallback( + (paths: SimulationPath[]): Process[] => { + if (!paths || paths.length === 0) return []; + + const visited = new Set(); + const processes: Process[] = []; + const pathMap = new Map(); + paths.forEach((path) => pathMap.set(path.modeluuid, path)); + + for (const path of paths) { + if (!visited.has(path.modeluuid) && hasSpawnAction(path)) { + const connectedPaths = getAllConnectedPaths(path, paths, visited); + const adjustedPaths = adjustPathPointsOrder(connectedPaths); + const process = createProcess(adjustedPaths); + processes.push(process); + } + } + + return processes; + }, + [createProcess, getAllConnectedPaths, hasSpawnAction] + ); + + return { + processes, + createProcessesFromPaths, + setProcesses, + }; +} + +const ProcessCreator: React.FC = React.memo( + ({ onProcessesCreated }) => { + const { simulationPaths } = useSimulationPaths(); + const { createProcessesFromPaths } = useProcessCreation(); + const prevPathsRef = useRef([]); + const prevProcessesRef = useRef([]); + + const convertedPaths = useMemo((): SimulationPath[] => { + if (!simulationPaths) return []; + return simulationPaths.map((path) => + convertToSimulationPath( + path as ConveyorEventsSchema | VehicleEventsSchema + ) + ); + }, [simulationPaths]); + + const pathsDependency = useMemo(() => { + if (!convertedPaths) return null; + return convertedPaths.map((path) => ({ + id: path.modeluuid, + hasSpawn: path.points.some((p: PathPoint) => + p.actions.some((a: PointAction) => a.type.toLowerCase() === "spawn") + ), + connections: path.points + .flatMap((p: PathPoint) => + p.connections.targets.map((t: { pathUUID: string }) => t.pathUUID) + ) + .join(","), + })); + }, [convertedPaths]); + + useEffect(() => { + if (!convertedPaths || convertedPaths.length === 0) { + if (prevProcessesRef.current.length > 0) { + onProcessesCreated([]); + prevProcessesRef.current = []; + } + return; + } + + if (areArraysEqual(prevPathsRef.current, convertedPaths)) { + return; + } + + prevPathsRef.current = convertedPaths; + const newProcesses = createProcessesFromPaths(convertedPaths); + + // console.log("--- Action Types in Paths ---"); + // convertedPaths.forEach((path) => { + // path.points.forEach((point) => { + // point.actions.forEach((action) => { + // console.log( + // `Path ${path.modeluuid}, Point ${point.uuid}: ${action.type}` + // ); + // }); + // }); + // }); + // console.log("New processes:", newProcesses); + + if ( + newProcesses.length !== prevProcessesRef.current.length || + !newProcesses.every( + (proc, i) => + proc.paths.length === prevProcessesRef.current[i]?.paths.length && + proc.paths.every( + (path, j) => + path.modeluuid === + prevProcessesRef.current[i]?.paths[j]?.modeluuid + ) + ) + ) { + onProcessesCreated(newProcesses); + // prevProcessesRef.current = newProcesses; + } + }, [ + pathsDependency, + onProcessesCreated, + convertedPaths, + createProcessesFromPaths, + ]); + + return null; + } +); + +export default ProcessCreator; diff --git a/app/src/modules/simulation/simulation.tsx b/app/src/modules/simulation/simulation.tsx index 739a92b..6fb0036 100644 --- a/app/src/modules/simulation/simulation.tsx +++ b/app/src/modules/simulation/simulation.tsx @@ -5,6 +5,7 @@ import Behaviour from './behaviour/behaviour'; import PathCreation from './path/pathCreation'; import PathConnector from './path/pathConnector'; import useModuleStore from '../../store/useModuleStore'; +import ProcessContainer from './process/processContainer'; function Simulation() { const { activeModule } = useModuleStore(); @@ -35,6 +36,7 @@ function Simulation() { <> + )} From 00ea0432b5f48818a8e661773e3f535b791ed1bb Mon Sep 17 00:00:00 2001 From: Jerald-Golden-B Date: Tue, 1 Apr 2025 19:04:15 +0530 Subject: [PATCH 6/6] feat: implement conveyor path creation with dynamic points and actions --- .../simulation/behaviour/behaviour.tsx | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/app/src/modules/simulation/behaviour/behaviour.tsx b/app/src/modules/simulation/behaviour/behaviour.tsx index 53af231..d127705 100644 --- a/app/src/modules/simulation/behaviour/behaviour.tsx +++ b/app/src/modules/simulation/behaviour/behaviour.tsx @@ -11,16 +11,49 @@ function Behaviour() { const newPaths: (Types.ConveyorEventsSchema | Types.VehicleEventsSchema)[] = []; floorItems.forEach((item: Types.FloorItemType) => { - // console.log('item: ', item); - if (item.eventData && item.eventData.type === 'Conveyor') { + if (item.modelfileID === "672a090f80d91ac979f4d0bd") { + console.log('item: ', item); + const point1Position = new THREE.Vector3(0, 0.85, 2.2); + const middlePointPosition = new THREE.Vector3(0, 0.85, 0); + const point2Position = new THREE.Vector3(0, 0.85, -2.2); + + const point1UUID = THREE.MathUtils.generateUUID(); + const middlePointUUID = THREE.MathUtils.generateUUID(); + const point2UUID = THREE.MathUtils.generateUUID(); + const newPath: Types.ConveyorEventsSchema = { modeluuid: item.modeluuid, modelName: item.modelname, type: 'Conveyor', - points: item.eventData.points, + points: [ + { + uuid: point1UUID, + position: [point1Position.x, point1Position.y, point1Position.z], + rotation: [0, 0, 0], + actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], + triggers: [], + connections: { source: { pathUUID: item.modeluuid, pointUUID: point1UUID }, targets: [] }, + }, + { + uuid: middlePointUUID, + position: [middlePointPosition.x, middlePointPosition.y, middlePointPosition.z], + rotation: [0, 0, 0], + actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], + triggers: [], + connections: { source: { pathUUID: item.modeluuid, pointUUID: middlePointUUID }, targets: [] }, + }, + { + uuid: point2UUID, + position: [point2Position.x, point2Position.y, point2Position.z], + rotation: [0, 0, 0], + actions: [{ uuid: THREE.MathUtils.generateUUID(), name: 'Action 1', type: 'Inherit', material: 'Inherit', delay: 'Inherit', spawnInterval: 'Inherit', isUsed: true }], + triggers: [], + connections: { source: { pathUUID: item.modeluuid, pointUUID: point2UUID }, targets: [] }, + }, + ], position: [...item.position], rotation: [item.rotation.x, item.rotation.y, item.rotation.z], - speed: item.eventData.speed, + speed: 'Inherit', }; newPaths.push(newPath); @@ -48,6 +81,7 @@ function Behaviour() { setSimulationPaths(newPaths); }, [floorItems]); + return null; }