Skip to main content

Crate arris

Crate arris 

Source
Expand description

arris — a B-Rep geometric kernel written from scratch in Rust.

This crate is the facade: it re-exports the workspace crates as modules (arris::math through arris::io) so a consumer depends on one name and reaches everything through it. Nothing lives here that does not live in a lower crate; the guarantees are each crate’s (docs/ARCHITECTURE.md).

use arris::math::{Axis, Control, Point3};
use arris::topo::Model;
use arris::check::{Level, check};

let mut m = Model::default();
let (cylinder, provenance) =
    arris::ops::primitive_cylinder(&mut m, Axis::z_at(Point3::origin()), 4.0, 12.0, &Control::NONE)
        .unwrap();
assert!(check(&m, cylinder, Level::Full).is_ok());
assert_eq!(provenance.outputs().len(), 10);
let text = arris::io::step::write(&m, &[cylinder]).unwrap();
assert!(text.starts_with("ISO-10303-21;"));

// And back: every solid of a file, each its own result.
let mut back = Model::default();
let read = arris::io::step::read(&mut back, &text, &Default::default(), &Control::NONE).unwrap();
let body = read.solids[0].result.as_ref().unwrap().body;
assert!(check(&back, body, Level::Full).is_ok());

A profile carried along a path is sweep: a path of lines, arcs, helices or NURBS curves, each span exact where a quadric holds it and fitted where none does.

use arris::Control;
use arris::geom::profile::{Profile, ProfileLoop};
use arris::geom::{FrameLaw, Path, PathSegment};
use arris::math::{Frame, Point2, Point3, Vec3};
use arris::ops::sweep;
use arris::topo::Model;

let profile = Profile {
    plane: Frame::new(Point3::origin(), Vec3::z(), Vec3::x()).unwrap(),
    outer: ProfileLoop::Circle { center: Point2::origin(), radius: 1.0 },
    holes: vec![],
};
let path = Path {
    start: Point3::origin(),
    segments: vec![PathSegment::LineTo(Point3::new(0.0, 0.0, 10.0))],
};
let mut m = Model::default();
let (pipe, provenance) =
    sweep(&mut m, &profile, &path, FrameLaw::default(), &Control::NONE).unwrap();
assert_eq!(m.faces(pipe).unwrap().len(), 3, "two caps and the wall");
assert_eq!(provenance.outputs().len(), 10);

Profiles placed at different heights are lofted: loft goes through two or more of them, ruled (each span straight) or smooth (one surface).

use arris::Control;
use arris::geom::profile::{Profile, ProfileLoop};
use arris::math::{Frame, Point2, Point3, Vec3};
use arris::ops::{LoftKind, loft};
use arris::topo::Model;

let disc = |z: f64, radius: f64| Profile {
    plane: Frame::new(Point3::new(0.0, 0.0, z), Vec3::z(), Vec3::x()).unwrap(),
    outer: ProfileLoop::Circle { center: Point2::origin(), radius },
    holes: vec![],
};
let mut m = Model::default();
let sections = [disc(0.0, 2.0), disc(3.0, 1.0)];
let (cone, _) = loft(&mut m, &sections, LoftKind::Ruled, false, &Control::NONE).unwrap();
assert_eq!(m.faces(cone).unwrap().len(), 3, "two caps and the cone");

Every operation on a model takes a Control last, so its caller can stop it: a poll it answers from whatever its platform has (here an AtomicBool another thread sets), a budget of steps, both or neither. A stop is Interrupted in the operation’s own error type, and the model is as it was before the call.

use arris::{Control, Stop};
use arris::ops::{OpError, primitive_box};
use arris::topo::Model;
use core::sync::atomic::{AtomicBool, Ordering};

let mut m = Model::default();

// A poll: the consumer's flag, asked at every step.
let cancel = AtomicBool::new(true);
let poll = || cancel.load(Ordering::Relaxed);
let stopped = primitive_box(&mut m, [0.0; 3], [1.0; 3], &Control::poll(&poll));
let Err(OpError::Interrupted(stop)) = stopped else { panic!("not stopped") };
assert_eq!(stop.by, Stop::Poll);
assert!(m.body(arris::topo::BodyId::new(0, 0)).is_err(), "nothing was left in the model");

// A budget: the same input and budget stop at the same step everywhere.
let spent = primitive_box(&mut m, [0.0; 3], [1.0; 3], &Control::budget(0));
assert!(matches!(spent, Err(OpError::Interrupted(s)) if s.by == Stop::Budget && s.steps == 0));

// Neither: run to the end. A budget that is enough changes nothing.
cancel.store(false, Ordering::Relaxed);
assert!(primitive_box(&mut m, [0.0; 3], [1.0; 3], &Control::poll(&poll).with_budget(100)).is_ok());

Re-exports§

pub use arris_check as check;
pub use arris_geom as geom;
pub use arris_io as io;
pub use arris_math as math;
pub use arris_mesh as mesh;
pub use arris_ops as ops;
pub use arris_topo as topo;

Structs§

Control
How a caller stops an operation: a poll, a budget of steps, both or neither. Every operation on a model, and every long query beside one, takes one last; Control::NONE is what a caller that does not care passes.
Interrupted
An operation that was stopped, by the caller and not by its input. The model is as it was before the call.

Enums§

Stop
What stopped an operation.