diff --git a/app/.env b/app/.env index 36093e3..4bfb63a 100644 --- a/app/.env +++ b/app/.env @@ -1,11 +1,11 @@ # PORT for the main application (frontend/backend or another service). -PORT=8200 +PORT=8400 # Base URL for the server socket API, used for real-time communication (e.g., WebSockets). -REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:7999 +REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:9902 # 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:4999 +REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:9901 # Base URL for the server marketplace, used for market place model blob. REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 diff --git a/app/package-lock.json b/app/package-lock.json index 5da4733..7bb6a97 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -2026,7 +2026,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2038,7 +2038,7 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -4180,6 +4180,25 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@testing-library/jest-dom": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", @@ -4291,25 +4310,25 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "devOptional": true }, "node_modules/@turf/along": { "version": "7.2.0", @@ -9063,7 +9082,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cross-env": { "version": "7.0.3", @@ -9940,7 +9959,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -15324,7 +15343,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -20801,7 +20820,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -20844,7 +20863,7 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, + "devOptional": true, "dependencies": { "acorn": "^8.11.0" }, @@ -20856,7 +20875,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/tsconfig-paths": { "version": "3.15.0", @@ -21352,7 +21371,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "8.1.1", @@ -22411,7 +22430,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } diff --git a/app/src/assets/cursors/ask.svg b/app/src/assets/cursors/ask.svg new file mode 100644 index 0000000..39f66a1 --- /dev/null +++ b/app/src/assets/cursors/ask.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/assets/cursors/cell.svg b/app/src/assets/cursors/cell.svg new file mode 100644 index 0000000..2632892 --- /dev/null +++ b/app/src/assets/cursors/cell.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/assets/cursors/close.svg b/app/src/assets/cursors/close.svg new file mode 100644 index 0000000..6fdebed --- /dev/null +++ b/app/src/assets/cursors/close.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/assets/cursors/cross.svg b/app/src/assets/cursors/cross.svg new file mode 100644 index 0000000..1ac52f1 --- /dev/null +++ b/app/src/assets/cursors/cross.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/assets/cursors/default.svg b/app/src/assets/cursors/default.svg new file mode 100644 index 0000000..bf6a33b --- /dev/null +++ b/app/src/assets/cursors/default.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/assets/cursors/export.svg b/app/src/assets/cursors/export.svg new file mode 100644 index 0000000..a560635 --- /dev/null +++ b/app/src/assets/cursors/export.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/assets/cursors/move.svg b/app/src/assets/cursors/move.svg new file mode 100644 index 0000000..12b1692 --- /dev/null +++ b/app/src/assets/cursors/move.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/assets/cursors/open.svg b/app/src/assets/cursors/open.svg new file mode 100644 index 0000000..695087a --- /dev/null +++ b/app/src/assets/cursors/open.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/assets/cursors/pen.svg b/app/src/assets/cursors/pen.svg new file mode 100644 index 0000000..ff956b5 --- /dev/null +++ b/app/src/assets/cursors/pen.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/app/src/assets/cursors/pointing.svg b/app/src/assets/cursors/pointing.svg new file mode 100644 index 0000000..4a50a4b --- /dev/null +++ b/app/src/assets/cursors/pointing.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/components/Dashboard/SidePannel.tsx b/app/src/components/Dashboard/SidePannel.tsx index 5cfaeb0..89ce5ea 100644 --- a/app/src/components/Dashboard/SidePannel.tsx +++ b/app/src/components/Dashboard/SidePannel.tsx @@ -38,9 +38,11 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { const handleCreateNewProject = async () => { const token = localStorage.getItem("token"); + const refreshToken = localStorage.getItem("refreshToken") + console.log('refreshToken: ', refreshToken); try { const projectId = generateProjectId(); - useSocketStore.getState().initializeSocket(email, organization, token); + useSocketStore.getState().initializeSocket(email, organization, token, refreshToken); //API for creating new Project // const project = await createProject( @@ -56,7 +58,8 @@ const SidePannel: React.FC = ({ setActiveTab, activeTab }) => { organization: organization, projectUuid: projectId, }; - + + console.log('projectSocket: ', projectSocket); if (projectSocket) { const handleResponse = (data: any) => { if (data.message === "Project created successfully") { diff --git a/app/src/components/icons/DashboardIcon.tsx b/app/src/components/icons/DashboardIcon.tsx index f1f4d7d..1aa94b5 100644 --- a/app/src/components/icons/DashboardIcon.tsx +++ b/app/src/components/icons/DashboardIcon.tsx @@ -9,13 +9,13 @@ export function NotificationIcon() { > @@ -34,7 +34,7 @@ export function HomeIcon() { > ); @@ -51,7 +51,7 @@ export function ProjectsIcon() { > ); @@ -70,103 +70,103 @@ export function TutorialsIcon() { cx="8.157" cy="8.35866" r="6.17928" - stroke="var(--text-color)" + stroke="var(--text-button-color)" strokeWidth="0.562865" /> @@ -184,12 +184,12 @@ export function DocumentationIcon() { > @@ -208,7 +208,7 @@ export function HelpIcon() { @@ -236,17 +236,17 @@ export function LogoutIcon() { > diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx index ed9ef9f..252e44d 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/EventProperties.tsx @@ -28,7 +28,6 @@ const EventProperties: React.FC = () => { const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); - useEffect(() => { const event = getCurrentEventData(); setCurrentEventData(event); diff --git a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx index eb9e41c..57efd87 100644 --- a/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx +++ b/app/src/components/layout/sidebarRight/properties/eventProperties/mechanics/vehicleMechanics.tsx @@ -14,6 +14,7 @@ import { useProductContext } from "../../../../../../modules/simulation/products import { useParams } from "react-router-dom"; import { useVersionContext } from "../../../../../../modules/builder/version/versionContext"; import { useSceneContext } from "../../../../../../modules/scene/sceneContext"; +import { useSelectedPath } from "../../../../../../store/builder/store"; function VehicleMechanics() { const [activeOption, setActiveOption] = useState<"default" | "travel">("default"); @@ -27,6 +28,7 @@ function VehicleMechanics() { const { selectedVersionStore } = useVersionContext(); const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); + const { selectedPath, setSelectedPath } = useSelectedPath(); useEffect(() => { if (selectedEventData && selectedEventData.data.type === "vehicle") { @@ -282,9 +284,34 @@ function VehicleMechanics() { type={"Vehicle"} /> +
+ + +
- )} + ) + } ); } diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index ab9434b..a98805b 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -15,6 +15,7 @@ import { useToolMode, useRenderDistance, useLimitDistance, + useLoadingProgress, } from "../../store/builder/store"; ////////// 3D Function Imports ////////// @@ -56,6 +57,7 @@ export default function Builder() { const { projectId } = useParams(); const { setHoveredPoint, setHoveredLine } = useBuilderStore(); const { userId, organization } = getUserData(); + const { loadingProgress } = useLoadingProgress(); useEffect(() => { if (!toggleView) { @@ -115,8 +117,7 @@ export default function Builder() { - - + {loadingProgress == 0 && } diff --git a/app/src/modules/builder/wall/Instances/instance/wall.tsx b/app/src/modules/builder/wall/Instances/instance/wall.tsx index 452b69a..a441ea8 100644 --- a/app/src/modules/builder/wall/Instances/instance/wall.tsx +++ b/app/src/modules/builder/wall/Instances/instance/wall.tsx @@ -152,9 +152,9 @@ function Wall({ wall }: { readonly wall: Wall }) { > - {wall.decals.map((decal) => ( + {/* {wall.decals.map((decal) => ( - ))} + ))} */} ); diff --git a/app/src/modules/simulation/vehicle/instances/animator/interactivePoint.tsx b/app/src/modules/simulation/vehicle/instances/animator/interactivePoint.tsx new file mode 100644 index 0000000..5705cc5 --- /dev/null +++ b/app/src/modules/simulation/vehicle/instances/animator/interactivePoint.tsx @@ -0,0 +1,328 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import * as THREE from 'three'; +import { Canvas, useThree, useFrame, ThreeEvent } from '@react-three/fiber'; +import { Line, OrbitControls } from '@react-three/drei'; +import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; +import { useActiveTool, useSelectedPath } from '../../../../../store/builder/store'; + +interface InteractivePointsProps { + agvUuid: string; +} + + +export default function InteractivePoints({ agvUuid }: InteractivePointsProps) { + const { gl, scene, raycaster } = useThree(); + const [points, setPoints] = useState<[number, number, number][]>([]); + const { isPaused } = usePauseButtonStore(); + const { isPlaying } = usePlayButtonStore(); + const { speed } = useAnimationPlaySpeed(); + const plane = useRef(new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)); // XZ plane + const progressRef = useRef(0); + const { selectedPath } = useSelectedPath(); + const lastTimeRef = useRef(performance.now()); + const [isAnyDragging, setIsAnyDragging] = useState(""); + const { activeTool } = useActiveTool(); + const hasClicked = useRef(false); + + useFrame(() => { + if (!isPlaying) return + const now = performance.now(); + const delta = (now - lastTimeRef.current) / 1000; + lastTimeRef.current = now; + + const object = scene.getObjectByProperty('uuid', agvUuid); + if (!object || points.length < 2) return; + if (isPaused) return; + + let totalDistance = 0; + const distances = []; + let accumulatedDistance = 0; + let index = 0; + const rotationSpeed = 1; + + for (let i = 0; i < points.length - 1; i++) { + const start = new THREE.Vector3(...points[i]); + const end = new THREE.Vector3(...points[i + 1]); + const segmentDistance = start.distanceTo(end); + distances.push(segmentDistance); + totalDistance += segmentDistance; + } + + while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) { + accumulatedDistance += distances[index]; + index++; + } + + if (index < distances.length) { + const start = new THREE.Vector3(...points[index]); + const end = new THREE.Vector3(...points[index + 1]); + const segmentDistance = distances[index]; + + const currentDirection = new THREE.Vector3().subVectors(end, start).normalize(); + const targetAngle = Math.atan2(currentDirection.x, currentDirection.z); + const currentAngle = object.rotation.y; + + let angleDifference = targetAngle - currentAngle; + if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI; + if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI; + + const maxRotationStep = (rotationSpeed * speed * 2) * delta; + object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep); + const isAligned = Math.abs(angleDifference) < 0.01; + + if (isAligned) { + progressRef.current += delta * (speed * 2); + const t = (progressRef.current - accumulatedDistance) / segmentDistance; + const position = start.clone().lerp(end, t); + object.position.copy(position); + } + } + }); + + const downPosition = useRef<{ x: number; y: number } | null>(null); + + const handleMouseDown = useCallback((e: MouseEvent) => { + hasClicked.current = false; + downPosition.current = { x: e.clientX, y: e.clientY }; + }, []); + + const handleClick = useCallback((e: MouseEvent) => { + if (hasClicked.current) return; + hasClicked.current = true; + + if ( + !downPosition.current || + Math.abs(downPosition.current.x - e.clientX) > 2 || + Math.abs(downPosition.current.y - e.clientY) > 2 + ) { + return; + } + + const intersection = new THREE.Vector3(); + if (raycaster.ray.intersectPlane(plane.current, intersection) && activeTool !== "pen") { + const pointArray = intersection.toArray() as [number, number, number]; + + // ✅ Check if this point already exists + const alreadyExists = points.some((p) => + Math.abs(p[0] - pointArray[0]) < 0.01 && + Math.abs(p[1] - pointArray[1]) < 0.01 && + Math.abs(p[2] - pointArray[2]) < 0.01 + ); + + if (!alreadyExists) { + console.log("pointArray: ", pointArray); + setPoints((prev) => [...prev, pointArray]); + console.log("points created"); + } + } + }, [activeTool, raycaster, points]); + + + useEffect(() => { + if (isPlaying) return; + const domElement = gl.domElement; + + domElement.addEventListener('mousedown', handleMouseDown); + domElement.addEventListener('mouseup', handleClick); + + + return () => { + domElement.removeEventListener('mousedown', handleMouseDown); + domElement.removeEventListener('mouseup', handleClick); + ; + }; + }, [isPlaying, handleClick, handleMouseDown]); + + + const updatePoint = (index: number, pos: THREE.Vector3) => { + const updated = [...points]; + updated[index] = pos.toArray() as [number, number, number]; + setPoints(updated); + }; + + + return ( + <> + {selectedPath === "manual" && + + {points.length > 0 && ( + + {points.map((pos, i) => + ( + + + ) + + + )} + + ) + } + {points && ( + points.map((pos, i) => { + if (i < points.length - 1) { + return ( + { + const updated = [...points]; + updated[i0] = p0.toArray() as [number, number, number]; + updated[i1] = p1.toArray() as [number, number, number]; + setPoints(updated); + }} + isAnyDragging={isAnyDragging} + setIsAnyDragging={setIsAnyDragging} + /> + ); + } + return null; + }) + )} + + } + + ); +} + + + +function DraggableSphere({ + index, + position, + onMove, + isAnyDragging, + setIsAnyDragging, +}: { + index: number; + position: THREE.Vector3; + onMove: (index: number, pos: THREE.Vector3) => void; + isAnyDragging: string; + setIsAnyDragging: (val: string) => void; +}) { + const meshRef = useRef(null); + const { gl, controls, raycaster } = useThree(); + const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); + const { activeTool } = useActiveTool(); + + const onPointerDown = (e: ThreeEvent) => { + e.stopPropagation() + if (activeTool !== 'pen') return; + setIsAnyDragging("point"); + gl.domElement.style.cursor = 'grabbing'; + if (controls) (controls as any).enabled = false; + }; + + const onPointerMove = (e: ThreeEvent) => { + if (isAnyDragging !== "point" || activeTool !== 'pen') return; + + const intersect = new THREE.Vector3(); + if (raycaster.ray.intersectPlane(plane, intersect)) { + meshRef.current!.position.copy(intersect); + onMove(index, intersect); + } + }; + + const onPointerUp = () => { + if (activeTool !== 'pen') return; + setIsAnyDragging(""); + gl.domElement.style.cursor = 'default'; + if (controls) (controls as any).enabled = true; + }; + useEffect(() => { + gl.domElement.addEventListener("pointerup", onPointerUp); + return (() => { + gl.domElement.removeEventListener("pointerup", onPointerUp); + }) + }, [activeTool]) + + return ( + + + + + ); +} + +function DraggableLineSegment({ + index, + start, + end, + updatePoints, + isAnyDragging, + setIsAnyDragging, +}: { + index: number; + start: THREE.Vector3; + end: THREE.Vector3; + updatePoints: (i0: number, p0: THREE.Vector3, i1: number, p1: THREE.Vector3) => void; + isAnyDragging: string; + setIsAnyDragging: (val: string) => void; +}) { + const { gl, raycaster, controls } = useThree(); + const { activeTool } = useActiveTool(); + const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); + const dragStart = useRef(null); + + const onPointerDown = () => { + if (activeTool !== 'pen' || isAnyDragging) return; + setIsAnyDragging("line"); + gl.domElement.style.cursor = 'grabbing'; + if (controls) (controls as any).enabled = false; + }; + + const onPointerMove = (e: ThreeEvent) => { + if (isAnyDragging !== "line" || activeTool !== 'pen') return; + + const intersect = new THREE.Vector3(); + if (raycaster.ray.intersectPlane(plane, intersect)) { + if (!dragStart.current) dragStart.current = intersect.clone(); + const offset = new THREE.Vector3().subVectors(intersect, dragStart.current); + const newStart = start.clone().add(offset); + const newEnd = end.clone().add(offset); + updatePoints(index, newStart, index + 1, newEnd); + } + }; + + const onPointerUp = () => { + if (activeTool !== 'pen') return; + setIsAnyDragging(""); + dragStart.current = null; + gl.domElement.style.cursor = 'default'; + if (controls) (controls as any).enabled = true; + }; + useEffect(() => { + gl.domElement.addEventListener("pointerup", onPointerUp); + return (() => { + gl.domElement.removeEventListener("pointerup", onPointerUp); + }) + }, [activeTool]) + + return ( + + ); +} \ No newline at end of file diff --git a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx index be10213..7699c9c 100644 --- a/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx +++ b/app/src/modules/simulation/vehicle/instances/animator/vehicleAnimator.tsx @@ -1,9 +1,12 @@ -import { useEffect, useRef, useState } from 'react' -import { useFrame, useThree } from '@react-three/fiber'; + +import React, { useEffect, useMemo, useRef, useState } from 'react' +import { useFrame, useThree, ThreeEvent } from '@react-three/fiber'; import * as THREE from 'three'; -import { Line } from '@react-three/drei'; +import { Line, TransformControls } from '@react-three/drei'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore, useResetButtonStore } from '../../../../../store/usePlayButtonStore'; import { useSceneContext } from '../../../../scene/sceneContext'; +import { useActiveTool, useSelectedPath } from '../../../../../store/builder/store'; + interface VehicleAnimatorProps { path: [number, number, number][]; @@ -28,10 +31,13 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai const [objectRotation, setObjectRotation] = useState<{ x: number; y: number; z: number } | undefined>(agvDetail.point?.action?.pickUpPoint?.rotation || { x: 0, y: 0, z: 0 }) const [restRotation, setRestingRotation] = useState(true); const [currentPath, setCurrentPath] = useState<[number, number, number][]>([]); - const { scene } = useThree(); + const { scene, controls } = useThree(); + const { selectedPath } = useSelectedPath(); + const [isAnyDragging, setIsAnyDragging] = useState(""); + useEffect(() => { - if (currentPhase === 'stationed-pickup' && path.length > 0) { + if (currentPhase === 'stationed-pickup' && path.length > 0 && selectedPath === "auto") { setCurrentPath(path); setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation) } else if (currentPhase === 'pickup-drop' && path.length > 0) { @@ -41,7 +47,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai setObjectRotation(agvDetail.point.action?.pickUpPoint?.rotation) setCurrentPath(path); } - }, [currentPhase, path, objectRotation]); + }, [currentPhase, path, objectRotation, selectedPath]); useEffect(() => { completedRef.current = false; @@ -68,6 +74,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai const lastTimeRef = useRef(performance.now()); useFrame(() => { + if (!isPlaying) return const now = performance.now(); const delta = (now - lastTimeRef.current) / 1000; lastTimeRef.current = now; @@ -162,26 +169,198 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai handleCallBack(); if (currentPhase === 'pickup-drop') { requestAnimationFrame(startUnloadingProcess); + } } }); + const updatePoint = (index: number, pos: THREE.Vector3) => { + const updated = [...currentPath]; + updated[index] = pos.toArray() as [number, number, number]; + setCurrentPath(updated); + }; + + return ( <> - {currentPath.length > 0 && ( - // helper - - - {currentPath.map((point, index) => ( - - - - - ))} - - )} + {selectedPath === "auto" && + {currentPath.map((pos, i) => { + if (i < currentPath.length - 1) { + return ( + { + const updated = [...currentPath]; + updated[i0] = p0.toArray() as [number, number, number]; + updated[i1] = p1.toArray() as [number, number, number]; + setCurrentPath(updated); + }} + isAnyDragging={isAnyDragging} + setIsAnyDragging={setIsAnyDragging} + /> + ); + } + return null; + })} + {currentPath.length > 0 && ( + { if (controls) (controls as any).enabled = true; }}> + {currentPath.map((pos, i) => + ( + ) + )} + + ) + } + + } ); } -export default VehicleAnimator; \ No newline at end of file + + +export default VehicleAnimator; + + + + +function DraggableSphere({ + index, + position, + onMove, + isAnyDragging, + setIsAnyDragging, +}: { + index: number; + position: THREE.Vector3; + onMove: (index: number, pos: THREE.Vector3) => void; + isAnyDragging: string; + setIsAnyDragging: (val: string) => void; +}) { + const meshRef = useRef(null); + const { gl, controls, raycaster } = useThree(); + const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); + const { activeTool } = useActiveTool(); + + const onPointerDown = (e: ThreeEvent) => { + e.stopPropagation() + if (activeTool !== 'pen') return; + setIsAnyDragging("point"); + gl.domElement.style.cursor = 'grabbing'; + if (controls) (controls as any).enabled = false; + }; + + const onPointerMove = (e: ThreeEvent) => { + if (isAnyDragging !== "point" || activeTool !== 'pen') return; + + const intersect = new THREE.Vector3(); + if (raycaster.ray.intersectPlane(plane, intersect)) { + meshRef.current!.position.copy(intersect); + onMove(index, intersect); + } + }; + + const onPointerUp = () => { + if (activeTool !== 'pen') return; + setIsAnyDragging(""); + gl.domElement.style.cursor = 'default'; + if (controls) (controls as any).enabled = true; + }; + useEffect(() => { + gl.domElement.addEventListener("pointerup", onPointerUp); + return (() => { + gl.domElement.removeEventListener("pointerup", onPointerUp); + }) + }, [activeTool]) + + return ( + + + + + ); +} + +function DraggableLineSegment({ + index, + start, + end, + updatePoints, + isAnyDragging, + setIsAnyDragging, +}: { + index: number; + start: THREE.Vector3; + end: THREE.Vector3; + updatePoints: (i0: number, p0: THREE.Vector3, i1: number, p1: THREE.Vector3) => void; + isAnyDragging: string; + setIsAnyDragging: (val: string) => void; +}) { + const { gl, raycaster, controls } = useThree(); + const { activeTool } = useActiveTool(); + const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0); + const dragStart = useRef(null); + + const onPointerDown = () => { + if (activeTool !== 'pen' || isAnyDragging) return; + setIsAnyDragging("line"); + gl.domElement.style.cursor = 'grabbing'; + if (controls) (controls as any).enabled = false; + }; + + const onPointerMove = (e: ThreeEvent) => { + console.log('isAnyDragging: ', isAnyDragging); + if (isAnyDragging !== "line" || activeTool !== 'pen') return; + + const intersect = new THREE.Vector3(); + if (raycaster.ray.intersectPlane(plane, intersect)) { + if (!dragStart.current) dragStart.current = intersect.clone(); + const offset = new THREE.Vector3().subVectors(intersect, dragStart.current); + const newStart = start.clone().add(offset); + const newEnd = end.clone().add(offset); + updatePoints(index, newStart, index + 1, newEnd); + } + }; + + const onPointerUp = () => { + if (activeTool !== 'pen') return; + setIsAnyDragging(""); + dragStart.current = null; + gl.domElement.style.cursor = 'default'; + if (controls) (controls as any).enabled = true; + }; + useEffect(() => { + gl.domElement.addEventListener("pointerup", onPointerUp); + return (() => { + gl.domElement.removeEventListener("pointerup", onPointerUp); + }) + }, [activeTool]) + return ( + + ); +} diff --git a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx index 5c49901..3e910a7 100644 --- a/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx +++ b/app/src/modules/simulation/vehicle/instances/instance/vehicleInstance.tsx @@ -1,11 +1,12 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import * as THREE from 'three'; import { NavMeshQuery } from '@recast-navigation/core'; -import { useNavMesh } from '../../../../../store/builder/store'; +import { useNavMesh, useSelectedPath } from '../../../../../store/builder/store'; import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore'; import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler'; import { useSceneContext } from '../../../../scene/sceneContext'; import { useProductContext } from '../../../products/productContext'; +import InteractivePoints from '../animator/interactivePoint'; import MaterialAnimator from '../animator/materialAnimator'; import VehicleAnimator from '../animator/vehicleAnimator'; @@ -24,7 +25,6 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) const { selectedProductStore } = useProductContext(); const { selectedProduct } = selectedProductStore(); const { vehicles, setVehicleActive, setVehicleState, setVehiclePicking, clearCurrentMaterials, setVehicleLoad, decrementVehicleLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = vehicleStore(); - const [currentPhase, setCurrentPhase] = useState('stationed'); const [path, setPath] = useState<[number, number, number][]>([]); const pauseTimeRef = useRef(null); @@ -38,6 +38,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) const { isPaused } = usePauseButtonStore(); const previousTimeRef = useRef(null); const animationFrameIdRef = useRef(null); + const { selectedPath, setSelectedPath } = useSelectedPath(); useEffect(() => { isPausedRef.current = isPaused; @@ -57,12 +58,14 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(end.x) && Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(end.z) ) { + console.log('segmentPath: ', segmentPath); return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || []; } else { console.log("There is no path here...Choose valid path") const { path: segmentPaths } = navMeshQuery.computePath(start, start); return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || []; } + } catch { console.error("Failed to compute path"); return []; @@ -97,7 +100,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) } useEffect(() => { - if (isPlaying) { + if (isPlaying || selectedPath === "auto") { if (!agvDetail.point.action.unLoadPoint || !agvDetail.point.action.pickUpPoint) return; if (!agvDetail.isActive && agvDetail.state === 'idle' && currentPhase === 'stationed') { @@ -105,10 +108,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]), agvDetail?.point?.action?.pickUpPoint?.position ); - // const toPickupPath = computePath( - // new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]), - // new THREE.Vector3(agvDetail?.position[0], agvDetail?.position[1], agvDetail?.position[2]) - // ); + setPath(toPickupPath); setCurrentPhase('stationed-pickup'); setVehicleState(agvDetail.modelUuid, 'running'); @@ -149,8 +149,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) else { reset() } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [vehicles, currentPhase, path, isPlaying]); + }, [vehicles, currentPhase, path, isPlaying, selectedPath]); function animate(currentTime: number) { if (previousTimeRef.current === null) { @@ -600,6 +599,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) reset={reset} startUnloadingProcess={startUnloadingProcess} /> + {selectedPath === "manual" && ()} ); diff --git a/app/src/pages/Dashboard.tsx b/app/src/pages/Dashboard.tsx index 15959c1..606765c 100644 --- a/app/src/pages/Dashboard.tsx +++ b/app/src/pages/Dashboard.tsx @@ -16,8 +16,9 @@ const Dashboard: React.FC = () => { useEffect(() => { const token = localStorage.getItem("token"); + const refreshToken = localStorage.getItem("refreshToken") if (token) { - useSocketStore.getState().initializeSocket(email, organization, token); + useSocketStore.getState().initializeSocket(email, organization, token, refreshToken); } else { } diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index bf01b3a..bd4e2bc 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -1,17 +1,14 @@ import React, { useEffect } from "react"; import useModuleStore from "../store/useModuleStore"; import { - useSocketStore, - useOrganization, - useUserName, - useWallItems, - useSaveVersion, - useViewSceneStore, - useProjectName, - useRenameModeStore, - useSelectedFloorItem, - useZones, - useSelectedComment, + useSocketStore, + useOrganization, + useUserName, + useWallItems, + useSaveVersion, + useProjectName, + useZones, + useActiveTool, } from "../store/builder/store"; import { useNavigate, useParams } from "react-router-dom"; import { useSelectedUserStore } from "../store/collaboration/useCollabStore"; @@ -30,124 +27,136 @@ import { getVersionHistoryApi } from "../services/factoryBuilder/versionControl/ import { useVersionHistoryStore } from "../store/builder/useVersionHistoryStore"; import { VersionProvider } from "../modules/builder/version/versionContext"; import { sharedWithMeProjects } from "../services/dashboard/sharedWithMeProject"; +import { handleCanvasCursors } from "../utils/handleCanvasCursors"; const Project: React.FC = () => { - let navigate = useNavigate(); - const echo = useLogger(); - const { setToggleUI } = useToggleStore(); - const { setActiveModule } = useModuleStore(); - const { setUserName } = useUserName(); - const { setOrganization } = useOrganization(); - const { setWallItems } = useWallItems(); - const { setZones } = useZones(); - const { isVersionSaved } = useSaveVersion(); - const { projectId } = useParams(); - const { setProjectName } = useProjectName(); - const { userId, email, organization, userName } = getUserData(); - const { selectedUser } = useSelectedUserStore(); - const { isLogListVisible } = useLogger(); - const { setVersions } = useVersionHistoryStore(); + let navigate = useNavigate(); + const echo = useLogger(); + const { setToggleUI } = useToggleStore(); + const { setActiveModule } = useModuleStore(); + const { setUserName } = useUserName(); + const { setOrganization } = useOrganization(); + const { setWallItems } = useWallItems(); + const { setZones } = useZones(); + const { isVersionSaved } = useSaveVersion(); + const { projectId } = useParams(); + const { setProjectName } = useProjectName(); + const { userId, email, organization, userName } = getUserData(); + const { selectedUser } = useSelectedUserStore(); + const { isLogListVisible } = useLogger(); + const { setVersions } = useVersionHistoryStore(); + const { activeTool } = useActiveTool(); + useEffect(() => { + if (!email || !userId) { + console.error("User data not found in localStorage"); + return; + } - useEffect(() => { - if (!email || !userId) { - console.error("User data not found in localStorage"); - return; - } + const fetchProjects = async () => { + try { + const projects = await getAllProjects(userId, organization); + const shared = await sharedWithMeProjects(); - const fetchProjects = async () => { - try { - const projects = await getAllProjects(userId, organization); - const shared = await sharedWithMeProjects(); + const allProjects = [...(projects?.Projects || []), ...(shared || [])]; - const allProjects = [...(projects?.Projects || []), ...(shared || [])]; + const matchedProject = allProjects.find( + (val: any) => val.projectUuid === projectId || val._id === projectId + ); - const matchedProject = allProjects.find( - (val: any) => val.projectUuid === projectId || val._id === projectId - ); + if (matchedProject) { + setProjectName(matchedProject.projectName); + await viewProject(organization, matchedProject._id, userId); + } else { + console.warn("Project not found with given ID:", projectId); + } + } catch (error) { + console.error("Error fetching projects:", error); + } + }; - if (matchedProject) { - setProjectName(matchedProject.projectName); - await viewProject(organization, matchedProject._id, userId); - } else { - console.warn("Project not found with given ID:", projectId); - } - } catch (error) { - console.error("Error fetching projects:", error); - } - }; + fetchProjects(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - fetchProjects(); - }, []); + useEffect(() => { + if (!projectId) return; + getVersionHistoryApi(projectId) + .then((data) => { + const versions: VersionHistory = []; + data.versions.forEach((version: any) => { + versions.push({ + version: version.version, + versionId: version.versionId, + versionName: version.versionName, + versionDescription: version.description, + timeStamp: version.createdAt, + createdBy: version.createdBy.userName, + }); + }); + setVersions(versions); + }) + .catch(() => { + console.error("Error fetching version history"); + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [projectId]); + useEffect(() => { + if (!isVersionSaved) { + setToggleUI(true, true); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isVersionSaved]); - useEffect(() => { - if (!projectId) return; - getVersionHistoryApi(projectId).then((data) => { - const versions: VersionHistory = []; - data.versions.forEach((version: any) => { - versions.push({ - version: version.version, - versionId: version.versionId, - versionName: version.versionName, - versionDescription: version.description, - timeStamp: version.createdAt, - createdBy: version.createdBy.userName - }) - }) - setVersions(versions); - }).catch(() => { - console.error("Error fetching version history") - }) - }, [projectId]) + useEffect(() => { + setWallItems([]); + setZones([]); + setActiveModule("builder"); + if (email) { + const token = localStorage.getItem("token"); + const refreshToken = localStorage.getItem("refreshToken"); + if (token) { + useSocketStore + .getState() + .initializeSocket(email, organization, token, refreshToken); + } + if (organization && userName) { + setOrganization(organization); + setUserName(userName); + } + echo.success("Log in successful"); + } else { + navigate("/"); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - useEffect(() => { - if (!isVersionSaved) { - setToggleUI(true, true); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isVersionSaved]); + useEffect(() => { + handleCanvasCursors(activeTool); + console.log('activeTool: ', activeTool); + }, [activeTool]); - useEffect(() => { - setWallItems([]); - setZones([]); - setActiveModule("builder"); - if (email) { - const token = localStorage.getItem("token"); - if (token) { - useSocketStore.getState().initializeSocket(email, organization, token); - } - if (organization && userName) { - setOrganization(organization); - setUserName(userName); - } - echo.success("Log in successful"); - } else { - navigate("/"); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( -
- - - - - - - - - - - {selectedUser && } - {isLogListVisible && ( - - - - )} -
- ); + return ( +
+ + + + + + + + + + + {selectedUser && } + {isLogListVisible && ( + + + + )} +
+ ); }; export default Project; diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts index 06cb1fb..529e42f 100644 --- a/app/src/store/builder/store.ts +++ b/app/src/store/builder/store.ts @@ -5,7 +5,12 @@ import * as CONSTANTS from "../../types/world/worldConstants"; export const useSocketStore = create((set: any, get: any) => ({ socket: null, - initializeSocket: (email?: string, organization?: string, token?: string) => { + initializeSocket: ( + email?: string, + organization?: string, + token?: string, + refreshToken?: string + ) => { const existingSocket = get().socket; if (existingSocket) { return; @@ -15,7 +20,7 @@ export const useSocketStore = create((set: any, get: any) => ({ `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Builder_v1`, { reconnection: true, - auth: { token }, + auth: { token, refreshToken }, } ); @@ -23,7 +28,7 @@ export const useSocketStore = create((set: any, get: any) => ({ `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/Visualization_v1`, { reconnection: true, - auth: { token }, + auth: { token, refreshToken }, } ); @@ -31,21 +36,21 @@ export const useSocketStore = create((set: any, get: any) => ({ `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/dashboard`, { reconnection: true, - auth: { token }, + auth: { token, refreshToken }, } ); const projectSocket = io( `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/project`, { reconnection: true, - auth: { token }, + auth: { token, refreshToken }, } ); const threadSocket = io( `http://${process.env.REACT_APP_SERVER_SOCKET_API_BASE_URL}/thread`, { reconnection: true, - auth: { token }, + auth: { token, refreshToken }, } ); @@ -724,3 +729,7 @@ export const useSelectedComment = create((set: any) => ({ commentPositionState: null, setCommentPositionState: (x: any) => set({ commentPositionState: x }), })); +export const useSelectedPath = create((set: any) => ({ + selectedPath: "", + setSelectedPath: (x: any) => set({ selectedPath: x }), +})); diff --git a/app/src/styles/main.scss b/app/src/styles/main.scss index 3de3896..df75860 100644 --- a/app/src/styles/main.scss +++ b/app/src/styles/main.scss @@ -45,4 +45,5 @@ // @use "./scene/scene"; -@use "./scene/comments"; \ No newline at end of file +@use "./scene/comments"; +@use "./scene/cursors.scss"; diff --git a/app/src/styles/pages/dashboard.scss b/app/src/styles/pages/dashboard.scss index 474c060..5a628a4 100644 --- a/app/src/styles/pages/dashboard.scss +++ b/app/src/styles/pages/dashboard.scss @@ -84,12 +84,6 @@ font-weight: var(--font-weight-medium); background: var(--background-color-button); - svg { - path { - stroke: var(--background-color-selected); - } - } - &:hover { background: var(--background-color-button); } diff --git a/app/src/styles/scene/cursors.scss b/app/src/styles/scene/cursors.scss new file mode 100644 index 0000000..cef9b92 --- /dev/null +++ b/app/src/styles/scene/cursors.scss @@ -0,0 +1,47 @@ +$cursor-default: url("../../assets/cursors/default.svg") 0 0, default; +$cursor-pen: url("../../assets/cursors/pen.svg") 0 0, default; +$cursor-delete: url("../../assets/cursors/pointing.svg") 4 0, default; +$cursor-draw: url("../../assets/cursors/cell.svg") 8 8, default; +$cursor-cross: url("../../assets/cursors/cross.svg") 8 8, default; +$cursor-grab: url("../../assets/cursors/open.svg") 8 8, default; +$cursor-grabing: url("../../assets/cursors/close.svg") 8 8, default; + +.scene-container { + canvas { + cursor: $cursor-default !important; + } +} + +.scene-container.draw { + canvas { + cursor: $cursor-draw !important; + } +} + +.scene-container.pointer { + canvas { + cursor: $cursor-delete !important; + } +} + +.scene-container.pen { + canvas { + cursor: $cursor-pen !important; + } +} + +.scene-container.measure { + canvas { + cursor: $cursor-cross !important; + } +} + +.scene-container.hand { + canvas { + cursor: $cursor-grab !important; + &:active{ + cursor: $cursor-grabing !important; + } + } +} + diff --git a/app/src/utils/handleCanvasCursors.ts b/app/src/utils/handleCanvasCursors.ts new file mode 100644 index 0000000..72e2bd6 --- /dev/null +++ b/app/src/utils/handleCanvasCursors.ts @@ -0,0 +1,31 @@ +export const handleCanvasCursors = (name: string) => { + const canvas = document.getElementById('work-space-three-d-canvas'); + if (!canvas) return; + + const cursorMap: Record = { + 'draw-wall': 'draw', + 'draw-aisle': 'draw', + 'draw-zone': 'draw', + 'draw-floor': 'draw', + measure: 'measure', + delete: 'pointer', + pen: 'pen', + 'free-hand': 'hand', + // Add more mappings as needed + }; + + const validCursorClasses = new Set(Object.values(cursorMap)); + + // Remove previously applied cursor-related classes + canvas.classList.forEach((cls) => { + if (validCursorClasses.has(cls)) { + canvas.classList.remove(cls); + } + }); + + // Add the new cursor class + const newCursorClass = cursorMap[name]; + if (newCursorClass) { + canvas.classList.add(newCursorClass); + } +}; diff --git a/compose.yaml b/compose.yaml index 60ae66f..3697f47 100644 --- a/compose.yaml +++ b/compose.yaml @@ -4,13 +4,13 @@ services: context: ./app dockerfile: Dockerfile args: - - REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:7999 - - REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:4999 + - REACT_APP_SERVER_SOCKET_API_BASE_URL=185.100.212.76:9902 + - REACT_APP_SERVER_REST_API_BASE_URL=185.100.212.76:9901 - REACT_APP_SERVER_MARKETPLACE_URL=185.100.212.76:50011 - container_name: aalai-beta-Demo + container_name: aalaiDemoTwo stdin_open: true tty: true ports: - - "8300:80" + - "8400:80" volumes: - ./app:/app