Refactor distance finding controls and move controls for improved functionality and performance

- Updated `getRoomsFromLines.ts` to filter valid line features before polygonization.
- Enhanced `distanceFindingControls.tsx` to streamline measurement line updates and improve text label rendering.
- Refactored `moveControls.tsx` to optimize event handling and asset movement logic, ensuring smoother interactions.
- Adjusted `selectionControls.tsx` to safely parse stored floor items from local storage.
- Commented out unused imports in `roboticArmInstances.tsx` for cleaner code.
- Modified `polygonGenerator.tsx` to ensure only valid line features are processed for polygon generation.
This commit is contained in:
Vishnu 2025-05-10 18:12:04 +05:30
parent 32b3247765
commit 87d1c4ae12
6 changed files with 622 additions and 526 deletions

View File

@ -27,7 +27,12 @@ async function getRoomsFromLines(lines: Types.RefLines) {
}))
);
const lineFeatures = result.map(line => turf.lineString(line.map(p => p.position)));
const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
const validLineFeatures = lineFeatures.filter((line) => {
const coords = line.geometry.coordinates;
return coords.length >= 4;
});
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures));
let union: any[] = [];

View File

@ -1,253 +1,270 @@
import React, { useEffect, useRef } from "react";
import { Vector3, Raycaster, BufferGeometry, LineBasicMaterial, Line, Object3D, Mesh } from "three";
import React, { useRef } from "react";
import {
Vector3,
Raycaster,
BufferGeometry,
LineBasicMaterial,
Line,
Mesh,
Group,
} from "three";
import { useThree, useFrame } from "@react-three/fiber";
import { Group } from "three";
import { Html } from "@react-three/drei";
import * as THREE from "three";
interface DistanceFindingControlsProps {
boundingBoxRef: React.RefObject<Mesh>;
boundingBoxRef: React.RefObject<Mesh>;
object: number;
}
const DistanceFindingControls = ({ boundingBoxRef }: DistanceFindingControlsProps) => {
const { camera, scene } = useThree();
const DistanceFindingControls = ({
boundingBoxRef,
object,
}: DistanceFindingControlsProps) => {
const { camera, scene } = useThree();
// Refs for measurement lines
const line1 = useRef<Line>(null);
const line2 = useRef<Line>(null);
const line3 = useRef<Line>(null);
const line4 = useRef<Line>(null);
const line5 = useRef<Line>(null);
// Refs for measurement lines
const line1 = useRef<Line>(null);
const line2 = useRef<Line>(null);
const line3 = useRef<Line>(null);
const line4 = useRef<Line>(null);
const line5 = useRef<Line>(null);
// Refs for measurement text labels
const textPosX = useRef<Group>(null);
const textNegX = useRef<Group>(null);
const textPosZ = useRef<Group>(null);
const textNegZ = useRef<Group>(null);
const textPosY = useRef<Group>(null);
// Refs for measurement text labels
const textPosX = useRef<Group>(null);
const textNegX = useRef<Group>(null);
const textPosZ = useRef<Group>(null);
const textNegZ = useRef<Group>(null);
const textPosY = useRef<Group>(null);
// Store line geometries to avoid recreation
const lineGeometries = useRef({
posX: new BufferGeometry(),
negX: new BufferGeometry(),
posZ: new BufferGeometry(),
negZ: new BufferGeometry(),
posY: new BufferGeometry(),
});
// Store line geometries to avoid recreation
const lineGeometries = useRef({
posX: new BufferGeometry(),
negX: new BufferGeometry(),
posZ: new BufferGeometry(),
negZ: new BufferGeometry(),
posY: new BufferGeometry()
});
useFrame(() => {
if (!boundingBoxRef?.current) return;
boundingBoxRef.current.geometry.computeBoundingBox();
const bbox = boundingBoxRef.current.geometry.boundingBox;
useFrame(() => {
if (!boundingBoxRef?.current) return;
if (!bbox) return;
boundingBoxRef.current.geometry.computeBoundingBox();
const bbox = boundingBoxRef.current.geometry.boundingBox;
if (!bbox) return;
const size = {
x: bbox.max.x - bbox.min.x,
y: bbox.max.y - bbox.min.y,
z: bbox.max.z - bbox.min.z
};
const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone();
if (!vec) return;
updateLine({
line: line1.current,
geometry: lineGeometries.current.posX,
direction: new Vector3(1, 0, 0), // Positive X
angle: 'pos',
mesh: textPosX,
vec,
size
});
updateLine({
line: line2.current,
geometry: lineGeometries.current.negX,
direction: new Vector3(-1, 0, 0), // Negative X
angle: 'neg',
mesh: textNegX,
vec,
size
});
updateLine({
line: line3.current,
geometry: lineGeometries.current.posZ,
direction: new Vector3(0, 0, 1), // Positive Z
angle: 'pos',
mesh: textPosZ,
vec,
size
});
updateLine({
line: line4.current,
geometry: lineGeometries.current.negZ,
direction: new Vector3(0, 0, -1), // Negative Z
angle: 'neg',
mesh: textNegZ,
vec,
size
});
updateLine({
line: line5.current,
geometry: lineGeometries.current.posY,
direction: new Vector3(0, -1, 0), // Down (Y)
angle: 'posY',
mesh: textPosY,
vec,
size
});
});
const updateLine = ({
line,
geometry,
direction,
angle,
mesh,
vec,
size
}: {
line: Line | null,
geometry: BufferGeometry,
direction: Vector3,
angle: string,
mesh: React.RefObject<Group>,
vec: Vector3,
size: { x: number, y: number, z: number }
}) => {
if (!line) return;
const points = [];
if (angle === "pos") {
points[0] = new Vector3(vec.x, vec.y, vec.z).add(
new Vector3((direction.x * size.x) / 2, 0, (direction.z * size.z) / 2)
);
} else if (angle === "neg") {
points[0] = new Vector3(vec.x, vec.y, vec.z).sub(
new Vector3((-direction.x * size.x) / 2, 0, (-direction.z * size.z) / 2)
);
} else if (angle === "posY") {
points[0] = new Vector3(vec.x, vec.y, vec.z).sub(
new Vector3(0, size.y / 2, 0)
);
}
const ray = new Raycaster();
if (camera) ray.camera = camera;
ray.set(new Vector3(vec.x, vec.y, vec.z), direction);
ray.params.Line.threshold = 0.1;
// Find intersection points
const wallsGroup = scene.children.find(val => val?.name.includes("Walls"));
const intersects = wallsGroup ? ray.intersectObjects([wallsGroup], true) : [];
// Find intersection point
if (intersects[0]) {
for (const intersect of intersects) {
if (intersect.object.name.includes("Wall")) {
points[1] = angle !== "posY" ? intersect.point : new Vector3(vec.x, 0, vec.z); // Floor
break;
}
}
}
// Update line geometry
if (points[1]) {
geometry.dispose();
geometry.setFromPoints([points[0], points[1]]);
line.geometry = geometry;
// Update measurement text
if (mesh?.current) {
geometry.computeBoundingSphere();
const center = geometry.boundingSphere?.center;
if (center) {
mesh.current.position.copy(center);
}
const label = document.getElementById(mesh.current.name);
if (label) {
label.innerText = `${points[0].distanceTo(points[1]).toFixed(2)}m`;
}
}
} else {
// No intersection found - clear the line
geometry.dispose();
geometry.setFromPoints([new Vector3(), new Vector3()]);
line.geometry = geometry;
const label = document.getElementById(mesh?.current?.name || "");
if (label) label.innerText = "";
}
const size = {
x: bbox.max.x - bbox.min.x,
y: bbox.max.y - bbox.min.y,
z: bbox.max.z - bbox.min.z,
};
const Material = new LineBasicMaterial({ color: "red" });
const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone();
return (
<>
{/* Measurement text labels */}
{boundingBoxRef.current && (
<>
<group name="textPosX" ref={textPosX}>
<Html zIndexRange={[1, 0]} style={{ pointerEvents: 'none' }}>
<div className="distance-label" id="textPosX" style={{
background: 'rgba(0,0,0,0.7)',
color: 'white',
padding: '2px 5px',
borderRadius: '3px',
fontSize: '12px',
whiteSpace: 'nowrap'
}}></div>
</Html>
</group>
<group name="textNegX" ref={textNegX}>
<Html zIndexRange={[1, 0]} style={{ pointerEvents: 'none' }}>
<div className="distance-label" id="textNegX" style={{
background: 'rgba(0,0,0,0.7)',
color: 'white',
padding: '2px 5px',
borderRadius: '3px',
fontSize: '12px',
whiteSpace: 'nowrap'
}}></div>
</Html>
</group>
<group name="textPosZ" ref={textPosZ}>
<Html zIndexRange={[2, 0]} style={{ pointerEvents: 'none' }}>
<div className="distance-label" id="textPosZ" style={{
background: 'rgba(0,0,0,0.7)',
color: 'white',
padding: '2px 5px',
borderRadius: '3px',
fontSize: '12px',
whiteSpace: 'nowrap'
}}></div>
</Html>
</group>
<group name="textNegZ" ref={textNegZ}>
<Html zIndexRange={[1, 0]} style={{ pointerEvents: 'none' }}>
<div className="distance-label" id="textNegZ" style={{
background: 'rgba(0,0,0,0.7)',
color: 'white',
padding: '2px 5px',
borderRadius: '3px',
fontSize: '12px',
whiteSpace: 'nowrap'
}}></div>
</Html>
</group>
if (!vec) return;
updateLine({
line: line1.current,
geometry: lineGeometries.current.posX,
direction: new Vector3(1, 0, 0), // Positive X
angle: "pos",
mesh: textPosX,
vec,
size,
});
updateLine({
line: line2.current,
geometry: lineGeometries.current.negX,
direction: new Vector3(-1, 0, 0), // Negative X
angle: "neg",
mesh: textNegX,
vec,
size,
});
updateLine({
line: line3.current,
geometry: lineGeometries.current.posZ,
direction: new Vector3(0, 0, 1), // Positive Z
angle: "pos",
mesh: textPosZ,
vec,
size,
});
updateLine({
line: line4.current,
geometry: lineGeometries.current.negZ,
direction: new Vector3(0, 0, -1), // Negative Z
angle: "neg",
mesh: textNegZ,
vec,
size,
});
updateLine({
line: line5.current,
geometry: lineGeometries.current.posY,
direction: new Vector3(0, -1, 0), // Down (Y)
angle: "posY",
mesh: textPosY,
vec,
size,
});
});
{/* Measurement lines */}
<primitive object={new Line(new BufferGeometry(), Material)} ref={line1} />
<primitive object={new Line(new BufferGeometry(), Material)} ref={line2} />
<primitive object={new Line(new BufferGeometry(), Material)} ref={line3} />
<primitive object={new Line(new BufferGeometry(), Material)} ref={line4} />
</>
)}
</>
const updateLine = ({
line,
geometry,
direction,
angle,
mesh,
vec,
size,
}: {
line: Line | null;
geometry: BufferGeometry;
direction: Vector3;
angle: string;
mesh: React.RefObject<Group>;
vec: Vector3;
size: { x: number; y: number; z: number };
}) => {
if (!line) return;
const points = [];
if (angle === "pos") {
points[0] = new Vector3(vec.x, vec.y, vec.z).add(
new Vector3((direction.x * size.x) / 2, 0, (direction.z * size.z) / 2)
);
} else if (angle === "neg") {
points[0] = new Vector3(vec.x, vec.y, vec.z).sub(
new Vector3((-direction.x * size.x) / 2, 0, (-direction.z * size.z) / 2)
);
} else if (angle === "posY") {
points[0] = new Vector3(vec.x, vec.y, vec.z).sub(
new Vector3(0, size.y / 2, 0)
);
}
const ray = new Raycaster();
if (camera) ray.camera = camera;
ray.set(new Vector3(vec.x, vec.y, vec.z), direction);
ray.params.Line.threshold = 0.1;
// Find intersection points
const wallsGroup = scene.children.find((val) =>
val?.name.includes("Walls")
);
const intersects = wallsGroup
? ray.intersectObjects([wallsGroup], true)
: [];
// Find intersection point
if (intersects[0]) {
for (const intersect of intersects) {
if (intersect.object.name.includes("Wall")) {
points[1] =
angle !== "posY" ? intersect.point : new Vector3(vec.x, 0, vec.z); // Floor
break;
}
}
}
// Update line geometry
if (points[1]) {
geometry.dispose();
geometry.setFromPoints([points[0], points[1]]);
line.geometry = geometry;
// Update measurement text
if (mesh?.current) {
geometry.computeBoundingSphere();
const center = geometry.boundingSphere?.center;
if (center) {
mesh.current.position.copy(center);
}
const label = document.getElementById(mesh.current.name);
if (label) {
label.innerText = `${points[0].distanceTo(points[1]).toFixed(2)}m`;
}
}
} else {
// No intersection found - clear the line
geometry.dispose();
geometry.setFromPoints([new Vector3(), new Vector3()]);
line.geometry = geometry;
const label = document.getElementById(mesh?.current?.name ?? "");
if (label) label.innerText = "";
}
};
const Material = new LineBasicMaterial({ color: "#d2baff" });
return (
<>
{/* Measurement text labels */}
{boundingBoxRef.current && object > 0 && (
<>
<group name="textPosX" ref={textPosX}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{ pointerEvents: "none" }}
>
<div className="distance-label" id="textPosX"></div>
</Html>
</group>
<group name="textNegX" ref={textNegX}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{ pointerEvents: "none" }}
>
<div className="distance-label" id="textNegX"></div>
</Html>
</group>
<group name="textPosZ" ref={textPosZ}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[2, 0]}
style={{ pointerEvents: "none" }}
>
<div className="distance-label" id="textPosZ"></div>
</Html>
</group>
<group name="textNegZ" ref={textNegZ}>
<Html
wrapperClass="distance-text-wrapper"
className="distance-text"
zIndexRange={[1, 0]}
style={{ pointerEvents: "none" }}
>
<div className="distance-label" id="textNegZ"></div>
</Html>
</group>
{/* Measurement lines */}
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line1}
/>
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line2}
/>
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line3}
/>
<primitive
object={new Line(new BufferGeometry(), Material)}
ref={line4}
/>
</>
)}
</>
);
};
export default DistanceFindingControls;
export default DistanceFindingControls;

