Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ builtin = [ "opencascade-sys/builtin" ]
[dependencies]
cxx = "1"
iter_fixed = "0.4.0"
opencascade-sys = { git = "https://github.com/bschwind/opencascade-rs", rev = "d1db1bf1fb58dd094144532aa0e5c22106d61083" }
opencascade-sys = { git = "https://github.com/bschwind/opencascade-rs", rev = "c30da56647c2a60393984458439180886ecaf951" }
tempfile = "3.19.1"

[dev-dependencies]
Expand Down
17 changes: 17 additions & 0 deletions src/core/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@ impl<const DIM: usize> Dir<DIM> {
pub fn dot(&self, other: Self) -> f64 {
self.0.into_iter().zip(other.0).map(|(a, b)| a * b).sum()
}

/// Return true if this `Dir` has less than a 0.000001% difference to another.
///
/// ```rust
/// use anvil::dir;
///
/// assert!(dir!(1, 1).approx_eq(dir!(1.00000001, 1)));
/// assert!(!dir!(1, 1).approx_eq(dir!(0.5, 1)));
/// ```
pub fn approx_eq(&self, other: Dir<DIM>) -> bool {
for (s, o) in self.0.iter().zip(other.0) {
if (s / o - 1.).abs() > 0.0000001 {
return false;
}
}
true
}
}

