diff --git a/app/src/components/temporary/SelectFloorPlan.tsx b/app/src/components/temporary/SelectFloorPlan.tsx index 8f16006..2035173 100644 --- a/app/src/components/temporary/SelectFloorPlan.tsx +++ b/app/src/components/temporary/SelectFloorPlan.tsx @@ -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(); + // Local state to store the parsed DXF file + const [parsedFile, setParsedFile] = useState(undefined); + + // Flag to trigger generation after file upload const [generate, setGenerate] = useState(false); + // Handles file upload and DXF parsing const handleFileUpload = async (event: React.ChangeEvent) => { + 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 (
Preset Layouts +
+
-
+ ); }; -export default SelectFloorPlan; - - - - - +export default SelectFloorPlan; \ No newline at end of file diff --git a/app/src/modules/builder/builder.tsx b/app/src/modules/builder/builder.tsx index 82c93ba..d6df0d0 100644 --- a/app/src/modules/builder/builder.tsx +++ b/app/src/modules/builder/builder.tsx @@ -314,7 +314,13 @@ export default function Builder() { - + diff --git a/app/src/modules/builder/dfx/LoadBlueprint.tsx b/app/src/modules/builder/dfx/LoadBlueprint.tsx index 9be72dc..66fc558 100644 --- a/app/src/modules/builder/dfx/LoadBlueprint.tsx +++ b/app/src/modules/builder/dfx/LoadBlueprint.tsx @@ -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([]); + + /** + * 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) => ( - - - 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 ( + - - - ))} + object={line} + onMouseUp={() => handleTransformChange(index)} + > + {/* Render the line with current position from state */} + + + ); + })} ); -} +}; -export default DxfFile; +export default DxfFile; \ No newline at end of file diff --git a/app/src/modules/builder/dfx/functions/getWallPointsFromBlueprint.ts b/app/src/modules/builder/dfx/functions/getWallPointsFromBlueprint.ts index 72057dd..13cae84 100644 --- a/app/src/modules/builder/dfx/functions/getWallPointsFromBlueprint.ts +++ b/app/src/modules/builder/dfx/functions/getWallPointsFromBlueprint.ts @@ -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); } diff --git a/app/src/store/builder/store.ts b/app/src/store/builder/store.ts index c0af867..01dc754 100644 --- a/app/src/store/builder/store.ts +++ b/app/src/store/builder/store.ts @@ -494,8 +494,10 @@ export const useProcessBar = create((set: any) => ({ export const useDfxUpload = create((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((set) => ({ - comparePopUp: false, + comparePopUp: true, setComparePopUp: (value) => set({ comparePopUp: value }), toggleComparePopUp: () => set((state) => ({ comparePopUp: !state.comparePopUp })),