kerf-engine/engine/output/dxf.py
jlightner 9540f37f70 test: Wire post-processing into /engine/trace, add output_format routin…
- "engine/api/routes.py"
- "engine/tests/test_api.py"

GSD-Task: S02/T03
2026-03-26 04:39:52 +00:00

66 lines
1.9 KiB
Python

"""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")