vcad
Parametric CAD in Rust. Define parts with CSG operations and export to STL, glTF, USD, and DXF.
Built on manifold for boolean operations and mesh generation.
Quick start
use ;
// Plate with four mounting holes
let plate = centered_cube;
let hole = centered_cylinder;
let holes = hole.linear_pattern
.linear_pattern
.translate;
let part = plate - holes; // operator overloads for CSG
part.write_stl.unwrap;
Features
Primitives — cube, cylinder, cone, sphere, centered variants
CSG — union (+), difference (-), intersection (&), plus named methods
Transforms — translate, rotate, scale, mirror, linear/circular pattern
Inspection — volume, surface area, bounding box, center of mass, triangle count
Export formats:
| Format | Use case | Feature flag |
|---|---|---|
| STL | 3D printing, CNC | always on |
| glTF/GLB | Web viewers, PBR materials | gltf (default) |
| USD/USDA | Isaac Sim, Omniverse | usd |
| DXF | Laser cutting (2D profiles) | always on |
| STEP | Interchange (requires OCCT) | step |
Materials — PBR material database loaded from TOML, with part-to-material assignments.
Scenes — Multi-part assemblies that preserve per-part materials for rendering.
Installation
[]
= "0.1"
Without glTF support:
[]
= { = "0.1", = false }
Examples
Boolean operations
use ;
let block = centered_cube;
let bore = centered_cylinder;
let result = block.difference;
Bolt pattern
use ;
let flange = centered_cube;
let holes = bolt_pattern;
let part = flange.difference;
Multi-material scene (glTF)
use ;
use ;
let materials = parse.unwrap;
let mut scene = new;
scene.add;
scene.add;
export_scene_glb.unwrap;
DXF for laser cutting
use DxfDocument;
let mut doc = new;
doc.add_rectangle; // outer profile
doc.add_circle; // center hole
doc.add_circle; // mounting hole
doc.add_circle; // mounting hole
doc.add_bend_line; // bend (BEND layer)
doc.export.unwrap;
Materials from TOML
# materials.toml
[]
= [0.85, 0.85, 0.88]
= 0.95
= 0.35
= 2700
= "6061-T6 Aluminum"
[]
= [0.08, 0.08, 0.08]
= 0.0
= 0.7
= 1040
[]
= "aluminum_6061"
= "abs_black"
use Materials;
let mats = load.unwrap;
let frame_mat = mats.get_for_part.unwrap;
assert_eq!;
API reference
Primitives
| Constructor | Description |
|---|---|
Part::cube(name, x, y, z) |
Box with corner at origin |
Part::cylinder(name, r, h, segments) |
Cylinder along Z |
Part::cone(name, r_bot, r_top, h, segments) |
Tapered cylinder |
Part::sphere(name, r, segments) |
Sphere at origin |
Part::empty(name) |
Empty geometry (identity for union) |
centered_cube(name, x, y, z) |
Box centered at origin |
centered_cylinder(name, r, h, segments) |
Cylinder centered at origin |
counterbore_hole(d, cb_d, cb_depth, depth, seg) |
Through hole + counterbore |
bolt_pattern(n, bcd, hole_d, depth, seg) |
Circle of holes |
CSG operations
| Method / Operator | Description |
|---|---|
a.union(&b) or a + b |
Boolean union |
a.difference(&b) or a - b |
Boolean difference |
a.intersection(&b) or a & b |
Boolean intersection |
All operators work on both Part and &Part.
Transforms
| Method | Description |
|---|---|
.translate(x, y, z) |
Move |
.translate_vec(v) |
Move by nalgebra Vector3 |
.rotate(x_deg, y_deg, z_deg) |
Rotate (degrees) |
.scale(x, y, z) |
Non-uniform scale |
.scale_uniform(s) |
Uniform scale |
.mirror_x() / .mirror_y() / .mirror_z() |
Mirror across plane |
.linear_pattern(dx, dy, dz, count) |
N copies along vector |
.circular_pattern(radius, count) |
N copies around Z axis |
Inspection
| Method | Returns |
|---|---|
.volume() |
f64 — mesh volume |
.surface_area() |
f64 — total surface area |
.bounding_box() |
([f64; 3], [f64; 3]) — AABB min/max |
.center_of_mass() |
[f64; 3] — volume-weighted centroid |
.num_triangles() |
usize — triangle count |
.is_empty() |
bool — has geometry? |
Export
| Method / Function | Format |
|---|---|
part.write_stl(path) |
Binary STL file |
part.to_stl() |
Binary STL bytes |
export_glb(part, material, path) |
glTF binary (single part) |
export_scene_glb(scene, materials, path) |
glTF binary (multi-material) |
export_usd(part, material, path) |
USD with physics |
export_robot_usd(body, wheels, ...) |
USD articulated robot |
DxfDocument::new() + .export(path) |
DXF R12 for laser cutting |
Cookbook
Mounting plate
let plate = centered_cube;
let holes = centered_cylinder
.linear_pattern
.linear_pattern
.translate;
let part = plate - holes;
Symmetric bracket
let arm = centered_cube.translate;
let bracket = &arm + &arm.mirror_x; // symmetric about YZ plane
Flanged hub
let hub = centered_cylinder;
let flange = centered_cylinder.translate;
let bore = centered_cylinder;
let bolt_holes = bolt_pattern.translate;
let part = hub + flange - bore - bolt_holes;
Enclosure with lid
let wall = 2.0;
let outer = centered_cube;
let inner = centered_cube
.translate;
let box_part = outer - inner;
let lid = centered_cube.translate;
Radial vent pattern
let slot = centered_cube;
let vents = slot.circular_pattern;
let panel = centered_cylinder - vents;
Blender integration
vcad pairs well with the Blender MCP server for AI-assisted 3D workflows. Export a GLB from vcad, then import and preview it in Blender — all from a single conversation with an AI agent.
// Generate and export
let plate = centered_cube;
let holes = bolt_pattern;
let part = plate - holes;
part.write_stl.unwrap;
// Export multi-material scene as GLB
export_scene_glb.unwrap;
Then in Blender (via MCP):
# Import the GLB into the current scene
The MCP server exposes tools for scene inspection, viewport screenshots, and Python execution — so an AI agent can generate geometry with vcad, import it into Blender, position cameras, and render previews in a single loop.
Units
vcad is unit-agnostic — coordinates are just f64. By convention, the projects using vcad treat values as millimeters (matching STL/DXF conventions for manufacturing).