Getting Started
This guide introduces you to the core abstractions in crater.rs
. Understanding these concepts is essential for working effectively with the library.
Installation
Add Crater.rs to your Cargo.toml
:
[dependencies]
crater = "0.7.0"
Scalar Fields
A scalar field is a function that assigns a single number to every point in space. In Crater.rs, we use scalar fields to represent geometry implicitly.
For a 3D scalar field, we have a function where:
- means the point is inside the shape
- means the point is on the surface
- means the point is outside the shape
Example: Sphere
A sphere centered at origin with radius has the scalar field:
In Crater.rs:
use crater::{csg::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere = Field3D::<Wgpu>::sphere(1.0, device.clone()); // Create a grid of origins in R³ // [n³, 3] let n = 8; // Grid size let grid_data = (0..n).flat_map( |i| (0..n).flat_map( move |j| (0..n).flat_map( move |k| [i as f32, j as f32, k as f32] ) ) ).collect::<Vec<_>>(); let origins = Tensor::<Wgpu, 1, Float>::from_data( grid_data.as_slice(), &device ).reshape([n * n * n, 3]); // Evaluate the scalar field at the origins // [n³, 1] let evaluations = sphere.evaluate(origins); }
Isosurfaces
An isosurface is the set of all points where a scalar field equals a constant value. The most common isosurface is at level 0, which represents the surface of the shape.
Creating Isosurfaces
use crater::csg::prelude::*; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere = Field3D::<Wgpu>::sphere(1.0, device.clone()); // Continuing from the previous example let surface = sphere.clone().into_isosurface(0.0); // Points slightly outside the sphere (f = 0.1) let outer_surface = sphere.clone().into_isosurface(0.1); // Points slightly inside the sphere (f = -0.1) let inner_surface = sphere.clone().into_isosurface(-0.1); }
Regions
A region represents a solid volume in space. It's the continuous "inside" of an isosurface, defined by where a scalar field is negative.
From Field to Region
use crater::csg::prelude::*; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere = Field3D::<Wgpu>::sphere(1.0, device.clone()); let surface = sphere.into_isosurface(0.0); // Convert to region (solid volume) let sphere_region = surface.region(); }
The .region()
method creates a solid region from the isosurface.
Region Operations
Regions support intuitive boolean operations:
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere = Field3D::<Wgpu>::sphere(1.0, device.clone()).into_isosurface(0.0).region(); let cube = BoundingBox::new([-0.8, -0.8, -0.8], [0.8, 0.8, 0.8]) .into_region(device.clone()); // Union (logical OR) let union = &sphere | &cube; // Intersection (logical AND) let intersection = &sphere & &cube; // Negation (logical NOT) let negation = -&sphere; // Difference (logical \ and NOT) let difference = sphere & -cube; }
Meshing
Isosurfaces can be visualized using marching cubes:
use crater::{csg::prelude::*, primitives::prelude::*, serde::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere_field = Field3D::<Wgpu>::sphere(1.0, device.clone()); // Convert isosurface to region for marching cubes let region = sphere_field.into_isosurface(0.0).region(); // Generate mesh let mesh = marching_cubes::<Wgpu>( &MarchingCubesParams { region, bounds: BoundingBox::new([-2.0, -2.0, -2.0], [2.0, 2.0, 2.0]), resolution: (64, 64, 64), algebra: Algebra::default(), }, &device ); // Export for visualization let mesh_collection: MeshCollection = mesh.into(); mesh_collection.write_stl("sphere.stl").unwrap(); }
Constructive Solid Geometry (CSG)
CSG allows you to build complex shapes by combining simpler ones using boolean operations.
Basic Operations
Union (OR: |
)
Combines two shapes into one larger shape:
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let shape1 = Field3D::<Wgpu>::sphere(1.0, device.clone()) .into_isosurface(0.0) .region(); let shape2 = Field3D::<Wgpu>::sphere(1.0, device.clone()) .into_isosurface(0.0) .region() .transform(Translate([1.5, 0.0, 0.0])); let union = &shape1 | &shape2; // Dumbbell shape }
Intersection (AND: &
)
Keeps only the overlapping parts:
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere = Field3D::<Wgpu>::sphere(1.0, device.clone()) .into_isosurface(0.0) .region(); let cube = BoundingBox::new([-0.8, -0.8, -0.8], [0.8, 0.8, 0.8]) .into_region(device.clone()); let intersection = sphere & cube; // Rounded cube }
Transformations
Regions can be transformed using standard geometric operations:
Translation
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere_region = Field3D::<Wgpu>::sphere(1.0, device.clone()).into_isosurface(0.0).region(); let moved_sphere = sphere_region.transform(Translate([1.0, 2.0, 0.0])); }
Rotation
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let cylinder_region = Field3D::<Wgpu>::cylinder(1.0, device.clone()).into_isosurface(0.0).region(); let rotated_cylinder = cylinder_region.transform( RotateAbout::new(0, 1, std::f32::consts::FRAC_PI_2, &device) ); }
Scaling
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere_region = Field3D::<Wgpu>::sphere(1.0, device.clone()).into_isosurface(0.0).region(); let scaled_sphere = sphere_region .transform(ScaleDim(0, 2.0)) .transform(ScaleDim(1, 1.0)) .transform(ScaleDim(2, 1.0)); }
Working with Different Dimensions
Crater.rs supports arbitrary dimensions through type parameters:
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); // 2D shapes let circle = Field2D::<Wgpu>::circle(1.0, device.clone()); let line = Field2D::<Wgpu>::line([0.0, 1.0], device.clone()); // 3D shapes let sphere = Field3D::<Wgpu>::sphere(1.0, device.clone()); let cone = Field3D::<Wgpu>::cone([0.0, 0.0, 1.0], 0.5, device.clone()); // N-dimensional shapes let hypersphere = FieldND::<4, Wgpu>::hypersphere(1.0, device.clone()); }
Next Steps
Now that you understand the core concepts:
- Ray Casting - Learn how to ray cast against Regions
- Theory - Mathematical underpinnings of the library