diff --git a/app/src/components/layout/sidebarRight/Header.tsx b/app/src/components/layout/sidebarRight/Header.tsx index e51bf9d..6922314 100644 --- a/app/src/components/layout/sidebarRight/Header.tsx +++ b/app/src/components/layout/sidebarRight/Header.tsx @@ -45,7 +45,7 @@ const Header: React.FC = () => {
{user.userName[0]}
diff --git a/app/src/components/ui/ModuleToggle.tsx b/app/src/components/ui/ModuleToggle.tsx index b7421af..053e251 100644 --- a/app/src/components/ui/ModuleToggle.tsx +++ b/app/src/components/ui/ModuleToggle.tsx @@ -7,13 +7,11 @@ import { VisualizationIcon, } from "../icons/ExportModuleIcons"; import useToggleStore from "../../store/useUIToggleStore"; -import { useSelectedZoneStore } from "../../store/useZoneStore"; const ModuleToggle: React.FC = () => { const { activeModule, setActiveModule } = useModuleStore(); const { setToggleUI } = useToggleStore(); - return (
{ const { templates } = useTemplateStore(); - const [activeSubTool, setActiveSubTool] = useState("cursor"); + const { activeSubTool, setActiveSubTool } = useActiveSubTool(); const { toggleThreeD, setToggleThreeD } = useThreeDStore(); const { setToggleUI } = useToggleStore(); @@ -56,8 +57,7 @@ const Tools: React.FC = () => { const { widgets3D } = use3DWidget(); - const zones = useDroppedObjectsStore((state) => state.zones); - + const zones = useDroppedObjectsStore((state) => state.zones); // wall options const { toggleView, setToggleView } = useToggleView(); diff --git a/app/src/modules/builder/geomentries/lines/distanceText.tsx b/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx similarity index 89% rename from app/src/modules/builder/geomentries/lines/distanceText.tsx rename to app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx index 98e20a6..39162ad 100644 --- a/app/src/modules/builder/geomentries/lines/distanceText.tsx +++ b/app/src/modules/builder/geomentries/lines/distanceText/distanceText.tsx @@ -1,15 +1,15 @@ import { useEffect, useState } from "react"; -import { getLines } from "../../../../services/factoryBuilder/lines/getLinesApi"; +import { getLines } from "../../../../../services/factoryBuilder/lines/getLinesApi"; import * as THREE from "three"; import { useActiveLayer, useDeletedLines, useNewLines, useToggleView, -} from "../../../../store/store"; -import objectLinesToArray from "./lineConvertions/objectLinesToArray"; +} from "../../../../../store/store"; +import objectLinesToArray from "../lineConvertions/objectLinesToArray"; import { Html } from "@react-three/drei"; -import * as Types from "../../../../types/world/worldTypes"; +import * as Types from "../../../../../types/world/worldTypes"; const DistanceText = () => { const [lines, setLines] = useState< @@ -122,7 +122,7 @@ const DistanceText = () => { wrapperClass="distance-text-wrapper" className="distance-text" // other - zIndexRange={[100, 0]} + zIndexRange={[1, 0]} prepend sprite > diff --git a/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx b/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx new file mode 100644 index 0000000..039d30f --- /dev/null +++ b/app/src/modules/builder/geomentries/lines/distanceText/referenceDistanceText.tsx @@ -0,0 +1,71 @@ +import * as THREE from "three"; +import { Html } from "@react-three/drei"; +import { useState, useEffect } from "react"; +import { useActiveLayer } from "../../../../../store/store"; + +const ReferenceDistanceText = ({ line }: { line: any }) => { + interface TextState { + distance: string; + position: THREE.Vector3; + userData: any; + layer: any; + } + + const [text, setTexts] = useState(null); + const { activeLayer } = useActiveLayer(); + + useEffect(() => { + if (line) { + if (line.parent === null) { + setTexts(null); + return; + } + const distance = line.userData.linePoints.cursorPosition.distanceTo( + line.userData.linePoints.startPoint + ); + const midpoint = new THREE.Vector3() + .addVectors( + line.userData.linePoints.cursorPosition, + line.userData.linePoints.startPoint + ) + .divideScalar(2); + const newTexts = { + distance: distance.toFixed(1), + position: midpoint, + userData: line, + layer: activeLayer, + }; + setTexts(newTexts); + } + }); + + return ( + + + {text !== null && ( + +
+ {text.distance} m +
+ + )} +
+
+ ); +}; + +export default ReferenceDistanceText; diff --git a/app/src/modules/builder/geomentries/lines/referenceDistanceText.tsx b/app/src/modules/builder/geomentries/lines/referenceDistanceText.tsx deleted file mode 100644 index cc1ca30..0000000 --- a/app/src/modules/builder/geomentries/lines/referenceDistanceText.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import * as THREE from 'three'; -import { Html } from '@react-three/drei'; -import { useState, useEffect } from 'react'; -import { useActiveLayer } from '../../../../store/store'; - -const ReferenceDistanceText = ({ line }: { line: any }) => { - interface TextState { - distance: string; - position: THREE.Vector3; - userData: any; - layer: any; - } - - const [text, setTexts] = useState(null); - const { activeLayer } = useActiveLayer(); - - useEffect(() => { - if (line) { - if (line.parent === null) { - setTexts(null); - return; - } - const distance = line.userData.linePoints.cursorPosition.distanceTo(line.userData.linePoints.startPoint); - const midpoint = new THREE.Vector3().addVectors(line.userData.linePoints.cursorPosition, line.userData.linePoints.startPoint).divideScalar(2); - const newTexts = { - distance: distance.toFixed(1), - position: midpoint, - userData: line, - layer: activeLayer - }; - setTexts(newTexts); - } - }); - - return ( - - - {text !== null && - < Html transform sprite key={text.distance} userData={text.userData} scale={5} position={[text.position.x, 1, text.position.z]} style={{ pointerEvents: 'none' }}> -
{text.distance} m
- - } -
-
- ); -}; - -export default ReferenceDistanceText; \ No newline at end of file diff --git a/app/src/modules/builder/groups/zoneGroup.tsx b/app/src/modules/builder/groups/zoneGroup.tsx index 7eb82e1..d218534 100644 --- a/app/src/modules/builder/groups/zoneGroup.tsx +++ b/app/src/modules/builder/groups/zoneGroup.tsx @@ -69,10 +69,7 @@ const ZoneGroup: React.FC = () => { }, transparent: true, depthWrite: false, - blending: THREE.NormalBlending, - }), - [] - ); + }), []); useEffect(() => { const fetchZones = async () => { diff --git a/app/src/modules/collaboration/collabCams.tsx b/app/src/modules/collaboration/collabCams.tsx index a1383f2..95a9833 100644 --- a/app/src/modules/collaboration/collabCams.tsx +++ b/app/src/modules/collaboration/collabCams.tsx @@ -185,10 +185,9 @@ const CamModelsGroup = () => { position={[-0.015, 0, 0.7]} > diff --git a/app/src/modules/collaboration/collabUserIcon.tsx b/app/src/modules/collaboration/collabUserIcon.tsx index 4cea436..a8738ce 100644 --- a/app/src/modules/collaboration/collabUserIcon.tsx +++ b/app/src/modules/collaboration/collabUserIcon.tsx @@ -4,14 +4,12 @@ import CustomAvatar from "./users/Avatar"; interface CollabUserIconProps { userName: string; userImage?: string; - index?: number; color: string; } const CollabUserIcon: React.FC = ({ userImage, userName, - index = 0, color, }) => { return ( @@ -20,24 +18,7 @@ const CollabUserIcon: React.FC = ({ {userImage ? ( {userName} ) : ( - - //
- // {userName[0]} - //
+ )}
diff --git a/app/src/modules/collaboration/users/Avatar.tsx b/app/src/modules/collaboration/users/Avatar.tsx index b04ff7d..d3e5dca 100644 --- a/app/src/modules/collaboration/users/Avatar.tsx +++ b/app/src/modules/collaboration/users/Avatar.tsx @@ -5,7 +5,6 @@ import { getAvatarColor } from "./functions/getAvatarColor"; interface AvatarProps { name: string; // Name can be a full name or initials size?: number; - index?: number; textColor?: string; color?: string; // Optional color prop for future use } @@ -13,7 +12,6 @@ interface AvatarProps { const CustomAvatar: React.FC = ({ name, size = 100, - index = 0, textColor = "#ffffff", color, // Optional color prop for future use }) => { @@ -28,7 +26,7 @@ const CustomAvatar: React.FC = ({ const initials = getInitials(name); // Convert name to initials if needed // Draw background - ctx.fillStyle = color || getAvatarColor(index); // Use color prop or generate color based on index + ctx.fillStyle = color || "#323232"; // Use color prop or generate color based on index ctx.fillRect(0, 0, size, size); // Draw initials @@ -42,7 +40,7 @@ const CustomAvatar: React.FC = ({ const dataURL = canvas.toDataURL("image/png"); setImageSrc(dataURL); } - }, [name, size, textColor, index]); + }, [name, size, textColor]); if (!imageSrc) { return null; // Return null while the image is being generated @@ -55,18 +53,6 @@ const CustomAvatar: React.FC = ({ alt="User Avatar" style={{ width: "100%", height: "100%" }} /> - //
- // {name[0]} - //
); }; diff --git a/app/src/modules/collaboration/users/functions/getAvatarColor.ts b/app/src/modules/collaboration/users/functions/getAvatarColor.ts index 3deacca..f2bd816 100644 --- a/app/src/modules/collaboration/users/functions/getAvatarColor.ts +++ b/app/src/modules/collaboration/users/functions/getAvatarColor.ts @@ -1,26 +1,67 @@ const avatarColors: string[] = [ - "#FF5733", // Red Orange + "#FF5733", // Vivid Orange "#48ac2a", // Leaf Green - "#0050eb", // Royal Blue + "#0050eb", // Bright Blue "#FF33A1", // Hot Pink - "#FF8C33", // Deep Orange - "#8C33FF", // Violet - "#FF3333", // Bright Red + "#FF8C33", // Sunset Orange + "#8C33FF", // Violet Purple + "#FF3333", // Fiery Red "#43c06d", // Emerald Green - "#A133FF", // Amethyst Purple - "#C70039", // Crimson - "#900C3F", // Maroon - "#581845", // Plum - "#3498DB", // Sky Blue - "#2ECC71", // Green Mint - "#E74C3C", // Tomato Red - "#00adff", // Azure - "#DBAD05", // Amber Yellow - "#FF5733", // Red Orange - "#FF33A1", // Hot Pink - "#900C3F", // Maroon + "#A133FF", // Royal Purple + "#C70039", // Crimson Red + "#900C3F", // Deep Burgundy + "#581845", // Plum Purple + "#3859AD", // Steel Blue + "#08873E", // Forest Green + "#E74C3C", // Cherry Red + "#00adff", // Sky Blue + "#DBAD05", // Golden Yellow + "#A13E31", // Brick Red + "#94C40E", // Lime Green + "#060C47", // Midnight Blue + "#2FAFAF", // Teal ]; -export function getAvatarColor(index: number): string { +export function getAvatarColor(index: number, name?: string): string { + // Check if the color is already stored in localStorage + const localStorageKey = "userAvatarColors"; + // Helper function to check if local storage is available + function isLocalStorageAvailable(): boolean { + try { + const testKey = "__test__"; + localStorage.setItem(testKey, "test"); + localStorage.removeItem(testKey); + return true; + } catch (e) { + return false; + } + } + // Check if local storage is available + if (isLocalStorageAvailable() && name) { + let userColors = JSON.parse(localStorage.getItem(localStorageKey) || "{}"); + + // Check if the user already has an assigned color + if (userColors[name]) { + return userColors[name]; + } + + // Find a new color not already assigned + const usedColors = Object.values(userColors); + const availableColors = avatarColors.filter(color => !usedColors.includes(color)); + + // Assign a new color + const assignedColor = availableColors.length > 0 + ? availableColors[0] + : avatarColors[index % avatarColors.length]; + + userColors[name] = assignedColor; + + // Save back to local storage + localStorage.setItem(localStorageKey, JSON.stringify(userColors)); + + return assignedColor; + } + + // Fallback: Assign a color using the index if no name or local storage is unavailable return avatarColors[index % avatarColors.length]; } diff --git a/app/src/modules/scene/tools/measurementTool.tsx b/app/src/modules/scene/tools/measurementTool.tsx index f8054af..9f9fa03 100644 --- a/app/src/modules/scene/tools/measurementTool.tsx +++ b/app/src/modules/scene/tools/measurementTool.tsx @@ -1,190 +1,244 @@ -import * as THREE from 'three'; -import { useEffect, useRef, useState } from 'react'; -import { useThree, useFrame } from '@react-three/fiber'; -import { useToolMode } from '../../../store/store'; -import { Html } from '@react-three/drei'; +import * as THREE from "three"; +import { useEffect, useRef, useState } from "react"; +import { useThree, useFrame } from "@react-three/fiber"; +import { useToolMode } from "../../../store/store"; +import { Html } from "@react-three/drei"; const MeasurementTool = () => { - const { gl, raycaster, pointer, camera, scene } = useThree(); - const { toolMode } = useToolMode(); + const { gl, raycaster, pointer, camera, scene } = useThree(); + const { toolMode } = useToolMode(); - const [points, setPoints] = useState([]); - const [tubeGeometry, setTubeGeometry] = useState(null); - const groupRef = useRef(null); - const [startConePosition, setStartConePosition] = useState(null); - const [endConePosition, setEndConePosition] = useState(null); - const [startConeQuaternion, setStartConeQuaternion] = useState(new THREE.Quaternion()); - const [endConeQuaternion, setEndConeQuaternion] = useState(new THREE.Quaternion()); - const [coneSize, setConeSize] = useState({ radius: 0.2, height: 0.5 }); + const [points, setPoints] = useState([]); + const [tubeGeometry, setTubeGeometry] = useState( + null + ); + const groupRef = useRef(null); + const [startConePosition, setStartConePosition] = + useState(null); + const [endConePosition, setEndConePosition] = useState( + null + ); + const [startConeQuaternion, setStartConeQuaternion] = useState( + new THREE.Quaternion() + ); + const [endConeQuaternion, setEndConeQuaternion] = useState( + new THREE.Quaternion() + ); + const [coneSize, setConeSize] = useState({ radius: 0.2, height: 0.5 }); + const MIN_RADIUS = 0.001, + MAX_RADIUS = 0.1; + const MIN_CONE_RADIUS = 0.01, + MAX_CONE_RADIUS = 0.4; + const MIN_CONE_HEIGHT = 0.035, + MAX_CONE_HEIGHT = 2.0; - const MIN_RADIUS = 0.001, MAX_RADIUS = 0.1; - const MIN_CONE_RADIUS = 0.01, MAX_CONE_RADIUS = 0.4; - const MIN_CONE_HEIGHT = 0.035, MAX_CONE_HEIGHT = 2.0; + useEffect(() => { + const canvasElement = gl.domElement; + let drag = false; + let isLeftMouseDown = false; - useEffect(() => { - const canvasElement = gl.domElement; - let drag = false; - let isLeftMouseDown = false; - - const onMouseDown = () => { - isLeftMouseDown = true; - drag = false; - }; - - const onMouseUp = (evt: any) => { - isLeftMouseDown = false; - if (evt.button === 0 && !drag) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && !(intersect.object.type === "GridHelper")); - - if (intersects.length > 0) { - const intersectionPoint = intersects[0].point.clone(); - if (points.length < 2) { - setPoints([...points, intersectionPoint]); - } else { - setPoints([intersectionPoint]); - } - } - } - }; - - const onMouseMove = () => { - if (isLeftMouseDown) drag = true; - }; - - const onContextMenu = (evt: any) => { - evt.preventDefault(); - if (!drag) { - evt.preventDefault(); - setPoints([]); - setTubeGeometry(null); - } - }; - - if (toolMode === "MeasurementScale") { - canvasElement.addEventListener("pointerdown", onMouseDown); - canvasElement.addEventListener("pointermove", onMouseMove); - canvasElement.addEventListener("pointerup", onMouseUp); - canvasElement.addEventListener("contextmenu", onContextMenu); - } else { - resetMeasurement(); - setPoints([]); - } - - return () => { - canvasElement.removeEventListener("pointerdown", onMouseDown); - canvasElement.removeEventListener("pointermove", onMouseMove); - canvasElement.removeEventListener("pointerup", onMouseUp); - canvasElement.removeEventListener("contextmenu", onContextMenu); - }; - }, [toolMode, camera, raycaster, pointer, scene, points]); - - useFrame(() => { - if (points.length === 1) { - raycaster.setFromCamera(pointer, camera); - const intersects = raycaster.intersectObjects(scene.children, true).filter(intersect => !intersect.object.name.includes("Roof") && !intersect.object.name.includes("MeasurementReference") && !intersect.object.name.includes("agv-collider") && !(intersect.object.type === "GridHelper")); - - if (intersects.length > 0) { - updateMeasurement(points[0], intersects[0].point); - } - } else if (points.length === 2) { - updateMeasurement(points[0], points[1]); - } else { - resetMeasurement(); - } - }); - - const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => { - const distance = start.distanceTo(end); - - const radius = THREE.MathUtils.clamp(distance * 0.02, MIN_RADIUS, MAX_RADIUS); - const coneRadius = THREE.MathUtils.clamp(distance * 0.05, MIN_CONE_RADIUS, MAX_CONE_RADIUS); - const coneHeight = THREE.MathUtils.clamp(distance * 0.2, MIN_CONE_HEIGHT, MAX_CONE_HEIGHT); - - setConeSize({ radius: coneRadius, height: coneHeight }); - - const direction = new THREE.Vector3().subVectors(end, start).normalize(); - - const offset = direction.clone().multiplyScalar(coneHeight * 0.5); - - let tubeStart = start.clone().add(offset); - let tubeEnd = end.clone().sub(offset); - - tubeStart.y = Math.max(tubeStart.y, 0); - tubeEnd.y = Math.max(tubeEnd.y, 0); - - const curve = new THREE.CatmullRomCurve3([tubeStart, tubeEnd]); - setTubeGeometry(new THREE.TubeGeometry(curve, 20, radius, 8, false)); - - setStartConePosition(tubeStart); - setEndConePosition(tubeEnd); - setStartConeQuaternion(getArrowOrientation(start, end)); - setEndConeQuaternion(getArrowOrientation(end, start)); + const onMouseDown = () => { + isLeftMouseDown = true; + drag = false; }; - const resetMeasurement = () => { + const onMouseUp = (evt: any) => { + isLeftMouseDown = false; + if (evt.button === 0 && !drag) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("agv-collider") && + !(intersect.object.type === "GridHelper") + ); + + if (intersects.length > 0) { + const intersectionPoint = intersects[0].point.clone(); + if (points.length < 2) { + setPoints([...points, intersectionPoint]); + } else { + setPoints([intersectionPoint]); + } + } + } + }; + + const onMouseMove = () => { + if (isLeftMouseDown) drag = true; + }; + + const onContextMenu = (evt: any) => { + evt.preventDefault(); + if (!drag) { + evt.preventDefault(); + setPoints([]); setTubeGeometry(null); - setStartConePosition(null); - setEndConePosition(null); + } }; - const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => { - const direction = new THREE.Vector3().subVectors(end, start).normalize().negate(); - const quaternion = new THREE.Quaternion(); - quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction); - return quaternion; + if (toolMode === "MeasurementScale") { + canvasElement.addEventListener("pointerdown", onMouseDown); + canvasElement.addEventListener("pointermove", onMouseMove); + canvasElement.addEventListener("pointerup", onMouseUp); + canvasElement.addEventListener("contextmenu", onContextMenu); + } else { + resetMeasurement(); + setPoints([]); + } + + return () => { + canvasElement.removeEventListener("pointerdown", onMouseDown); + canvasElement.removeEventListener("pointermove", onMouseMove); + canvasElement.removeEventListener("pointerup", onMouseUp); + canvasElement.removeEventListener("contextmenu", onContextMenu); }; + }, [toolMode, camera, raycaster, pointer, scene, points]); - useEffect(() => { - if (points.length === 2) { - console.log(points[0].distanceTo(points[1])); - } - }, [points]) + useFrame(() => { + if (points.length === 1) { + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster + .intersectObjects(scene.children, true) + .filter( + (intersect) => + !intersect.object.name.includes("Roof") && + !intersect.object.name.includes("MeasurementReference") && + !intersect.object.name.includes("agv-collider") && + !(intersect.object.type === "GridHelper") + ); - return ( - - {startConePosition && ( - - - - - )} - {endConePosition && ( - - - - - )} - {tubeGeometry && ( - - - - )} + if (intersects.length > 0) { + updateMeasurement(points[0], intersects[0].point); + } + } else if (points.length === 2) { + updateMeasurement(points[0], points[1]); + } else { + resetMeasurement(); + } + }); - {startConePosition && endConePosition && ( - -
{startConePosition.distanceTo(endConePosition).toFixed(2)} m
- - )} -
+ const updateMeasurement = (start: THREE.Vector3, end: THREE.Vector3) => { + const distance = start.distanceTo(end); + + const radius = THREE.MathUtils.clamp( + distance * 0.02, + MIN_RADIUS, + MAX_RADIUS + ); + const coneRadius = THREE.MathUtils.clamp( + distance * 0.05, + MIN_CONE_RADIUS, + MAX_CONE_RADIUS + ); + const coneHeight = THREE.MathUtils.clamp( + distance * 0.2, + MIN_CONE_HEIGHT, + MAX_CONE_HEIGHT ); + setConeSize({ radius: coneRadius, height: coneHeight }); + + const direction = new THREE.Vector3().subVectors(end, start).normalize(); + + const offset = direction.clone().multiplyScalar(coneHeight * 0.5); + + let tubeStart = start.clone().add(offset); + let tubeEnd = end.clone().sub(offset); + + tubeStart.y = Math.max(tubeStart.y, 0); + tubeEnd.y = Math.max(tubeEnd.y, 0); + + const curve = new THREE.CatmullRomCurve3([tubeStart, tubeEnd]); + setTubeGeometry(new THREE.TubeGeometry(curve, 20, radius, 8, false)); + + setStartConePosition(tubeStart); + setEndConePosition(tubeEnd); + setStartConeQuaternion(getArrowOrientation(start, end)); + setEndConeQuaternion(getArrowOrientation(end, start)); + }; + + const resetMeasurement = () => { + setTubeGeometry(null); + setStartConePosition(null); + setEndConePosition(null); + }; + + const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => { + const direction = new THREE.Vector3() + .subVectors(end, start) + .normalize() + .negate(); + const quaternion = new THREE.Quaternion(); + quaternion.setFromUnitVectors(new THREE.Vector3(0, 1, 0), direction); + return quaternion; + }; + + useEffect(() => { + if (points.length === 2) { + console.log(points[0].distanceTo(points[1])); + } + }, [points]); + + return ( + + {startConePosition && ( + + + + + )} + {endConePosition && ( + + + + + )} + {tubeGeometry && ( + + + + )} + + {startConePosition && endConePosition && ( + +
+ {startConePosition.distanceTo(endConePosition).toFixed(2)} m +
+ + )} +
+ ); }; export default MeasurementTool; diff --git a/app/src/modules/scene/world/world.tsx b/app/src/modules/scene/world/world.tsx index 678d1a4..df7f9c0 100644 --- a/app/src/modules/scene/world/world.tsx +++ b/app/src/modules/scene/world/world.tsx @@ -6,8 +6,8 @@ import { useThree, useFrame } from "@react-three/fiber"; ////////// Component Imports ////////// -import DistanceText from "../../builder/geomentries/lines/distanceText"; -import ReferenceDistanceText from "../../builder/geomentries/lines/referenceDistanceText"; +import DistanceText from "../../builder/geomentries/lines/distanceText/distanceText"; +import ReferenceDistanceText from "../../builder/geomentries/lines/distanceText/referenceDistanceText"; ////////// Assests Imports ////////// diff --git a/app/src/pages/Project.tsx b/app/src/pages/Project.tsx index 54fe61f..ac05db8 100644 --- a/app/src/pages/Project.tsx +++ b/app/src/pages/Project.tsx @@ -22,17 +22,17 @@ import LoadingPage from "../components/templates/LoadingPage"; import SimulationPlayer from "../components/ui/simulation/simulationPlayer"; import RenderOverlay from "../components/templates/Overlay"; import MenuBar from "../components/ui/menu/menu"; +import KeyPressListener from "../utils/shortcutkeys/handleShortcutKeys"; const Project: React.FC = () => { let navigate = useNavigate(); const { activeModule } = useModuleStore(); - const { loadingProgress, setLoadingProgress } = useLoadingProgress(); + const { loadingProgress } = useLoadingProgress(); const { setUserName } = useUserName(); const { setOrganization } = useOrganization(); const { setFloorItems } = useFloorItems(); const { setWallItems } = useWallItems(); const { setZones } = useZones(); - const [openMenu, setOpenMenu] = useState(true); useEffect(() => { setFloorItems([]); @@ -56,6 +56,7 @@ const Project: React.FC = () => { return (
+ {loadingProgress && } {!isPlaying && ( <> diff --git a/app/src/store/store.ts b/app/src/store/store.ts index d39e8e1..57fd6a6 100644 --- a/app/src/store/store.ts +++ b/app/src/store/store.ts @@ -248,6 +248,11 @@ export const useActiveTool = create((set: any) => ({ setActiveTool: (x: any) => set({ activeTool: x }), })); +export const useActiveSubTool = create((set: any) => ({ + activeSubTool: "cursor", + setActiveSubTool: (x: any) => set({ activeSubTool: x }), +})); + export const use2DUndoRedo = create((set: any) => ({ is2DUndoRedo: null, set2DUndoRedo: (x: any) => set({ is2DUndoRedo: x }), diff --git a/app/src/styles/layout/popup.scss b/app/src/styles/layout/popup.scss index 9d22e64..a354c10 100644 --- a/app/src/styles/layout/popup.scss +++ b/app/src/styles/layout/popup.scss @@ -146,5 +146,6 @@ font-size: var(--font-size-regulaar); font-weight: var(--font-size-regulaar); text-transform: capitalize; + white-space: nowrap; } } diff --git a/app/src/styles/pages/realTimeViz.scss b/app/src/styles/pages/realTimeViz.scss index aa07fe4..19cd2b4 100644 --- a/app/src/styles/pages/realTimeViz.scss +++ b/app/src/styles/pages/realTimeViz.scss @@ -68,17 +68,16 @@ display: flex; background-color: var(--background-color); position: absolute; - bottom: 10px; + bottom: 0px; left: 50%; gap: 6px; - border-radius: 8px; max-width: 80%; overflow: auto; max-width: calc(100% - 500px); min-width: 150px; z-index: 3; - transform: translate(-50%, -0%); + transform: translate(-50%, -10%); &::-webkit-scrollbar { display: none; diff --git a/app/src/styles/scene/scene.scss b/app/src/styles/scene/scene.scss index 8fdb90f..fc3b824 100644 --- a/app/src/styles/scene/scene.scss +++ b/app/src/styles/scene/scene.scss @@ -6,9 +6,9 @@ } .distance-text { pointer-events: none !important; - .distance { + div { position: absolute; - transform: translate(-50%, -50%) scale(.8); + transform: translate(-50%, -50%) scale(0.8); pointer-events: none !important; white-space: nowrap; // style @@ -22,6 +22,6 @@ } } -.pointer-none{ +.pointer-none { pointer-events: none; } diff --git a/app/src/utils/shortcutkeys/handleShortcutKeys.ts b/app/src/utils/shortcutkeys/handleShortcutKeys.ts new file mode 100644 index 0000000..73388d5 --- /dev/null +++ b/app/src/utils/shortcutkeys/handleShortcutKeys.ts @@ -0,0 +1,187 @@ +// Importing React and useEffect from React library +import React, { useEffect } from "react"; +// Importing the necessary hooks and types from React and TypeScript +import useModuleStore, { useThreeDStore } from "../../store/useModuleStore"; +import useToggleStore from "../../store/useUIToggleStore"; +import { useActiveSubTool, useActiveTool, useAddAction, useDeleteTool, useSelectedWallItem, useToggleView, useToolMode } from "../../store/store"; +import { usePlayButtonStore } from "../../store/usePlayButtonStore"; + +const KeyPressListener: React.FC = () => { + // Function to detect if Shift, Ctrl, Alt, or combinations are pressed + const detectModifierKeys = (event: KeyboardEvent): string => { + const modifiers = [ + event.ctrlKey ? "Ctrl" : "", + event.altKey ? "Alt" : "", + event.shiftKey ? "Shift" : "", + event.metaKey ? "Meta" : "" // Add support for Command/Win key + ].filter(Boolean); + + // Ignore modifier keys when they're pressed alone + const isModifierKey = [ + "Control", "Shift", "Alt", "Meta", + "Ctrl", "AltGraph", "OS" // Additional modifier key aliases + ].includes(event.key); + + const mainKey = isModifierKey ? "" : event.key.toUpperCase(); + + // Handle special cases for keys with different representations + const normalizedKey = mainKey === " " ? "Space" : mainKey; + + // Build the combination string + if (modifiers.length > 0 && normalizedKey) { + return `${modifiers.join("+")}+${normalizedKey}`; + } else if (modifiers.length > 0) { + return modifiers.join("+"); + } else { + return normalizedKey; + } + }; + + // Importing the necessary hooks from the store + const { activeModule, setActiveModule } = useModuleStore(); + const { setActiveSubTool } = useActiveSubTool(); + const { toggleUI, setToggleUI } = useToggleStore(); + const { setToggleThreeD } = useThreeDStore(); + const { setToolMode } = useToolMode(); + const { setIsPlaying } = usePlayButtonStore(); + + // wall options + const { toggleView, setToggleView } = useToggleView(); + const { setDeleteTool } = useDeleteTool(); + const { setAddAction } = useAddAction(); + const { setSelectedWallItem } = useSelectedWallItem(); + const { setActiveTool } = useActiveTool(); + + useEffect(() => { + // Function to handle keydown events + const handleKeyPress = (event: KeyboardEvent) => { + // Allow default behavior for F5 and F12 + const keyCombination = detectModifierKeys(event); + + if (["F5", "F11", "F12"].includes(event.key) || keyCombination === "Ctrl+R") { + return; + } + + // Prevent default action for the key press + event.preventDefault(); + + // Detect the key combination pressed + if (keyCombination) { + // Check for specific key combinations to switch modules + if (keyCombination === "1" && !toggleView) { + setActiveTool("cursor"); + setActiveSubTool("cursor"); + setActiveModule("builder"); + } + if (keyCombination === "2" && !toggleView) { + setActiveTool("cursor"); + setActiveSubTool("cursor"); + setActiveModule("simulation"); + } + if (keyCombination === "3" && !toggleView) { + setActiveTool("cursor"); + setActiveSubTool("cursor"); + setActiveModule("visualization"); + } + if (keyCombination === "4" && !toggleView) { + setActiveTool("cursor"); + setActiveSubTool("cursor"); + setToggleUI(false); + setActiveModule("market"); + } + + // sidebar toggle + if (keyCombination === "Ctrl+." && activeModule !== "market") { + setToggleUI(!toggleUI); + } + + // tools toggle + if (keyCombination === "V") { + setActiveTool("cursor"); + setActiveSubTool("cursor"); + } + if (keyCombination === "X") { + setActiveTool("delete"); + setActiveSubTool("delete"); + } + if (keyCombination === "H") { + setActiveTool("free-hand"); + setActiveSubTool("free-hand"); + } + + // player toggle + if (keyCombination === "Ctrl+P" && !toggleView) { + setIsPlaying(true); + } + + // builder key combination + if (activeModule === "builder") { + if (keyCombination === "TAB") { + if (toggleView) { + setToggleView(false); + setToggleThreeD(true); + setActiveTool("cursor"); + } else { + setSelectedWallItem(null); + setDeleteTool(false); + setAddAction(null); + setToggleView(true); + setToggleThreeD(false); + setActiveTool("cursor"); + } + } + // builder tools + if (toggleView) { + if (keyCombination === "Q" || keyCombination === "6") { + setActiveTool("draw-wall"); + setToolMode("Wall"); + } + if (keyCombination === "R" || keyCombination === "7") { + setActiveTool("draw-aisle"); + setToolMode("Aisle"); + } + if (keyCombination === "E" || keyCombination === "8") { + setActiveTool("draw-zone"); + setToolMode("Zone"); + } + if (keyCombination === "T" || keyCombination === "9") { + setActiveTool("draw-floor"); + setToolMode("Floor"); + } + } else { + if (keyCombination === "M") { + setActiveTool("measure"); + setToolMode("MeasurementScale"); + } + } + } + + // Undo redo + if (keyCombination === "Ctrl+Z") { + // Handle undo action here + } + if (keyCombination === "Ctrl+Y" || keyCombination === "Ctrl+Shift+Z") { + // Handle redo action here + } + + // cleanup function to remove event listener + if (keyCombination === "ESCAPE") { + setActiveTool("cursor"); + setIsPlaying(false); + } + } + }; + + // Add event listener for keydown + window.addEventListener("keydown", handleKeyPress); + + // Cleanup function to remove event listener + return () => { + window.removeEventListener("keydown", handleKeyPress); + }; + }, [activeModule, toggleUI, toggleView]); // Empty dependency array ensures this runs only once + + return null; // No UI component to render, so return null +}; + +export default KeyPressListener;