added area for room and zone
This commit is contained in:
@@ -52,6 +52,7 @@ import Layer2DVisibility from "./geomentries/layers/layer2DVisibility";
|
|||||||
import ZoneGroup from "./groups/zoneGroup";
|
import ZoneGroup from "./groups/zoneGroup";
|
||||||
import MeasurementTool from "../scene/tools/measurementTool";
|
import MeasurementTool from "../scene/tools/measurementTool";
|
||||||
import NavMesh from "../simulation/vehicle/navMesh/navMesh";
|
import NavMesh from "../simulation/vehicle/navMesh/navMesh";
|
||||||
|
import CalculateAreaGroup from "./groups/calculateAreaGroup";
|
||||||
|
|
||||||
export default function Builder() {
|
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.
|
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 />
|
<MeasurementTool />
|
||||||
|
<CalculateAreaGroup />
|
||||||
<NavMesh lines={lines} />
|
<NavMesh lines={lines} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
29
app/src/modules/builder/functions/computeArea.ts
Normal file
29
app/src/modules/builder/functions/computeArea.ts
Normal file
@@ -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";
|
} from "../../../../../store/store";
|
||||||
import objectLinesToArray from "../lineConvertions/objectLinesToArray";
|
import objectLinesToArray from "../lineConvertions/objectLinesToArray";
|
||||||
import { Html } from "@react-three/drei";
|
import { Html } from "@react-three/drei";
|
||||||
|
import { Vector2 } from "three";
|
||||||
import * as Types from "../../../../../types/world/worldTypes";
|
import * as Types from "../../../../../types/world/worldTypes";
|
||||||
import getRoomsFromLines from "../getRoomsFromLines";
|
import getRoomsFromLines from "../getRoomsFromLines";
|
||||||
|
import * as turf from '@turf/turf';
|
||||||
|
|
||||||
const DistanceText = () => {
|
const DistanceText = () => {
|
||||||
const [lines, setLines] = useState<
|
const [lines, setLines] = useState<
|
||||||
@@ -29,10 +31,52 @@ const DistanceText = () => {
|
|||||||
const [linesState, setLinesState] = useState<Types.Lines>([]);
|
const [linesState, setLinesState] = useState<Types.Lines>([]);
|
||||||
const { roomsState, setRoomsState } = useRoomsState();
|
const { roomsState, setRoomsState } = useRoomsState();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (linesState.length === 0) return;
|
if (linesState.length === 0) return;
|
||||||
const getLines = async () => {
|
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) {
|
if (lines.length > 2) {
|
||||||
const linesByLayer = linesState.reduce((acc: { [key: number]: any[] }, pair) => {
|
const linesByLayer = linesState.reduce((acc: { [key: number]: any[] }, pair) => {
|
||||||
const layer = pair[0][2];
|
const layer = pair[0][2];
|
||||||
@@ -41,15 +85,16 @@ const DistanceText = () => {
|
|||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
|
||||||
for (const layer in linesByLayer) {
|
for (const layer in linesByLayer) {
|
||||||
|
|
||||||
const rooms: Types.Rooms = await getRoomsFromLines({ current: linesByLayer[layer] });
|
const rooms: Types.Rooms = await getRoomsFromLines({ current: linesByLayer[layer] });
|
||||||
|
setRoomsState(rooms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getLines();
|
getLines();
|
||||||
}, [linesState])
|
}, [linesState, roomsState])
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const email = localStorage.getItem("email");
|
const email = localStorage.getItem("email");
|
||||||
@@ -143,6 +188,7 @@ const DistanceText = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
setDeletedLines([]);
|
setDeletedLines([]);
|
||||||
|
setRoomsState([])
|
||||||
}
|
}
|
||||||
}, [deletedLines]);
|
}, [deletedLines]);
|
||||||
|
|
||||||
|
|||||||
65
app/src/modules/builder/groups/calculateAreaGroup.tsx
Normal file
65
app/src/modules/builder/groups/calculateAreaGroup.tsx
Normal file
@@ -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 CONSTANTS from "../../../types/world/worldConstants";
|
||||||
import * as turf from '@turf/turf';
|
import * as turf from '@turf/turf';
|
||||||
import { computeAreaFrom3DPoints } from "../functions/computeAreaFrom3DPoints";
|
import { computeArea } from "../functions/computeArea";
|
||||||
|
|
||||||
const ZoneGroup: React.FC = () => {
|
const ZoneGroup: React.FC = () => {
|
||||||
const { camera, pointer, gl, raycaster, scene, controls } = useThree();
|
const { camera, pointer, gl, raycaster, scene, controls } = useThree();
|
||||||
@@ -597,8 +597,9 @@ const ZoneGroup: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
|
|
||||||
</group>
|
</group>
|
||||||
<group name="zoneArea">
|
<group name="zoneArea" visible={toggleView}>
|
||||||
{zones.map((zone: any, index: any) => {
|
{zones.map((zone: any, index: any) => {
|
||||||
|
if (!toggleView) return null;
|
||||||
const points3D = zone.points;
|
const points3D = zone.points;
|
||||||
const coords2D = points3D.map((p: any) => [p[0], p[2]]);
|
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 sumY = points3D.reduce((sum: number, p: any) => sum + p[1], 0);
|
||||||
const avgY = sumY / points3D.length;
|
const avgY = sumY / points3D.length;
|
||||||
|
|
||||||
const area = computeAreaFrom3DPoints(points3D);
|
const area = computeArea(points3D, "zone");
|
||||||
const formattedArea = `${area.toFixed(2)} m²`;
|
const formattedArea = `${area.toFixed(2)} m²`;
|
||||||
|
|
||||||
const htmlPosition: [number, number, number] = [
|
const htmlPosition: [number, number, number] = [
|
||||||
@@ -686,3 +687,12 @@ const ZoneGroup: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default ZoneGroup;
|
export default ZoneGroup;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export const useWalls = create<any>((set: any) => ({
|
|||||||
|
|
||||||
export const useRoomsState = create<any>((set: any) => ({
|
export const useRoomsState = create<any>((set: any) => ({
|
||||||
roomsState: [],
|
roomsState: [],
|
||||||
setRoomsState: (x: any) => set(() => ({ walls: x })),
|
setRoomsState: (x: any) => set(() => ({ roomsState: x })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const useZones = create<any>((set: any) => ({
|
export const useZones = create<any>((set: any) => ({
|
||||||
|
|||||||
Reference in New Issue
Block a user