Torus

Build a torus (donut) with rotational sweeps, keeping the geometry and exports in the library just like the cube section. For the cylinder example, see modeling_cylinder.md.

Build a torus with rsweep

What rsweep does

rsweep spins a geometric element around an axis to create the next-level element:

  • spin a vertex → you get a wire (circle)
  • spin a wire → you get a shell

A torus is built by two rotations: one to form the circle, another to spin that circle into the donut.

src/torus.rs

#![allow(unused)]
fn main() {
use truck_modeling::*;
}
#![allow(unused)]
fn main() {
pub fn torus() -> Solid {
  // STEPS 1-3 GO HERE

}
}

1. Place a vertex at (0, 0, 1).

#![allow(unused)]
fn main() {
  let vertex: Vertex = builder::vertex(Point3::new(0.0, 0.0, 1.0));
}
what this code does

Creates a B-rep vertex at (0, 0, 1) to act as the seed point for the rotational sweeps.

visual
        y
        │
        │
   ●    │   (start at z = 1)
        │
        └──── x
       /
      z

2. Spin the vertex around +X to form a circular wire.

#![allow(unused)]
fn main() {
  let circle: Wire = builder::rsweep(
      &vertex,
      Point3::new(0.0, 0.5, 1.0), // point on rotation axis
      Vector3::unit_x(),         // axis direction
      Rad(7.0),                  // > 2π ensures closure
  );
}
what this code does

rsweep clones the vertex and spins it around the +X axis through (0, 0.5, 1.0); the start and end positions connect into a circular wire.

visual
Axis: +X through (0, 0.5, 1.0)

   (spins around X)
       ^
       |
   ●---+---●  (wire)
       |
     axis

3. Spin the circle around +Y to form the torus shell.

#![allow(unused)]
fn main() {
  builder::rsweep(&circle, Point3::origin(), Vector3::unit_y(), Rad(7.0))
}
what this code does

Rotates the circle about the Y axis at the origin; the swept surface forms the torus shell.

visual
Second rotation around +Y wraps the circle into a torus:

      ●─────●
     /       \
    ●         ●
     \       /
      ●─────●

Update src/lib.rs to expose torus

#![allow(unused)]
fn main() {
pub mod torus;
pub use torus::torus;
}

Example entry point

examples/torus.rs stays tiny and calls into the library:

fn main() {
    let torus = truck_brep::torus();
    truck_brep::save_obj(&torus, "output/torus.obj").unwrap();
    truck_brep::save_step(&torus, "output/torus.step").unwrap();
}

Directory tree (for this section)
truck_brep/
├─ src/
│  ├─ lib.rs      # helpers + re-exports
│  ├─ cube.rs     # from previous section (optional)
│  └─ torus.rs    # torus()
├─ examples/
│  ├─ cube.rs
│  └─ torus.rs
└─ output/        # created at runtime
Complete code

src/lib.rs

#![allow(unused)]
fn main() {
use std::{fs, io, path::Path};

use truck_meshalgo::prelude::*;
use truck_modeling::*;
use truck_stepio::out::{CompleteStepDisplay, StepModel};
use truck_topology::compress::{CompressedShell, CompressedSolid};

pub mod cube;
pub use cube::cube;

pub mod torus;
pub use torus::torus;

/// Helper to compress modeling shapes into STEP-compatible data.
pub trait StepCompress {
    type Compressed;
    fn compress_for_step(&self) -> Self::Compressed;
}

impl StepCompress for Shell {
    type Compressed = CompressedShell<Point3, Curve, Surface>;
    fn compress_for_step(&self) -> Self::Compressed { self.compress() }
}

impl StepCompress for Solid {
    type Compressed = CompressedSolid<Point3, Curve, Surface>;
    fn compress_for_step(&self) -> Self::Compressed { self.compress() }
}

/// Export any B-rep (Solid or Shell) to STEP.
pub fn save_step<T, P>(brep: &T, path: P) -> io::Result<()>
where
    T: StepCompress,
    for<'a> StepModel<'a, Point3, Curve, Surface>: From<&'a T::Compressed>,
    P: AsRef<Path>,
{
    let path = path.as_ref();
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let compressed = brep.compress_for_step();
    let display = CompleteStepDisplay::new(
        StepModel::from(&compressed),
        Default::default(),
    );
    fs::write(path, display.to_string())
}

/// Triangulate any B-rep (Solid or Shell) and write an OBJ mesh.
pub fn save_obj(shape: &impl MeshableShape, path: impl AsRef<Path>) -> io::Result<()> {
    let mesh = shape.triangulation(0.01).to_polygon();
    let path = path.as_ref();
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let mut obj = fs::File::create(path)?;
    obj::write(&mesh, &mut obj).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
}

}

src/torus.rs

#![allow(unused)]
fn main() {
use truck_modeling::*;

pub fn torus() -> Shell {
    let vertex: Vertex = builder::vertex(Point3::new(0.0, 0.0, 1.0));
    let circle: Wire = builder::rsweep(
        &vertex,
        Point3::new(0.0, 0.5, 1.0), // point on rotation axis
        Vector3::unit_x(),         // axis direction
        Rad(7.0),                  // > 2π ensures closure
    );
    builder::rsweep(&circle, Point3::origin(), Vector3::unit_y(), Rad(7.0))
}
}

examples/torus.rs

fn main() {
    let torus = truck_brep::torus();
    truck_brep::save_obj(&torus, "output/torus.obj").unwrap();
    truck_brep::save_step_any(&torus, "output/torus.step").unwrap();
}