v0.12.0
v0.12.0
Highlights
- Layered architecture:
geo→ops→cnclayering established with HSM motion assembly moved fromgeotoops::assembly. - CNC terminology: Laser-centric naming replaced with CNC-milling terminology (
cut_speed→feed_rate,travel_speed→rapid_rate,air_assist→CoolantMode, etc.). - 2D/3D naming convention: All functions now follow
bare = 2D, _3d = 3D. - Polygon/Polyline module split: Polyline functions moved from
polygonto dedicatedpolylinemodule. - New geometry primitives: Medial axis routing, A* pathfinding, FEM meshing, spiral/helix/ramp/trochoid primitives, polylabel, offset improvements.
- New CNC module:
raygeo.cncwithTool,StateStrategy,EntryStrategy, andadaptive_clear_hsmorchestrator.
Breaking changes
See the full migration guide below.
CNC terminology rename
State.cut_speed→State.feed_rateState.travel_speed→State.rapid_rateState.active_laser_uid→State.active_head_uidState.spindle_speed→State.spindle_rpmOps.set_cut_speed()→Ops.set_feed_rate()Ops.set_travel_speed()→Ops.set_rapid_rate()Ops.set_laser()→Ops.set_head()Ops.set_spindle_speed()→Ops.set_spindle_rpm()ops.travel_distance()→ops.distance()enable_air_assist()removed → useset_coolant()estimate_time/estimate_command_timesparameter rename:default_cut_speed→default_feed_rate,default_travel_speed→default_rapid_rateget_frame(speed=)→get_frame(feed_rate=)CommandType.SetCutSpeed→SetFeedRate,SetTravelSpeed→SetRapidRate,SetLaser→SetHead,SetSpindleSpeed→SetSpindleRpmCommandType.EnableAirAssist/DisableAirAssistremoved →SetCoolant
Coolant mode enum
State.air_assist: bool→State.coolant: Option<CoolantMode>CoolantModevalues:Off,Flood,Mist,Air
ScanMode uppercase
ScanMode.Segmented→ScanMode.SEGMENTEDScanMode.FullSweep→ScanMode.FULL_SWEEP
JoinStyle enum
offset_polygon(..., join_style="round")→offset_polygon(..., join_style=JoinStyle.Round)offset_contour_group(..., join_style="miter")→offset_contour_group(..., join_style=JoinStyle.Miter)- Rust:
offset_polygon_with_stylerenamed back tooffset_polygon
Rect/Rect3D struct change
Rect(x1, y1, x2, y2)→Rect { min: Point, max: Point }Rect.0/.1/.2/.3→Rect.min.x/.y,Rect.max.x/.yRect3D.x_min/.x_max/.y_min/.y_max/.z_min/.z_max→Rect3D.min/.max
2D/3D naming
circumcenteris now 2D;circumcenter_3dis 3D (swap)simplify_polyline→simplify_polyline_3d(2D wrapper removed)midpoint→midpoint_3d,transform_point→transform_point_3dnormal_from_clockwise→normal_from_clockwise_3dgenerate_helix→generate_helix_3d,generate_spiral→generate_spiral_3d,generate_ramp→generate_ramp_3d,trochoid_along→trochoid_along_3dsmooth_polyline→smooth_polyline_3dflatten_to_points→flatten_to_points_3dresample_polyline(3D) moved fromgeo.algo.smoothtogeo.shape.polygon3d
Module reorganization
ops.lead_in_out→ops.assembly.lead_in_out(re-exported)ops.overscan→ops.assembly.overscan(re-exported)ops.tabs→ops.assembly.tabs(re-exported)ops.polyline→ops.assembly.polyline(re-exported)ops.raster→ops.assembly.raster(re-exported)- Polygon module split: polyline functions moved to
geo.shape.polyline
New features
CNC orchestration (raygeo.cnc)
Tool,ToolShape,ToolMaterialclassesStateStrategywithconstant()factoryEntryStrategywith helix/ramp supportadaptive_clear_hsm— full HSM clearing orchestrator
Geometry additions
- Medial axis computation and MAT path routing
- A* pathfinding with rasterized grid
- FEM PDE meshing with Laplace solver
- Flat Archimedean spiral primitive
ClearedAreawithfragments(),bites(),bite_in_direction(),incorporate(),all_bites(),remaining_in_inset(),add_cleared_polygons()- Adaptive entry (helix/spiral + zigzag ramp) and adaptive wavefronts
- Polylabel:
find_largest_circle, pole-of-inaccessibility algorithm - Offset improvements:
find_deepest_cores,compute_inset_region,apply_minimum_curvature JoinStyleenum (Miter/Round/Square) for offset operations- Fillet arc ends, safe sweep end detection
walk_along_polyline_3d,walk_along_polygon_3d,fillet_polyline_3dpolyline_to_ops,link_passes,find_pass_entry/find_pass_exitapply_lead_in_out,apply_overscan,apply_tab_gaps,apply_tab_powerget_ray_polygon_intersection,does_line_cross_polygon,get_segment_segment_distanceget_polyline_closest_point,trim_polyline_at,trim_polyline_angular_endssplit_polyline_at_v_junctions,resample_polygonget_polygon_boundary_distance,get_polygon_vertex_centroidshortcut_pathsmoothing, shortcut + Gaussian relaxationdeduplicate_polyline_3d,get_polyline_end_tangent_3dfit_cubic_bezier,nearest_tangent_circle_on_bezierarc_through_point,generate_arc_between_two_points,generate_linking_arcinterpolated_segment_3d,circumcenter_2dget_polygon_area_3d,get_polygon_signed_area_3dorder_nearest_neighbor,blend_tangent,get_polyline_turn_signcompute_valid_tool_area
Ops additions
Ops::apply_state(&State)for domain-neutral state emissionOps.distance(),Ops.cut_distance()CoolantModeenum
Migration Guide
Migration Guide
1. 2D/3D Naming Convention
Function names now consistently follow the rule: bare = 2D, _3d = 3D.
Tier 1 — Pair inversions
circumcenter / circumcenter_3d swap
The 3D circumcenter (takes Point3D) was renamed from circumcenter to circumcenter_3d. The 2D circumcenter (takes Point) was renamed from circumcenter_2d to circumcenter.
// Rust
-use raygeo::geo::shape::point::circumcenter; // was 3D, now 2D
+use raygeo::geo::shape::point::circumcenter_3d; // 3D circumcenter (returns Option<Point3D>)
+use raygeo::geo::shape::point::circumcenter; // 2D circumcenter (returns (Point, f64)) # Python
-from raygeo.geo.shape.point import circumcenter # was 3D
-from raygeo.geo.shape.point import circumcenter_2d # was 2D
+from raygeo.geo.shape.point import circumcenter_3d # 3D circumcenter
+from raygeo.geo.shape.point import circumcenter # 2D circumcentersimplify_polyline → simplify_polyline_3d
// Rust
-use crate::geo::algo::simplify::simplify_polyline;
+use crate::geo::algo::simplify::simplify_polyline_3d;The Python 2D wrapper was removed entirely — only simplify_polyline_3d exists:
# Python
-from raygeo.geo.algo.simplify import simplify_polyline # removed
-from raygeo.geo.algo.simplify import simplify_polyline_3d
+from raygeo.geo.algo.simplify import simplify_polyline_3d # only nameConvert 2D calls by adding z=0:
-result = simplify_polyline([(0, 0), (5, 5), (10, 0)], tolerance=0.5)
+result = simplify_polyline_3d([(0, 0, 0), (5, 5, 0), (10, 0, 0)], tolerance=0.5)Tier 3 — Misnamed _2d helpers → _xy
Private functions that accept Point3D but only measure XY:
| File | Old name | New name |
|---|---|---|
src/ops/assembly/tabs.rs |
bezier_arc_length_2d |
bezier_arc_length_xy |
src/ops/assembly/tabs.rs |
distance_2d |
distance_xy |
src/ops/optimize.rs |
dist_2d |
dist_xy |
No Python impact (all private).
Tier 4 — 3D orphans → _3d suffix
Functions that return 3D types and have no 2D counterpart:
Point operations
// Rust
-use crate::geo::shape::point::midpoint;
-use crate::geo::shape::point::transform_point;
+use crate::geo::shape::point::midpoint_3d;
+use crate::geo::shape::point::transform_point_3d; # Python
-from raygeo.geo.shape.point import midpoint
-from raygeo.geo.shape.point import transform_point
+from raygeo.geo.shape.point import midpoint_3d
+from raygeo.geo.shape.point import transform_point_3dArc operations
// Rust
-use crate::geo::shape::arc::normal_from_clockwise;
+use crate::geo::shape::arc::normal_from_clockwise_3d;No Python impact (no direct binding).
Helix / Spiral / Ramp / Trochoid
// Rust
-use crate::geo::algo::helix::generate_helix;
-use crate::geo::algo::spiral::generate_spiral;
-use crate::geo::algo::ramp::generate_ramp;
-use crate::geo::algo::trochoid::trochoid_along;
+use crate::geo::algo::helix::generate_helix_3d;
+use crate::geo::algo::spiral::generate_spiral_3d;
+use crate::geo::algo::ramp::generate_ramp_3d;
+use crate::geo::algo::trochoid::trochoid_along_3d; # Python
-from raygeo.geo.algo.helix import generate_helix
-from raygeo.geo.algo.spiral import generate_spiral
-from raygeo.geo.algo.ramp import generate_ramp
-from raygeo.geo.algo.trochoid import trochoid_along
+from raygeo.geo.algo.helix import generate_helix_3d
+from raygeo.geo.algo.spiral import generate_spiral_3d
+from raygeo.geo.algo.ramp import generate_ramp_3d
+from raygeo.geo.algo.trochoid import trochoid_along_3dSmoothing
// Rust
-use crate::geo::algo::smooth::smooth_polyline;
+use crate::geo::algo::smooth::smooth_polyline_3d; # Python
-from raygeo.geo.algo.smooth import smooth_polyline
+from raygeo.geo.algo.smooth import smooth_polyline_3dFitting
// Rust
-use crate::geo::algo::fitting::flatten_to_points;
+use crate::geo::algo::fitting::flatten_to_points_3d; # Python
-from raygeo.geo.algo.fitting import flatten_to_points
+from raygeo.geo.algo.fitting import flatten_to_points_3dTier 5 — Rect re-export
Rect is now exported from the crate root alongside Rect3D:
// Rust
-use raygeo::Rect3D;
+use raygeo::{Rect, Rect3D};2. Polygon / Polyline module split
polygon.rs was split into separate polygon and polyline modules. If you import polyline functions from the polygon module, update your imports.
Rust imports
-use crate::geo::shape::polygon::get_polyline_bounds;
-use crate::geo::shape::polygon::get_polyline_closest_point;
-use crate::geo::shape::polygon::resample_polyline;
-use crate::geo::shape::polygon::split_polyline_at_v_junctions;
-use crate::geo::shape::polygon::trim_polyline_angular_ends;
-use crate::geo::shape::polygon::trim_polyline_at;
+use crate::geo::shape::polyline::get_polyline_bounds;
+use crate::geo::shape::polyline::get_polyline_closest_point;
+use crate::geo::shape::polyline::resample_polyline;
+use crate::geo::shape::polyline::split_polyline_at_v_junctions;
+use crate::geo::shape::polyline::trim_polyline_angular_ends;
+use crate::geo::shape::polyline::trim_polyline_at;Python imports
-from raygeo.geo.shape.polygon import get_polyline_bounds
-from raygeo.geo.shape.polygon import get_polyline_closest_point
-from raygeo.geo.shape.polygon import resample_polyline
-from raygeo.geo.shape.polygon import split_polyline_at_v_junctions
-from raygeo.geo.shape.polygon import trim_polyline_angular_ends
-from raygeo.geo.shape.polygon import trim_polyline_at
+from raygeo.geo.shape.polyline import get_polyline_bounds
+from raygeo.geo.shape.polyline import get_polyline_closest_point
+from raygeo.geo.shape.polyline import resample_polyline
+from raygeo.geo.shape.polyline import split_polyline_at_v_junctions
+from raygeo.geo.shape.polyline import trim_polyline_angular_ends
+from raygeo.geo.shape.polyline import trim_polyline_at3. resample_polyline_3d relocation
The 3D resample_polyline was moved from smooth.rs to polygon3d.rs and renamed to resample_polyline_3d for consistency.
Rust
-use crate::geo::algo::smooth::resample_polyline;
+use crate::geo::shape::polygon3d::resample_polyline_3d;Python
-from raygeo.geo.algo.smooth import resample_polyline
+from raygeo.geo.shape.polygon3d import resample_polyline_3dThe 2D resample_polyline in raygeo.geo.shape.polyline is unchanged.
4. CNC terminology rename
Laser-specific terminology was replaced with CNC-milling terminology across the API.
Python State
from raygeo.ops.state import State
-s = State(power=1.0, air_assist=False, cut_speed=1200, travel_speed=8000, active_laser_uid="co2", spindle_speed=18000)
+s = State(power=1.0, feed_rate=1200, rapid_rate=8000, active_head_uid="spindle", spindle_rpm=18000)Field renames:
| Old name | New name |
|---|---|
air_assist |
removed — use coolant instead |
cut_speed |
feed_rate |
travel_speed |
rapid_rate |
active_laser_uid |
active_head_uid |
spindle_speed |
spindle_rpm |
Rust State
let s = State {
power: 1.0,
- air_assist: false,
- cut_speed: Some(1200),
- travel_speed: Some(8000),
- active_laser_uid: Some("co2".into()),
- spindle_speed: Some(18000),
+ coolant: Some(CoolantMode::Flood),
+ feed_rate: Some(1200),
+ rapid_rate: Some(8000),
+ active_head_uid: Some("spindle".into()),
+ spindle_rpm: Some(18000),
..Default::default()
};Ops methods
-ops.set_cut_speed(1200)
-ops.set_travel_speed(8000)
-ops.set_laser("co2")
-ops.set_spindle_speed(18000)
-ops.enable_air_assist(True)
+ops.set_feed_rate(1200)
+ops.set_rapid_rate(8000)
+ops.set_head("spindle")
+ops.set_spindle_rpm(18000)
+ops.set_coolant(CoolantMode.Flood)estimate_time / estimate_command_times
-ops.estimate_time(default_cut_speed=1200.0, default_travel_speed=8000.0, acceleration=500.0)
+ops.estimate_time(default_feed_rate=1200.0, default_rapid_rate=8000.0, acceleration=500.0)get_frame
-ops.get_frame(power=1.0, speed=1200.0)
+ops.get_frame(power=1.0, feed_rate=1200.0)travel_distance → distance
-ops.travel_distance()
+ops.distance()CommandType enum (Python)
-CommandType.SetCutSpeed
-CommandType.SetTravelSpeed
-CommandType.SetLaser
-CommandType.SetSpindleSpeed
-CommandType.EnableAirAssist
-CommandType.DisableAirAssist
+CommandType.SetFeedRate
+CommandType.SetRapidRate
+CommandType.SetHead
+CommandType.SetSpindleRpm
+CommandType.SetCoolant5. Coolant mode replaces air assist
The boolean air_assist field was replaced by a CoolantMode enum with four values.
Python
from raygeo.ops.state import CoolantMode
# Old
s = State(power=1.0, air_assist=True)
ops.enable_air_assist(True)
# New
s = State(power=1.0, coolant=CoolantMode.AIR)
ops.set_coolant(CoolantMode.AIR)Rust
use crate::ops::state::CoolantMode;
// CoolantMode values: Off, Flood, Mist, Air
state.coolant = Some(CoolantMode::Air);
ops.set_coolant(CoolantMode::Air);Serialization
The serialized dict key "air_assist" was removed. "coolant" is used instead:
# Old
cmd_dict["air_assist"] # True / False
# New
cmd_dict["coolant"] # "Off" | "Flood" | "Mist" | "Air"6. ScanMode values are now uppercase
-ScanMode.Segmented
-ScanMode.FullSweep
+ScanMode.SEGMENTED
+ScanMode.FULL_SWEEPThis affects rasterize_power_modulation, rasterize_mask_scan, rasterize_mask_lines, and extract_zero_power_segments.
7. JoinStyle enum replaces string parameter
offset_polygon and offset_contour_group now take a JoinStyle enum instead of a string.
Python
from raygeo.geo.shape.polygon import JoinStyle
# Old
offset_polygon(poly, 5.0, join_style="round")
offset_contour_group(solid, holes, 5.0, join_style="miter")
# New
offset_polygon(poly, 5.0, join_style=JoinStyle.Round)
offset_contour_group(solid, holes, 5.0, join_style=JoinStyle.Miter)Rust
use crate::geo::shape::polygon::JoinStyle;
// Old
offset_polygon_with_style(&poly, 5.0, JoinStyle::Round)
// New (function renamed back to offset_polygon)
offset_polygon(&poly, 5.0, JoinStyle::Round)8. Rect / Rect3D field access
Rect and Rect3D were converted from tuple/flat-field structs to min/max structs using glam vector types.
Rust
let r = compute_rect();
// Old
-r.0 // x_min
-r.1 // y_min
-r.2 // x_max
-r.3 // y_max
+ r.min.x
+ r.min.y
+ r.max.x
+ r.max.y
let r3 = compute_rect_3d();
// Old
-r3.x_min
-r3.x_max
-r3.y_min
-r3.y_max
-r3.z_min
-r3.z_max
+ r3.min.x
+ r3.min.y
+ r3.max.zConstruction:
// Old
-Rect(0.0, 0.0, 100.0, 100.0)
+Rect::new(0.0, 0.0, 100.0, 100.0)