impl Dir<2> {
Expand Down
48 changes: 28 additions & 20 deletions src/core/length.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::ops::{Add, Div, Mul, Neg, Sub};
use std::{
fmt::Debug,
ops::{Add, Div, Mul, Neg, Sub},
};

use crate::{Dir, IntoF64, Point};

Expand Down Expand Up @@ -27,7 +30,7 @@ use crate::{Dir, IntoF64, Point};
/// assert_eq!(4.5.cm(), Length::from_cm(4.5));
/// assert_eq!(12.in_(), Length::from_in(12.));
/// ```
#[derive(Debug, PartialEq, Copy, Clone, PartialOrd)]
#[derive(PartialEq, Copy, Clone, PartialOrd)]
pub struct Length {
meters: f64,
}
Expand All @@ -41,7 +44,7 @@ impl Length {
/// let len = Length::zero();
/// assert_eq!(len.m(), 0.);
/// ```
pub fn zero() -> Self {
pub const fn zero() -> Self {
Self { meters: 0. }
}
/// Construct a `Length` from a value of unit meters.
Expand All @@ -53,11 +56,11 @@ impl Length {
/// let len = Length::from_m(3.2);
/// assert_eq!(len.mm(), 3200.);
/// ```
pub fn from_m(value: f64) -> Self {
pub const fn from_m(value: f64) -> Self {
Self { meters: value }
}
/// Return the value of this `Length` in millimeters.
pub fn m(&self) -> f64 {
pub const fn m(&self) -> f64 {
self.meters
}
/// Construct a `Length` from a value of unit yards.
Expand All @@ -69,11 +72,11 @@ impl Length {
/// let len = Length::from_yd(1.);
/// assert_eq!(len.m(), 0.9144);
/// ```
pub fn from_yd(value: f64) -> Self {
pub const fn from_yd(value: f64) -> Self {
Self::from_m(value * 0.9144)
}
/// Return the value of this `Length` in yards.
pub fn yd(&self) -> f64 {
pub const fn yd(&self) -> f64 {
self.m() / 0.9144
}
/// Construct a `Length` from a value of unit feet.
Expand All @@ -85,11 +88,11 @@ impl Length {
/// let len = Length::from_ft(1.);
/// assert_eq!(len.cm(), 30.48);
/// ```
pub fn from_ft(value: f64) -> Self {
pub const fn from_ft(value: f64) -> Self {
Self::from_m(value * 0.3048)
}
/// Return the value of this `Length` in feet.
pub fn ft(&self) -> f64 {
pub const fn ft(&self) -> f64 {
self.m() / 0.3048
}
/// Construct a `Length` from a value of unit decimeters.
Expand All @@ -101,11 +104,11 @@ impl Length {
/// let len = Length::from_dm(5.1);
/// assert_eq!(len.mm(), 510.);
/// ```
pub fn from_dm(value: f64) -> Self {
pub const fn from_dm(value: f64) -> Self {
Self::from_m(value / 10.)
}
/// Return the value of this `Length` in decimeters.
pub fn dm(&self) -> f64 {
pub const fn dm(&self) -> f64 {
self.m() * 10.
}
/// Construct a `Length` from a value of unit inches.
Expand All @@ -117,14 +120,14 @@ impl Length {
/// let len = Length::from_in(1.);
/// assert_eq!(len.cm(), 2.54);
/// ```
pub fn from_in(value: f64) -> Self {
pub const fn from_in(value: f64) -> Self {
Self::from_m(value * 0.0254)
}
/// Return the value of this `Length` in inches.
///
/// This method breaks the pattern with the trailing underscore, because `in` is a reserved
/// keyword in Rust.
pub fn in_(&self) -> f64 {
pub const fn in_(&self) -> f64 {
self.m() / 0.0254
}
/// Construct a `Length` from a value of unit centimeters.
Expand All @@ -136,11 +139,11 @@ impl Length {
/// let len = Length::from_cm(5.1);
/// assert_eq!(len.mm(), 51.);
/// ```
pub fn from_cm(value: f64) -> Self {
pub const fn from_cm(value: f64) -> Self {
Self::from_m(value / 100.)
}
/// Return the value of this `Length` in centimeters.
pub fn cm(&self) -> f64 {
pub const fn cm(&self) -> f64 {
self.m() * 100.
}
/// Construct a `Length` from a value of unit millimeters.
Expand All @@ -152,11 +155,11 @@ impl Length {
/// let len = Length::from_mm(5.4);
/// assert_eq!(len.m(), 0.0054);
/// ```
pub fn from_mm(value: f64) -> Self {
pub const fn from_mm(value: f64) -> Self {
Self::from_m(value / 1000.)
}
/// Return the value of this `Length` in millimeters.
pub fn mm(&self) -> f64 {
pub const fn mm(&self) -> f64 {
self.m() * 1000.
}

Expand All @@ -168,7 +171,7 @@ impl Length {
/// assert_eq!((-5).m().abs(), 5.m());
/// assert_eq!(5.m().abs(), 5.m());
/// ```
pub fn abs(&self) -> Self {
pub const fn abs(&self) -> Self {
Self {
meters: self.meters.abs(),
}
Expand All @@ -184,7 +187,7 @@ impl Length {
/// assert_eq!(len1.min(&len2), len1);
/// assert_eq!(len2.min(&len1), len1);
/// ```
pub fn min(&self, other: &Self) -> Self {
pub const fn min(&self, other: &Self) -> Self {
Length::from_m(self.m().min(other.m()))
}
/// Return the larger of two lengths.
Expand All @@ -198,10 +201,15 @@ impl Length {
/// assert_eq!(len1.max(&len2), len2);
/// assert_eq!(len2.max(&len1), len2);
/// ```
pub fn max(&self, other: &Self) -> Self {
pub const fn max(&self, other: &Self) -> Self {
Length::from_m(self.m().max(other.m()))
}
}
impl Debug for Length {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(format!("{}m", self.m()).as_str())
}
}

impl Add<Length> for Length {
type Output = Length;
Expand Down
7 changes: 5 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ pub enum Error {
/// Occurs when a `Part` could not be written to a .stl file at a given path.
StlWrite(PathBuf),

/// Occurs when an operation that requires a length is performed on a `Dir3D` with a magnitude of zero.
ZeroVector,
/// Occurs when a `Face` or `Part` can not be triangulated.
Triangulation,

/// Occurs when two vectors that are required to be orthogonal, are not.
VectorsNotOrthogonal(Dir<3>, Dir<3>),

/// Occurs when an operation that requires a length is performed on a `Dir3D` with a magnitude of zero.
ZeroVector,
}
impl StdError for Error {}
impl fmt::Display for Error {
Expand Down
16 changes: 16 additions & 0 deletions src/faces/face.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use cxx::UniquePtr;
use opencascade_sys::ffi;

/// A 2D surface that has a clear bound.
pub struct Face(pub(crate) UniquePtr<ffi::TopoDS_Face>);
impl Face {
pub(crate) fn from_occt(occt: &ffi::TopoDS_Face) -> Self {
Self(ffi::TopoDS_Face_to_owned(occt))
}
}

impl Clone for Face {
fn clone(&self) -> Self {
Self::from_occt(&self.0)
}
}
90 changes: 90 additions & 0 deletions src/faces/iterator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use cxx::UniquePtr;
use opencascade_sys::ffi;

use crate::Part;

use super::face::Face;

/// Iterator over the `Face`s of a `Part`.
///
/// ```rust
/// use anvil::{Cube, Face, FaceIterator, IntoLength};
///
/// let face_iterator: FaceIterator = Cube::from_size(1.m()).faces();
/// for face in face_iterator {
/// // ...
/// }
/// ```
pub enum FaceIterator {
/// A FaceIterator that is not empty.
NotEmpty(Part, UniquePtr<ffi::TopExp_Explorer>),
/// A FaceIterator from an empty shape.
Empty,
}

impl Iterator for FaceIterator {
type Item = Face;

fn next(&mut self) -> Option<Self::Item> {
match self {
Self::NotEmpty(_, explorer) => {
if explorer.More() {
let face = ffi::TopoDS_cast_to_face(explorer.Current());
let face = Face::from_occt(face);
explorer.pin_mut().Next();
Some(face)
} else {
None
}
}
Self::Empty => None,
}
}
}
impl ExactSizeIterator for FaceIterator {
fn len(&self) -> usize {
match self {
Self::NotEmpty(_, _) => {
let mut len = 0;
for _ in self.clone_without_position() {
len += 1;
}
len
}
Self::Empty => 0,
}
}
}
impl FaceIterator {
/// Return `true` if this `FaceIterator` has a length of 0.
pub fn is_empty(self) -> bool {
self.len() == 0
}
fn clone_without_position(&self) -> Self {
match self {
Self::NotEmpty(part, _) => part.faces(),
Self::Empty => Self::Empty,
}
}
}
impl From<&Part> for FaceIterator {
fn from(value: &Part) -> Self {
match &value.inner {
Some(inner) => {
let explorer = ffi::TopExp_Explorer_ctor(inner, ffi::TopAbs_ShapeEnum::TopAbs_FACE);
Self::NotEmpty(value.clone(), explorer)
}
None => Self::Empty,
}
}
}

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

#[test]
fn empty() {
assert!(Part::empty().faces().is_empty())
}
}
5 changes: 5 additions & 0 deletions src/faces/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod face;
mod iterator;

pub use face::Face;
pub use iterator::FaceIterator;
6 changes: 6 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
#![doc = "A CAD engine."]
#![allow(clippy::approx_constant)]
#![warn(missing_docs)]
#![warn(clippy::todo)]
#![warn(clippy::unimplemented)]

mod core;
mod errors;
mod faces;
mod meshes;
mod parts;
mod sketches;

pub use core::{
Angle, Axis, Dir, Edge, IntoAngle, IntoF64, IntoLength, Length, Path, Plane, Point,
};
pub use errors::Error;
pub use faces::{Face, FaceIterator};
pub use meshes::RenderMesh;
pub use parts::{
Part,
primitives::{Cube, Cuboid, Cylinder, Sphere},
Expand Down
3 changes: 3 additions & 0 deletions src/meshes/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod render_mesh;

pub use render_mesh::RenderMesh;
Loading