mikedh / trimesh

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

Intersecting volumes as mesh cannot always be split when vertices overlap #2236

Closed Theyiot closed 5 months ago

Theyiot commented 5 months ago

Hey, I am using Trimesh to compute the boolean union of a mesh containing multiple closed volumes that intersect. First, I had some issue when some edges were overlapping and the mesh would become not watertight. I solved this issue using the code proposed by @mikedh in the issue #145 opened by @Marviel, namely:

def is_watertight(edges, edges_sorted=None):

    # passing edges_sorted is a speedup only                                                 
    if edges_sorted is None:                                                                 
        edges_sorted = np.sort(edges, axis=1)                                                

    # find the unique edges and the inverse to reconstruct                                   
    unique, inverse = grouping.unique_rows(edges_sorted)                                     

    # if every edge is shared by an even number of faces                                     
    watertight = (np.mod(np.bincount(inverse), 2) == 0).all()                                

    if watertight:                                                                           
        # a check to see if every shared edge is reversed                                    
        # note that this is robust for edges shared by two faces                             
        # but is subject to random ordering for edges shared by 4+ faces                     
        # and will return arbitrary results                                                  
        ordered = edges[inverse.argsort()]                                                   
        winding = not bool(np.equal(ordered[0::2],                                           
                                    ordered[1::2]).all(axis=1).any())                        
    else:                                                                                    
        winding = False                                                                      

    return bool(watertight), bool(winding)

This code works well and the meshes with overlapping vertices are watertight, as expected. However, I have an example (see archive below) for which two sheared cylinders are watertight but the split() function returns an empty array. In the zip file, you have the overlapping mesh (Failing.obj) and a slightly shifted one (Working.obj).

For completeness, I tried a minimal example where two parallelepiped intersect on one end (also in the archive). At first it failed as well, because the mesh was not considered watertight. But with the modification of the script, this example now works fine and can be split as expected.

Why does that happen ? Ideally there should be no constraints on the input volume. Thanks !

Examples.zip

Theyiot commented 5 months ago

The solution was to modify the function handle_mesh(), defined in the function load_kwargs() in the file "exchange/load.py" to return Trimesh(**kwargs, process=False). By default, process is set to True and the overlaping vertices are merged together. Setting this parameter to False prevent Trimesh to do so.

In my case, the default behaviour seems strange. But there are probably a good reason to do so.