Csgrs
Multi-modal constructive solid geometry kernel in Rust
Install / Use
/learn @timschmidt/CsgrsREADME
csgrs
A fast, optionally multithreaded Constructive Solid Geometry (CSG)
library in Rust, built around Boolean operations (union, difference,
intersection, xor) on several different internal geometry representations.
csgrs provides data structures and methods for constructing 2D and 3D geometry
with an OpenSCAD-like syntax. Our aim is for csgrs
to be light weight and full featured through integration with the
Dimforge ecosystem
(e.g., nalgebra,
Parry,
and Rapier) and
geo for robust processing of
Simple Features.
csgrs has a number of functions useful for generating CNC toolpaths. The
library can be built for 32bit or 64bit floats, and for WASM. Dependencies are
100% rust and nearly all optional.
Earcut and constrained delaunay algorithms used for triangulation work only in 2D, so csgrs rotates 3D polygons into 2D for triangulation then back to 3D.

Community
Getting started
A simple CSG example
Install the Rust language tools from rustup.rs.
Use cargo to create a new project, my_cad_project, and add the csgrs dependency:
cargo new my_cad_project
cd my_cad_project
cargo add csgrs
main.rs
Change src/main.rs to the following code:
use csgrs::traits::CSG;
type Mesh = csgrs::mesh::Mesh<()>;
fn main() {
// Create a cube
let cube: Mesh = Mesh::cube(2.0, None); // 2×2×2 cube at origin, no metadata
// Create sphere at (1, 1, 1) with radius 1.25:
let sphere: Mesh = Mesh::sphere(1.25, 16, 8, None).translate(1.0, 1.0, 1.0);
// Perform a difference operation:
let result = cube.difference(&sphere);
// Write the result as an ASCII STL:
let stl = result.to_stl_ascii("cube_minus_sphere");
std::fs::write("cube_sphere_difference.stl", stl).unwrap();
}
Build and run
cargo build
cargo run
This results in a file named cube_sphere_difference.stl in the current directory
and it can be viewed in a STL viewer such as f3d
with, f3d cube_sphere_difference.stl, and should look like this:

