Cylinder

Build a cylinder with rsweep, try_attach_plane, and tsweep

What happens at each step
  • Rotational sweep (`rsweep`): spin a vertex to make a circular wire.
  • Plane attachment: cap the wire into a disk with try_attach_plane.
  • Translational sweep (`tsweep`): extrude the disk to form the solid.

src/cylinder.rs

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

}
}

1. Place a vertex on the base circle at (0, 0, -1).

#![allow(unused)]
fn main() {
  let vertex: Vertex = builder::vertex(Point3::new(0.0, 0.0, -1.0));
}
visual
          y
          │
          │
   ●      │   (start at z = -1)
          │
          └──── x
         /
        z

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

#![allow(unused)]
fn main() {
  let circle: Wire = builder::rsweep(
      &vertex,
      Point3::new(0.0, 1.0, -1.0), // point on rotation axis
      Vector3::unit_z(),          // axis direction
      Rad(7.0),                   // > 2π ensures closure
  );
}
visual
Axis: +Z through (0, 1, -1)

     ●---●---●   (circle in XY plane at z = -1)
      \  |  /
       \ | /
         +
        axis

3. Cap the wire into a planar disk.

#![allow(unused)]
fn main() {
  let disk: Face =
      builder::try_attach_plane(&vec![circle]).expect("cannot attach plane");
}
visual
The circular wire is filled to a disk (face) if the wire is closed and planar.

     +------+
    /        \
   |    ●     |
    \        /
     +------+

4. Extrude the disk 2 units along +Z to make the solid.

#![allow(unused)]
fn main() {
  builder::tsweep(&disk, 2.0 * Vector3::unit_z())
}
visual
Sweeping the disk upward (+Z) forms the cylinder volume:

     ●───────●
    /|       /|
   ●───────●  |
   | |      | |
   | ●──────|-●
   |/       |/
   ●───────●

Update src/lib.rs to expose cylinder

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

Keep helpers in lib.rs and cylinder in src/cylinder.rs.

Example entry point

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

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

Directory tree (for this section)
truck_brep/
├─ src/
│  ├─ lib.rs      # helpers + re-exports
│  ├─ cube.rs
│  ├─ torus.rs
│  └─ cylinder.rs # this section
├─ examples/
│  ├─ cube.rs
│  ├─ torus.rs
│  └─ cylinder.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;

pub mod cylinder;
pub use cylinder::cylinder;

/// 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/cylinder.rs

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

pub fn cylinder() -> Solid {
    let vertex: Vertex = builder::vertex(Point3::new(0.0, 0.0, -1.0));
    let circle: Wire = builder::rsweep(
        &vertex,
        Point3::new(0.0, 1.0, -1.0),
        Vector3::unit_z(),
        Rad(7.0),
    );
    let disk: Face = builder::try_attach_plane(&vec![circle]).expect("cannot attach plane");
    builder::tsweep(&disk, 2.0 * Vector3::unit_z())
}
}

examples/cylinder.rs

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