# cadrum
[](https://github.com/lzpel/cadrum/blob/main/LICENSE)
[](https://crates.io/crates/cadrum)
Minimal Rust bindings for [OpenCASCADE](https://dev.opencascade.org/) (OCCT 7.9.3) — a solid modeling kernel used in CAD/CAM software.
<p align="center">
<img src="figure/chijin.svg" alt="chijin — a drum of Amami Oshima" width="360"/>
</p>
## Usage
Add this to your `Cargo.toml`:
```toml
[dependencies]
cadrum = "^0.4"
```
```rust
use cadrum::Solid;
use glam::DVec3;
fn main() {
let shape = Solid::box_from_corners(DVec3::ZERO, DVec3::new(10.0, 20.0, 30.0));
let mut f = std::fs::File::create("box.step").expect("failed to create file");
cadrum::write_step(&[shape], &mut f).expect("failed to write STEP");
}
```
Provides safe, ergonomic wrappers around the OCC C++ kernel for:
- Reading/writing STEP and BRep formats (stream-based, no temp files)
- Constructing primitive shapes (box, cylinder, half-space)
- Boolean operations (union, subtract, intersect)
- Face/edge topology traversal
- Meshing with customizable tolerance
- SVG export with hidden-line removal and face colors (`color` feature)
## Requirements
- A C++17 compiler (GCC, Clang, or MSVC)
- CMake (`bundled` feature only)
Tested with GCC 15.2.0 (MinGW-w64) and CMake 3.31.11 on Windows.
## Features
- `bundled` (default): Downloads and builds OCCT 7.9.3 from source during `cargo build`.
The built library is installed into `target/occt/` inside the crate directory.
- `prebuilt`: Use a pre-built OCCT pointed to by the `OCCT_ROOT` environment variable.
`OCCT_ROOT` can be any directory that contains OCCT headers and static libraries —
including the `target/occt/` generated by a previous `bundled` build.
```toml
# Cargo.toml
cadrum = { version = "0.4", features = ["prebuilt"], default-features = false }
```
```sh
export OCCT_ROOT=/path/to/occt
cargo build
```
- `color` (default): Colored STEP I/O via XDE (`STEPCAFControl`). Enables `write_step_with_colors`,
`read_step_with_colors`, and per-face color on `Solid`.
Colors are preserved through boolean operations and other transformations.
## Examples
### Full: build a chijin drum with colors, boolean ops, and SVG export ([`examples/chijin.rs`](examples/chijin.rs))
This produces the image shown at the top of this page.
```sh
cargo run --example chijin
```
<details>
<summary>Source code</summary>
```rust
use cadrum::{Boolean, Face, Rgb, Shape, Solid};
use glam::DVec3;
use std::f64::consts::PI;
fn chijin() -> Solid {
// Body (cylinder): r=15, h=8, centered at origin (z=-4..+4)
let cylinder: Solid = Solid::cylinder(DVec3::new(0.0, 0.0, -4.0), 15.0, DVec3::Z, 8.0)
.color_paint(Rgb::from_hex("#999").unwrap());
// Rim: cross-section polygon revolved 360° around Z,
// mirrored across z=0 to create rims on both top and bottom.
let cross_section = Face::from_polygon(&[
DVec3::new(0.0, 0.0, 5.0),
DVec3::new(0.0, 15.0, 5.0),
DVec3::new(0.0, 17.0, 3.0),
DVec3::new(0.0, 15.0, 4.0),
DVec3::new(0.0, 0.0, 4.0),
DVec3::new(0.0, 0.0, 5.0),
])
.unwrap();
let sheet = cross_section
.revolve(DVec3::ZERO, DVec3::Z, 2.0 * PI)
.unwrap()
.color_paint(Rgb::from_hex("#fff").unwrap());
let sheets = [sheet.mirrored(DVec3::ZERO, DVec3::Z), sheet];
// Lacing blocks: 2x8x1, rotated 60° around Z, placed at y=15
let block_proto =
Solid::box_from_corners(DVec3::new(-1.0, -4.0, -0.5), DVec3::new(1.0, 4.0, 0.5))
.rotate(DVec3::ZERO, DVec3::Z, 60.0_f64.to_radians())
.translate(DVec3::new(0.0, 15.0, 0.0));
// Lacing holes: thin cylinders through each block
let hole_proto = Solid::cylinder(
DVec3::new(-5.0, 16.0, -15.0), 0.7, DVec3::new(10.0, 0.0, 30.0), 30.0,
);
// Distribute 20 blocks and holes evenly around Z, each block in a rainbow color
let n = 20usize;
let mut blocks: Vec<Solid> = Vec::with_capacity(n);
let mut holes: Vec<Solid> = Vec::with_capacity(n);
for i in 0..n {
let angle = 2.0 * PI * (i as f64) / (n as f64);
let color = Rgb::from_hsv(i as f32 / n as f32, 1.0, 1.0);
blocks.push(block_proto.clone().rotate(DVec3::ZERO, DVec3::Z, angle).color_paint(color));
holes.push(hole_proto.clone().rotate(DVec3::ZERO, DVec3::Z, angle));
}
let blocks = blocks.into_iter().map(|v| vec![v])
.reduce(|a, b| Boolean::union(&a, &b).unwrap().solids).unwrap();
let holes = holes.into_iter().map(|v| vec![v])
.reduce(|a, b| Boolean::union(&a, &b).unwrap().solids).unwrap();
// Assemble with boolean operations: union, subtract, union
let combined: Vec<Solid> = Boolean::union(&[cylinder], &sheets).unwrap().into();
let result: Vec<Solid> = Boolean::subtract(&combined, &holes).unwrap().into();
let result: Vec<Solid> = Boolean::union(&result, &blocks).unwrap().into();
result.into_iter().next().unwrap()
}
fn main() {
let result = vec![chijin()];
std::fs::create_dir_all("out").unwrap();
let mut f = std::fs::File::create("out/chijin.step").unwrap();
cadrum::write_step_with_colors(&result, &mut f).unwrap();
let svg = result.to_svg(DVec3::new(1.0, 1.0, 1.0), 0.5).unwrap();
let mut f = std::fs::File::create("out/chijin.svg").unwrap();
std::io::Write::write_all(&mut f, svg.as_bytes()).unwrap();
}
```
</details>
## Name
The library was originally named **chijin** after the **チヂン**, a hand drum traditional to Amami Oshima, a subtropical island of southern Japan.
It was renamed to **cadrum** (CAD + drum) to better convey its purpose as a CAD library while keeping the drum heritage.
The 3D figure at the top of this page is generated entirely with cadrum itself: [`examples/chijin.rs`](examples/chijin.rs).
<img src="figure/chijin_real.jpg" alt="本物のチヂン" width="360"/>
## License
This project is licensed under the MIT License.