v2-ui #84
|
@ -52,6 +52,7 @@ import Layer2DVisibility from "./geomentries/layers/layer2DVisibility";
|
|||
import ZoneGroup from "./groups/zoneGroup";
|
||||
import MeasurementTool from "../scene/tools/measurementTool";
|
||||
import NavMesh from "../simulation/vehicle/navMesh/navMesh";
|
||||
import CalculateAreaGroup from "./groups/calculateAreaGroup";
|
||||
|
||||
export default function Builder() {
|
||||
const state = useThree<Types.ThreeState>(); // Importing the state from the useThree hook, which contains the scene, camera, and other Three.js elements.
|
||||
|
@ -341,7 +342,7 @@ export default function Builder() {
|
|||
/>
|
||||
|
||||
<MeasurementTool />
|
||||
|
||||
<CalculateAreaGroup />
|
||||
<NavMesh lines={lines} />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import { Vector2 } from "three";
|
||||
|
||||
export function computeArea(data: any, type: "zone" | "rooms" | "aisle"): any {
|
||||
if (type === "zone") {
|
||||
const points3D = data.map((p: any) => new Vector2(p[0], p[2]));
|
||||
let area = 0;
|
||||
for (let i = 0; i < points3D.length - 1; i++) {
|
||||
const current = points3D[i];
|
||||
const next = points3D[i + 1];
|
||||
area += current.x * next.y - next.x * current.y;
|
||||
}
|
||||
|
||||
return Math.abs(area) / 2;
|
||||
}
|
||||
|
||||
if (type === "rooms") {
|
||||
const points2D = data.coordinates.map(
|
||||
(coordinate: any) =>
|
||||
new Vector2(coordinate.position.x, coordinate.position.z)
|
||||
);
|
||||
let area = 0;
|
||||
for (let i = 0; i < points2D.length - 1; i++) {
|
||||
const current = points2D[i];
|
||||
const next = points2D[i + 1];
|
||||
area += current.x * next.y - next.x * current.y;
|
||||
}
|
||||
return Math.abs(area) / 2;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
import { Vector2 } from "three";
|
||||
|
||||
export function computeAreaFrom3DPoints(points3D: any) {
|
||||
// Project 3D points onto XZ plane
|
||||
const projected = points3D.map((p: any) => new Vector2(p[0], p[2]));
|
||||
|
||||
// Shoelace formula for 2D polygon
|
||||
let area = 0;
|
||||
const n = projected.length;
|
||||
for (let i = 0; i < n - 1; i++) {
|
||||
const curr = projected[i];
|
||||
const next = projected[i + 1];
|
||||
area += curr.x * next.y - next.x * curr.y;
|
||||
}
|
||||
|
||||
return Math.abs(area) / 2;
|
||||
}
|
|
@ -10,8 +10,10 @@ import {
|
|||
} from "../../../../../store/store";
|
||||
import objectLinesToArray from "../lineConvertions/objectLinesToArray";
|
||||
import { Html } from "@react-three/drei";
|
||||
import { Vector2 } from "three";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import getRoomsFromLines from "../getRoomsFromLines";
|
||||
import * as turf from '@turf/turf';
|
||||
|
||||
const DistanceText = () => {
|
||||
const [lines, setLines] = useState<
|
||||
|
@ -29,10 +31,52 @@ const DistanceText = () => {
|
|||
const [linesState, setLinesState] = useState<Types.Lines>([]);
|
||||
const { roomsState, setRoomsState } = useRoomsState();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (linesState.length === 0) return;
|
||||
const getLines = async () => {
|
||||
const points3D = linesState.map(line => {
|
||||
const startPoint = line[0][0]; // First point of each wall line
|
||||
return [startPoint.x, 0, startPoint.z];
|
||||
});
|
||||
|
||||
// Ensure the polygon is closed
|
||||
if (
|
||||
points3D[0][0] !== points3D[points3D.length - 1][0] ||
|
||||
points3D[0][1] !== points3D[points3D.length - 1][1]
|
||||
) {
|
||||
points3D.push(points3D[0]);
|
||||
}
|
||||
|
||||
// Convert to 2D for turf (x, z)
|
||||
const coords2D = points3D.map(p => [p[0], p[1]]);
|
||||
|
||||
const projected = points3D.map((p: any) => new Vector2(p[0], p[1]));
|
||||
|
||||
// Shoelace formula for 2D polygon
|
||||
let area = 0;
|
||||
const n = projected.length;
|
||||
for (let i = 0; i < n - 1; i++) {
|
||||
const curr = projected[i];
|
||||
const next = projected[i + 1];
|
||||
area += curr.x * next.y - next.x * curr.y;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// return Math.abs(area) / 2;
|
||||
|
||||
// Build polygon and compute area
|
||||
// const polygon = turf.polygon([coords2D]);
|
||||
// const area = turf.area(polygon);
|
||||
// const area = computeAreaFrom3DPoints(coords2D)
|
||||
|
||||
//
|
||||
if (lines.length > 2) {
|
||||
const linesByLayer = linesState.reduce((acc: { [key: number]: any[] }, pair) => {
|
||||
const layer = pair[0][2];
|
||||
|
@ -41,15 +85,16 @@ const DistanceText = () => {
|
|||
return acc;
|
||||
}, {});
|
||||
|
||||
|
||||
for (const layer in linesByLayer) {
|
||||
|
||||
const rooms: Types.Rooms = await getRoomsFromLines({ current: linesByLayer[layer] });
|
||||
|
||||
setRoomsState(rooms)
|
||||
}
|
||||
}
|
||||
}
|
||||
getLines();
|
||||
}, [linesState])
|
||||
}, [linesState, roomsState])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const email = localStorage.getItem("email");
|
||||
|
@ -143,6 +188,7 @@ const DistanceText = () => {
|
|||
);
|
||||
|
||||
setDeletedLines([]);
|
||||
setRoomsState([])
|
||||
}
|
||||
}, [deletedLines]);
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { useRoomsState, useToggleView } from '../../../store/store';
|
||||
import { computeArea } from '../functions/computeArea';
|
||||
import { Html } from '@react-three/drei';
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import * as turf from '@turf/turf';
|
||||
|
||||
const CalculateAreaGroup = () => {
|
||||
const { roomsState } = useRoomsState();
|
||||
const { toggleView } = useToggleView();
|
||||
|
||||
return (
|
||||
<group name="roomArea" visible={toggleView}>
|
||||
{roomsState.length > 0 &&
|
||||
roomsState.flat().map((room: any, index: number) => {
|
||||
if (!toggleView) return null;
|
||||
const coordinates = room.coordinates;
|
||||
|
||||
if (!coordinates || coordinates.length < 3) return null;
|
||||
|
||||
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;
|
|
@ -18,7 +18,7 @@ import { getZonesApi } from "../../../services/factoryBuilder/zones/getZonesApi"
|
|||
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import * as turf from '@turf/turf';
|
||||
import { computeAreaFrom3DPoints } from "../functions/computeAreaFrom3DPoints";
|
||||
import { computeArea } from "../functions/computeArea";
|
||||
|
||||
const ZoneGroup: React.FC = () => {
|
||||
const { camera, pointer, gl, raycaster, scene, controls } = useThree();
|
||||
|
@ -597,8 +597,9 @@ const ZoneGroup: React.FC = () => {
|
|||
))}
|
||||
|
||||
</group>
|
||||
<group name="zoneArea">
|
||||
<group name="zoneArea" visible={toggleView}>
|
||||
{zones.map((zone: any, index: any) => {
|
||||
if (!toggleView) return null;
|
||||
const points3D = zone.points;
|
||||
const coords2D = points3D.map((p: any) => [p[0], p[2]]);
|
||||
|
||||
|
@ -616,7 +617,7 @@ const ZoneGroup: React.FC = () => {
|
|||
const sumY = points3D.reduce((sum: number, p: any) => sum + p[1], 0);
|
||||
const avgY = sumY / points3D.length;
|
||||
|
||||
const area = computeAreaFrom3DPoints(points3D);
|
||||
const area = computeArea(points3D, "zone");
|
||||
const formattedArea = `${area.toFixed(2)} m²`;
|
||||
|
||||
const htmlPosition: [number, number, number] = [
|
||||
|
@ -686,3 +687,12 @@ const ZoneGroup: React.FC = () => {
|
|||
};
|
||||
|
||||
export default ZoneGroup;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ export const useWalls = create<any>((set: any) => ({
|
|||
|
||||
export const useRoomsState = create<any>((set: any) => ({
|
||||
roomsState: [],
|
||||
setRoomsState: (x: any) => set(() => ({ walls: x })),
|
||||
setRoomsState: (x: any) => set(() => ({ roomsState: x })),
|
||||
}));
|
||||
|
||||
export const useZones = create<any>((set: any) => ({
|
||||
|
|
Loading…
Reference in New Issue