wolph / numpy-stl

Simple library to make working with STL files (and 3D objects in general) fast and easy.
http://numpy-stl.readthedocs.org/
BSD 3-Clause "New" or "Revised" License
605 stars 103 forks source link

'is_closed' giving false positives #123

Closed efahl closed 4 years ago

efahl commented 4 years ago

In 2.10.1, reading STL and getting mass properties was silent, suddenly getting the 'Your mesh is not closed' message when reading with 2.11.0. Looks like it's related to #122 - normalized normals...

Dug into the check, and extracted its logic to see what's going on:

partMesh = mesh.Mesh.from_file(shell)
print(partMesh.normals.sum(axis=0))  # Innards of 'is_closed', base.py line 348

In 2.10.1 this produces

[ 0.00108147 -0.00066566  0.00480509]

But in 2.11.0 it gives very large values, triggering the message output and reporting as non-closed.

[  82.79478 -115.55531 -491.01715]
efahl commented 4 years ago

I should have noted that the mass properties are fine, identical in both versions, it's just the 'is_checked' that's firing off the 'not closed' message that is the issue.

wolph commented 4 years ago

The check is a very simple sanity check to see if a mesh is likely to be closed which is often going to be wrong unfortunately. The problem is that if the mesh is not closed the methods are going to give completely wrong numbers: https://github.com/WoLpH/numpy-stl/issues/69

With the normalized normals the check was actually broken since it expects the normals to be non-normalized.

You can safely ignore it if you feel that it's wrong in your case. But be sure to check if the output looks sane. Perhaps I should add an easy way of hiding the warnings...

James-Archer commented 4 years ago

I have just had the same issue, and I think it's an easy fix. The current check is:

if (self.normals.sum(axis=0) >= 1e-4).any():
            return False

Change it to:

if (numpy.abs(self.normals.sum(axis=0)) >= 1e-4).any():

This gives me correct behaviour for a closed sphere (True) and a single triangle with normal vector (0,0,-1) (False).

Alternatively you can use isclose in numpy:

        """Check the mesh is closed or not"""                     
        if np.isclose(self.normals.sum(axis=0), 0, atol=1e-4).all(): 
            return True                                           
        else:                                                     
            return False                                          

I can make a PR if you agree with this fix.

wolph commented 4 years ago

I'm happy with any fix that works well and provides good results. A pull request would be greatly appreciated @James-Archer!

wolph commented 4 years ago

The new release is online :)