A comprehensive Python package for creating, reading, modifying, and writing DXF (Drawing Exchange Format) documents with support for multiple DXF versions.
—
Vector path representation and manipulation for advanced geometric operations. The path system provides unified handling of complex geometric shapes with support for conversion between different representations.
Core path representation with vertices and drawing commands for complex geometric shapes.
class Path:
"""Vector path with vertices and drawing commands"""
def __init__(self, start = None): ...
@property
def start(self) -> Vec3:
"""Path start point"""
@property
def end(self) -> Vec3:
"""Path end point"""
@property
def vertices(self) -> List[Vec3]:
"""All path vertices"""
@property
def commands(self):
"""Path command sequence"""
@property
def is_closed(self) -> bool:
"""True if path forms a closed loop"""
def line_to(self, location) -> 'Path':
"""Add line segment to location"""
def curve3_to(self, location, ctrl) -> 'Path':
"""Add quadratic Bézier curve"""
def curve4_to(self, location, ctrl1, ctrl2) -> 'Path':
"""Add cubic Bézier curve"""
def close(self) -> 'Path':
"""Close path with line to start"""
def close_sub_path(self) -> 'Path':
"""Close current sub-path"""
def move_to(self, location) -> 'Path':
"""Start new sub-path at location"""
def transform(self, matrix: Matrix44) -> 'Path':
"""Transform all vertices by matrix"""
def reversed(self) -> 'Path':
"""Return path with reversed direction"""
def clockwise(self) -> 'Path':
"""Return path with clockwise orientation"""
def counter_clockwise(self) -> 'Path':
"""Return path with counter-clockwise orientation"""
def approximate(self, segments: int = 20) -> List[Vec3]:
"""Approximate path with line segments"""
def flattening(self, distance: float) -> List[Vec3]:
"""Flatten path to vertices within distance tolerance"""
class Command(Enum):
"""Path command types"""
LINE_TO = 1
CURVE3_TO = 2 # Quadratic Bézier
CURVE4_TO = 3 # Cubic Bézier
MOVE_TO = 4Usage examples:
from ezdxf.path import Path
from ezdxf.math import Vec3
# Create a complex path
path = Path(Vec3(0, 0))
path.line_to((10, 0))
path.curve4_to((20, 10), (12, -5), (18, 15)) # Cubic Bézier
path.line_to((0, 10))
path.close()
# Transform path
from ezdxf.math import Matrix44
matrix = Matrix44.scale(2, 2, 1)
scaled_path = path.transform(matrix)
# Convert to line segments
vertices = path.approximate(segments=50)
polyline = msp.add_lwpolyline(vertices)Convert DXF entities to path representations for unified geometric processing.
def make_path(entity) -> Path:
"""
Convert DXF entity to path representation.
Parameters:
- entity: DXF entity (LINE, ARC, CIRCLE, ELLIPSE, SPLINE, LWPOLYLINE, etc.)
Returns:
Path: Vector path representation
"""
def from_vertices(vertices, close: bool = False) -> Path:
"""Create path from vertex sequence"""
def from_hatch(hatch) -> List[Path]:
"""
Create paths from hatch boundary definitions.
Parameters:
- hatch: HATCH entity
Returns:
List[Path]: Boundary paths (exterior and holes)
"""
def from_hatch_boundary_path(boundary_path) -> Path:
"""Create path from single hatch boundary path"""Usage examples:
from ezdxf.path import make_path, from_vertices
from ezdxf.math import Vec3
# Convert entities to paths
line_entity = msp.add_line((0, 0), (10, 10))
line_path = make_path(line_entity)
circle_entity = msp.add_circle((5, 5), 3)
circle_path = make_path(circle_entity) # Closed circular path
spline_entity = msp.add_spline([(0, 0), (5, 10), (10, 0)])
spline_path = make_path(spline_entity)
# Create path from vertices
vertices = [Vec3(0, 0), Vec3(5, 10), Vec3(10, 5), Vec3(15, 0)]
vertex_path = from_vertices(vertices, close=True)
# Extract paths from hatch
hatch = msp.add_hatch()
# ... configure hatch boundaries ...
hatch_paths = from_hatch(hatch)Convert paths back to DXF entities in various formats.
def to_lines(paths) -> List[Line]:
"""Convert paths to LINE entities"""
def to_polylines2d(paths, *, close: bool = True, max_flattening_distance: float = 0.01,
dxfattribs: dict = None) -> List[Polyline]:
"""Convert paths to 2D POLYLINE entities"""
def to_polylines3d(paths, *, close: bool = True, max_flattening_distance: float = 0.01,
dxfattribs: dict = None) -> List[Polyline]:
"""Convert paths to 3D POLYLINE entities"""
def to_lwpolylines(paths, *, close: bool = True, max_flattening_distance: float = 0.01,
dxfattribs: dict = None) -> List[LWPolyline]:
"""Convert paths to LWPOLYLINE entities"""
def to_hatches(paths, *, edge_path: bool = True, max_flattening_distance: float = 0.01,
dxfattribs: dict = None) -> List[Hatch]:
"""Convert paths to HATCH entities"""
def to_mpolygons(paths, *, max_flattening_distance: float = 0.01,
dxfattribs: dict = None) -> List[MPolygon]:
"""Convert paths to MPOLYGON entities"""
def to_bsplines_and_vertices(paths, *, g1_tol: float = 1e-4, max_flattening_distance: float = 0.01,
dxfattribs: dict = None) -> List:
"""Convert paths to B-splines and vertices"""
def to_splines_and_polylines(paths, *, g1_tol: float = 1e-4, max_flattening_distance: float = 0.01,
dxfattribs: dict = None) -> List:
"""Convert paths to splines and polylines"""Usage examples:
from ezdxf.path import to_lwpolylines, to_hatches, to_splines_and_polylines
# Convert paths to different entity types
paths = [circle_path, spline_path, vertex_path]
# Convert to lightweight polylines
lwpolylines = to_lwpolylines(paths, close=True, dxfattribs={'layer': 'PATHS'})
for poly in lwpolylines:
msp.add_entity(poly)
# Convert to hatches with solid fill
hatches = to_hatches(paths, dxfattribs={'color': 1})
for hatch in hatches:
hatch.set_solid_fill()
msp.add_entity(hatch)
# Convert to splines where possible, polylines elsewhere
entities = to_splines_and_polylines(paths, g1_tol=1e-3)
for entity in entities:
msp.add_entity(entity)Generate paths for common geometric shapes and patterns.
def unit_circle(start_angle: float = 0, end_angle: float = 360,
segments: int = None) -> Path:
"""
Create circular path with unit radius.
Parameters:
- start_angle: start angle in degrees
- end_angle: end angle in degrees
- segments: number of line segments (auto if None)
Returns:
Path: Circular path
"""
def elliptic_transformation(path: Path, center, rx: float, ry: float,
start_angle: float = 0) -> Path:
"""Transform unit circle path to ellipse"""
def rect(width: float = 1, height: float = 1, center: bool = True) -> Path:
"""Create rectangular path"""
def wedge(start_angle: float, end_angle: float, radius: float = 1) -> Path:
"""Create wedge-shaped path (pie slice)"""
def star(count: int, r1: float, r2: float, rotation: float = 0) -> Path:
"""Create star-shaped path with alternating radii"""
def gear(count: int, top_width: float, bottom_width: float, height: float,
outside_radius: float = 1) -> Path:
"""Create gear-shaped path"""
def ngon(count: int, radius: float = 1, rotation: float = 0,
center: bool = True) -> Path:
"""Create regular polygon path"""
def helix(radius: float, pitch: float, turns: float, ccw: bool = True) -> Path:
"""Create helical path"""Usage examples:
from ezdxf.path.shapes import unit_circle, rect, star, ngon
from ezdxf.math import Vec3
import math
# Create basic shapes
circle = unit_circle(0, 360, segments=32) # Full circle
arc = unit_circle(45, 135, segments=16) # Quarter arc
# Transform to ellipse
ellipse = elliptic_transformation(circle, Vec3(10, 5), rx=8, ry=4)
# Create rectangle
rectangle = rect(width=10, height=6, center=True)
# Create star shape
star_path = star(count=6, r1=5, r2=2.5, rotation=math.pi/6)
# Create regular polygon
hexagon = ngon(count=6, radius=4)
# Convert to entities
for path in [ellipse, rectangle, star_path, hexagon]:
entities = to_lwpolylines([path], close=True)
for entity in entities:
msp.add_entity(entity)Utility functions for path analysis, transformation, and processing.
def bbox(paths) -> tuple:
"""
Calculate bounding box of paths.
Parameters:
- paths: sequence of Path objects
Returns:
tuple: (min_x, min_y, max_x, max_y) bounding box
"""
def precise_bbox(paths) -> tuple:
"""Calculate precise bounding box including curve control points"""
def fit_paths_into_box(paths, size, uniform: bool = True, source_box = None):
"""
Scale paths to fit within specified box.
Parameters:
- paths: sequence of Path objects
- size: target box size (width, height)
- uniform: maintain aspect ratio
- source_box: explicit source bounding box
Returns:
List[Path]: Scaled paths
"""
def transform_paths(paths, matrix: Matrix44):
"""Transform paths by transformation matrix"""
def transform_paths_to_ocs(paths, ocs):
"""Transform paths to Object Coordinate System"""
def reverse_paths(paths):
"""Reverse direction of all paths"""
def clockwise_paths(paths):
"""Ensure all paths have clockwise orientation"""
def counter_clockwise_paths(paths):
"""Ensure all paths have counter-clockwise orientation"""Usage examples:
from ezdxf.path.tools import bbox, fit_paths_into_box, transform_paths
from ezdxf.math import Matrix44
# Calculate bounding box
paths = [circle_path, star_path, hexagon]
min_x, min_y, max_x, max_y = bbox(paths)
print(f"Bounding box: ({min_x}, {min_y}) to ({max_x}, {max_y})")
# Fit paths into specific size
target_size = (100, 80) # 100 units wide, 80 units tall
fitted_paths = fit_paths_into_box(paths, target_size, uniform=True)
# Transform paths
rotation = Matrix44.z_rotate(math.radians(30))
translation = Matrix44.translate(50, 50, 0)
combined = translation @ rotation
transformed_paths = transform_paths(paths, combined)Advanced path organization for complex polygon structures with holes and islands.
def make_polygon_structure(paths):
"""
Create hierarchical polygon structure from paths.
Parameters:
- paths: sequence of closed Path objects
Returns:
List: Nested polygon structure with exterior and holes
"""
def winding_deconstruction(polygons):
"""Apply winding rules to polygon structure"""
def group_paths(paths):
"""Group related paths by proximity and containment"""
def flatten_polygons(polygons):
"""Flatten polygon hierarchy to simple path list"""
class PolygonStructure:
"""Hierarchical polygon representation with holes"""
def __init__(self, exterior: Path, holes: List[Path] = None): ...
@property
def exterior(self) -> Path:
"""Exterior boundary path"""
@property
def holes(self) -> List[Path]:
"""Interior hole paths"""
def add_hole(self, hole: Path): ...
def remove_hole(self, hole: Path): ...
def area(self) -> float:
"""Calculate polygon area (exterior minus holes)"""
def contains_point(self, point) -> bool:
"""Test if point is inside polygon"""Specialized rendering functions for different entity types.
def render_lines(layout, paths, *, dxfattribs: dict = None): ...
def render_lwpolylines(layout, paths, *, close: bool = True, dxfattribs: dict = None): ...
def render_splines(layout, paths, *, degree: int = 3, dxfattribs: dict = None): ...
def render_hatches(layout, paths, *, pattern: str = 'SOLID', dxfattribs: dict = None): ...
def add_bezier4p(path: Path, start, ctrl1, ctrl2, end): ...
def add_bezier3p(path: Path, start, ctrl, end): ...
def add_ellipse(path: Path, center, rx: float, ry: float, start_param: float = 0,
end_param: float = 2*math.pi): ...class LineTo:
"""Line command for paths"""
def __init__(self, end: Vec3): ...
class Curve3To:
"""Quadratic Bézier curve command"""
def __init__(self, end: Vec3, ctrl: Vec3): ...
class Curve4To:
"""Cubic Bézier curve command"""
def __init__(self, end: Vec3, ctrl1: Vec3, ctrl2: Vec3): ...
class MoveTo:
"""Move command for starting new sub-paths"""
def __init__(self, end: Vec3): ...
# Type aliases
AnyCurve = Union[Curve3To, Curve4To]
PathElement = Union[LineTo, Curve3To, Curve4To, MoveTo]Complete usage example:
import ezdxf
from ezdxf.path import Path, make_path, to_lwpolylines
from ezdxf.path.shapes import unit_circle, star, rect
from ezdxf.path.tools import bbox, fit_paths_into_box
from ezdxf.math import Vec3, Matrix44
import math
# Create document
doc = ezdxf.new()
msp = doc.modelspace()
# Create various paths
circle = unit_circle(segments=24)
star_path = star(count=5, r1=4, r2=2)
rectangle = rect(width=6, height=4)
# Position paths
circle = circle.transform(Matrix44.translate(0, 0, 0))
star_path = star_path.transform(Matrix44.translate(10, 0, 0))
rectangle = rectangle.transform(Matrix44.translate(20, 0, 0))
# Combine and process
all_paths = [circle, star_path, rectangle]
# Fit to standard size
fitted_paths = fit_paths_into_box(all_paths, (50, 30), uniform=True)
# Convert to entities and add to layout
entities = to_lwpolylines(fitted_paths, close=True, dxfattribs={'layer': 'PATHS'})
for entity in entities:
msp.add_entity(entity)
# Save document
doc.saveas('path_processing_example.dxf')Install with Tessl CLI
npx tessl i tessl/pypi-ezdxf