mikedh / trimesh

Python library for loading and using triangular meshes.
https://trimesh.org
MIT License
3k stars 581 forks source link

Are Self Intersecting Meshes Volumes? #1974

Open Eric-Vin opened 1 year ago

Eric-Vin commented 1 year ago

Is a mesh considered a volume if it has self intersections? It doesn't seem like this is considered in the description of is_volume, but I've seen it used in other definitions. I've attached an example of a mesh that appears to have intersecting faces but is registered as a volume by Trimesh. table_mesh.zip

caseymjohnson commented 1 year ago

I took a peek at the attached .obj in meshlab, and wasn't able to detect any self intersections

and, anyone feel free to correct me if this is inaccurate, but with respect to the is_volume question:

meshes are inherently a representation of 'inside' versus 'outside', so given consistent winding (no discontinuities wrt. 'inside' vs 'outside' between adjacent faces) and a mesh surface with no holes, the mesh will possess volumetric data

will the magnitude of that volume be particularly meaningful? I believe that depends on the calculation algorithm used, but in general it may not be a particularly useful thing to ask 'how much air is in this impossible balloon'

hope this is useful!

@mikedh do you think this could be closed?

Eric-Vin commented 1 year ago

There are definitely intersecting faces in the Trimesh object, but I think the issue is that the Trimesh object actually contains multiple meshes, which intersect with each other (i.e. the different support beams). Since is_volume is called over the whole Trimesh object, I just wanted to know what the intended semantics were in this case.

As an example, here's some code that returns True on my machine using the above mesh, indicating the bodies/faces intersect:

mesh.split()[0].intersection(mesh.split()[8]).is_volume
caseymjohnson commented 1 year ago

ah you're absolutely correct, I missed the funkiness at the corners

Screenshot 2023-10-26 at 1 49 30 PM

so looking at is_volume

valid = bool(
            self.is_watertight
            and self.is_winding_consistent
            and np.isfinite(self.center_mass).all()
            and self.volume > 0.0
        )

does this mean that an object of multiple mesh components is said to be a volume if all of the constituent components meet all four criteria?

just starting to dive into the nitty gritty of the code so I appreciate the insight :)

Eric-Vin commented 1 year ago

At the moment this behavior seems undefined, but I think the example is not a manifold mesh which means that if someone passed it to an engine like Manifold, I assume it would reject it unless it has some mechanism to repair it. If the current is_volume semantics that you highlighted are what's desired (just checking that all mesh components are volumes), then it might be useful to add an is_manifold check that's stricter, though I don't know how we would implement that as there appear to be some tricky cases. @mikedh any thoughts?

Eric-Vin commented 1 year ago

Just to add what a potential repair function could look like. The function could just split the submeshes by volumetric body and then union them together, but the current split() function just uses face adjacency so that won't work (if you split a hollow sphere you get two sets of faces back, the inner surface facing inwards and the outside surface facing outwards). I haven't looked too much into whether or not there's an easy way to do this volumetric decomposition.

pca006132 commented 1 year ago

note that manifold does not have a way to check for self-intersections as of today, so we will not reject it but will get incorrect results (should still be a manifold, but geometrically incorrect)

Eric-Vin commented 1 year ago

That's good to know, thanks for the info @pca006132 !

mikedh commented 1 year ago

Sorry for the delayed response as I had no good answers haha. And thanks for all the work on manifold+bindings it is such a huge improvement over the previous stuff! As pointed out yeah is_volume is currently the all of these:

Currently is_volume is returning true for multibody meshes (which may or may not be reasonable). I'm definitely open to making is_volume stricter although it would be nice to avoid any super slow operations if possible.

Is a mesh considered a volume if it has self intersections?

I think this is tough: if the self-intersection avoids any vertex-vertex collisions it will currently return is_volume=True (i.e. like the pointy tail of a rabbit penetrates the rabbit body but none of the tail vertices interfere with the body vertices). I have no idea how you'd check for that case without it being incredibly slow, but if someone has a good idea I'm open to it! The naive way that comes to mind would be to test every edge against every face for edges that intersect faces outside of the face's boundary.

Checking for multi-body meshes is a lot easier, it currently uses mesh.edges_sparse which is a scipy.sparse.csgraph object and is pretty fast. We could just add a mesh.body_count == 1 to the list of checks if that was helpful:

In [9]: m = trimesh.creation.box(bounds = [[0,0,0],[1,1,1]]) + trimesh.creation.box(bounds=[[2,2,2],[3,3,3]])

In [10]: m.is_volume
Out[10]: True

In [11]: m.body_count
Out[11]: 2

In [12]: m.body_count?
Type:        property
String form: <property object at 0x7f9b9ffd3fb0>
Docstring:  
How many connected groups of vertices exist in this mesh.
Note that this number may differ from result in mesh.split,
which is calculated from FACE rather than vertex adjacency.

Returns
-----------
count : int
  Number of connected vertex groups