View File

@ -1,7 +1,12 @@
import * as THREE from "three";
import { useEffect, useMemo, useRef, useState } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import { useFloorItems, useSelectedAssets, useSocketStore, useStartSimulation, useToggleView } from "../../../../store/store";
import {
useFloorItems,
useSelectedAssets,
useSocketStore,
useToggleView,
} from "../../../../store/store";
// import { setFloorItemApi } from '../../../../services/factoryBuilder/assest/floorAsset/setFloorItemApi';
import { toast } from "react-toastify";
import * as Types from "../../../../types/world/worldTypes";
@ -13,307 +18,371 @@ import { upsertProductOrEventApi } from "../../../../services/simulation/UpsertP
import { snapControls } from "../../../../utils/handleSnap";
import DistanceFindingControls from "./distanceFindingControls";
function MoveControls({ movedObjects, setMovedObjects, itemsGroupRef, copiedObjects, setCopiedObjects, pastedObjects, setpastedObjects, duplicatedObjects, setDuplicatedObjects, selectionGroup, rotatedObjects, setRotatedObjects, boundingBoxRef }: any) {
function MoveControls({
movedObjects,
setMovedObjects,
itemsGroupRef,
copiedObjects,
setCopiedObjects,
pastedObjects,
setpastedObjects,
duplicatedObjects,
setDuplicatedObjects,
selectionGroup,
rotatedObjects,
setRotatedObjects,
boundingBoxRef,
}: any) {
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(
() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0),
[]
);
const { camera, controls, gl, scene, pointer, raycaster } = useThree();
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { selectedProduct } = useSelectedProduct();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("")
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData
})
}
useEffect(() => {
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
let isMoving = false;
const onPointerDown = () => {
isMoving = false;
};
const onPointerMove = () => {
isMoving = true;
};
const onKeyUp = (event: KeyboardEvent) => {
// When any modifier is released, reset snap
const isModifierKey =
event.key === "Control" || event.key === "Shift";
if (isModifierKey) {
setKeyEvent("");
}
};
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && movedObjects.length > 0 && event.button === 0) {
event.preventDefault();
placeMovedAssets();
}
if (!isMoving && movedObjects.length > 0 && event.button === 2) {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
setKeyEvent("")
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (pastedObjects.length > 0 || duplicatedObjects.length > 0 || rotatedObjects.length > 0) return;
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
// update state here
setKeyEvent(keyCombination)
} else {
setKeyEvent("")
}
if (keyCombination === "G") {
if (selectedAssets.length > 0) {
moveAssets();
itemsData.current = floorItems.filter((item: { modelUuid: string }) => selectedAssets.some((asset: any) => asset.uuid === item.modelUuid));
}
}
if (keyCombination === "ESCAPE") {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
canvasElement?.addEventListener("keyup", onKeyUp);
}
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
canvasElement?.removeEventListener("keyup", onKeyUp);
};
}, [camera, controls, scene, toggleView, selectedAssets, socket, floorItems, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent]);
let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25;
useFrame(() => {
if (movedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
let targetX = point.x;
let targetZ = point.z;
if (keyEvent === "Ctrl") {
targetX = snapControls(targetX, "Ctrl");
targetZ = snapControls(targetZ, "Ctrl");
}
// else if (keyEvent === "Ctrl+Shift") {
// targetX = snapControls(targetX, "Ctrl+Shift");
// targetZ = snapControls(targetZ, "Ctrl+Shift");
// } else if (keyEvent === "Shift") {
// targetX = snapControls(targetX, "Shift");
// targetZ = snapControls(targetZ, "Shift");
// } else {
// }
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current.getWorldPosition(position);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (position.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (position.z - selectionGroup.current.position.z)
),
moveSpeed
);
} else {
const box = new THREE.Box3();
movedObjects.forEach((obj: THREE.Object3D) => box.expandByObject(obj));
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (center.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (center.z - selectionGroup.current.position.z)
),
moveSpeed
);
}
}
}
const { toggleView } = useToggleView();
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
const { selectedProduct } = useSelectedProduct();
const { floorItems, setFloorItems } = useFloorItems();
const { socket } = useSocketStore();
const itemsData = useRef<Types.FloorItems>([]);
const [keyEvent, setKeyEvent] = useState<
"Ctrl" | "Shift" | "Ctrl+Shift" | ""
>("");
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
const updateBackend = (
productName: string,
productId: string,
organization: string,
eventData: EventsSchema
) => {
upsertProductOrEventApi({
productName: productName,
productId: productId,
organization: organization,
eventDatas: eventData,
});
};
useEffect(() => {
if (!camera || !scene || toggleView || !itemsGroupRef.current) return;
const moveAssets = () => {
const updatedItems = floorItems.filter((item: { modelUuid: string }) => !selectedAssets.some((asset: any) => asset.uuid === item.modelUuid));
setFloorItems(updatedItems);
setMovedObjects(selectedAssets);
selectedAssets.forEach((asset: any) => { selectionGroup.current.attach(asset); });
}
const canvasElement = gl.domElement;
canvasElement.tabIndex = 0;
const placeMovedAssets = () => {
if (movedObjects.length === 0) return;
let isMoving = false;
movedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
obj.getWorldPosition(worldPosition);
const onPointerDown = () => {
isMoving = false;
};
selectionGroup.current.remove(obj);
obj.position.copy(worldPosition);
const onPointerMove = () => {
isMoving = true;
};
const onKeyUp = (event: KeyboardEvent) => {
// When any modifier is released, reset snap
const isModifierKey = event.key === "Control" || event.key === "Shift";
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modelUuid: obj.uuid,
modelName: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, },
isLocked: false,
isVisible: true
};
if (isModifierKey) {
setKeyEvent("");
}
};
if (obj.userData.eventData) {
const eventData = useEventsStore.getState().getEventByModelUuid(obj.userData.modelUuid);
const productData = useProductStore.getState().getEventByModelUuid(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid);
const onPointerUp = (event: PointerEvent) => {
if (!isMoving && movedObjects.length > 0 && event.button === 0) {
event.preventDefault();
placeMovedAssets();
}
if (!isMoving && movedObjects.length > 0 && event.button === 2) {
event.preventDefault();
if (eventData) {
useEventsStore.getState().updateEvent(obj.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
})
}
if (productData) {
const event = useProductStore.getState().updateEvent(useSelectedProduct.getState().selectedProduct.productId, obj.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
})
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
newFloorItem.eventData = eventData;
}
}
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
//REST
// await setFloorItemApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id,
};
socket.emit("v2:model-asset:add", data);
itemsGroupRef.current.add(obj);
}
});
toast.success("Object moved!");
itemsData.current = [];
clearSelection();
}
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
setKeyEvent("")
itemsData.current = [];
}
setKeyEvent("");
};
const onKeyDown = (event: KeyboardEvent) => {
const keyCombination = detectModifierKeys(event);
if (
pastedObjects.length > 0 ||
duplicatedObjects.length > 0 ||
rotatedObjects.length > 0
)
return;
if (
keyCombination === "Ctrl" ||
keyCombination === "Ctrl+Shift" ||
keyCombination === "Shift"
) {
// update state here
setKeyEvent(keyCombination);
} else {
setKeyEvent("");
}
if (keyCombination === "G") {
if (selectedAssets.length > 0) {
moveAssets();
itemsData.current = floorItems.filter((item: { modelUuid: string }) =>
selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)
);
}
}
if (keyCombination === "ESCAPE") {
event.preventDefault();
clearSelection();
movedObjects.forEach((asset: any) => {
if (itemsGroupRef.current) {
itemsGroupRef.current.attach(asset);
}
});
setFloorItems([...floorItems, ...itemsData.current]);
setMovedObjects([]);
itemsData.current = [];
}
};
if (!toggleView) {
canvasElement.addEventListener("pointerdown", onPointerDown);
canvasElement.addEventListener("pointermove", onPointerMove);
canvasElement.addEventListener("pointerup", onPointerUp);
canvasElement.addEventListener("keydown", onKeyDown);
canvasElement?.addEventListener("keyup", onKeyUp);
}
return <DistanceFindingControls boundingBoxRef={boundingBoxRef} />;
return () => {
canvasElement.removeEventListener("pointerdown", onPointerDown);
canvasElement.removeEventListener("pointermove", onPointerMove);
canvasElement.removeEventListener("pointerup", onPointerUp);
canvasElement.removeEventListener("keydown", onKeyDown);
canvasElement?.removeEventListener("keyup", onKeyUp);
};
}, [
camera,
controls,
scene,
toggleView,
selectedAssets,
socket,
floorItems,
pastedObjects,
duplicatedObjects,
movedObjects,
rotatedObjects,
keyEvent,
]);
let moveSpeed = keyEvent === "Ctrl" || "Ctrl+Shift" ? 1 : 0.25;
useFrame(() => {
if (movedObjects.length > 0) {
const intersectionPoint = new THREE.Vector3();
raycaster.setFromCamera(pointer, camera);
const point = raycaster.ray.intersectPlane(plane, intersectionPoint);
if (point) {
let targetX = point.x;
let targetZ = point.z;
if (keyEvent === "Ctrl") {
targetX = snapControls(targetX, "Ctrl");
targetZ = snapControls(targetZ, "Ctrl");
}
// else if (keyEvent === "Ctrl+Shift") {
// targetX = snapControls(targetX, "Ctrl+Shift");
// targetZ = snapControls(targetZ, "Ctrl+Shift");
// } else if (keyEvent === "Shift") {
// targetX = snapControls(targetX, "Shift");
// targetZ = snapControls(targetZ, "Shift");
// } else {
// }
const position = new THREE.Vector3();
if (boundingBoxRef.current) {
boundingBoxRef.current.getWorldPosition(position);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (position.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (position.z - selectionGroup.current.position.z)
),
moveSpeed
);
} else {
const box = new THREE.Box3();
movedObjects.forEach((obj: THREE.Object3D) =>
box.expandByObject(obj)
);
const center = new THREE.Vector3();
box.getCenter(center);
selectionGroup.current.position.lerp(
new THREE.Vector3(
targetX - (center.x - selectionGroup.current.position.x),
selectionGroup.current.position.y,
targetZ - (center.z - selectionGroup.current.position.z)
),
moveSpeed
);
}
}
}
});
const moveAssets = () => {
const updatedItems = floorItems.filter(
(item: { modelUuid: string }) =>
!selectedAssets.some((asset: any) => asset.uuid === item.modelUuid)
);
setFloorItems(updatedItems);
setMovedObjects(selectedAssets);
selectedAssets.forEach((asset: any) => {
selectionGroup.current.attach(asset);
});
};
const placeMovedAssets = () => {
if (movedObjects.length === 0) return;
movedObjects.forEach(async (obj: THREE.Object3D) => {
const worldPosition = new THREE.Vector3();
obj.getWorldPosition(worldPosition);
selectionGroup.current.remove(obj);
obj.position.copy(worldPosition);
if (itemsGroupRef.current) {
const newFloorItem: Types.FloorItemType = {
modelUuid: obj.uuid,
modelName: obj.userData.name,
modelfileID: obj.userData.modelId,
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
};
if (obj.userData.eventData) {
const eventData = useEventsStore
.getState()
.getEventByModelUuid(obj.userData.modelUuid);
const productData = useProductStore
.getState()
.getEventByModelUuid(
useSelectedProduct.getState().selectedProduct.productId,
obj.userData.modelUuid
);
if (eventData) {
useEventsStore.getState().updateEvent(obj.userData.modelUuid, {
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
});
}
if (productData) {
const event = useProductStore
.getState()
.updateEvent(
useSelectedProduct.getState().selectedProduct.productId,
obj.userData.modelUuid,
{
position: [worldPosition.x, worldPosition.y, worldPosition.z],
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
}
);
if (event) {
updateBackend(
selectedProduct.productName,
selectedProduct.productId,
organization,
event
);
}
newFloorItem.eventData = eventData;
}
}
setFloorItems((prevItems: Types.FloorItems) => {
const updatedItems = [...(prevItems || []), newFloorItem];
localStorage.setItem("FloorItems", JSON.stringify(updatedItems));
return updatedItems;
});
//REST
// await setFloorItemApi(
// organization,
// obj.uuid,
// obj.userData.name,
// [worldPosition.x, worldPosition.y, worldPosition.z],
// { "x": obj.rotation.x, "y": obj.rotation.y, "z": obj.rotation.z },
// obj.userData.modelId,
// false,
// true,
// );
//SOCKET
const data = {
organization,
modelUuid: newFloorItem.modelUuid,
modelName: newFloorItem.modelName,
modelfileID: newFloorItem.modelfileID,
position: newFloorItem.position,
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
isLocked: false,
isVisible: true,
socketId: socket.id,
};
socket.emit("v2:model-asset:add", data);
itemsGroupRef.current.add(obj);
}
});
toast.success("Object moved!");
itemsData.current = [];
clearSelection();
};
const clearSelection = () => {
selectionGroup.current.children = [];
selectionGroup.current.position.set(0, 0, 0);
selectionGroup.current.rotation.set(0, 0, 0);
setpastedObjects([]);
setDuplicatedObjects([]);
setMovedObjects([]);
setRotatedObjects([]);
setSelectedAssets([]);
setKeyEvent("");
};
return (
<DistanceFindingControls
boundingBoxRef={boundingBoxRef}
object={movedObjects.length}
/>
);
}
export default MoveControls
export default MoveControls;

