Update Generated DXF Walls to backend

This commit is contained in:
Poovizhi99 2025-05-28 12:07:31 +05:30
parent a2af3b445d
commit 3d5159f1b6
5 changed files with 254 additions and 79 deletions

View File

@ -2,20 +2,25 @@ import React, { useEffect, useState } from "react";
import useLayoutStore from "../../store/builder/uselayoutStore";
import { useDfxUpload } from "../../store/builder/store";
import DxfParser from "dxf-parser";
import { Box3, BufferGeometry, MathUtils, Vector3 } from "three";
import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint";
import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree";
// Define types for DXF entities and geometries
const SelectFloorPlan: React.FC = () => {
// Access layout state and state setters
const { currentLayout, setLayout } = useLayoutStore();
const { setDfxUploaded, setDfxGenerate } = useDfxUpload();
// Access DXF-related states and setters
const { setDfxUploaded, setDfxGenerate, setObjValue, objValue } = useDfxUpload();
const [parsedFile, setParsedFile] = useState<DXFData>();
// Local state to store the parsed DXF file
const [parsedFile, setParsedFile] = useState<DXFData | undefined>(undefined);
// Flag to trigger generation after file upload
const [generate, setGenerate] = useState<boolean>(false);
// Handles file upload and DXF parsing
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
setLayout(null); // Reset current layout
setParsedFile(undefined); // Clear any previously parsed file
const file = event.target.files?.[0];
if (!file || !file.name.endsWith(".dxf")) {
alert("Please upload a valid .dxf file.");
@ -23,47 +28,60 @@ const SelectFloorPlan: React.FC = () => {
}
const reader = new FileReader();
reader.onload = async (e) => {
const dxfContent = e.target?.result as string;
try {
const parser = new DxfParser();
const parsedData = parser.parse(dxfContent) as DXFData;
const geometries = convertDXFToThree(parsedData);
setParsedFile(parsedData);
const parsedDatas = parser.parse(dxfContent) as DXFData;
const geometries = convertDXFToThree(parsedDatas);
setParsedFile(parsedDatas);
setObjValue({ x: 0, y: 0, z: 0 });
setDfxUploaded(geometries);
} catch (error) {
echo.error("Failed to import your .dxf file")
console.error("Failed to import your .dxf file", error);
} finally {
// ✅ Reset input AFTER processing
event.target.value = "";
}
};
reader.readAsText(file);
reader.readAsText(file); // Read the uploaded file as text
};
// Trigger wall point generation when `generate` flag changes
useEffect(() => {
if (parsedFile !== undefined)
getWallPointsFromBlueprint({ parsedData: parsedFile, setDfxGenerate })
}, [generate])
if (parsedFile !== undefined) {
getWallPointsFromBlueprint({ parsedData: parsedFile, setDfxGenerate, objValue });
}
}, [generate]);
useEffect(() => {
console.log("parsedFile: ", parsedFile);
}, [parsedFile]);
return (
<div className="select-floorplane-wrapper">
Preset Layouts
<div className="presets-container">
<button
className={`preset ${currentLayout === "layout1" ? "active" : ""}`}
onClick={() => {
setLayout("layout1");
setDfxUploaded([]);
setGenerate(false);
}}
>
Preset 1
</button>
<button
className={`preset ${currentLayout === "layout2" ? "active" : ""}`}
onClick={() => {
setLayout("layout2");
setDfxUploaded([]);
setGenerate(false);
}}
>
Preset 2
@ -76,6 +94,7 @@ const SelectFloorPlan: React.FC = () => {
style={{ display: "none", width: "10px" }}
onChange={handleFileUpload}
/>
<label
htmlFor="file-upload"
className="preset"
@ -86,11 +105,13 @@ const SelectFloorPlan: React.FC = () => {
>
Upload
</label>
<button
type="button"
id="generate-upload"
onClick={() => {
setDfxUploaded([])
setDfxUploaded([]);
setLayout(null);
setGenerate(!generate);
}}
>
@ -102,8 +123,3 @@ const SelectFloorPlan: React.FC = () => {
};
export default SelectFloorPlan;

View File

@ -314,7 +314,13 @@ export default function Builder() {
<CalculateAreaGroup />
<NavMesh lines={lines} />
<DxfFile lines={lines} dragPointControls={dragPointControls} currentLayerPoint={currentLayerPoint} floorPlanGroupLine={floorPlanGroupLine} floorPlanGroupPoint={floorPlanGroupPoint} setUpdateScene={setUpdateScene} />
<DxfFile
lines={lines}
dragPointControls={dragPointControls}
currentLayerPoint={currentLayerPoint}
floorPlanGroupLine={floorPlanGroupLine}
floorPlanGroupPoint={floorPlanGroupPoint}
/>
<LayoutImage />
</Bvh>

View File

@ -1,38 +1,145 @@
import { useEffect } from 'react';
import { useDfxUpload, useToggleView } from '../../../store/builder/store';
import { LineBasicMaterial, Line } from "three";
import { useEffect, useRef } from 'react';
import { useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
import { LineBasicMaterial, Line } from 'three';
import loadInitialPoint from '../IntialLoad/loadInitialPoint';
import loadInitialLine from '../IntialLoad/loadInitialLine';
import { TransformControls } from '@react-three/drei';
import { getWallPointsFromBlueprint } from './functions/getWallPointsFromBlueprint';
import * as Types from '../../../types/world/worldTypes';
import arrayLineToObject from '../geomentries/lines/lineConvertions/arrayLineToObject';
const DxfFile = ({ floorPlanGroupPoint, currentLayerPoint, dragPointControls, floorPlanGroupLine, lines, setUpdateScene }: any) => {
const { dfxuploaded, dfxWallGenerate } = useDfxUpload();
// Interface defining the props for the DxfFile component
interface DxfFileProps {
lines: Types.RefLines; // Reference to lines in the DXF file
floorPlanGroupPoint: Types.RefGroup; // Reference to floor plan points group
dragPointControls: Types.RefDragControl; // Reference to drag controls
floorPlanGroupLine: Types.RefGroup; // Reference to floor plan lines group
currentLayerPoint: Types.RefMeshArray; // Reference to current layer points
}
/**
* DxfFile component handles the rendering and manipulation of DXf file data in a 3D scene.
* It processes the DXF data to create points and lines representing walls and allows
* transformation controls for interactive editing.
*/
const DxfFile = ({
floorPlanGroupPoint,
currentLayerPoint,
dragPointControls,
floorPlanGroupLine,
lines,
}: DxfFileProps) => {
// State management hooks
const { dfxuploaded, dfxWallGenerate, setObjValue, objValue } = useDfxUpload();
const { setUpdateScene } = useUpdateScene();
const { toggleView } = useToggleView();
const { socket } = useSocketStore();
// Refs for storing line objects
const lineRefs = useRef<Line[]>([]);
/**
* Effect hook that runs when DXF wall generation is triggered.
* Loads initial points and lines from the DXF data and updates the scene.
*/
useEffect(() => {
if (dfxWallGenerate && dragPointControls && floorPlanGroupPoint && currentLayerPoint && floorPlanGroupLine) {
lines.current = dfxWallGenerate;
if (
dfxWallGenerate &&
dragPointControls &&
floorPlanGroupPoint &&
currentLayerPoint &&
floorPlanGroupLine
) {
// Store generated lines in ref
lines.current.push(...dfxWallGenerate);
dfxWallGenerate.map((line: any) => {
const lineData = arrayLineToObject(line as Types.Line);
const email = localStorage.getItem('email')
const organization = (email!.split("@")[1]).split(".")[0];
//REST
// setLine(organization, lineData.layer!, lineData.line!, lineData.type!);
//SOCKET
const input = {
organization: organization,
layer: lineData.layer,
line: lineData.line,
type: lineData.type,
socketId: socket.id
}
socket.emit('v1:Line:create', input);
})
// Load initial points and lines from DXF data
loadInitialPoint(lines, floorPlanGroupPoint, currentLayerPoint, dragPointControls);
loadInitialLine(floorPlanGroupLine, lines);
// Trigger scene update
setUpdateScene(true);
}
}, [lines, floorPlanGroupLine, floorPlanGroupPoint, currentLayerPoint, dragPointControls, dfxWallGenerate])
}, [
lines,
floorPlanGroupLine,
floorPlanGroupPoint,
currentLayerPoint,
dragPointControls,
dfxWallGenerate,
]);
/**
* Handles transformation changes for individual lines.
* Updates the object value state with new position and recalculates wall points.
* @param index - Index of the line being transformed
*/
const handleTransformChange = (index: number) => {
const line = lineRefs.current[index];
if (!line) return;
// Get current position of the line
const position = line.position;
// Update state with new position
setObjValue({ x: position.x, y: position.y, z: position.z });
// Recalculate wall points based on new position
getWallPointsFromBlueprint({
objValue: { x: position.x, y: position.y, z: position.z },
setDfxGenerate: () => { },
});
};
return (
<>
{dfxuploaded && dfxuploaded.length > 0 && toggleView && dfxuploaded?.map((geometry: any, index: any) => (
<TransformControls>
<group>
<primitive
{/* Render DXF lines with transform controls when DXF data is available and view is toggled */}
{dfxuploaded &&
dfxuploaded.length > 0 &&
toggleView &&
dfxuploaded?.map((geometry: any, index: number) => {
// Create a new line object for each geometry in the DXF data
const line = new Line(geometry, new LineBasicMaterial({ color: 'red' }));
line.rotation.set(-Math.PI / 2, 0, 0);
// Store line reference
lineRefs.current[index] = line;
return (
<TransformControls
key={index}
object={new Line(geometry, new LineBasicMaterial({ color: 'red' }))}
rotation={[-Math.PI / 2, 0, 0]}
/>
</group>
object={line}
onMouseUp={() => handleTransformChange(index)}
>
{/* Render the line with current position from state */}
<primitive object={line} position={[objValue.x, objValue.y, objValue.z]} />
</TransformControls>
))}
);
})}
</>
);
}
};
export default DxfFile;

View File

@ -1,41 +1,59 @@
import { MathUtils, Vector3, BufferGeometry } from "three";
interface Props {
parsedData: DXFData;
setDfxGenerate: (walls: WallLineVertex[][]) => void;
parsedData?: DXFData; // Parsed DXF file data
setDfxGenerate?: (walls: WallLineVertex[][]) => void; // Callback to set generated walls
objValue: any; // Object position values for offset calculation
}
/**
* Processes DXF entities to generate wall points for a blueprint.
* Handles LINE, LWPOLYLINE, and ARC entity types, converting them to wall segments.
* Applies unit conversion and positional offsets to all points.
*
* @param {Props} params - Configuration parameters
* @param {DXFData} params.parsedData - Parsed DXF file data
* @param {Function} params.setDfxGenerate - Callback to store generated walls
* @param {Object} params.objValue - Contains x,y,z offsets for position adjustment
*/
export function getWallPointsFromBlueprint({
parsedData,
setDfxGenerate,
objValue,
}: Props) {
// Early return if no data is provided
if (!parsedData) return;
if (!parsedData.entities) return;
const unit = 1000; // Convert mm to meters
const wallVertex: WallLineVertex[][] = [];
const unit = 1000; // Conversion factor from millimeters to meters
const wallVertex: WallLineVertex[][] = []; // Stores all generated wall segments
// Process each entity in the DXF file
parsedData.entities.forEach((entity: DXFEntity) => {
// Handle LINE entities
if (entity.type === "LINE" && entity.vertices) {
// Create start and end vectors with unit conversion and position offset
const startVec = new Vector3(
entity.vertices[0].x / unit,
0.01,
-entity.vertices[0].y / unit
);
0.01, // Slightly above ground to avoid z-fighting
-entity.vertices[0].y / unit // Invert Y-axis to match Three.js coordinate system
).add(new Vector3(objValue.x, 0, objValue.z));
const endVec = new Vector3(
entity.vertices[1].x / unit,
0.01,
-entity.vertices[1].y / unit
);
).add(new Vector3(objValue.x, 0, objValue.z));
// Check if points already exist to avoid duplicates
const existingStart = wallVertex
.flat()
.find((v) => v[0].equals(startVec));
const startPoint: WallLineVertex = existingStart || [
startVec,
MathUtils.generateUUID(),
1,
"WallLine",
MathUtils.generateUUID(), // Generate unique ID for new points
1, // Default weight
"WallLine", // Type identifier
];
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
@ -46,22 +64,29 @@ export function getWallPointsFromBlueprint({
"WallLine",
];
// Add the line segment to our collection
wallVertex.push([startPoint, endPoint]);
} else if (entity.type === "LWPOLYLINE" && entity.vertices) {
let firstPoint: WallLineVertex | undefined;
}
// Handle LWPOLYLINE entities (connected line segments)
else if (entity.type === "LWPOLYLINE" && entity.vertices) {
let firstPoint: WallLineVertex | undefined; // Store first point for closing the polyline
// Process each vertex pair in the polyline
for (let i = 0; i < entity.vertices.length - 1; i++) {
// Convert vertices to Three.js vectors with offset
const startVec = new Vector3(
entity.vertices[i].x / unit,
0.01,
-entity.vertices[i].y / unit
);
).add(new Vector3(objValue.x, 0, objValue.z));
const endVec = new Vector3(
entity.vertices[i + 1].x / unit,
0.01,
-entity.vertices[i + 1].y / unit
);
).add(new Vector3(objValue.x, 0, objValue.z));
// Check for existing points
const existingStart = wallVertex
.flat()
.find((v) => v[0].equals(startVec));
@ -82,36 +107,52 @@ export function getWallPointsFromBlueprint({
wallVertex.push([startPoint, endPoint]);
// Store first point and create closing segment if this is the last vertex
if (i === 0) firstPoint = startPoint;
if (i === entity.vertices.length - 2 && firstPoint)
if (i === entity.vertices.length - 2 && firstPoint) {
wallVertex.push([endPoint, firstPoint]);
}
} else if (entity.type === "ARC") {
}
}
// Handle ARC entities
else if (entity.type === "ARC") {
const { center, radius, startAngle, endAngle } = entity;
// Validate required ARC properties
if (
!center ||
radius === undefined ||
startAngle === undefined ||
endAngle === undefined
)
) {
return;
const numSegments = 16;
const angleStep = (endAngle - startAngle) / numSegments;
const arcPoints: Vector3[] = [];
for (let i = 0; i <= numSegments; i++) {
const angle = startAngle + i * angleStep;
const x = center.x + radius * Math.cos(angle);
const y = -center.y + radius * Math.sin(angle);
arcPoints.push(new Vector3(x / unit, 0.01, y / unit));
}
// Convert ARC to series of line segments
const numSegments = 16; // Number of segments to approximate the arc
const angleStep = (endAngle - startAngle) / numSegments;
const arcPoints: Vector3[] = []; // Stores points along the arc
// Generate points along the arc
for (let i = 0; i <= numSegments; i++) {
const angle = startAngle + i * angleStep;
// Calculate arc point in DXF coordinate system
const x = center.x + radius * Math.cos(angle);
const y = -center.y + radius * Math.sin(angle); // Invert Y-axis
// Convert to Three.js vector with offset
const vec = new Vector3(x / unit, 0.01, y / unit).add(
new Vector3(objValue.x, 0, objValue.z)
);
arcPoints.push(vec);
}
// Create line segments between arc points
for (let i = 0; i < arcPoints.length - 1; i++) {
const startVec = arcPoints[i];
const endVec = arcPoints[i + 1];
// Check for existing points
const existingStart = wallVertex
.flat()
.find((v) => v[0].equals(startVec));
@ -132,10 +173,13 @@ export function getWallPointsFromBlueprint({
wallVertex.push([startPoint, endPoint]);
}
} else {
}
// Log unsupported entity types
else {
console.error("Unsupported entity type:", entity.type);
}
});
setDfxGenerate(wallVertex);
// Return the generated walls through callback if provided
setDfxGenerate && setDfxGenerate(wallVertex);
}

View File

@ -494,8 +494,10 @@ export const useProcessBar = create<any>((set: any) => ({
export const useDfxUpload = create<any>((set: any) => ({
dfxuploaded: [],
dfxWallGenerate: [],
objValue: { x: 0, y: 0, z: 0 },
setDfxUploaded: (x: any) => set({ dfxuploaded: x }),
setDfxGenerate: (x: any) => set({ dfxWallGenerate: x }),
setObjValue: (x: any) => set({ objValue: x }),
}));
type InputValuesStore = {
@ -551,7 +553,7 @@ interface CompareStore {
}
export const useCompareStore = create<CompareStore>((set) => ({
comparePopUp: false,
comparePopUp: true,
setComparePopUp: (value) => set({ comparePopUp: value }),
toggleComparePopUp: () =>
set((state) => ({ comparePopUp: !state.comparePopUp })),