Skip to content
Merged
17 changes: 17 additions & 0 deletions crates/opencascade-sys/include/wrapper.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Fuse.hxx>
#include <BRepAlgoAPI_Section.hxx>
#include <BRepBndLib.hxx>
#include <BRepBuilderAPI_GTransform.hxx>
#include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
Expand Down Expand Up @@ -523,3 +524,19 @@ cast_section_to_builderalgo(std::unique_ptr<BRepAlgoAPI_Section> section) {
return section;
}
// namespace BRepAlgoAPI

// Bnd_Box
inline std::unique_ptr<Bnd_Box> Bnd_Box_ctor() { return std::unique_ptr<Bnd_Box>(new Bnd_Box()); }
inline std::unique_ptr<gp_Pnt> Bnd_Box_CornerMin(const Bnd_Box &box) {
auto p = box.CornerMin();
return std::unique_ptr<gp_Pnt>(new gp_Pnt(p));
}
inline std::unique_ptr<gp_Pnt> Bnd_Box_CornerMax(const Bnd_Box &box) {
auto p = box.CornerMax();
return std::unique_ptr<gp_Pnt>(new gp_Pnt(p));
}

// BRepBndLib
inline void BRepBndLib_Add(const TopoDS_Shape &shape, Bnd_Box &box, const Standard_Boolean useTriangulation) {
BRepBndLib::Add(shape, box, useTriangulation);
}
28 changes: 28 additions & 0 deletions crates/opencascade-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,34 @@ pub mod ffi {
shared: bool,
wires: Pin<&mut HandleTopTools_HSequenceOfShape>,
);

// BndBox
// Describes a bounding box in 3D space.
type Bnd_Box;

#[cxx_name = "construct_unique"]
pub fn Bnd_Box_ctor() -> UniquePtr<Bnd_Box>;
pub fn IsVoid(self: &Bnd_Box) -> bool;
pub fn Get(
self: &Bnd_Box,
xMin: &mut f64,
yMin: &mut f64,
zMin: &mut f64,
xMax: &mut f64,
yMax: &mut f64,
zMax: &mut f64,
);
pub fn Bnd_Box_CornerMin(b: &Bnd_Box) -> UniquePtr<gp_Pnt>;
pub fn Bnd_Box_CornerMax(b: &Bnd_Box) -> UniquePtr<gp_Pnt>;
pub fn GetGap(self: &Bnd_Box) -> f64;
pub fn Set(self: Pin<&mut Bnd_Box>, p: &gp_Pnt);
pub fn SetGap(self: Pin<&mut Bnd_Box>, gap: f64);

// BRepBndLib
// Bounding boxes for curves and surfaces.
type BRepBndLib;

pub fn BRepBndLib_Add(shape: &TopoDS_Shape, bb: Pin<&mut Bnd_Box>, use_triangulation: bool);
}
}

Expand Down
85 changes: 85 additions & 0 deletions crates/opencascade/src/bounding_box.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use cxx::UniquePtr;
use glam::DVec3;
use opencascade_sys::ffi;

use crate::primitives::Shape;

/// A wrapper around the `Bnd_Box` API of OCC. Note that a `Bnd_Box` has a `Gap`
/// property, which is a small tolerance value added to all dimensions. This
/// means that the point values of a `BoundingBox` will often be slightly larger
/// or smaller than expected of the geometry of known shapes.
pub struct BoundingBox {
pub(crate) inner: UniquePtr<ffi::Bnd_Box>,
}
impl BoundingBox {
/// Create a new void box. A void box in OCC is defined as a box that contains no points.
pub fn void() -> BoundingBox {
Self { inner: ffi::Bnd_Box_ctor() }
}

pub fn is_void(&self) -> bool {
self.inner.IsVoid()
}

pub fn get_gap(&self) -> f64 {
self.inner.GetGap()
}

pub fn min(&self) -> DVec3 {
let p = ffi::Bnd_Box_CornerMin(self.inner.as_ref().unwrap());
glam::dvec3(p.X(), p.Y(), p.Z())
}

pub fn max(&self) -> DVec3 {
let p = ffi::Bnd_Box_CornerMax(self.inner.as_ref().unwrap());
glam::dvec3(p.X(), p.Y(), p.Z())
}

/// Get a vector corresponding to the `gap` of this box in all dimensions.
pub fn gap_vec(&self) -> DVec3 {
glam::DVec3::ONE * self.get_gap()
}
}

