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 .github/workflows/parry-ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- name: Check serialization
run: cargo check --features bytemuck-serialize,serde-serialize,rkyv-serialize;
- name: Run tests
run: cargo test
run: cargo test --features wavefront
build-wasm:
runs-on: ubuntu-latest
env:
Expand Down
5 changes: 5 additions & 0 deletions crates/parry3d-f64/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ simd-stable = ["simba/wide", "simd-is-enabled"]
simd-nightly = ["simba/portable_simd", "simd-is-enabled"]
enhanced-determinism = ["simba/libm_force", "indexmap"]
parallel = ["rayon"]
# Adds `TriMesh:to_obj_file` function.
wavefront = ["obj"]
Comment on lines +47 to 48
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding documentation on cargo.toml is idiomatic to rust ecosystem (see serde or bevy)

alloc = []
improved_fixed_point_support = []
Expand Down Expand Up @@ -88,3 +89,7 @@ thiserror = { version = "1", optional = true }
oorandom = "11"
ptree = "0.4.0"
rand = { version = "0.8" }

[package.metadata.docs.rs]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is already present in parry3d (we're in parry3d-f64 here)

features = ["wavefront"]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows to see the function in rustdoc.rs

2 changes: 2 additions & 0 deletions crates/parry3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ simd-stable = ["simba/wide", "simd-is-enabled"]
simd-nightly = ["simba/portable_simd", "simd-is-enabled"]
enhanced-determinism = ["simba/libm_force", "indexmap"]
parallel = ["rayon"]
# Adds `TriMesh:to_obj_file` function.
wavefront = ["obj"]
alloc = []
improved_fixed_point_support = []
Expand Down Expand Up @@ -95,6 +96,7 @@ rand_isaac = "0.3"
[package.metadata.docs.rs]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
features = ["wavefront"]

# The following listing is to allow for examples to be scraped,
# see https://doc.rust-lang.org/rustdoc/scraped-examples.html#scraped-examples for details.
Expand Down
80 changes: 56 additions & 24 deletions src/transformation/mesh_intersection/mesh_intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ use crate::query::{visitors::BoundingVolumeIntersectionsSimultaneousVisitor, Poi
use crate::shape::{TriMesh, Triangle};
use crate::utils;
use na::{Point3, Vector3};
#[cfg(feature = "wavefront")]
use obj::{Group, IndexTuple, ObjData, Object, SimplePolygon};
use rstar::RTree;
use spade::{ConstrainedDelaunayTriangulation, InsertionError, Triangulation as _};
use std::collections::BTreeMap;
Expand Down Expand Up @@ -513,7 +511,7 @@ fn insert_into_set(
fn smallest_angle(points: &[Point3<Real>]) -> Real {
let n = points.len();

let mut worst_cos = -2.0;
let mut worst_cos: Real = -2.0;
for i in 0..points.len() {
let d1 = (points[i] - points[(i + 1) % n]).normalize();
let d2 = (points[(i + 2) % n] - points[(i + 1) % n]).normalize();
Expand Down Expand Up @@ -669,9 +667,9 @@ fn merge_triangle_sets(
#[cfg(test)]
mod tests {
use super::*;
use crate::shape::TriMeshFlags;
use crate::transformation::wavefront::*;
use crate::shape::{Ball, Cuboid, TriMeshFlags};
use obj::Obj;
use obj::ObjData;

#[test]
fn test_same_mesh_intersection() {
Expand All @@ -685,17 +683,18 @@ mod tests {
let mesh = TriMesh::with_flags(
position
.iter()
.map(|v| Point3::new(v[0] as f64, v[1] as f64, v[2] as f64))
.map(|v| Point3::new(v[0] as Real, v[1] as Real, v[2] as Real))
.collect::<Vec<_>>(),
objects[0].groups[0]
.polys
.iter()
.map(|p| [p.0[0].0 as u32, p.0[1].0 as u32, p.0[2].0 as u32])
.collect::<Vec<_>>(),
TriMeshFlags::all(),
);
)
.unwrap();

let res = intersect_meshes(
let _ = intersect_meshes(
&Isometry::identity(),
&mesh,
false,
Expand All @@ -706,7 +705,34 @@ mod tests {
.unwrap()
.unwrap();

mesh.to_obj_file(&PathBuf::from("same_test.obj"));
let _ = mesh.to_obj_file(&PathBuf::from("same_test.obj"));
}

#[test]
fn test_non_origin_pos1_pos2_intersection() {
let ball = Ball::new(2f32 as Real).to_trimesh(10, 10);
let cuboid = Cuboid::new(Vector3::new(2.0, 1.0, 1.0)).to_trimesh();
let mut sphere_mesh = TriMesh::new(ball.0, ball.1).unwrap();
sphere_mesh.set_flags(TriMeshFlags::all()).unwrap();
let mut cuboid_mesh = TriMesh::new(cuboid.0, cuboid.1).unwrap();
cuboid_mesh.set_flags(TriMeshFlags::all()).unwrap();

let res = intersect_meshes(
&Isometry::translation(1.0, 0.0, 0.0),
&cuboid_mesh,
false,
&Isometry::translation(2.0, 0.0, 0.0),
&sphere_mesh,
false,
)
.unwrap()
.unwrap();

let _ = res.to_obj_file(&PathBuf::from("test_non_origin_pos1_pos2_intersection.obj"));

let bounding_sphere = res.local_bounding_sphere();
assert!(bounding_sphere.center == Point3::new(1.5, 0.0, 0.0));
assert_relative_eq!(2.0615528, bounding_sphere.radius, epsilon = 1.0e-5);
}

#[test]
Expand All @@ -721,15 +747,16 @@ mod tests {
let offset_mesh = TriMesh::with_flags(
position
.iter()
.map(|v| Point3::new(v[0] as f64, v[1] as f64, v[2] as f64))
.map(|v| Point3::new(v[0] as Real, v[1] as Real, v[2] as Real))
.collect::<Vec<_>>(),
objects[0].groups[0]
.polys
.iter()
.map(|p| [p.0[0].0 as u32, p.0[1].0 as u32, p.0[2].0 as u32])
.collect::<Vec<_>>(),
TriMeshFlags::all(),
);
)
.unwrap();

let Obj {
data: ObjData {
Expand All @@ -741,15 +768,16 @@ mod tests {
let center_mesh = TriMesh::with_flags(
position
.iter()
.map(|v| Point3::new(v[0] as f64, v[1] as f64, v[2] as f64))
.map(|v| Point3::new(v[0] as Real, v[1] as Real, v[2] as Real))
.collect::<Vec<_>>(),
objects[0].groups[0]
.polys
.iter()
.map(|p| [p.0[0].0 as u32, p.0[1].0 as u32, p.0[2].0 as u32])
.collect::<Vec<_>>(),
TriMeshFlags::all(),
);
)
.unwrap();

let res = intersect_meshes(
&Isometry::identity(),
Expand All @@ -762,7 +790,7 @@ mod tests {
.unwrap()
.unwrap();

res.to_obj_file(&PathBuf::from("offset_test.obj"));
let _ = res.to_obj_file(&PathBuf::from("offset_test.obj"));
}

#[test]
Expand All @@ -777,15 +805,16 @@ mod tests {
let stair_mesh = TriMesh::with_flags(
position
.iter()
.map(|v| Point3::new(v[0] as f64, v[1] as f64, v[2] as f64))
.map(|v| Point3::new(v[0] as Real, v[1] as Real, v[2] as Real))
.collect::<Vec<_>>(),
objects[0].groups[0]
.polys
.iter()
.map(|p| [p.0[0].0 as u32, p.0[1].0 as u32, p.0[2].0 as u32])
.collect::<Vec<_>>(),
TriMeshFlags::all(),
);
)
.unwrap();

let Obj {
data: ObjData {
Expand All @@ -797,15 +826,16 @@ mod tests {
let bar_mesh = TriMesh::with_flags(
position
.iter()
.map(|v| Point3::new(v[0] as f64, v[1] as f64, v[2] as f64))
.map(|v| Point3::new(v[0] as Real, v[1] as Real, v[2] as Real))
.collect::<Vec<_>>(),
objects[0].groups[0]
.polys
.iter()
.map(|p| [p.0[0].0 as u32, p.0[1].0 as u32, p.0[2].0 as u32])
.collect::<Vec<_>>(),
TriMeshFlags::all(),
);
)
.unwrap();

let res = intersect_meshes(
&Isometry::identity(),
Expand All @@ -818,7 +848,7 @@ mod tests {
.unwrap()
.unwrap();

res.to_obj_file(&PathBuf::from("stair_test.obj"));
let _ = res.to_obj_file(&PathBuf::from("stair_test.obj"));
}

#[test]
Expand All @@ -833,15 +863,16 @@ mod tests {
let bunny_mesh = TriMesh::with_flags(
position
.iter()
.map(|v| Point3::new(v[0] as f64, v[1] as f64, v[2] as f64))
.map(|v| Point3::new(v[0] as Real, v[1] as Real, v[2] as Real))
.collect::<Vec<_>>(),
objects[0].groups[0]
.polys
.iter()
.map(|p| [p.0[0].0 as u32, p.0[1].0 as u32, p.0[2].0 as u32])
.collect::<Vec<_>>(),
TriMeshFlags::all(),
);
)
.unwrap();

let Obj {
data: ObjData {
Expand All @@ -853,15 +884,16 @@ mod tests {
let cylinder_mesh = TriMesh::with_flags(
position
.iter()
.map(|v| Point3::new(v[0] as f64, v[1] as f64, v[2] as f64))
.map(|v| Point3::new(v[0] as Real, v[1] as Real, v[2] as Real))
.collect::<Vec<_>>(),
objects[0].groups[0]
.polys
.iter()
.map(|p| [p.0[0].0 as u32, p.0[1].0 as u32, p.0[2].0 as u32])
.collect::<Vec<_>>(),
TriMeshFlags::all(),
);
)
.unwrap();

let res = intersect_meshes(
&Isometry::identity(),
Expand All @@ -874,6 +906,6 @@ mod tests {
.unwrap()
.unwrap();

res.to_obj_file(&PathBuf::from("complex_test.obj"));
let _ = res.to_obj_file(&PathBuf::from("complex_test.obj"));
}
}
2 changes: 1 addition & 1 deletion src/transformation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ mod to_trimesh;
pub mod utils;

#[cfg(feature = "wavefront")]
pub mod wavefront;
mod wavefront;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module doesn´t have to be public, as it's implementing functions directly on TriMesh, which is public.

Making it public warns about undocumented module, as well as having nothing to show in it (the added function is within TriMesh)

8 changes: 6 additions & 2 deletions src/transformation/wavefront.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ use obj::{Group, IndexTuple, ObjData, ObjError, Object, SimplePolygon};
use std::path::PathBuf;

impl TriMesh {
/// Outputs a Wavefront (`.obj`) file at the given path.
///
/// This function is enabled by the `wavefront` feature flag.
pub fn to_obj_file(&self, path: &PathBuf) -> Result<(), ObjError> {
let mut file = std::fs::File::create(path).unwrap();

ObjData {
#[expect(clippy::unnecessary_cast)]
position: self
.vertices()
.into_iter()
.iter()
.map(|v| [v.x as f32, v.y as f32, v.z as f32])
.collect(),
objects: vec![Object {
groups: vec![Group {
polys: self
.indices()
.into_iter()
.iter()
.map(|tri| {
SimplePolygon(vec![
IndexTuple(tri[0] as usize, None, None),
Expand Down