Update Generated DXF Walls to backend
This commit is contained in:
parent
a2af3b445d
commit
3d5159f1b6
|
@ -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,24 +105,21 @@ const SelectFloorPlan: React.FC = () => {
|
|||
>
|
||||
Upload
|
||||
</label>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
id="generate-upload"
|
||||
onClick={() => {
|
||||
setDfxUploaded([])
|
||||
setDfxUploaded([]);
|
||||
setLayout(null);
|
||||
setGenerate(!generate);
|
||||
}}
|
||||
>
|
||||
Generate
|
||||
</button>
|
||||
</div>
|
||||
</div >
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectFloorPlan;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 })),
|
||||
|
|
Loading…
Reference in New Issue