Volume Estimation
Volume estimation is the process of computing the volume of a region in space. Since regions in crater.rs can represent complex CSG Region
s where analytical volumes are difficult, we use Monte Carlo methods for robust volume approximation.
The Monte Carlo approach:
- Sample: Generate random points in a bounding box
- Test: Check if each point is inside the region
- Estimate: Volume = (inside_count / total_count) × bounding_box_volume
Basic Volume Estimation
The monte_carlo_volume
method is available on all regions:
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::backend::Autodiff; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); // Create a unit sphere let sphere = Field3D::<Autodiff<Wgpu>>::sphere(1.0, device.clone()) .into_isosurface(0.0) .region(); // Define bounding box that encompasses the sphere let bounds = BoundingBox::new([-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]); // Estimate volume with 100,000 samples let volume = sphere.monte_carlo_volume(100_000, &bounds, &Algebra::default()); // Analytical volume of unit sphere = 4/3 * π ≈ 4.19 println!("Estimated volume: {:.2}", volume); }
Understanding Parameters
Sample Count
More samples improve accuracy but increase computation time:
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::backend::Autodiff; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere = Field3D::<Autodiff<Wgpu>>::sphere(1.0, device.clone()).into_isosurface(0.0).region(); let bounds = BoundingBox::new([-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]); // Low accuracy, fast let volume_1k = sphere.monte_carlo_volume(1_000, &bounds, &Algebra::default()); // Medium accuracy, moderate speed let volume_100k = sphere.monte_carlo_volume(100_000, &bounds, &Algebra::default()); // High accuracy, slower (~3x more accurate than 100k samples) let volume_1m = sphere.monte_carlo_volume(1_000_000, &bounds, &Algebra::default()); }
Error decreases as O(1/√N) where N is the sample count.
Bounding Box
The bounding box should tightly encompass your region:
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::backend::Autodiff; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); // Sphere with radius 2.0 let sphere = Field3D::<Autodiff<Wgpu>>::sphere(2.0, device.clone()) .into_isosurface(0.0) .region(); // Good: tight bounds let tight_bounds = BoundingBox::new([-2.0, -2.0, -2.0], [2.0, 2.0, 2.0]); // Bad: loose bounds (wastes samples) let loose_bounds = BoundingBox::new([-10.0, -10.0, -10.0], [10.0, 10.0, 10.0]); let volume_tight = sphere.monte_carlo_volume(100_000, &tight_bounds, &Algebra::default()); let volume_loose = sphere.monte_carlo_volume(100_000, &loose_bounds, &Algebra::default()); // Both give same result, but tight bounds are more efficient }
Volume Estimation with Primitives
2D Shapes
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::backend::Autodiff; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); // Unit circle let circle = Field2D::<Autodiff<Wgpu>>::circle(1.0, device.clone()) .into_isosurface(0.0) .region(); let bounds_2d = BoundingBox::new([-1.0, -1.0], [1.0, 1.0]); let circle_area = circle.monte_carlo_volume(100_000, &bounds_2d, &Algebra::default()); // Analytical area = π ≈ 3.14 println!("Circle area: {:.2}", circle_area); }
3D Shapes
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::backend::Autodiff; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); // Cube let cube_bounds = BoundingBox::new([-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]); let cube: Region<3, Autodiff<Wgpu>> = cube_bounds.into_region(device.clone()); let cube_volume = cube.monte_carlo_volume(100_000, &cube_bounds, &Algebra::default()); // Cone let cone = Field3D::<Autodiff<Wgpu>>::cone( [0.0, 0.0, 1.0], // axis std::f32::consts::FRAC_PI_4, // 45-degree half-angle device.clone() ).into_isosurface(0.0).region(); let cone_bounds = BoundingBox::new([-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]); let cone_volume = cone.monte_carlo_volume(100_000, &cone_bounds, &Algebra::default()); }
Higher Dimensions
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::backend::Autodiff; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); // 4D hypersphere let hypersphere = FieldND::<4, Autodiff<Wgpu>>::hypersphere(1.0, device.clone()) .into_isosurface(0.0) .region(); let bounds_4d = BoundingBox::new([-1.0; 4], [1.0; 4]); let hypervolume = hypersphere.monte_carlo_volume(100_000, &bounds_4d, &Algebra::default()); // Analytical 4D sphere volume = π²/2 * r⁴ ≈ 4.93 for r=1 println!("4D sphere volume: {:.2}", hypervolume); }
Bounding Box Optimization
Tight bounds are more efficient for achieving an expected uncertainty.
use crater::{csg::prelude::*, primitives::prelude::*}; use burn::backend::wgpu::{Wgpu, WgpuDevice}; use burn::backend::Autodiff; use burn::prelude::*; fn main() { let device = WgpuDevice::default(); let sphere = Field3D::<Autodiff<Wgpu>>::sphere(1.0, device.clone()).into_isosurface(0.0).region(); // Tight bounds: 8 volume units, ~50% hit rate let tight = BoundingBox::new([-1.0, -1.0, -1.0], [1.0, 1.0, 1.0]); // Loose bounds: 1000 volume units, ~0.4% hit rate let loose = BoundingBox::new([-5.0, -5.0, -5.0], [5.0, 5.0, 5.0]); let tight_volume = sphere.monte_carlo_volume(100_000, &tight, &Algebra::default()); let loose_volume = sphere.monte_carlo_volume(100_000, &loose, &Algebra::default()); }