merged to main
This commit is contained in:
commit
c953c71f3f
|
@ -45,7 +45,7 @@ const Header: React.FC = () => {
|
|||
<div
|
||||
key={index}
|
||||
className="user-profile"
|
||||
style={{ background: getAvatarColor(index) }}
|
||||
style={{ background: getAvatarColor(index, user.userName) }}
|
||||
>
|
||||
{user.userName[0]}
|
||||
</div>
|
||||
|
|
|
@ -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 (
|
||||
<div className="module-toggle-container">
|
||||
<div
|
||||
|
|
|
@ -31,6 +31,7 @@ import {
|
|||
useToggleView,
|
||||
useToolMode,
|
||||
useTransformMode,
|
||||
useActiveSubTool,
|
||||
} from "../../store/store";
|
||||
import useToggleStore from "../../store/useUIToggleStore";
|
||||
import {
|
||||
|
@ -41,7 +42,7 @@ import {
|
|||
|
||||
const Tools: React.FC = () => {
|
||||
const { templates } = useTemplateStore();
|
||||
const [activeSubTool, setActiveSubTool] = useState("cursor");
|
||||
const { activeSubTool, setActiveSubTool } = useActiveSubTool();
|
||||
const { toggleThreeD, setToggleThreeD } = useThreeDStore();
|
||||
const { setToggleUI } = useToggleStore();
|
||||
|
||||
|
@ -58,7 +59,6 @@ const Tools: React.FC = () => {
|
|||
|
||||
const zones = useDroppedObjectsStore((state) => state.zones);
|
||||
|
||||
|
||||
// wall options
|
||||
const { toggleView, setToggleView } = useToggleView();
|
||||
const { setDeleteTool } = useDeleteTool();
|
||||
|
|
|
@ -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
|
||||
>
|
|
@ -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<TextState | null>(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 (
|
||||
<group name="Reference_Distance_Text">
|
||||
<mesh>
|
||||
{text !== null && (
|
||||
<Html
|
||||
// data
|
||||
key={text.distance}
|
||||
userData={text.userData}
|
||||
position={[text.position.x, 1, text.position.z]}
|
||||
// class
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
// other
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
sprite
|
||||
>
|
||||
<div
|
||||
className={`Reference_Distance line-${text.userData.userData}`}
|
||||
>
|
||||
{text.distance} m
|
||||
</div>
|
||||
</Html>
|
||||
)}
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReferenceDistanceText;
|
|
@ -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<TextState | null>(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 (
|
||||
<group name='Reference_Distance_Text'>
|
||||
<mesh>
|
||||
{text !== null &&
|
||||
< Html transform sprite key={text.distance} userData={text.userData} scale={5} position={[text.position.x, 1, text.position.z]} style={{ pointerEvents: 'none' }}>
|
||||
<div className={`Reference_Distance line-${text.userData.userData}`}>{text.distance} m</div>
|
||||
</Html>
|
||||
}
|
||||
</mesh>
|
||||
</group >
|
||||
);
|
||||
};
|
||||
|
||||
export default ReferenceDistanceText;
|
|
@ -69,10 +69,7 @@ const ZoneGroup: React.FC = () => {
|
|||
},
|
||||
transparent: true,
|
||||
depthWrite: false,
|
||||
blending: THREE.NormalBlending,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
}), []);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchZones = async () => {
|
||||
|
|
|
@ -185,10 +185,9 @@ const CamModelsGroup = () => {
|
|||
position={[-0.015, 0, 0.7]}
|
||||
>
|
||||
<CollabUserIcon
|
||||
userImage={""}
|
||||
userImage={cam.userData.userImage ||""}
|
||||
userName={cam.userData.userName}
|
||||
index={index}
|
||||
color={getAvatarColor(index)}
|
||||
color={getAvatarColor(index, cam.userData.userName)}
|
||||
/>
|
||||
</Html>
|
||||
</primitive>
|
||||
|
|
|
@ -4,14 +4,12 @@ import CustomAvatar from "./users/Avatar";
|
|||
interface CollabUserIconProps {
|
||||
userName: string;
|
||||
userImage?: string;
|
||||
index?: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
const CollabUserIcon: React.FC<CollabUserIconProps> = ({
|
||||
userImage,
|
||||
userName,
|
||||
index = 0,
|
||||
color,
|
||||
}) => {
|
||||
return (
|
||||
|
@ -20,24 +18,7 @@ const CollabUserIcon: React.FC<CollabUserIconProps> = ({
|
|||
{userImage ? (
|
||||
<img className="user-image" src={userImage} alt={userName} />
|
||||
) : (
|
||||
<CustomAvatar name={userName} index={index} color={color} />
|
||||
// <div
|
||||
// className="user-image"
|
||||
// style={{
|
||||
// lineHeight: "30px",
|
||||
// textTransform: "uppercase",
|
||||
// textAlign: "center",
|
||||
// fontSize: "16px",
|
||||
// borderRadius: "50%",
|
||||
// backgroundColor: color,
|
||||
// overflow: "hidden",
|
||||
// backgroundSize: "cover",
|
||||
// backgroundPosition: "center",
|
||||
// color: "white",
|
||||
// fontWeight: "bold",
|
||||
// }}>
|
||||
// {userName[0]}
|
||||
// </div>
|
||||
<CustomAvatar name={userName} color={color} />
|
||||
)}
|
||||
</div>
|
||||
<div className="user-name" style={{ backgroundColor: color }}>
|
||||
|
|
|
@ -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<AvatarProps> = ({
|
||||
name,
|
||||
size = 100,
|
||||
index = 0,
|
||||
textColor = "#ffffff",
|
||||
color, // Optional color prop for future use
|
||||
}) => {
|
||||
|
@ -28,7 +26,7 @@ const CustomAvatar: React.FC<AvatarProps> = ({
|
|||
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<AvatarProps> = ({
|
|||
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<AvatarProps> = ({
|
|||
alt="User Avatar"
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
/>
|
||||
// <div
|
||||
// className="user-image"
|
||||
// style={{
|
||||
// width: size,
|
||||
// height: size,
|
||||
// borderRadius: "50%",
|
||||
// overflow: "hidden",
|
||||
// backgroundSize: "cover",
|
||||
// backgroundPosition: "center",
|
||||
// }}>
|
||||
// {name[0]}
|
||||
// </div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -1,26 +1,37 @@
|
|||
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 [points, setPoints] = useState<THREE.Vector3[]>([]);
|
||||
const [tubeGeometry, setTubeGeometry] = useState<THREE.TubeGeometry | null>(null);
|
||||
const [tubeGeometry, setTubeGeometry] = useState<THREE.TubeGeometry | null>(
|
||||
null
|
||||
);
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const [startConePosition, setStartConePosition] = useState<THREE.Vector3 | null>(null);
|
||||
const [endConePosition, setEndConePosition] = useState<THREE.Vector3 | null>(null);
|
||||
const [startConeQuaternion, setStartConeQuaternion] = useState(new THREE.Quaternion());
|
||||
const [endConeQuaternion, setEndConeQuaternion] = useState(new THREE.Quaternion());
|
||||
const [startConePosition, setStartConePosition] =
|
||||
useState<THREE.Vector3 | null>(null);
|
||||
const [endConePosition, setEndConePosition] = useState<THREE.Vector3 | null>(
|
||||
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;
|
||||
|
@ -36,7 +47,15 @@ const MeasurementTool = () => {
|
|||
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"));
|
||||
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();
|
||||
|
@ -83,7 +102,15 @@ const MeasurementTool = () => {
|
|||
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"));
|
||||
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);
|
||||
|
@ -98,9 +125,21 @@ const MeasurementTool = () => {
|
|||
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);
|
||||
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 });
|
||||
|
||||
|
@ -130,7 +169,10 @@ const MeasurementTool = () => {
|
|||
};
|
||||
|
||||
const getArrowOrientation = (start: THREE.Vector3, end: THREE.Vector3) => {
|
||||
const direction = new THREE.Vector3().subVectors(end, start).normalize().negate();
|
||||
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;
|
||||
|
@ -140,51 +182,63 @@ const MeasurementTool = () => {
|
|||
if (points.length === 2) {
|
||||
console.log(points[0].distanceTo(points[1]));
|
||||
}
|
||||
}, [points])
|
||||
}, [points]);
|
||||
|
||||
return (
|
||||
<group ref={groupRef} name="MeasurementGroup">
|
||||
{startConePosition && (
|
||||
<mesh name='MeasurementReference' position={startConePosition} quaternion={startConeQuaternion}>
|
||||
<mesh
|
||||
name="MeasurementReference"
|
||||
position={startConePosition}
|
||||
quaternion={startConeQuaternion}
|
||||
>
|
||||
<coneGeometry args={[coneSize.radius, coneSize.height, 16]} />
|
||||
<meshBasicMaterial color="yellow" />
|
||||
</mesh>
|
||||
)}
|
||||
{endConePosition && (
|
||||
<mesh name='MeasurementReference' position={endConePosition} quaternion={endConeQuaternion}>
|
||||
<mesh
|
||||
name="MeasurementReference"
|
||||
position={endConePosition}
|
||||
quaternion={endConeQuaternion}
|
||||
>
|
||||
<coneGeometry args={[coneSize.radius, coneSize.height, 16]} />
|
||||
<meshBasicMaterial color="yellow" />
|
||||
</mesh>
|
||||
)}
|
||||
{tubeGeometry && (
|
||||
<mesh name='MeasurementReference' geometry={tubeGeometry}>
|
||||
<mesh name="MeasurementReference" geometry={tubeGeometry}>
|
||||
<meshBasicMaterial color="yellow" />
|
||||
</mesh>
|
||||
)}
|
||||
|
||||
{startConePosition && endConePosition && (
|
||||
<Html
|
||||
as="div"
|
||||
center
|
||||
scale={THREE.MathUtils.clamp(
|
||||
startConePosition.distanceTo(endConePosition) * 0.25,
|
||||
0,
|
||||
10
|
||||
)}
|
||||
position={[
|
||||
(startConePosition.x + endConePosition.x) / 2,
|
||||
(startConePosition.y + endConePosition.y) / 2,
|
||||
(startConePosition.z + endConePosition.z) / 2,
|
||||
]}
|
||||
// class
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
// other
|
||||
zIndexRange={[1, 0]}
|
||||
style={{
|
||||
padding: "10px",
|
||||
color: "white",
|
||||
borderRadius: "8px",
|
||||
textAlign: "center",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
}}
|
||||
transform
|
||||
prepend
|
||||
sprite
|
||||
scale={THREE.MathUtils.clamp(startConePosition.distanceTo(endConePosition) * 0.25, 0, 10)}
|
||||
position={[(startConePosition.x + endConePosition.x) / 2, (startConePosition.y + endConePosition.y) / 2, (startConePosition.z + endConePosition.z) / 2]}
|
||||
>
|
||||
<div style={{ color: "black" }} >{startConePosition.distanceTo(endConePosition).toFixed(2)} m</div>
|
||||
<div>
|
||||
{startConePosition.distanceTo(endConePosition).toFixed(2)} m
|
||||
</div>
|
||||
</Html>
|
||||
)}
|
||||
</group>
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
export default MeasurementTool;
|
||||
|
|
|
@ -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 //////////
|
||||
|
||||
|
|
|
@ -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<boolean>(true);
|
||||
|
||||
useEffect(() => {
|
||||
setFloorItems([]);
|
||||
|
@ -56,6 +56,7 @@ const Project: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="project-main">
|
||||
<KeyPressListener />
|
||||
{loadingProgress && <LoadingPage progress={loadingProgress} />}
|
||||
{!isPlaying && (
|
||||
<>
|
||||
|
|
|
@ -248,6 +248,11 @@ export const useActiveTool = create<any>((set: any) => ({
|
|||
setActiveTool: (x: any) => set({ activeTool: x }),
|
||||
}));
|
||||
|
||||
export const useActiveSubTool = create<any>((set: any) => ({
|
||||
activeSubTool: "cursor",
|
||||
setActiveSubTool: (x: any) => set({ activeSubTool: x }),
|
||||
}));
|
||||
|
||||
export const use2DUndoRedo = create<any>((set: any) => ({
|
||||
is2DUndoRedo: null,
|
||||
set2DUndoRedo: (x: any) => set({ is2DUndoRedo: x }),
|
||||
|
|
|
@ -146,5 +146,6 @@
|
|||
font-size: var(--font-size-regulaar);
|
||||
font-weight: var(--font-size-regulaar);
|
||||
text-transform: capitalize;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
Loading…
Reference in New Issue