/// Compute the axis-aligned bounding box of `shape` using the `BRepBndLib`
/// package.
pub fn aabb(shape: &Shape) -> BoundingBox {
let mut bb = BoundingBox::void();
ffi::BRepBndLib_Add(
shape.inner.as_ref().expect("Input shape ref was null"),
bb.inner.pin_mut(),
true,
);
bb
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn create_bounding_box() {
let bb = BoundingBox::void();
assert!(bb.is_void());
}

#[test]
fn get_bounding_box_of_sphere() {
let s = Shape::sphere(1.0).build();

let bb = aabb(&s);

assert_eq!(bb.min(), glam::dvec3(-1.0, -1.0, -1.0) - bb.gap_vec());
assert_eq!(bb.max(), glam::dvec3(1.0, 1.0, 1.0) + bb.gap_vec());
}

#[test]
fn get_bounding_box_of_sphere_transformed() {
let s = Shape::sphere(1.0).at(glam::dvec3(1.0, 2.0, 3.0)).build();

let bb = aabb(&s);
let gap = bb.gap_vec();
assert_eq!(bb.min(), glam::dvec3(0.0, 1.0, 2.0) - gap);
assert_eq!(bb.max(), glam::dvec3(2.0, 3.0, 4.0) + gap);
}
}
1 change: 1 addition & 0 deletions crates/opencascade/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use thiserror::Error;

pub mod angle;
pub mod bounding_box;
pub mod kicad;
pub mod mesh;
pub mod primitives;
Expand Down
35 changes: 35 additions & 0 deletions examples/src/bounding_box.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use opencascade::{
bounding_box,
primitives::{Compound, IntoShape, Shape},
workplane::Workplane,
};

pub fn shape() -> Shape {
let mut shapes = vec![];

// Create some non-orthogonal shapes.
for i in 0..=1 {
// NOTE: Try changing the range values and the position vector
let v = (2i32.pow(i)) as f64;
let s = Shape::sphere(1.0).at(glam::dvec3(v, v, v * 2.0)).build();
shapes.push(s);
}

// Add a circle to show that edges work, too.
shapes.push(Workplane::xy().circle(0.0, 0.0, 2.0).into_shape());

// Combine them to create a bounding box.
let c = Compound::from_shapes(shapes);

// Create the bounding box.
let bb = bounding_box::aabb(&Shape::from(&c));

// Create a box geometry for rendering.
let bb_shape = Shape::box_from_corners(bb.min(), bb.max());

let all_shapes =
[vec![c.into_shape()], bb_shape.edges().map(|e| e.into_shape()).collect::<Vec<_>>()];

// Combine the bounded and bounding geometry.
Compound::from_shapes(all_shapes.iter().flatten()).into_shape()
}
3 changes: 3 additions & 0 deletions examples/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::ValueEnum;
use opencascade::primitives::Shape;

pub mod airfoil;
pub mod bounding_box;
pub mod box_shape;
pub mod cable_bracket;
pub mod chamfer;
Expand All @@ -26,6 +27,7 @@ pub mod zbox_case;
#[derive(Debug, Copy, Clone, PartialEq, ValueEnum)]
pub enum Example {
Airfoil,
BoundingBox,
CableBracket,
BoxShape,
Chamfer,
Expand All @@ -52,6 +54,7 @@ impl Example {
pub fn shape(self) -> Shape {
match self {
Example::Airfoil => airfoil::shape(),
Example::BoundingBox => bounding_box::shape(),
Example::CableBracket => cable_bracket::shape(),
Example::BoxShape => box_shape::shape(),
Example::Chamfer => chamfer::shape(),
Expand Down