-
Notifications
You must be signed in to change notification settings - Fork 2
Meshing #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Meshing #16
Changes from 7 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
db239c5
feat: Part.faces()
52f3f64
ci: make clippy warn for todo and unimplemented macros
26d7a4c
feat: triangulation for points
fc73bc0
feat: create custom Length Debug implementation
6a4218c
feat: triangulation for other fields
735a0da
fix: make TexturedMesh index order deterministic
51d1002
lint: obey clippy
17958fa
refactor: extract FaceIterator.len() into ExactSizeIterator
5c5f1f0
refactor: privatise bad clone function
e21cad8
fix: transformation of normals
983e9ac
refactor: rename TexturedMesh to RenderMesh
d77af3b
feat: Dir.approx_eq()
d82c25f
test: test rotated mesh normals
14906c8
feat: RenderMesh.sorted()
2e1ace4
docs: document and publish RenderedMesh
b2e20ed
feat: make Length methods const
7132be7
feat: transforming Part into a RenderMesh
3e33b74
feat: RenderMesh.center() and .area()
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
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(); | ||
/// assert_eq!(face_iterator.clone().len(), 6); | ||
/// 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 FaceIterator { | ||
/// Return `true` if this `FaceIterator` has a length of 0. | ||
pub fn is_empty(self) -> bool { | ||
self.len() == 0 | ||
} | ||
/// Return the number of `Face`s in this `FaceIterator`. | ||
pub fn len(self) -> usize { | ||
match self { | ||
Self::NotEmpty(_, _) => { | ||
let self_clone = self.clone(); | ||
let mut len = 0; | ||
for _ in self_clone { | ||
len += 1; | ||
} | ||
len | ||
} | ||
Self::Empty => 0, | ||
} | ||
} | ||
} | ||
impl Clone for FaceIterator { | ||
/// Return a clone of this `FaceIterator`. | ||
/// | ||
/// WARNING: Iterator position will not be cloned. | ||
unexcellent marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
fn clone(&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()) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
mod textured_mesh; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
use opencascade_sys::ffi; | ||
|
||
use crate::{Dir, Error, Face, IntoLength, Length, Point}; | ||
|
||
#[derive(Clone, Debug, PartialEq)] | ||
pub struct TexturedMesh { | ||
unexcellent marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
points: Vec<Point<3>>, | ||
indices: Vec<[usize; 3]>, | ||
normals: Vec<Dir<3>>, | ||
uvs: Vec<[f64; 2]>, | ||
} | ||
impl TryFrom<Face> for TexturedMesh { | ||
type Error = Error; | ||
fn try_from(value: Face) -> Result<Self, Self::Error> { | ||
Self::try_from((value, 0.1.mm())) | ||
} | ||
} | ||
impl TryFrom<(Face, Length)> for TexturedMesh { | ||
type Error = Error; | ||
fn try_from(value: (Face, Length)) -> Result<Self, Self::Error> { | ||
let mesh = ffi::BRepMesh_IncrementalMesh_ctor( | ||
ffi::cast_face_to_shape(value.0.0.as_ref().unwrap()), | ||
value.1.m(), | ||
); | ||
let face = ffi::TopoDS_cast_to_face(mesh.as_ref().unwrap().Shape()); | ||
|
||
let mut location = ffi::TopLoc_Location_ctor(); | ||
|
||
let triangulation_handle = ffi::BRep_Tool_Triangulation(face, location.pin_mut()); | ||
let transformation = ffi::TopLoc_Location_Transformation(&location); | ||
|
||
if let Ok(triangulation) = ffi::HandlePoly_Triangulation_Get(&triangulation_handle) { | ||
let mut points = vec![]; | ||
let mut indices = vec![]; | ||
let mut normals = vec![]; | ||
let mut uvs = vec![]; | ||
|
||
let orientation = face.Orientation(); | ||
let face_point_count = triangulation.NbNodes(); | ||
ffi::compute_normals(face, &triangulation_handle); | ||
|
||
for node_index in 1..=face_point_count { | ||
let mut point = ffi::Poly_Triangulation_Node(triangulation, node_index); | ||
point.pin_mut().Transform(&transformation); | ||
points.push(Point::<3>::new([ | ||
point.X().m(), | ||
point.Y().m(), | ||
point.Z().m(), | ||
])); | ||
|
||
let uv = ffi::Poly_Triangulation_UV(triangulation, node_index); | ||
uvs.push([uv.X(), uv.Y()]); | ||
|
||
let normal = ffi::Poly_Triangulation_Normal(triangulation, node_index); | ||
unexcellent marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
let m = if orientation == ffi::TopAbs_Orientation::TopAbs_REVERSED { | ||
-1. | ||
} else { | ||
1. | ||
}; | ||
normals.push( | ||
Dir::try_from([normal.X() * m, normal.Y() * m, normal.Z() * m]) | ||
.expect("normals should not be zero"), | ||
); | ||
} | ||
|
||
let mut u_min = f64::INFINITY; | ||
let mut v_min = f64::INFINITY; | ||
let mut u_max = f64::NEG_INFINITY; | ||
let mut v_max = f64::NEG_INFINITY; | ||
|
||
for &[u, v] in &uvs { | ||
u_min = u_min.min(u); | ||
v_min = v_min.min(v); | ||
u_max = u_max.max(u); | ||
v_max = v_max.max(v); | ||
} | ||
|
||
for [u, v] in &mut uvs { | ||
*u = (*u - u_min) / (u_max - u_min); | ||
*v = (*v - v_min) / (v_max - v_min); | ||
|
||
if orientation == ffi::TopAbs_Orientation::TopAbs_REVERSED { | ||
*u = 1.0 - *u; | ||
} | ||
} | ||
|
||
for triangle_index in 1..=triangulation.NbTriangles() { | ||
let triangle = triangulation.Triangle(triangle_index); | ||
let mut node_ids = [triangle.Value(1), triangle.Value(2), triangle.Value(3)] | ||
.map(|id| id as usize - 1); | ||
|
||
node_ids.sort(); // depending on device, nodes may be sorted differently - sorting them makes the order deterministic | ||
unexcellent marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
indices.push(node_ids); | ||
} | ||
|
||
Ok(TexturedMesh { | ||
points, | ||
indices, | ||
normals, | ||
uvs, | ||
}) | ||
} else { | ||
Err(Error::Triangulation) | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::{IntoLength, Path, Plane, Rectangle, dir, point}; | ||
|
||
use super::*; | ||
|
||
#[test] | ||
fn triangle() { | ||
let face = Path::at(point!(0, 0)) | ||
.line_to(point!(1.m(), 0.m())) | ||
.line_to(point!(0.m(), 1.m())) | ||
.close() | ||
.to_face(Plane::xy()) | ||
.unwrap(); | ||
|
||
assert_eq!( | ||
TexturedMesh::try_from(face), | ||
Ok(TexturedMesh { | ||
points: vec![ | ||
point!(0, 0, 0), | ||
point!(1.m(), 0.m(), 0.m()), | ||
point!(0.m(), 1.m(), 0.m()) | ||
], | ||
indices: vec![[0, 1, 2]], | ||
normals: vec![dir!(0, 0, 1), dir!(0, 0, 1), dir!(0, 0, 1)], | ||
uvs: vec![[0., 0.], [1., 0.], [0., 1.]] | ||
}) | ||
) | ||
} | ||
|
||
#[test] | ||
fn rectangle() { | ||
let face = Rectangle::from_corners(point!(0, 0), point!(1.m(), 1.m())) | ||
.to_face(Plane::xy()) | ||
.unwrap(); | ||
|
||
assert_eq!( | ||
TexturedMesh::try_from(face), | ||
Ok(TexturedMesh { | ||
points: vec![ | ||
point!(0, 0, 0), | ||
point!(1.m(), 0.m(), 0.m()), | ||
point!(1.m(), 1.m(), 0.m()), | ||
point!(0.m(), 1.m(), 0.m()), | ||
], | ||
indices: vec![[0, 1, 2], [0, 2, 3]], | ||
normals: vec![dir!(0, 0, 1), dir!(0, 0, 1), dir!(0, 0, 1), dir!(0, 0, 1)], | ||
uvs: vec![[0., 0.], [1., 0.], [1., 1.], [0., 1.]] | ||
}) | ||
) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.