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 useLayoutStore from "../../store/builder/uselayoutStore";
import { useDfxUpload } from "../../store/builder/store"; import { useDfxUpload } from "../../store/builder/store";
import DxfParser from "dxf-parser"; import DxfParser from "dxf-parser";
import { Box3, BufferGeometry, MathUtils, Vector3 } from "three";
import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint"; import { getWallPointsFromBlueprint } from "../../modules/builder/dfx/functions/getWallPointsFromBlueprint";
import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree"; import { convertDXFToThree } from "../../modules/builder/dfx/functions/convertDxfToThree";
// Define types for DXF entities and geometries
const SelectFloorPlan: React.FC = () => { const SelectFloorPlan: React.FC = () => {
// Access layout state and state setters
const { currentLayout, setLayout } = useLayoutStore(); 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); const [generate, setGenerate] = useState<boolean>(false);
// Handles file upload and DXF parsing
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => { 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]; const file = event.target.files?.[0];
if (!file || !file.name.endsWith(".dxf")) { if (!file || !file.name.endsWith(".dxf")) {
alert("Please upload a valid .dxf file."); alert("Please upload a valid .dxf file.");
@ -23,47 +28,60 @@ const SelectFloorPlan: React.FC = () => {
} }
const reader = new FileReader(); const reader = new FileReader();
reader.onload = async (e) => { reader.onload = async (e) => {
const dxfContent = e.target?.result as string; const dxfContent = e.target?.result as string;
try { try {
const parser = new DxfParser(); const parser = new DxfParser();
const parsedData = parser.parse(dxfContent) as DXFData; const parsedDatas = parser.parse(dxfContent) as DXFData;
const geometries = convertDXFToThree(parsedData); const geometries = convertDXFToThree(parsedDatas);
setParsedFile(parsedDatas);
setParsedFile(parsedData); setObjValue({ x: 0, y: 0, z: 0 });
setDfxUploaded(geometries); setDfxUploaded(geometries);
} catch (error) { } 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(() => { useEffect(() => {
if (parsedFile !== undefined) if (parsedFile !== undefined) {
getWallPointsFromBlueprint({ parsedData: parsedFile, setDfxGenerate }) getWallPointsFromBlueprint({ parsedData: parsedFile, setDfxGenerate, objValue });
}, [generate]) }
}, [generate]);
useEffect(() => {
console.log("parsedFile: ", parsedFile);
}, [parsedFile]);
return ( return (
<div className="select-floorplane-wrapper"> <div className="select-floorplane-wrapper">
Preset Layouts Preset Layouts
<div className="presets-container"> <div className="presets-container">
<button <button
className={`preset ${currentLayout === "layout1" ? "active" : ""}`} className={`preset ${currentLayout === "layout1" ? "active" : ""}`}
onClick={() => { onClick={() => {
setLayout("layout1"); setLayout("layout1");
setDfxUploaded([]);
setGenerate(false);
}} }}
> >
Preset 1 Preset 1
</button> </button>
<button <button
className={`preset ${currentLayout === "layout2" ? "active" : ""}`} className={`preset ${currentLayout === "layout2" ? "active" : ""}`}
onClick={() => { onClick={() => {
setLayout("layout2"); setLayout("layout2");
setDfxUploaded([]);
setGenerate(false);
}} }}
> >
Preset 2 Preset 2
@ -76,6 +94,7 @@ const SelectFloorPlan: React.FC = () => {
style={{ display: "none", width: "10px" }} style={{ display: "none", width: "10px" }}
onChange={handleFileUpload} onChange={handleFileUpload}
/> />
<label <label
htmlFor="file-upload" htmlFor="file-upload"
className="preset" className="preset"
@ -86,24 +105,21 @@ const SelectFloorPlan: React.FC = () => {
> >
Upload Upload
</label> </label>
<button <button
type="button" type="button"
id="generate-upload" id="generate-upload"
onClick={() => { onClick={() => {
setDfxUploaded([]) setDfxUploaded([]);
setLayout(null);
setGenerate(!generate); setGenerate(!generate);
}} }}
> >
Generate Generate
</button> </button>
</div> </div>
</div > </div>
); );
}; };
export default SelectFloorPlan; export default SelectFloorPlan;

View File

@ -314,7 +314,13 @@ export default function Builder() {
<CalculateAreaGroup /> <CalculateAreaGroup />
<NavMesh lines={lines} /> <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 /> <LayoutImage />
</Bvh> </Bvh>

View File

@ -1,38 +1,145 @@
import { useEffect } from 'react'; import { useEffect, useRef } from 'react';
import { useDfxUpload, useToggleView } from '../../../store/builder/store'; import { useDfxUpload, useSocketStore, useToggleView, useUpdateScene } from '../../../store/builder/store';
import { LineBasicMaterial, Line } from "three"; import { LineBasicMaterial, Line } from 'three';
import loadInitialPoint from '../IntialLoad/loadInitialPoint'; import loadInitialPoint from '../IntialLoad/loadInitialPoint';
import loadInitialLine from '../IntialLoad/loadInitialLine'; import loadInitialLine from '../IntialLoad/loadInitialLine';
import { TransformControls } from '@react-three/drei'; 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) => { // Interface defining the props for the DxfFile component
const { dfxuploaded, dfxWallGenerate } = useDfxUpload(); 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 { 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(() => { useEffect(() => {
if (dfxWallGenerate && dragPointControls && floorPlanGroupPoint && currentLayerPoint && floorPlanGroupLine) { if (
lines.current = dfxWallGenerate; 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); loadInitialPoint(lines, floorPlanGroupPoint, currentLayerPoint, dragPointControls);
loadInitialLine(floorPlanGroupLine, lines); loadInitialLine(floorPlanGroupLine, lines);
// Trigger scene update
setUpdateScene(true); 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 ( return (
<> <>
{dfxuploaded && dfxuploaded.length > 0 && toggleView && dfxuploaded?.map((geometry: any, index: any) => ( {/* Render DXF lines with transform controls when DXF data is available and view is toggled */}
<TransformControls> {dfxuploaded &&
<group> dfxuploaded.length > 0 &&
<primitive 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} key={index}
object={new Line(geometry, new LineBasicMaterial({ color: 'red' }))} object={line}
rotation={[-Math.PI / 2, 0, 0]} onMouseUp={() => handleTransformChange(index)}
/> >
</group> {/* Render the line with current position from state */}
<primitive object={line} position={[objValue.x, objValue.y, objValue.z]} />
</TransformControls> </TransformControls>
))} );
})}
</> </>
); );
} };
export default DxfFile; export default DxfFile;

View File

@ -1,41 +1,59 @@
import { MathUtils, Vector3, BufferGeometry } from "three"; import { MathUtils, Vector3, BufferGeometry } from "three";
interface Props { interface Props {
parsedData: DXFData; parsedData?: DXFData; // Parsed DXF file data
setDfxGenerate: (walls: WallLineVertex[][]) => void; 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({ export function getWallPointsFromBlueprint({
parsedData, parsedData,
setDfxGenerate, setDfxGenerate,
objValue,
}: Props) { }: Props) {
// Early return if no data is provided
if (!parsedData) return;
if (!parsedData.entities) return; if (!parsedData.entities) return;
const unit = 1000; // Convert mm to meters const unit = 1000; // Conversion factor from millimeters to meters
const wallVertex: WallLineVertex[][] = []; // Stores all generated wall segments
const wallVertex: WallLineVertex[][] = [];
// Process each entity in the DXF file
parsedData.entities.forEach((entity: DXFEntity) => { parsedData.entities.forEach((entity: DXFEntity) => {
// Handle LINE entities
if (entity.type === "LINE" && entity.vertices) { if (entity.type === "LINE" && entity.vertices) {
// Create start and end vectors with unit conversion and position offset
const startVec = new Vector3( const startVec = new Vector3(
entity.vertices[0].x / unit, entity.vertices[0].x / unit,
0.01, 0.01, // Slightly above ground to avoid z-fighting
-entity.vertices[0].y / unit -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( const endVec = new Vector3(
entity.vertices[1].x / unit, entity.vertices[1].x / unit,
0.01, 0.01,
-entity.vertices[1].y / unit -entity.vertices[1].y / unit
); ).add(new Vector3(objValue.x, 0, objValue.z));
// Check if points already exist to avoid duplicates
const existingStart = wallVertex const existingStart = wallVertex
.flat() .flat()
.find((v) => v[0].equals(startVec)); .find((v) => v[0].equals(startVec));
const startPoint: WallLineVertex = existingStart || [ const startPoint: WallLineVertex = existingStart || [
startVec, startVec,
MathUtils.generateUUID(), MathUtils.generateUUID(), // Generate unique ID for new points
1, 1, // Default weight
"WallLine", "WallLine", // Type identifier
]; ];
const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec)); const existingEnd = wallVertex.flat().find((v) => v[0].equals(endVec));
@ -46,22 +64,29 @@ export function getWallPointsFromBlueprint({
"WallLine", "WallLine",
]; ];
// Add the line segment to our collection
wallVertex.push([startPoint, endPoint]); 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++) { for (let i = 0; i < entity.vertices.length - 1; i++) {
// Convert vertices to Three.js vectors with offset
const startVec = new Vector3( const startVec = new Vector3(
entity.vertices[i].x / unit, entity.vertices[i].x / unit,
0.01, 0.01,
-entity.vertices[i].y / unit -entity.vertices[i].y / unit
); ).add(new Vector3(objValue.x, 0, objValue.z));
const endVec = new Vector3( const endVec = new Vector3(
entity.vertices[i + 1].x / unit, entity.vertices[i + 1].x / unit,
0.01, 0.01,
-entity.vertices[i + 1].y / unit -entity.vertices[i + 1].y / unit
); ).add(new Vector3(objValue.x, 0, objValue.z));
// Check for existing points
const existingStart = wallVertex const existingStart = wallVertex
.flat() .flat()
.find((v) => v[0].equals(startVec)); .find((v) => v[0].equals(startVec));
@ -82,36 +107,52 @@ export function getWallPointsFromBlueprint({
wallVertex.push([startPoint, endPoint]); wallVertex.push([startPoint, endPoint]);
// Store first point and create closing segment if this is the last vertex
if (i === 0) firstPoint = startPoint; if (i === 0) firstPoint = startPoint;
if (i === entity.vertices.length - 2 && firstPoint) if (i === entity.vertices.length - 2 && firstPoint) {
wallVertex.push([endPoint, firstPoint]); wallVertex.push([endPoint, firstPoint]);
} }
} else if (entity.type === "ARC") { }
}
// Handle ARC entities
else if (entity.type === "ARC") {
const { center, radius, startAngle, endAngle } = entity; const { center, radius, startAngle, endAngle } = entity;
// Validate required ARC properties
if ( if (
!center || !center ||
radius === undefined || radius === undefined ||
startAngle === undefined || startAngle === undefined ||
endAngle === undefined endAngle === undefined
) ) {
return; 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++) { for (let i = 0; i < arcPoints.length - 1; i++) {
const startVec = arcPoints[i]; const startVec = arcPoints[i];
const endVec = arcPoints[i + 1]; const endVec = arcPoints[i + 1];
// Check for existing points
const existingStart = wallVertex const existingStart = wallVertex
.flat() .flat()
.find((v) => v[0].equals(startVec)); .find((v) => v[0].equals(startVec));
@ -132,10 +173,13 @@ export function getWallPointsFromBlueprint({
wallVertex.push([startPoint, endPoint]); wallVertex.push([startPoint, endPoint]);
} }
} else { }
// Log unsupported entity types
else {
console.error("Unsupported entity type:", entity.type); 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) => ({ export const useDfxUpload = create<any>((set: any) => ({
dfxuploaded: [], dfxuploaded: [],
dfxWallGenerate: [], dfxWallGenerate: [],
objValue: { x: 0, y: 0, z: 0 },
setDfxUploaded: (x: any) => set({ dfxuploaded: x }), setDfxUploaded: (x: any) => set({ dfxuploaded: x }),
setDfxGenerate: (x: any) => set({ dfxWallGenerate: x }), setDfxGenerate: (x: any) => set({ dfxWallGenerate: x }),
setObjValue: (x: any) => set({ objValue: x }),
})); }));
type InputValuesStore = { type InputValuesStore = {
@ -551,7 +553,7 @@ interface CompareStore {
} }
export const useCompareStore = create<CompareStore>((set) => ({ export const useCompareStore = create<CompareStore>((set) => ({
comparePopUp: false, comparePopUp: true,
setComparePopUp: (value) => set({ comparePopUp: value }), setComparePopUp: (value) => set({ comparePopUp: value }),
toggleComparePopUp: () => toggleComparePopUp: () =>
set((state) => ({ comparePopUp: !state.comparePopUp })), set((state) => ({ comparePopUp: !state.comparePopUp })),