Skip to content
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

Add a function to easily create a concave polygon from a concave hull #340

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

robinmoussu
Copy link

Fix #338

This is an early POC to show how I am creating a concave polygon from a concave hull.

Remarks regarding this draft:

  • I don't fully understand how the 2D/3D directory layout works, you can run the example I added with cargo run --example concave. cargo check works from build/ncollide2d, but not from the root directory.
  • I added gnuplot as a dev-dependency, but it's just there to display how the concave polygon is created, and what points are tested. If this patch is ever merged, I am not sure it should be kept. I didn't check if anything was already used to display graphics on the screen.
  • The example (concave.rs) serve as unit test. If this PR is merged it should be transformed into a proper unit test. I did it this way to be able to display the polygon, and show that it works.
  • It adds a dependency to the spade crate. You will decide if it's ok or not.
  • spade doesn't uses the same trait to abstract f32/f64 unfortunately this must be visible in the public API of new_concave_polygon. I am not happy about this.
  • This code use a few assumption about spade, I asked them if those were reliable. I am waiting the answer.

Comment on lines +80 to +87
let indexes = {
let vertex_handle = face.as_triangle();
let mut indexes = [0; 3];
for i in 0..3 {
indexes[i] = vertex_handle[i].fix();
}
indexes
};
Copy link
Author

Choose a reason for hiding this comment

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

Note: this could be rewrote using the collect_tuple method of itertools:

let (a, b, c) = face
    .as_triangle()
    .iter()
    .map(|vertex_handle| vertex_handle.fix())
    .collect_tuple()
    .unwrap();

But I don't know what is the best way to create an array by applying a function to another array.

Copy link
Member

Choose a reason for hiding this comment

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

I didn't know about collect_tuple but that looks like an elegant solution.

/// counter-clockwise convex polyline.
pub fn new_concave_polygon(isometry: Isometry<N>, hull: &[(N, N)]) -> Compound<N>
where
N: spade::SpadeFloat,
Copy link
Author

Choose a reason for hiding this comment

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

I don't like to have to leak that we internally uses spade here.

Copy link
Member

Choose a reason for hiding this comment

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

Unfortunately, I don't think we have a viable alternative here. I am not sure why the SpadeFloat trait does not have a blanket impl in the first place…


/// Creates a new 2D concave polygon from a set of points assumed to describe a
/// counter-clockwise convex polyline.
pub fn new_concave_polygon(isometry: Isometry<N>, hull: &[(N, N)]) -> Compound<N>
Copy link
Author

Choose a reason for hiding this comment

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

I don't know if new_concave_polygon should be added as a new constructor of Compound or if a new struct that implements the same traits as Compound should be created.

The issue with the current proposal is that the triangles used for the creation of the Compound shape are leaked, since it's possible to manipulate them with Compound::shapes() and then cast them with .as_shape::<ConvexPolygon<f64>>().

Copy link
Member

Choose a reason for hiding this comment

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

I think that it is OK to have this method here for now. I would prefer the following signature:

#[cfg(all(feature = "dim2", feature = "spade"))]
pub fn from_convex_decomposition_of(position: Isometry<N>, vertices: &[Point2<N>]) -> Compound<N>

I think that ultimately we will want to use a TriMesh for this instead of a Compound. However, using a TriMesh is not possible right now because ncollide only supports it in 3D.

Copy link
Member

@sebcrozet sebcrozet left a comment

Choose a reason for hiding this comment

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

Hi! Thank you for this PR!


/// Creates a new 2D concave polygon from a set of points assumed to describe a
/// counter-clockwise convex polyline.
pub fn new_concave_polygon(isometry: Isometry<N>, hull: &[(N, N)]) -> Compound<N>
Copy link
Member

Choose a reason for hiding this comment

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

I think that it is OK to have this method here for now. I would prefer the following signature:

#[cfg(all(feature = "dim2", feature = "spade"))]
pub fn from_convex_decomposition_of(position: Isometry<N>, vertices: &[Point2<N>]) -> Compound<N>

I think that ultimately we will want to use a TriMesh for this instead of a Compound. However, using a TriMesh is not possible right now because ncollide only supports it in 3D.

/// counter-clockwise convex polyline.
pub fn new_concave_polygon(isometry: Isometry<N>, hull: &[(N, N)]) -> Compound<N>
where
N: spade::SpadeFloat,
Copy link
Member

Choose a reason for hiding this comment

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

Unfortunately, I don't think we have a viable alternative here. I am not sure why the SpadeFloat trait does not have a blanket impl in the first place…

delaunay
.triangles()
.filter_map(|face| {
let indexes = {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let indexes = {
let indices = {

Comment on lines +80 to +87
let indexes = {
let vertex_handle = face.as_triangle();
let mut indexes = [0; 3];
for i in 0..3 {
indexes[i] = vertex_handle[i].fix();
}
indexes
};
Copy link
Member

Choose a reason for hiding this comment

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

I didn't know about collect_tuple but that looks like an elegant solution.

@@ -39,7 +39,9 @@ simba = "0.1"
nalgebra = "0.21"
approx = { version = "0.3", default-features = false }
serde = { version = "1.0", optional = true, features = ["derive"]}
spade = "1.8.2"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
spade = "1.8.2"
spade = { version = "1", optional = true }

@robinmoussu
Copy link
Author

I tested it a bit more, and unfortunately the triangulation approach I took doesn't works.

image

The hull is the black line, and the orange triangles are the one detected as being inside the hull, using the algorithm in this PR.

As you can see:

  • one triangle (ABC) is partially outside the hull
  • the area EDC isn't considered inside the hull.
  • there is no way to fill the hull using only triangles from the triangulation, since the triangle ADC wasn't generated (only ABC and ADC).

So, … I need to restart from scratch. I think we can close this PR unfortunately.

@robinmoussu
Copy link
Author

I just noticed that there is maybe a way to solve the issue by using ConstrainedDelaunayTriangulation instead of FloatDelaunayTriangulation and adding all edges of the hull as constrains for the triangulation. I don't know if or when I will have to experiment it, so I'm letting this comment here in case someone (or my future self) is interested.

1 similar comment
@robinmoussu
Copy link
Author

I just noticed that there is maybe a way to solve the issue by using ConstrainedDelaunayTriangulation instead of FloatDelaunayTriangulation and adding all edges of the hull as constrains for the triangulation. I don't know if or when I will have to experiment it, so I'm letting this comment here in case someone (or my future self) is interested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

How to create a concave polygon from an ordered list of points?
2 participants