"""DXF output generator — AC1015+ DXF from PostProcessResult using ezdxf.""" from __future__ import annotations import io import ezdxf from pipeline.postprocess import PathInfo, PostProcessResult def _add_path_to_msp( msp: ezdxf.layouts.BaseLayout, path: PathInfo, layer: str = "0", ) -> None: """Add a single PathInfo as an LWPOLYLINE entity to the modelspace. Closed paths get the LWPOLYLINE close flag set. Islands are placed on a separate "ISLANDS" layer for downstream CAM tools. """ coords = path.simplified_coords if len(coords) < 2: return target_layer = "ISLANDS" if path.is_island else layer # LWPOLYLINE uses (x, y[, start_width, end_width, bulge]) tuples points = [(x, y) for x, y in coords] # Remove duplicate close point if the polyline close flag handles it if path.is_closed and len(points) > 1 and points[0] == points[-1]: points = points[:-1] msp.add_lwpolyline( points, dxfattribs={"layer": target_layer}, close=path.is_closed, ) def generate_dxf(result: PostProcessResult) -> bytes: """Generate an AC1015 (AutoCAD R2000) DXF file from post-processed path data. Creates LWPOLYLINE entities from simplified coordinate data. Islands (holes) are placed on an "ISLANDS" layer; outer contours on the default "0" layer. Args: result: PostProcessResult from the post-processing pipeline. Returns: DXF file content as bytes. """ doc = ezdxf.new(dxfversion="R2000") # AC1015 msp = doc.modelspace() # Create ISLANDS layer for hole/island paths doc.layers.add("ISLANDS", color=1) # color 1 = red in AutoCAD for path in result.paths: _add_path_to_msp(msp, path) # Write to string buffer, then encode to bytes stream = io.StringIO() doc.write(stream) return stream.getvalue().encode("utf-8")