View File

@ -205,7 +205,7 @@ const SelectionControls: React.FC = () => {
const email = localStorage.getItem("email");
const organization = email!.split("@")[1].split(".")[0];
const storedItems = JSON.parse(localStorage.getItem("FloorItems") || "[]");
const storedItems = JSON.parse(localStorage.getItem("FloorItems") ?? "[]");
const selectedUUIDs = selectedAssets.map((mesh: THREE.Object3D) => mesh.uuid);
const updatedStoredItems = storedItems.filter((item: { modelUuid: string }) => !selectedUUIDs.includes(item.modelUuid));

View File

@ -1,6 +1,6 @@
import RoboticArmInstance from "./armInstance/roboticArmInstance";
import { useArmBotStore } from "../../../../store/simulation/useArmBotStore";
import RoboticArmContentUi from "../../ui3d/RoboticArmContentUi";
// import RoboticArmContentUi from "../../ui3d/RoboticArmContentUi";
import React from "react";
function RoboticArmInstances() {
@ -11,7 +11,7 @@ function RoboticArmInstances() {
{armBots?.map((robot: ArmBotStatus) => (
<React.Fragment key={robot.modelUuid}>
<RoboticArmInstance armBot={robot} />
<RoboticArmContentUi roboticArm={robot} />
{/* <RoboticArmContentUi roboticArm={robot} /> */}
</React.Fragment>
))}
</>

View File

@ -38,8 +38,13 @@ export default function PolygonGenerator({
turf.lineString(line.map((p: any) => p?.position))
);
const polygons = turf.polygonize(turf.featureCollection(lineFeatures));
const validLineFeatures = lineFeatures.filter((line) => {
const coords = line.geometry.coordinates;
return coords.length >= 2;
});
const polygons = turf.polygonize(turf.featureCollection(validLineFeatures));
renderWallGeometry(wallPoints);
if (polygons.features.length > 0) {