Building for WASM
cargo install wasm-pack
wasm-pack build --release --target bundler --out-dir pkg -- --features wasm
Features and Structures
Sketch Structure
Sketch<S>is the type which stores and manipulates 2D polygonal geometry. It contains:- a
geoGeometryCollection<Real> - a bounding box wrapped in a OnceLock (bounding_box: OnceLock<Aabb>)
- an optional metadata field (
Option<S>) also defined by you
- a
Sketch<S> provides methods for working with 2D shapes made of points and lines.
You can build a Sketch<S> from geo Geometries with Sketch::from_geo(...).
Geometries can be open or closed, and can have holes, but must be planar in the XY.
Sketch's are triangulated when exported as an STL, or when a Geometry is
converted into a Mesh<S>.
2D Shapes in Sketch
- <img src="docs/square.png" width="128" alt="top down view of a square"/>
Sketch::square(width: Real, metadata: Option<S>) - <img src="docs/square.png" width="128" alt="top down view of a rectangle"/>
Sketch::rectangle(width: Real, length: Real, metadata: Option<S>) - <img src="docs/circle.png" width="128" alt="top down view of a circle"/>
Sketch::circle(radius: Real, segments: usize, metadata: Option<S>) - <img src="docs/polygon.png" width="128" alt="top down view of a triangle"/>
Sketch::polygon(&[[x1,y1],[x2,y2],...], metadata: Option<S>) - <img src="docs/rounded_rectangle.png" width="128" alt="top down view of a rectangle with rounded corners"/>
Sketch::rounded_rectangle(width: Real, height: Real, corner_radius: Real, corner_segments: usize, metadata: Option<S>) - <img src="docs/ellipse.png" width="128" alt="top down view of an ellipse"/>
Sketch::ellipse(width: Real, height: Real, segments: usize, metadata: Option<S>) - <img src="docs/ngon.png" width="128" alt="top down view of a 6 sided n-gon"/>
Sketch::regular_ngon(sides: usize, radius: Real, metadata: Option<S>) - <img src="docs/arrow_2d.png" width="128" alt="top down view of a 2D arrow"/>
Sketch::arrow(shaft_length: Real, shaft_width: Real, head_length: Real, head_width: Real, metadata: Option<S>) - <img src="docs/right_triangle.png" width="128" alt="top down view of a right triangle"/>
Sketch::right_triangle(width: Real, height: Real, metadata: Option<S>) - <img src="docs/trapezoid.png" width="128" alt="top down view of trapezoid"/>
Sketch::trapezoid(top_width: Real, bottom_width: Real, height: Real, top_offset: Real, metadata: Option<S>) - <img src="docs/star.png" width="128" alt="top down view of star"/>
Sketch::star(num_points: usize, outer_radius: Real, inner_radius: Real, metadata: Option<S>) - <img src="docs/teardrop.png" width="128" alt="top down view of a teardrop"/>
Sketch::teardrop(width: Real, height: Real, segments: usize, metadata: Option<S>) - <img src="docs/egg_outline.png" width="128" alt="top down view of an egg shape"/>
Sketch::egg(width: Real, length: Real, segments: usize, metadata: Option<S>) - <img src="docs/squircle.png" width="128" alt="top down view of a squircle"/>
Sketch::squircle(width: Real, height: Real, segments: usize, metadata: Option<S>) - <img src="docs/keyhole.png" width="128" alt="top down view of a keyhole"/>
Sketch::keyhole(circle_radius: Real, handle_width: Real, handle_height: Real, segments: usize, metadata: Option<S>) - <img src="docs/reuleaux3.png" width="128"/>
Sketch::reuleaux(sides: usize, radius: Real, arc_segments_per_side: usize, metadata: Option<S>) - <img src="docs/ring.png" width="128" alt="top down view of a ring"/>
Sketch::ring(id: Real, thickness: Real, segments: usize, metadata: Option<S>) - <img src="docs/pie_slice.png" width="128" alt="top down view of a slice of a circle"/>
Sketch::pie_slice(radius: Real, start_angle_deg: Real, end_angle_deg: Real, segments: usize, metadata: Option<S>) - <img src="docs/supershape.png" width="128"/>
Sketch::supershape(a: Real, b: Real, m: Real, n1: Real, n2: Real, n3: Real, segments: usize, metadata: Option<S>) - <img src="docs/circle_with_keyway.png" width="128" alt="top down view of a circle with a notch taken out of it"/>
Sketch::circle_with_keyway(radius: Real, segments: usize, key_width: Real, key_depth: Real, metadata: Option<S>) - <img src="docs/d.png" width="128" alt="top down view of a circle with a flat edge"/>
Sketch::circle_with_flat(radius: Real, segments: usize, flat_dist: Real, metadata: Option<S>) - <img src="docs/double_flat.png" width="128" alt="top down view of a circle with two flat edges"/>
Sketch::circle_with_two_flats(radius: Real, segments: usize, flat_dist: Real, metadata: Option<S>) - <img src="docs/from_image.png" width="128" alt="top down view of a pixleated circle"/>
Sketch::from_image(img: &GrayImage, threshold: u8, closepaths: bool, metadata: Option<S>)- Builds a new CSG from the “on” pixels of a grayscale image - <img src="docs/truetype.png" width="128" alt="top down view of the text 'HELLO'"/>
Sketch::text(text: &str, font_data: &[u8], size: Real, metadata: Option<S>)- generate 2D text geometry in the XY plane from TTF fonts - <img src="docs/metaballs_2d.png" width="128" alt="top down view of three metaballs merged"/>
Sketch::metaballs(balls: &[(nalgebra::Point2<Real>, Real)], resolution: (usize, usize), iso_value: Real, padding: Real, metadata: Option<S>) - <img src="docs/airfoil.png" width="128" alt="a side view of an airfoil"/>
Sketch::airfoil_naca4(max_camber: Real, camber_position: Real, thickness: Real, chord: Real, samples: usize, metadata: Option<S>)- NACA 4 digit airfoil - <img src="docs/bezier_extruded.png" width="128" alt="an angled view of a bezier cirve"/>
Sketch::bezier(control: &[[Real; 2]], segments: usize, metadata: Option<S>) - <img src="docs/bspline.png" width="128" alt="top down view of a neer semi-circle shape"/>
Sketch::bspline(control: &[[Real; 2]], p: usize, segments_per_span: usize, metadata: Option<S>) - <img src="docs/heart.png" width="128" alt="top down view of a cartune heart"/>
Sketch::heart(width: Real, height: Real, segments: usize, metadata: Option<S>) - <img src="docs/crescent.png" width="128" alt="top down view of a crescent"/>
Sketch::crescent(outer_r: Real, inner_r: Real, offset: Real, segments: usize, metadata: Option<S>)- - <img src="docs/hilbert.png" width="128" alt="top down view of a hilbert curve"/>
Sketch::hilbert(order: usize, padding: Real)- fill an existing Sketch with a hilbert curve - <img src="docs/gear_involute.png" width="128" alt="top down view of a involute gear profile"/>
Sketch::involute_gear(module: Real, teeth: usize, pressure_angle_deg: Real, clearance: Real, backlash: Real, segments_per_flank: usize, metadata: Option<S>) Sketch::cycloidal_gear(module_: Real, teeth: usize, pin_teeth: usize, clearance: Real, segments_per_flank: usize, metadata: Option<S>)- under construction- **`Sketch::involute_rack(module_: Real, num_teeth: usize, pr
