v0.11.0
v0.11.0 (2026-06-22)
78 commits since v0.10.0. ~10 400 insertions across 113 source files.
Breaking Changes
See the full Migration Guide below.
Rect— tuple struct → named fields (min/maxasPoint)Rect3D— flat fields →min/maxasPoint3Doffset_polygon()gained a requiredJoinStyleparameter (Python defaults to"miter")trochoid_along()output — added longitudinal component (true trochoidal loop)ScanModevariants renamed to UPPER_CASE (Segmented→SEGMENTED,FullSweep→FULL_SWEEP)
New Modules
raygeo.geo.algo.astar— A* pathfinding with rasterized grid support, obstacle avoidance, and configurable heuristics.raygeo.geo.algo.cleared_area— Incremental union of tool-swept polygons withSpatialGrid-windowed queries. Supportsincorporate,frontier,bites,fragments,remaining_in_inset, andadd_cleared_polygons.raygeo.geo.algo.fillet— Fillet arc-end trimming and linking primitives extracted from the HSM module.raygeo.geo.algo.medial_axis— Delaunay-circumcenter Medial Axis Transform with per-branch boustrophedon routing (mat_path).raygeo.geo.algo.polylabel— Pole of inaccessibility with hole/island support (find_largest_circle,get_polygon_closest_point).raygeo.geo.algo.spiral— Flat Archimedean spiral primitive (generate_spiral/SpiralOptions).raygeo.mesh— Full PDE meshing pipeline: constrained Delaunay triangulation (spade), linear FEM Laplace solver (nalgebraCG), gradient field computation, spiral tracing across triangle meshes, and remeshing. Submodules:build,laplace,gradient,pde,remesh,types.
HSM (High Speed Machining)
- Adaptive clearing wavefronts — inside-out expansion loop with frontier-based clipping, simplification, and multi-island support.
- Adaptive peeling — peeling algorithm for centred seed circles with full-ring cutting arc generation and filleted arc-end linking.
- Adaptive entry — Helix → spiral (wide area) or ZigZag ramp (tight slot) with
AdaptiveEntryOptions/AdaptiveEntryResult. - ClearedArea methods —
incorporate,frontier,bites,fragments,remaining_in_inset,add_cleared_polygons. - Constrained path smoothing —
smooth_path()with shortcut + Gaussian relaxation, obstacle clearance, integrated intolink_filleted_arcsandAdaptiveWavefrontOptions. compute_inset_region— replacescompute_valid_tool_areathroughout the HSM pipeline.
New Functions (existing modules)
raygeo.geo.shape
- point:
circumcenter - arc:
arc_through_point,get_polyline_turn_sign - bezier:
fit_cubic_bezier,nearest_tangent_circle_on_bezier - circle:
find_tangent_circle_centers,nearest_tangent_circle_on_polyline - line:
does_line_cross_polygon,get_segment_segment_distance,longest_line_through_point,get_interior_angle,interpolated_segment_3d - polygon:
apply_minimum_curvature,get_circle_polygon,get_polygon_closest_point,get_polygons_closest_point,get_polyline_bounds,get_segment_swept_polygon,does_path_sweep_intersect_polygon,get_polyline_closest_point,trim_polyline_at,trim_polyline_angular_ends - polygon3d:
walk_along_polyline_3d,walk_along_polygon_3d,deduplicate_polyline_3d,fillet_polyline_3d,get_polygon_area_3d,get_polygon_signed_area_3d,interpolated_segment_3d
raygeo.geo.algo
- fitting:
arc_between_two_points(exposed asgenerate_arc_between_two_points),generate_linking_arc - interp:
barycentric_interpolate,barycentric_weights - intersect:
get_ray_line_intersection,get_ray_polygon_intersection - offset:
compute_inset_region,find_deepest_cores(binary-search core detection) - smooth:
smooth_path
Refactors
ops/assembly— New submodule consolidatinghsm,lead_in_out,overscan,polyline,raster/, andtabs. HSM assembly moved fromgeo.algotoops.assemblyand refactored to returnOpscontainers.Rect/Rect3D— Converted to useglamvector types (DVec2/DVec3).Rectnow{ min: Point, max: Point };Rect3Dnow{ min: Point3D, max: Point3D }.- glam migration — Manual scalar math (
hypot,sqrt, manual dot/cross, manuallerp, manualnormalize) replaced withglammethods across all geo modules. offset_polygon— Restored original name (was temporarilyoffset_polygon_with_style), added properJoinStyleenum for Python API.ScanMode— Python-facing variants renamed to PEP8 UPPER_CASE via#[pyo3(name = "...")].- Module cleanup — Redundant aliases removed, local imports moved to module level, type annotations fixed in polygon stubs.
Infrastructure
- PDE meshing —
spade-based constrained Delaunay triangulation withTriangleMesh, boundary tagging, Steiner point insertion. - FEM Laplace solver — Linear FEM elements with conjugate-gradient solve (
nalgebra/nalgebra-sparse), gradient field computation, convergence history. - Profiling —
src/prof.rswith RAIIprof_guardfor operation timing. - New dependencies —
spade = "2",nalgebra = "0.35",nalgebra-sparse = "0.12". - Visual testing —
tools/visual_test.pysplit into submodule with one file per page; example image generator avoids re-generation if timestamps are unchanged. - Makefile — Added
venvtarget for virtual environment setup.
Removals
morph_spiralmodule removed (did not work well enough).geo.algo.hsmcompletely removed; remaining function moved toops.assembly.hsm.compute_valid_tool_areareplaced bycompute_inset_region.
Migration Guide: v0.10.0 → v0.11.0
This section covers only changes that break code written for v0.10.0.
New features (modules, functions added since v0.10.0) are not listed —
they require no migration.
1. Rect type: tuple struct → named fields
src/types.rs:47
// v0.10.0
pub struct Rect(pub f64, pub f64, pub f64, pub f64);
// x_min y_min x_max y_max
// v0.11.0
pub struct Rect {
pub min: Point, // DVec2
pub max: Point, // DVec2
}Rust code that constructs or destructures Rect as a 4-tuple will not
compile:
// BROKEN
let r = Rect(0.0, 0.0, 10.0, 20.0);
let Rect(x1, y1, x2, y2) = r;
// FIXED
let r = Rect::new(0.0, 0.0, 10.0, 20.0);
let x1 = r.min.x;
let y1 = r.min.y;
let x2 = r.max.x;
let y2 = r.max.y;Python impact: The Python API still passes Rect as
(x_min, y_min, x_max, y_max) tuples. Python code does not need
changes.
2. Rect3D type: flat fields → min/max
src/types.rs:181
// v0.10.0
pub struct Rect3D {
pub x_min: f64,
pub x_max: f64,
pub y_min: f64,
pub y_max: f64,
pub z_min: f64,
pub z_max: f64,
}
// v0.11.0
pub struct Rect3D {
pub min: Point3D,
pub max: Point3D,
}Replace field access:
| Old | New |
|---|---|
r.x_min |
r.min.x |
r.y_max |
r.max.y |
r.z_min |
r.min.z |
r.z_max |
r.max.z |
Construction changes from Rect3D { x_min: ..., x_max: ..., ... } to
Rect3D::new(min_pt, max_pt) or Rect3D { min: ..., max: ... }.
3. offset_polygon() gained a JoinStyle parameter
src/geo/shape/polygon.rs:648
The function name is unchanged, but the Rust signature now requires a
JoinStyle argument. Python binding keeps the old signature by
defaulting to "miter".
// Rust — new required parameter
offset_polygon(&poly, offset, JoinStyle::Miter);
offset_polygon(&poly, offset, JoinStyle::Round);
offset_polygon(&poly, offset, JoinStyle::Square);# Python — backwards compatible, defaults to JoinStyle.Miter
from raygeo.geo.shape.polygon import JoinStyle
offset_polygon(poly, offset) # defaults to Miter
offset_polygon(poly, offset, JoinStyle.Miter)
offset_polygon(poly, offset, JoinStyle.Round)
offset_polygon(poly, offset, JoinStyle.Square)4. trochoid_along() output geometry changed
src/geo/algo/trochoid.rs:108-109
v0.10.0 produced a sine-wave oscillation perpendicular to the carrier
path. v0.11.0 adds a longitudinal (tangential) component, making it a
true trochoidal loop where the tool reverses direction in each cycle:
// v0.10.0 — simple lateral sine wave
let x = pos.x + lateral * normal.x;
let y = pos.y + lateral * normal.y;
// v0.11.0 — true trochoidal loop
let longitudinal = loop_radius * theta.sin();
let x = pos.x + lateral * normal.x + longitudinal * tangent.x;
let y = pos.y + lateral * normal.y + longitudinal * tangent.y;This improves chip evacuation and limits tool engagement angle, but the
output path is completely different from v0.10.0 — expect longer
paths with backward/forward motion. Tests that assert exact point
positions on trochoidal paths will fail.
5. ScanMode enum variants renamed to UPPER_CASE
src/python/ops/raster.rs:26-28
PEP8 convention — enum members now use UPPER_CASE:
# v0.10.0
ScanMode.Segmented
ScanMode.FullSweep
# v0.11.0
ScanMode.SEGMENTED
ScanMode.FULL_SWEEPAll default parameters in rasterize_mask_lines, rasterize_mask_scan,
rasterize_multi_pass, and rasterize_power_modulation default to
ScanMode.SEGMENTED (same value, just renamed).
No Rust-level changes required — the Rust enum variants remain
Segmented / FullSweep; only the Python-facing names were updated via
#[pyo3(name = "...")].
Python code using ScanMode.Segmented or ScanMode.FullSweep must be
updated to the uppercase forms.