Merge remote-tracking branch 'origin/simulation-agv-v2' into v2
This commit is contained in:
commit
9e56948f99
|
@ -4,62 +4,102 @@ import { computeArea } from '../functions/computeArea';
|
|||
import { Html } from '@react-three/drei';
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import * as turf from '@turf/turf';
|
||||
import * as THREE from "three"
|
||||
|
||||
const CalculateAreaGroup = () => {
|
||||
const { roomsState } = useRoomsState();
|
||||
const { toggleView } = useToggleView();
|
||||
const { roomsState } = useRoomsState();
|
||||
const { toggleView } = useToggleView();
|
||||
const savedTheme: string | null = localStorage.getItem('theme');
|
||||
|
||||
return (
|
||||
<group name="roomArea" visible={toggleView}>
|
||||
{roomsState.length > 0 &&
|
||||
roomsState.flat().map((room: any, index: number) => {
|
||||
if (!toggleView) return null;
|
||||
const coordinates = room.coordinates;
|
||||
return (
|
||||
<group name="roomArea" visible={toggleView}>
|
||||
<group name="roomFills" visible={toggleView}>
|
||||
{roomsState.length > 0 &&
|
||||
roomsState.flat().map((room: any, index: number) => {
|
||||
const coordinates = room.coordinates;
|
||||
if (!coordinates || coordinates.length < 3) return null;
|
||||
|
||||
if (!coordinates || coordinates.length < 3) return null;
|
||||
const yPos = (room.layer || 0) * CONSTANTS.zoneConfig.height;
|
||||
const coords2D = coordinates.map((p: any) => new THREE.Vector2(p.position.x, p.position.z));
|
||||
// console.log('coords2D: ', coords2D);
|
||||
|
||||
let coords2D = coordinates.map((p: any) => [p.position.x, p.position.z]);
|
||||
if (!coords2D[0].equals(coords2D[coords2D.length - 1])) {
|
||||
coords2D.push(coords2D[0]);
|
||||
}
|
||||
|
||||
const first = coords2D[0];
|
||||
const last = coords2D[coords2D.length - 1];
|
||||
if (first[0] !== last[0] || first[1] !== last[1]) {
|
||||
coords2D.push(first);
|
||||
}
|
||||
const shape = new THREE.Shape(coords2D);
|
||||
const extrudeSettings = {
|
||||
depth: 0.01,
|
||||
bevelEnabled: false,
|
||||
};
|
||||
|
||||
const polygon = turf.polygon([coords2D]);
|
||||
const center2D = turf.center(polygon).geometry.coordinates;
|
||||
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
||||
geometry.rotateX(Math.PI / 2);
|
||||
|
||||
const sumY = coordinates.reduce((sum: number, p: any) => sum + p.position.y, 0);
|
||||
const avgY = sumY / coordinates.length;
|
||||
const material = new THREE.MeshBasicMaterial({
|
||||
color: savedTheme === "dark" ? "#d2baff" : '#6f42c1',
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.4,
|
||||
depthWrite: false,
|
||||
});
|
||||
|
||||
const area = computeArea(room, "rooms");
|
||||
const formattedArea = `${area.toFixed(2)} m²`;
|
||||
return (
|
||||
<group key={`roomFill-${index}`}>
|
||||
<mesh geometry={geometry} material={material} position={[0, yPos, 0]} />
|
||||
</group>
|
||||
);
|
||||
})}
|
||||
</group>
|
||||
{roomsState.length > 0 &&
|
||||
roomsState.flat().map((room: any, index: number) => {
|
||||
if (!toggleView) return null;
|
||||
const coordinates = room.coordinates;
|
||||
|
||||
const htmlPosition: [number, number, number] = [
|
||||
center2D[0],
|
||||
avgY + CONSTANTS.zoneConfig.height,
|
||||
center2D[1],
|
||||
];
|
||||
if (!coordinates || coordinates.length < 3) return null;
|
||||
|
||||
return (
|
||||
<Html
|
||||
key={`${index}-${room.layer || index}`}
|
||||
position={htmlPosition}
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
center
|
||||
sprite
|
||||
>
|
||||
<div className={`distance area line-${room.layer || index}`}>
|
||||
Room ({formattedArea})
|
||||
</div>
|
||||
</Html>
|
||||
);
|
||||
})}
|
||||
</group>
|
||||
);
|
||||
let coords2D = coordinates.map((p: any) => [p.position.x, p.position.z]);
|
||||
|
||||
const first = coords2D[0];
|
||||
const last = coords2D[coords2D.length - 1];
|
||||
if (first[0] !== last[0] || first[1] !== last[1]) {
|
||||
coords2D.push(first);
|
||||
}
|
||||
|
||||
const polygon = turf.polygon([coords2D]);
|
||||
const center2D = turf.center(polygon).geometry.coordinates;
|
||||
|
||||
const sumY = coordinates.reduce((sum: number, p: any) => sum + p.position.y, 0);
|
||||
const avgY = sumY / coordinates.length;
|
||||
|
||||
const area = computeArea(room, "rooms");
|
||||
const formattedArea = `${area.toFixed(2)} m²`;
|
||||
|
||||
const htmlPosition: [number, number, number] = [
|
||||
center2D[0],
|
||||
avgY + CONSTANTS.zoneConfig.height,
|
||||
center2D[1],
|
||||
];
|
||||
|
||||
return (
|
||||
<Html
|
||||
key={`${index}-${room.layer || index}`}
|
||||
position={htmlPosition}
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
zIndexRange={[1, 0]}
|
||||
prepend
|
||||
center
|
||||
sprite
|
||||
>
|
||||
<div className={`distance area line-${room.layer || index}`}>
|
||||
Room ({formattedArea})
|
||||
</div>
|
||||
</Html>
|
||||
);
|
||||
})}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default CalculateAreaGroup;
|
||||
|
|
|
@ -376,7 +376,6 @@ const ZoneGroup: React.FC = () => {
|
|||
setZonePoints(updatedZonePoints);
|
||||
|
||||
addZoneToBackend(newZone);
|
||||
|
||||
setStartPoint(null);
|
||||
setEndPoint(null);
|
||||
}
|
||||
|
|
|
@ -41,8 +41,17 @@ function Ui() {
|
|||
const AssetPreview: React.FC<AssetPreviewProps> = ({
|
||||
selectedCard,
|
||||
setSelectedCard,
|
||||
modelUrl,
|
||||
modelUrl
|
||||
}) => {
|
||||
// Ensure rating is a valid number between 0 and 5
|
||||
const rating = Math.max(
|
||||
0,
|
||||
Math.min(5, isNaN(selectedCard.rating) ? 0 : selectedCard.rating)
|
||||
);
|
||||
|
||||
// Ensure that the rating is a valid positive integer for array length
|
||||
const starsArray = Array.from({ length: rating }, (_, index) => index);
|
||||
|
||||
return (
|
||||
<div className="assetPreview-wrapper">
|
||||
<div className="assetPreview">
|
||||
|
@ -65,7 +74,7 @@ const AssetPreview: React.FC<AssetPreviewProps> = ({
|
|||
>
|
||||
<Suspense fallback={<Ui />}>
|
||||
{selectedCard.assetName && modelUrl && (
|
||||
<GltfLoader fromServer={modelUrl} />
|
||||
<GltfLoader fromServer={modelUrl} assetId={selectedCard?.AssetID} />
|
||||
)}
|
||||
<OrbitControls minPolarAngle={0} maxPolarAngle={Math.PI / 2} />
|
||||
<ContactShadows
|
||||
|
|
|
@ -16,7 +16,8 @@ interface CardProps {
|
|||
views: number;
|
||||
image: string;
|
||||
description: string;
|
||||
AssetID: string;
|
||||
AssetID: string
|
||||
modelUrl: string
|
||||
onSelectCard: (cardData: {
|
||||
assetName: string;
|
||||
uploadedOn: number;
|
||||
|
@ -36,18 +37,17 @@ const Card: React.FC<CardProps> = ({
|
|||
views,
|
||||
image,
|
||||
description,
|
||||
onSelectCard,
|
||||
AssetID,
|
||||
modelUrl,
|
||||
onSelectCard,
|
||||
|
||||
}) => {
|
||||
const handleCardSelect = () => {
|
||||
console.log('assetName: ', assetName);
|
||||
console.log('AssetID: ', AssetID);
|
||||
|
||||
onSelectCard({
|
||||
assetName,
|
||||
uploadedOn,
|
||||
price,
|
||||
rating,
|
||||
views,
|
||||
description,
|
||||
AssetID,
|
||||
assetName, uploadedOn, price, rating, views, description, AssetID
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ const CardsContainer: React.FC<ModelsProps> = ({ models }) => {
|
|||
AssetID={assetDetail.AssetID}
|
||||
image={assetDetail.thumbnail}
|
||||
description={assetDetail.description}
|
||||
modelUrl={modelUrl}
|
||||
/>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
|
|
@ -10,6 +10,7 @@ interface GltfLoaderProps {
|
|||
setAnimations?: (animations?: string[]) => void;
|
||||
selectedAnimation?: string;
|
||||
setSelectedAnimation?: (animation: string) => void;
|
||||
assetId: string
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,6 +19,7 @@ const GltfLoader: React.FC<GltfLoaderProps> = ({
|
|||
fromServer,
|
||||
setAnimations,
|
||||
selectedAnimation,
|
||||
assetId
|
||||
}) => {
|
||||
const { scene, animations } = useGLTF(fromServer ?? "") as {
|
||||
scene: Object3D;
|
||||
|
|
|
@ -33,6 +33,7 @@ const MarketPlace = () => {
|
|||
try {
|
||||
const filt = await getAssetImages("67d934ad0f42a1fdadb19aa6");
|
||||
setModels(filt.items);
|
||||
console.log('filt.items: ', filt.items);
|
||||
setFilteredModels(filt.items);
|
||||
setisLoading(false);
|
||||
} catch {
|
||||
|
|
|
@ -1,270 +1,339 @@
|
|||
import React, { useRef } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import {
|
||||
Vector3,
|
||||
Raycaster,
|
||||
BufferGeometry,
|
||||
LineBasicMaterial,
|
||||
Line,
|
||||
Mesh,
|
||||
Group,
|
||||
Vector3,
|
||||
Raycaster,
|
||||
BufferGeometry,
|
||||
LineBasicMaterial,
|
||||
Line,
|
||||
Mesh,
|
||||
Group,
|
||||
} from "three";
|
||||
import { useThree, useFrame } from "@react-three/fiber";
|
||||
import { Html } from "@react-three/drei";
|
||||
|
||||
interface DistanceFindingControlsProps {
|
||||
boundingBoxRef: React.RefObject<Mesh>;
|
||||
object: number;
|
||||
boundingBoxRef: React.RefObject<Mesh>;
|
||||
object: number;
|
||||
}
|
||||
|
||||
const DistanceFindingControls = ({
|
||||
boundingBoxRef,
|
||||
object,
|
||||
boundingBoxRef,
|
||||
object,
|
||||
}: DistanceFindingControlsProps) => {
|
||||
const { camera, scene } = useThree();
|
||||
const { camera, scene } = useThree();
|
||||
const [labelValues, setLabelValues] = useState<{
|
||||
textPosX: any;
|
||||
textNegX: any;
|
||||
textPosZ: any;
|
||||
textNegZ: any;
|
||||
}>({
|
||||
textPosX: "",
|
||||
textNegX: "",
|
||||
textPosZ: "",
|
||||
textNegZ: "",
|
||||
});
|
||||
|
||||
// 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;
|
||||
useFrame(() => {
|
||||
if (!boundingBoxRef?.current) return;
|
||||
|
||||
boundingBoxRef.current.geometry.computeBoundingBox();
|
||||
const bbox = boundingBoxRef.current.geometry.boundingBox;
|
||||
boundingBoxRef.current.geometry.computeBoundingBox();
|
||||
const bbox = boundingBoxRef.current.geometry.boundingBox;
|
||||
|
||||
if (!bbox) return;
|
||||
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: "#d2baff" });
|
||||
const vec = boundingBoxRef.current?.getWorldPosition(new Vector3()).clone();
|
||||
|
||||
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>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (points[1]) {
|
||||
geometry.dispose();
|
||||
geometry.setFromPoints([points[0], points[1]]);
|
||||
line.geometry = geometry;
|
||||
|
||||
// Calculate the distance only once
|
||||
const distance = points[0].distanceTo(points[1]).toFixed(2);
|
||||
|
||||
// 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 = `${distance}m`;
|
||||
|
||||
// Update specific label state based on the label ID
|
||||
switch (label.id) {
|
||||
case "textPosX":
|
||||
setLabelValues((prevState) => ({ ...prevState, textPosX: distance }));
|
||||
break;
|
||||
case "textNegX":
|
||||
setLabelValues((prevState) => ({ ...prevState, textNegX: distance }));
|
||||
break;
|
||||
case "textPosZ":
|
||||
setLabelValues((prevState) => ({ ...prevState, textPosZ: distance }));
|
||||
break;
|
||||
case "textNegZ":
|
||||
setLabelValues((prevState) => ({ ...prevState, textNegZ: distance }));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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 = "";
|
||||
|
||||
// Clear the corresponding label value in the state
|
||||
switch (label.id) {
|
||||
case "textPosX":
|
||||
setLabelValues((prevState) => ({ ...prevState, textPosX: "" }));
|
||||
break;
|
||||
case "textNegX":
|
||||
setLabelValues((prevState) => ({ ...prevState, textNegX: "" }));
|
||||
break;
|
||||
case "textPosZ":
|
||||
setLabelValues((prevState) => ({ ...prevState, textPosZ: "" }));
|
||||
break;
|
||||
case "textNegZ":
|
||||
setLabelValues((prevState) => ({ ...prevState, textNegZ: "" }));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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",
|
||||
visibility: labelValues.textPosX === "" ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<div className="distance-label" id="textPosX">{labelValues.textPosX}</div>
|
||||
</Html>
|
||||
</group>
|
||||
|
||||
<group name="textNegX" ref={textNegX}>
|
||||
<Html
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
zIndexRange={[1, 0]}
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
visibility: labelValues.textNegX === "" ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<div className="distance-label" id="textNegX">{labelValues.textNegX}</div>
|
||||
</Html>
|
||||
</group>
|
||||
|
||||
<group name="textPosZ" ref={textPosZ}>
|
||||
<Html
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
zIndexRange={[2, 0]}
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
visibility: labelValues.textPosZ === "" ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<div className="distance-label" id="textPosZ">{labelValues.textPosZ}</div>
|
||||
</Html>
|
||||
</group>
|
||||
|
||||
<group name="textNegZ" ref={textNegZ}>
|
||||
<Html
|
||||
wrapperClass="distance-text-wrapper"
|
||||
className="distance-text"
|
||||
zIndexRange={[1, 0]}
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
visibility: labelValues.textNegZ === "" ? "hidden" : "visible",
|
||||
}}
|
||||
>
|
||||
<div className="distance-label" id="textNegZ">{labelValues.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;
|
||||
|
|
|
@ -64,7 +64,91 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||
}
|
||||
}, [isReset, isPlaying])
|
||||
|
||||
useFrame((_, delta) => {
|
||||
// useFrame((_, delta) => {
|
||||
// const object = scene.getObjectByProperty('uuid', agvUuid);
|
||||
// if (!object || currentPath.length < 2) return;
|
||||
// if (isPaused) return;
|
||||
|
||||
// let totalDistance = 0;
|
||||
// const distances = [];
|
||||
// let accumulatedDistance = 0;
|
||||
// let index = 0;
|
||||
// const rotationSpeed = 1;
|
||||
|
||||
// for (let i = 0; i < currentPath.length - 1; i++) {
|
||||
// const start = new THREE.Vector3(...currentPath[i]);
|
||||
// const end = new THREE.Vector3(...currentPath[i + 1]);
|
||||
// const segmentDistance = start.distanceTo(end);
|
||||
// distances.push(segmentDistance);
|
||||
// totalDistance += segmentDistance;
|
||||
// }
|
||||
|
||||
// while (index < distances.length && progressRef.current > accumulatedDistance + distances[index]) {
|
||||
// accumulatedDistance += distances[index];
|
||||
// index++;
|
||||
// }
|
||||
|
||||
// if (index < distances.length) {
|
||||
// const start = new THREE.Vector3(...currentPath[index]);
|
||||
// const end = new THREE.Vector3(...currentPath[index + 1]);
|
||||
// const segmentDistance = distances[index];
|
||||
|
||||
// const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
// const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
|
||||
|
||||
// const currentAngle = object.rotation.y;
|
||||
|
||||
// let angleDifference = targetAngle - currentAngle;
|
||||
// if (angleDifference > Math.PI) angleDifference -= 2 * Math.PI;
|
||||
// if (angleDifference < -Math.PI) angleDifference += 2 * Math.PI;
|
||||
|
||||
// const maxRotationStep = (rotationSpeed * speed * agvDetail.speed) * delta;
|
||||
// object.rotation.y += Math.sign(angleDifference) * Math.min(Math.abs(angleDifference), maxRotationStep);
|
||||
// const isAligned = Math.abs(angleDifference) < 0.01;
|
||||
|
||||
// if (isAligned) {
|
||||
// progressRef.current += delta * (speed * agvDetail.speed);
|
||||
// const t = (progressRef.current - accumulatedDistance) / segmentDistance;
|
||||
// const position = start.clone().lerp(end, t);
|
||||
// object.position.copy(position);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (progressRef.current >= totalDistance) {
|
||||
// if (restRotation && objectRotation) {
|
||||
// const targetEuler = new THREE.Euler(
|
||||
// objectRotation.x,
|
||||
// objectRotation.y - (agvDetail.point.action.steeringAngle),
|
||||
// objectRotation.z
|
||||
// );
|
||||
// const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
||||
// object.quaternion.slerp(targetQuaternion, delta * (rotationSpeed * speed * agvDetail.speed));
|
||||
// if (object.quaternion.angleTo(targetQuaternion) < 0.01) {
|
||||
// object.quaternion.copy(targetQuaternion);
|
||||
// object.rotation.copy(targetEuler);
|
||||
// setRestingRotation(false);
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// if (progressRef.current >= totalDistance) {
|
||||
// setRestingRotation(true);
|
||||
// progressRef.current = 0;
|
||||
// movingForward.current = !movingForward.current;
|
||||
// setCurrentPath([]);
|
||||
// handleCallBack();
|
||||
// if (currentPhase === 'pickup-drop') {
|
||||
// requestAnimationFrame(startUnloadingProcess);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
const lastTimeRef = useRef(performance.now());
|
||||
|
||||
useFrame(() => {
|
||||
const now = performance.now();
|
||||
const delta = (now - lastTimeRef.current) / 1000;
|
||||
lastTimeRef.current = now;
|
||||
|
||||
const object = scene.getObjectByProperty('uuid', agvUuid);
|
||||
if (!object || currentPath.length < 2) return;
|
||||
if (isPaused) return;
|
||||
|
@ -95,7 +179,6 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||
|
||||
const currentDirection = new THREE.Vector3().subVectors(end, start).normalize();
|
||||
const targetAngle = Math.atan2(currentDirection.x, currentDirection.z);
|
||||
|
||||
const currentAngle = object.rotation.y;
|
||||
|
||||
let angleDifference = targetAngle - currentAngle;
|
||||
|
@ -118,7 +201,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||
if (restRotation && objectRotation) {
|
||||
const targetEuler = new THREE.Euler(
|
||||
objectRotation.x,
|
||||
objectRotation.y - (agvDetail.point.action.steeringAngle),
|
||||
objectRotation.y - agvDetail.point.action.steeringAngle,
|
||||
objectRotation.z
|
||||
);
|
||||
const targetQuaternion = new THREE.Quaternion().setFromEuler(targetEuler);
|
||||
|
@ -131,6 +214,7 @@ function VehicleAnimator({ path, handleCallBack, currentPhase, agvUuid, agvDetai
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (progressRef.current >= totalDistance) {
|
||||
setRestingRotation(true);
|
||||
progressRef.current = 0;
|
||||
|
|
|
@ -11,7 +11,10 @@ import { useProductStore } from '../../../../../store/simulation/useProductStore
|
|||
import { useSelectedProduct } from '../../../../../store/simulation/useSimulationStore';
|
||||
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
||||
import MaterialAnimator from '../animator/materialAnimator';
|
||||
|
||||
type Timer = {
|
||||
start: number | null;
|
||||
active: boolean;
|
||||
};
|
||||
function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>) {
|
||||
const { navMesh } = useNavMesh();
|
||||
const { isPlaying } = usePlayButtonStore();
|
||||
|
@ -51,7 +54,7 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||
);
|
||||
|
||||
function vehicleStatus(modelId: string, status: string) {
|
||||
// console.log(`${modelId} , ${status}`);
|
||||
//
|
||||
}
|
||||
|
||||
// Function to reset everything
|
||||
|
@ -144,6 +147,9 @@ function VehicleInstance({ agvDetail }: Readonly<{ agvDetail: VehicleStatus }>)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function startUnloadingProcess() {
|
||||
if (agvDetail.point.action.triggers.length > 0) {
|
||||
const trigger = getTriggerByUuid(selectedProduct.productId, agvDetail.point.action.triggers[0].triggerUuid);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
let BackEnd_url = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
export const getAssetDetails = async (filename: string) => {
|
||||
console.log('filename: ', filename);
|
||||
try {
|
||||
const response = await fetch(`${BackEnd_url}/api/v1/assetDetails`, {
|
||||
method: "POST",
|
||||
|
|
|
@ -23,4 +23,4 @@ export const fetchGltfUrl = async (filename: string, AssetID: string) => {
|
|||
//
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue