Exemple #1
0
    def test_permutate(self):
        def close(a, b):
            if len(a) == len(b) == 0:
                return False
            if a.shape != b.shape:
                return False
            return g.np.allclose(a, b)

        def make_assertions(mesh, test, rigid=False):
            if (close(test.face_adjacency, mesh.face_adjacency)
                    and len(mesh.faces) > MIN_FACES):
                raise ValueError(
                    'face adjacency of %s the same after permutation!',
                    mesh.metadata['file_name'])

            if (close(test.face_adjacency_edges, mesh.face_adjacency_edges)
                    and len(mesh.faces) > MIN_FACES):
                raise ValueError(
                    'face adjacency edges of %s the same after permutation!',
                    mesh.metadata['file_name'])

            self.assertFalse(close(test.faces, mesh.faces))
            self.assertFalse(close(test.vertices, mesh.vertices))
            self.assertFalse(test.md5() == mesh.md5())

            # rigid transforms don't change area or volume
            if rigid:
                assert g.np.allclose(mesh.area, test.area)

                # volume is very dependent on meshes being watertight and sane
                if (mesh.is_watertight and test.is_watertight
                        and mesh.is_winding_consistent
                        and test.is_winding_consistent):
                    assert g.np.allclose(mesh.volume, test.volume, rtol=.05)

        for mesh in g.get_meshes():
            if len(mesh.faces) < MIN_FACES:
                continue
            # warp the mesh to be a unit cube
            mesh.vertices /= mesh.extents
            original = mesh.copy()

            for i in range(5):
                mesh = original.copy()
                noise = g.trimesh.permutate.noise(mesh,
                                                  magnitude=mesh.scale / 50.0)
                # make sure that if we permutate vertices with no magnitude
                # area and volume remain the same
                no_noise = g.trimesh.permutate.noise(mesh, magnitude=0.0)

                transform = g.trimesh.permutate.transform(mesh)
                tesselate = g.trimesh.permutate.tesselation(mesh)

                make_assertions(mesh, noise, rigid=False)
                make_assertions(mesh, no_noise, rigid=True)
                make_assertions(mesh, transform, rigid=True)
                make_assertions(mesh, tesselate, rigid=True)

            # make sure permutate didn't alter the original mesh
            self.assertTrue(original.md5() == mesh.md5())
Exemple #2
0
    def test_minball(self):
        # how close do we need to be
        tol_fit = 1e-2

        # get some assorted mesh geometries to test performance
        # and a perfect sphere mesh to test the degenerate case
        for m in g.np.append(list(g.get_meshes(5)),
                             g.trimesh.primitives.Sphere()):

            s = m.bounding_sphere
            R_check = ((m.vertices -
                        s.primitive.center)**2).sum(axis=1).max()**.5

            assert len(s.primitive.center) == 3
            assert s.primitive.radius > 0.0
            assert abs(s.primitive.radius - R_check) < tol_fit
            assert s.volume > (m.volume - tol_fit)

        # check minimum n-sphere for points in 2, 3, 4 dimensions
        for d in [2, 3, 4]:
            for i in range(5):
                points = g.np.random.random((100, d))
                C, R = g.trimesh.nsphere.minimum_nsphere(points)
                R_check = ((points - C)**2).sum(axis=1).max()**.5
                assert len(C) == d
                assert R > 0.0
                assert abs(R - R_check) < g.tol.merge
Exemple #3
0
 def test_fill_holes(self):
     for mesh in g.get_meshes(5):
         if not mesh.is_watertight: continue
         mesh.faces = mesh.faces[1:-1]
         self.assertFalse(mesh.is_watertight)
         mesh.fill_holes()
         self.assertTrue(mesh.is_watertight)
Exemple #4
0
    def test_convex(self):
        for mesh in g.get_meshes(10):
            if not mesh.is_watertight:
                continue
            hulls = []
            for i in range(50):
                permutated = mesh.permutate.transform()
                if i % 10 == 0:
                    permutated = permutated.permutate.tesselation()
                hulls.append(permutated.convex_hull)

            volume = g.np.array([i.volume for i in hulls])

            if volume.ptp() > (mesh.scale / 1000):
                print(volume)
                raise ValueError('volume is inconsistent on {}'.format(
                    mesh.metadata['file_name']))
            self.assertTrue(volume.min() > 0.0)

            if not all(i.is_winding_consistent for i in hulls):
                raise ValueError(
                    'mesh %s reported bad winding on convex hull!',
                    mesh.metadata['file_name'])

            '''
Exemple #5
0
    def test_meshes(self):
        # make sure we can load everything we think we can
        # while getting a list of meshes to run tests on
        meshes = g.get_meshes(raise_error=True)
        g.log.info('Running tests on %d meshes', len(meshes))

        for mesh in meshes:
            g.log.info('Testing %s', mesh.metadata['file_name'])
            self.assertTrue(len(mesh.faces) > 0)
            self.assertTrue(len(mesh.vertices) > 0)

            self.assertTrue(len(mesh.edges) > 0)
            self.assertTrue(len(mesh.edges_unique) > 0)
            self.assertTrue(len(mesh.edges_sorted) > 0)
            self.assertTrue(len(mesh.edges_face) > 0)
            self.assertTrue(isinstance(mesh.euler_number, int))

            mesh.process()

            if not mesh.is_watertight:
                continue

            assert len(mesh.facets) == len(mesh.facets_area)
            assert len(mesh.facets) == len(mesh.facets_normal)
            assert len(mesh.facets) == len(mesh.facets_boundary)

            if len(mesh.facets) != 0:
                faces = mesh.facets[mesh.facets_area.argmax()]
                outline = mesh.outline(faces)

            smoothed = mesh.smoothed()

            assert mesh.volume > 0.0

            section = mesh.section(plane_normal=[0, 0, 1],
                                   plane_origin=mesh.centroid)

            sample = mesh.sample(1000)
            even_sample = g.trimesh.sample.sample_surface_even(mesh, 100)
            assert sample.shape == (1000, 3)
            g.log.info('finished testing meshes')

            # make sure vertex kdtree and triangles rtree exist

            t = mesh.kdtree()
            self.assertTrue(hasattr(t, 'query'))
            g.log.info('Creating triangles tree')
            r = mesh.triangles_tree()
            self.assertTrue(hasattr(r, 'intersection'))
            g.log.info('Triangles tree ok')

            # some memory issues only show up when you copy the mesh a bunch
            # specifically, if you cache c- objects then deepcopy the mesh this
            # generally segfaults randomly
            copy_count = 200
            g.log.info('Attempting to copy mesh %d times', copy_count)
            for i in range(copy_count):
                copied = mesh.copy()
            g.log.info('Multiple copies done')
            self.assertTrue(g.np.allclose(copied.identifier, mesh.identifier))
Exemple #6
0
 def test_fill_holes(self):
     for mesh in g.get_meshes(5):
         if not mesh.is_watertight: continue
         mesh.faces = mesh.faces[1:-1]
         self.assertFalse(mesh.is_watertight)
         mesh.fill_holes()
         self.assertTrue(mesh.is_watertight)
Exemple #7
0
    def test_export(self):
        file_types = list(g.trimesh.io.export._mesh_exporters.keys())
        for mesh in g.get_meshes(5):
            for file_type in file_types:
                export = mesh.export(file_type=file_type)
                if export is None:
                    raise ValueError('Exporting mesh %s to %s resulted in None!',
                                     mesh.metadata['file_name'],
                                     file_type)

                self.assertTrue(len(export) > 0)

                if file_type in ['dae',     # collada, no native importers
                                 'collada',  # collada, no native importers
                                 'msgpack',  # kind of flaky, but usually works
                                 'drc']:    # DRC is not a lossless format
                    g.log.warning(
                        'Still no native loaders implemented for collada!')
                    continue

                g.log.info('Export/import testing on %s',
                           mesh.metadata['file_name'])
                loaded = g.trimesh.load(file_obj=g.io_wrap(export),
                                        file_type=file_type)

                if (not g.trimesh.util.is_shape(loaded._data['faces'], (-1, 3)) or
                    not g.trimesh.util.is_shape(loaded._data['vertices'], (-1, 3)) or
                        loaded.faces.shape != mesh.faces.shape):
                    g.log.error('Export -> import for %s on %s wrong shape!',
                                file_type,
                                mesh.metadata['file_name'])

                if loaded.vertices is None:
                    g.log.error('Export -> import for %s on %s gave None for vertices!',
                                file_type,
                                mesh.metadata['file_name'])

                if loaded.faces.shape != mesh.faces.shape:
                    raise ValueError('Export -> import for {} on {} gave vertices {}->{}!'.format(
                        file_type,
                        mesh.metadata['file_name'],
                        str(mesh.faces.shape),
                        str(loaded.faces.shape)))
                self.assertTrue(loaded.vertices.shape == mesh.vertices.shape)

                # try exporting/importing certain file types by name
                if file_type in ['obj', 'stl', 'ply', 'off']:
                    temp = g.tempfile.NamedTemporaryFile(suffix='.' + file_type,
                                                         delete=False)
                    # windows throws permissions errors if you keep it open
                    temp.close()

                    mesh.export(temp.name)
                    load = g.trimesh.load(temp.name)
                    # manual cleanup
                    g.os.remove(temp.name)

                    assert mesh.faces.shape == load.faces.shape
                    assert mesh.vertices.shape == load.vertices.shape
Exemple #8
0
    def test_helper(self):
        # just make sure the plumbing returns something
        for mesh in g.get_meshes(2):
            points = (g.np.random.random((100, 3)) - .5) * 100

            a = mesh.nearest.on_surface(points)
            assert a is not None

            b = mesh.nearest.vertex(points)
            assert b is not None
Exemple #9
0
    def test_helper(self):
        # just make sure the plumbing returns something
        for mesh in g.get_meshes(2):
            points = (g.np.random.random((100, 3)) - .5) * 100

            a = mesh.nearest.on_surface(points)
            assert a is not None

            b = mesh.nearest.vertex(points)
            assert b is not None
Exemple #10
0
 def test_tesselation(self):
     for mesh in g.get_meshes(5):
         tess = g.trimesh.permutate.tesselation(mesh)
         #print(tess.area-mesh.area)
         self.assertTrue(tess.area   - mesh.area   < g.tol.merge)
         self.assertTrue(tess.volume - mesh.volume < g.tol.merge)
         self.assertTrue(len(mesh.faces) < len(tess.faces))
         if mesh.is_winding_consistent:
             self.assertTrue(tess.is_winding_consistent)
         if mesh.is_watertight:
             self.assertTrue(tess.is_watertight)
Exemple #11
0
 def test_tesselation(self):
     for mesh in g.get_meshes(5):
         tess = g.trimesh.permutate.tesselation(mesh)
         # print(tess.area-mesh.area)
         self.assertTrue(abs(tess.area - mesh.area) < g.tol.merge)
         volume_check = abs(tess.volume - mesh.volume) / mesh.scale
         self.assertTrue(volume_check < g.tol.merge)
         self.assertTrue(len(mesh.faces) < len(tess.faces))
         if mesh.is_winding_consistent:
             self.assertTrue(tess.is_winding_consistent)
         if mesh.is_watertight:
             self.assertTrue(tess.is_watertight)
Exemple #12
0
 def test_tesselation(self):
     for mesh in g.get_meshes(5):
         tess = g.trimesh.permutate.tessellation(mesh)
         # print(tess.area-mesh.area)
         assert abs(tess.area - mesh.area) < g.tol.merge
         volume_check = abs(tess.volume - mesh.volume) / mesh.scale
         assert volume_check < g.tol.merge
         assert len(mesh.faces) < len(tess.faces)
         if mesh.is_winding_consistent:
             assert tess.is_winding_consistent
         if mesh.is_watertight:
             assert tess.is_watertight
Exemple #13
0
    def test_helper(self):
        # just make sure the plumbing returns something
        for mesh in g.get_meshes(2):
            points = (g.np.random.random((100, 3)) - .5) * 100

            a = mesh.nearest.on_surface(points)
            self.assertTrue(a is not None)

            b = mesh.nearest.vertex(points)
            self.assertTrue(b is not None)

            c = mesh.nearest.contains([mesh.center_mass])
            assert len(c) == 1
Exemple #14
0
    def test_engine_time(self):
        for mesh in g.get_meshes():
            tic = [g.time.time()]
            for engine in self.engines:
                split = mesh.split(engine=engine, only_watertight=False)
                facets = g.trimesh.graph.facets(mesh=mesh, engine=engine)
                tic.append(g.time.time())

            tic_diff = g.np.diff(tic)
            tic_min = tic_diff.min()
            tic_diff /= tic_min
            g.log.info('graph engine on %s (scale %f sec):\n%s',
                       mesh.metadata['file_name'], tic_min,
                       str(g.np.column_stack((self.engines, tic_diff))))
Exemple #15
0
    def test_barycentric(self):
        for m in g.get_meshes(4):
            # a simple test which gets the barycentric coordinate at each of the three
            # vertices, checks to make sure the barycentric is [1,0,0] for the vertex
            # and then converts back to cartesian and makes sure the original points
            #  are the same as the conversion and back
            for method in ['cross', 'cramer']:
                for i in range(3):
                    barycentric = g.trimesh.triangles.points_to_barycentric(
                        m.triangles, m.triangles[:, i], method=method)
                    assert (g.np.abs(barycentric - g.np.roll([1.0, 0, 0], i)) <
                            1e-8).all()

                    points = g.trimesh.triangles.barycentric_to_points(
                        m.triangles, barycentric)
                    assert (g.np.abs(points - m.triangles[:, i]) < 1e-8).all()
Exemple #16
0
    def test_engine_time(self):
        for mesh in g.get_meshes():
            tic = [g.time.time()]
            for engine in self.engines:
                mesh.split(engine=engine, only_watertight=False)
                g.trimesh.graph.facets(mesh=mesh, engine=engine)
                tic.append(g.time.time())

            tic_diff = g.np.diff(tic)
            tic_min = tic_diff.min()
            tic_diff /= tic_min
            g.log.info('graph engine on %s (scale %f sec):\n%s',
                       mesh.metadata['file_name'],
                       tic_min,
                       str(g.np.column_stack((self.engines,
                                              tic_diff))))
Exemple #17
0
    def test_barycentric(self):
        for m in g.get_meshes(4):
            # a simple test which gets the barycentric coordinate at each of the three
            # vertices, checks to make sure the barycentric is [1,0,0] for the vertex
            # and then converts back to cartesian and makes sure the original points
            #  are the same as the conversion and back
            for method in ['cross', 'cramer']:
                for i in range(3):
                    barycentric = g.trimesh.triangles.points_to_barycentric(
                        m.triangles, m.triangles[:, i], method=method)
                    assert (g.np.abs(barycentric -
                                     g.np.roll([1.0, 0, 0], i)) < 1e-8).all()

                    points = g.trimesh.triangles.barycentric_to_points(
                        m.triangles, barycentric)
                    assert (g.np.abs(points - m.triangles[:, i]) < 1e-8).all()
Exemple #18
0
    def test_export(self):
        file_types = list(g.trimesh.io.export._mesh_exporters.keys())
        for mesh in g.get_meshes(5):
            for file_type in file_types:
                export = mesh.export(file_type=file_type)

                if export is None:
                    raise ValueError(
                        'Exporting mesh %s to %s resulted in None!',
                        mesh.metadata['file_name'], file_type)

                self.assertTrue(len(export) > 0)

                if file_type in [
                        'dae',  # collada, no native importers
                        'collada',  # collada, no native importers
                        'msgpack',  # kind of flaky, but usually works
                        'drc'
                ]:  # DRC is not a lossless format
                    g.log.warning(
                        'Still no native loaders implemented for collada!')
                    continue

                g.log.info('Export/import testing on %s',
                           mesh.metadata['file_name'])
                loaded = g.trimesh.load(file_obj=g.io_wrap(export),
                                        file_type=file_type)

                if (not g.trimesh.util.is_shape(loaded._data['faces'],
                                                (-1, 3)) or
                        not g.trimesh.util.is_shape(loaded._data['vertices'],
                                                    (-1, 3))
                        or loaded.faces.shape != mesh.faces.shape):
                    g.log.error('Export -> inport for %s on %s wrong shape!',
                                file_type, mesh.metadata['file_name'])

                if loaded.vertices is None:
                    g.log.error(
                        'Export -> import for %s on %s gave None for vertices!',
                        file_type, mesh.metadata['file_name'])

                if loaded.faces.shape != mesh.faces.shape:
                    raise ValueError(
                        'Export -> import for {} on {} gave vertices {}->{}!'.
                        format(file_type, mesh.metadata['file_name'],
                               str(mesh.faces.shape), str(loaded.faces.shape)))
                self.assertTrue(loaded.vertices.shape == mesh.vertices.shape)
Exemple #19
0
    def test_permutate(self):
        def close(a, b):
            if len(a) == len(b) == 0:
                return False
            if a.shape != b.shape:
                return False
            return g.np.allclose(a, b)

        def make_assertions(mesh, test):
            if (close(test.face_adjacency, mesh.face_adjacency)
                    and len(mesh.faces) > MIN_FACES):
                raise ValueError(
                    'face adjacency of %s the same after permutation!',
                    mesh.metadata['file_name'])

            if (close(test.face_adjacency_edges, mesh.face_adjacency_edges)
                    and len(mesh.faces) > MIN_FACES):
                raise ValueError(
                    'face adjacency edges of %s the same after permutation!',
                    mesh.metadata['file_name'])

            self.assertFalse(close(test.faces, mesh.faces))
            self.assertFalse(close(test.vertices, mesh.vertices))
            self.assertFalse(test.md5() == mesh.md5())

        for mesh in g.get_meshes():
            if len(mesh.faces) < MIN_FACES:
                continue
            # warp the mesh to be a unit cube
            mesh.vertices /= mesh.extents
            original = mesh.copy()

            for i in range(5):
                mesh = original.copy()
                noise = g.trimesh.permutate.noise(mesh,
                                                  magnitude=mesh.scale / 50.0)
                transform = g.trimesh.permutate.transform(mesh)
                tesselate = g.trimesh.permutate.tesselation(mesh)
                make_assertions(mesh, noise)
                make_assertions(mesh, transform)
                make_assertions(mesh, tesselate)

            # make sure permutate didn't alter the original mesh
            self.assertTrue(original.md5() == mesh.md5())
Exemple #20
0
 def test_identifier(self):
     count = 100
     for mesh in g.get_meshes(5):
         if not mesh.is_watertight:
             g.log.warning('Mesh %s is not watertight!',
                           mesh.metadata['file_name'])
             continue
         self.assertTrue(mesh.is_watertight)
         g.log.info('Trying hash at %d random transforms', count)
         result = g.deque()
         for i in range(count):
             permutated = mesh.permutate.transform()
             result.append(permutated.identifier)
         ok = (g.np.abs(g.np.diff(result, axis=0)) < 1e-3).all()
         if not ok:
             g.log.error('Hashes on %s differ after transform! diffs:\n %s\n', 
                       mesh.metadata['file_name'],
                       str(g.np.diff(result, axis=0)))
         self.assertTrue(ok)
Exemple #21
0
 def test_identifier(self):
     count = 100
     for mesh in g.get_meshes(5):
         if not mesh.is_watertight:
             g.log.warning('Mesh %s is not watertight!',
                           mesh.metadata['file_name'])
             continue
         self.assertTrue(mesh.is_watertight)
         g.log.info('Trying hash at %d random transforms', count)
         result = g.deque()
         for i in range(count):
             permutated = mesh.permutate.transform()
             result.append(permutated.identifier)
         ok = (g.np.abs(g.np.diff(result, axis=0)) < 1e-3).all()
         if not ok:
             g.log.error(
                 'Hashes on %s differ after transform! diffs:\n %s\n',
                 mesh.metadata['file_name'], str(g.np.diff(result, axis=0)))
         self.assertTrue(ok)
Exemple #22
0
    def test_identifier(self):
        count = 25
        meshes = g.np.append(list(g.get_meshes(10)),
                             g.get_mesh('fixed_top.ply'))
        for mesh in meshes:
            if not mesh.is_volume:
                g.log.warning('Mesh %s is not watertight!',
                              mesh.metadata['file_name'])
                continue

            g.log.info('Trying hash at %d random transforms', count)
            md5 = g.deque()
            idf = g.deque()
            for i in range(count):
                permutated = mesh.permutate.transform()
                permutated = permutated.permutate.tessellation()

                md5.append(permutated.identifier_md5)
                idf.append(permutated.identifier)

            result = g.np.array(md5)
            ok = (result[0] == result[1:]).all()

            if not ok:
                debug = []
                for a in idf:
                    as_int, exp = g.trimesh.util.sigfig_int(
                        a, g.trimesh.comparison.id_sigfig)

                    debug.append(as_int * (10**exp))
                g.log.error(
                    'Hashes on %s differ after transform! diffs:\n %s\n',
                    mesh.metadata['file_name'],
                    str(g.np.array(debug, dtype=g.np.int)))

                raise ValueError('values differ after transform!')

            if md5[-1] == permutated.permutate.noise(mesh.scale /
                                                     100.0).identifier_md5:
                raise ValueError('Hashes on %s didn\'t change after noise!',
                                 mesh.metadata['file_name'])
Exemple #23
0
    def test_copy(self):
        for mesh in g.get_meshes(raise_error=True):
            if not isinstance(mesh, g.trimesh.Trimesh):
                continue
            start = {mesh.md5(), mesh.crc()}

            # make sure some stuff is populated
            mesh.kdtree
            mesh.triangles_tree
            mesh.face_adjacency_angles
            mesh.facets
            assert 'triangles_tree' in mesh._cache
            assert len(mesh._cache) > 0

            # if you cache c-objects then deepcopy the mesh
            # it randomly segfaults
            copy_count = 200
            for i in range(copy_count):
                copied = mesh.copy(include_cache=False)
                assert len(copied._cache) == 0
                assert len(mesh._cache) > 0

                # deepcopy should clear the cache
                copied = g.copy.deepcopy(mesh)
                assert len(copied._cache) == 0
                assert len(mesh._cache) > 0

                # regular copy should try to preserve the cache
                copied = g.copy.copy(mesh)
                assert len(copied._cache) == len(mesh._cache)
                # the triangles_tree should be the SAME OBJECT
                assert id(copied.triangles_tree) == id(mesh.triangles_tree)

                # cache should be same data in different object
                assert id(copied._cache.cache) != id(mesh._cache.cache)
                assert id(copied._cache) != id(mesh._cache)
                # identifier shouldn't change
                assert g.np.allclose(copied.identifier, mesh.identifier)

            # ...still shouldn't have changed anything
            assert start == {mesh.md5(), mesh.crc()}
Exemple #24
0
    def test_export(self):
        file_types = list(g.trimesh.io.export._mesh_exporters.keys())
        for mesh in g.get_meshes(5):
            for file_type in file_types:
                export = mesh.export(file_type=file_type)

                if export is None:
                    raise ValueError(
                        'Exporting mesh %s to %s resulted in None!',
                        mesh.metadata['file_name'], file_type)

                self.assertTrue(len(export) > 0)

                # we don't have native loaders implemented for collada yet
                if file_type in ['dae', 'collada']:
                    g.log.warning(
                        'Still no native loaders implemented for collada!')
                    continue

                g.log.info('Export/import testing on %s',
                           mesh.metadata['file_name'])
                loaded = g.trimesh.load(file_obj=g.io_wrap(export),
                                        file_type=file_type)

                if (not g.trimesh.util.is_shape(loaded._data['faces'],
                                                (-1, 3)) or
                        not g.trimesh.util.is_shape(loaded._data['vertices'],
                                                    (-1, 3))
                        or loaded.faces.shape != mesh.faces.shape):
                    g.log.error('Export -> inport for %s on %s wrong shape!',
                                file_type, mesh.metadata['file_name'])

                if loaded.vertices is None:
                    log.error(
                        'Export -> import for %s on %s gave None for vertices!',
                        file_type, mesh.metadata['file_name'])
                self.assertTrue(loaded.faces.shape == mesh.faces.shape)
                self.assertTrue(loaded.vertices.shape == mesh.vertices.shape)
                g.log.info(
                    'Mesh vertices/faces consistent after export->import')
Exemple #25
0
    def test_identifier(self):
        count = 25
        meshes = g.np.append(list(g.get_meshes(10)),
                             g.get_mesh('fixed_top.ply'))
        for mesh in meshes:
            if not mesh.is_volume:
                g.log.warning('Mesh %s is not watertight!',
                              mesh.metadata['file_name'])
                continue

            g.log.info('Trying hash at %d random transforms', count)
            md5 = g.deque()
            idf = g.deque()
            for i in range(count):
                permutated = mesh.permutate.transform()
                permutated = permutated.permutate.tessellation()

                md5.append(permutated.identifier_md5)
                idf.append(permutated.identifier)

            result = g.np.array(md5)
            ok = (result[0] == result[1:]).all()

            if not ok:
                debug = []
                for a in idf:
                    as_int, exp = g.trimesh.util.sigfig_int(
                        a, g.trimesh.comparison.id_sigfig)

                    debug.append(as_int * (10**exp))
                g.log.error('Hashes on %s differ after transform! diffs:\n %s\n',
                            mesh.metadata['file_name'],
                            str(g.np.array(debug, dtype=g.np.int)))

                raise ValueError('values differ after transform!')

            if md5[-1] == permutated.permutate.noise(
                    mesh.scale / 100.0).identifier_md5:
                raise ValueError('Hashes on %s didn\'t change after noise!',
                                 mesh.metadata['file_name'])
Exemple #26
0
    def test_identifier(self):
        count = 25
        for mesh in g.get_meshes(10):
            if not (mesh.is_watertight and mesh.is_winding_consistent):
                g.log.warning('Mesh %s is not watertight!',
                              mesh.metadata['file_name'])
                continue
            self.assertTrue(mesh.is_watertight)
            g.log.info('Trying hash at %d random transforms', count)
            md5 = g.deque()
            idf = g.deque()
            for i in range(count):
                permutated = mesh.permutate.transform()
                permutated = permutated.permutate.tesselation()

                md5.append(permutated.identifier_md5)
                idf.append(permutated.identifier)

            result = g.np.array(md5)
            ok = (result[0] == result[1:]).all()

            if not ok:
                debug = []
                for a in idf:
                    as_int, exp = g.trimesh.util.sigfig_int(
                        a, g.trimesh.comparison.identifier_sigfig)

                    debug.append(as_int * (10**exp))
                g.log.error(
                    'Hashes on %s differ after transform! diffs:\n %s\n',
                    mesh.metadata['file_name'],
                    str(g.np.array(debug, dtype=g.np.int)))
                self.assertTrue(False)

            if md5[-1] == permutated.permutate.noise(mesh.scale /
                                                     100.0).identifier_md5:
                g.log.error('Hashes on %s didn\'t change after noise!',
                            mesh.metadata['file_name'])
                self.assertTrue(False)
Exemple #27
0
def typical_application():
    # make sure we can load everything we think we can
    # while getting a list of meshes to run tests on
    meshes = g.get_meshes(raise_error=True)
    g.log.info('Running tests on %d meshes', len(meshes))

    for mesh in meshes:
        g.log.info('Testing %s', mesh.metadata['file_name'])
        assert len(mesh.faces) > 0
        assert len(mesh.vertices) > 0

        assert len(mesh.edges) > 0
        assert len(mesh.edges_unique) > 0
        assert len(mesh.edges_sorted) > 0
        assert len(mesh.edges_face) > 0
        assert isinstance(mesh.euler_number, int)

        if not mesh.is_volume:
            continue

        assert len(mesh.facets) == len(mesh.facets_area)
        if len(mesh.facets) == 0:
            continue

        faces = mesh.facets[mesh.facets_area.argmax()]
        outline = mesh.outline(faces)
        smoothed = mesh.smoothed()

        assert mesh.volume > 0.0

        section = mesh.section(plane_normal=[0, 0, 1],
                               plane_origin=mesh.centroid)

        sample = mesh.sample(1000)
        assert sample.shape == (1000, 3)

        ident = mesh.identifier_md5
        assert len(ident) > 0
Exemple #28
0
def typical_application():
    # make sure we can load everything we think we can
    # while getting a list of meshes to run tests on
    meshes = g.get_meshes(raise_error=True)

    for mesh in meshes:
        g.log.info('Testing %s', mesh.metadata['file_name'])
        assert len(mesh.faces) > 0
        assert len(mesh.vertices) > 0

        assert len(mesh.edges) > 0
        assert len(mesh.edges_unique) > 0
        assert len(mesh.edges_sorted) > 0
        assert len(mesh.edges_face) > 0
        assert isinstance(mesh.euler_number, int)

        if not mesh.is_volume:
            continue

        assert len(mesh.facets) == len(mesh.facets_area)
        if len(mesh.facets) == 0:
            continue

        faces = mesh.facets[mesh.facets_area.argmax()]
        outline = mesh.outline(faces)  # NOQA
        smoothed = mesh.smoothed()  # NOQA

        assert mesh.volume > 0.0

        section = mesh.section(plane_normal=[0, 0, 1],  # NOQA
                               plane_origin=mesh.centroid)

        sample = mesh.sample(1000)
        assert sample.shape == (1000, 3)

        ident = mesh.identifier_md5
        assert len(ident) > 0
Exemple #29
0
    def test_minball(self):

        # get some assorted mesh geometries to test general performance
        # and a perfect sphere mesh to test the degenerate case
        for m in g.np.append(g.get_meshes(5), g.trimesh.primitives.Sphere()):

            s = m.bounding_sphere
            R_check = ((m.vertices -
                        s.primitive.center)**2).sum(axis=1).max()**.5

            self.assertTrue(len(s.primitive.center) == 3)
            self.assertTrue(s.primitive.radius > 0.0)
            self.assertTrue(abs(s.primitive.radius - R_check) < g.tol.fit)
            self.assertTrue(s.volume > (m.volume - g.tol.fit))

        # check minimum n-sphere for sets of points in 2,3, and 4 dimensions
        for d in [2, 3, 4]:
            for i in range(5):
                points = g.np.random.random((100, d))
                C, R = g.trimesh.nsphere.minimum_nsphere(points)
                R_check = ((points - C)**2).sum(axis=1).max()**.5
                self.assertTrue(len(C) == d)
                self.assertTrue(R > 0.0)
                self.assertTrue(abs(R - R_check) < g.tol.merge)
Exemple #30
0
    def test_export(self):
        file_types = list(g.trimesh.io.export._mesh_exporters.keys())
        for mesh in g.get_meshes(3):
            for file_type in file_types:
                export = mesh.export(file_type = file_type)
                self.assertTrue(len(export) > 0)
        
                # we don't have native loaders implemented for collada yet
                if file_type in ['dae', 'collada']:
                    g.log.warning('Still no native loaders implemented for collada!')
                    continue
                    
                g.log.info('Export/import testing on %s',
                         mesh.metadata['file_name'])
                loaded = g.trimesh.load(file_obj  = g.io_wrap(export),
                                        file_type = file_type)

                if loaded.faces.shape != mesh.faces.shape:
                    g.log.error('Export -> inport for %s on %s wrong shape!',
                                file_type, 
                                mesh.metadata['file_name'])
                self.assertTrue(loaded.faces.shape    == mesh.faces.shape)
                self.assertTrue(loaded.vertices.shape == mesh.vertices.shape)
                g.log.info('Mesh vertices/faces consistent after export->import')
Exemple #31
0
    def test_permutate(self):
        def make_assertions(mesh, test):
            self.assertFalse(g.np.allclose(test.faces, 
                                           mesh.faces))
            self.assertFalse(g.np.allclose(test.face_adjacency, 
                                           mesh.face_adjacency))
            self.assertFalse(g.np.allclose(test.face_adjacency_edges, 
                                           mesh.face_adjacency_edges))
            self.assertFalse(g.np.allclose(test.extents,
                                           mesh.extents))
            self.assertFalse(test.md5() == mesh.md5())

        for mesh in g.get_meshes(5):
            original = mesh.copy()
            noise     = g.trimesh.permutate.noise(mesh)
            noise_1   = g.trimesh.permutate.noise(mesh, magnitude=mesh.scale/10.0)
            transform = g.trimesh.permutate.transform(mesh)

            make_assertions(mesh, noise)
            make_assertions(mesh, noise_1)
            make_assertions(mesh, transform)
            
            # make sure permutate didn't alter the original mesh
            self.assertTrue(original.md5() == mesh.md5())
Exemple #32
0
    def test_permutate(self):
        def make_assertions(mesh, test):
            self.assertFalse(g.np.allclose(test.faces, mesh.faces))
            self.assertFalse(
                g.np.allclose(test.face_adjacency, mesh.face_adjacency))
            self.assertFalse(
                g.np.allclose(test.face_adjacency_edges,
                              mesh.face_adjacency_edges))
            self.assertFalse(g.np.allclose(test.extents, mesh.extents))
            self.assertFalse(test.md5() == mesh.md5())

        for mesh in g.get_meshes(5):
            original = mesh.copy()
            noise = g.trimesh.permutate.noise(mesh)
            noise_1 = g.trimesh.permutate.noise(mesh,
                                                magnitude=mesh.scale / 50.0)
            transform = g.trimesh.permutate.transform(mesh)

            make_assertions(mesh, noise)
            make_assertions(mesh, noise_1)
            make_assertions(mesh, transform)

            # make sure permutate didn't alter the original mesh
            self.assertTrue(original.md5() == mesh.md5())
Exemple #33
0
    def test_export(self):
        file_types = list(g.trimesh.io.export._mesh_exporters.keys())
        for mesh in g.get_meshes(5):
            for file_type in file_types:
                export = mesh.export(file_type = file_type)
                self.assertTrue(len(export) > 0)
        
                # we don't have native loaders implemented for collada yet
                if file_type in ['dae', 'collada']:
                    g.log.warning('Still no native loaders implemented for collada!')
                    continue
                    
                g.log.info('Export/import testing on %s',
                         mesh.metadata['file_name'])
                loaded = g.trimesh.load(file_obj  = g.io_wrap(export),
                                        file_type = file_type)

                if loaded.faces.shape != mesh.faces.shape:
                    g.log.error('Export -> inport for %s on %s wrong shape!',
                                file_type, 
                                mesh.metadata['file_name'])
                self.assertTrue(loaded.faces.shape    == mesh.faces.shape)
                self.assertTrue(loaded.vertices.shape == mesh.vertices.shape)
                g.log.info('Mesh vertices/faces consistent after export->import')
Exemple #34
0
    def test_meshes(self):
        self.meshes = g.get_meshes()

        has_gt = g.trimesh.graph._has_gt
        g.trimesh.graph._has_gt = False

        if not has_gt:
            g.log.warning('No graph-tool to test!')

        g.log.info('Running tests on %d meshes', len(self.meshes))
        for mesh in self.meshes:
            g.log.info('Testing %s', mesh.metadata['file_name'])
            self.assertTrue(len(mesh.faces) > 0)
            self.assertTrue(len(mesh.vertices) > 0)
            
            self.assertTrue(len(mesh.edges) > 0)
            self.assertTrue(len(mesh.edges_unique) > 0)
            self.assertTrue(len(mesh.edges_sorted) > 0)
            self.assertTrue(len(mesh.edges_face) > 0)
            self.assertFalse(mesh.euler_number is None)

            mesh.process()

            tic = [g.time.time()]

            if has_gt:
                g.trimesh.graph._has_gt = True 
                split     = g.trimesh.graph.split(mesh)
                tic.append(g.time.time())
                facets    = g.trimesh.graph.facets(mesh)
                tic.append(g.time.time())
                g.trimesh.graph._has_gt = False

            split     = g.trimesh.graph.split(mesh) 
            tic.append(g.time.time())
            facets    = g.trimesh.graph.facets(mesh)
            tic.append(g.time.time())

            facets, area = mesh.facets(return_area=True)
            self.assertTrue(len(facets) == len(area))
            if len(facets) == 0:
                continue
            faces = facets[g.np.argmax(area)]
            outline = mesh.outline(faces)
            smoothed = mesh.smoothed()

            if has_gt:
                times = g.np.diff(tic)
                g.log.info('Graph-tool sped up split by %f and facets by %f', 
                         (times[2] / times[0]), (times[3] / times[1]))

            self.assertTrue(mesh.volume > 0.0)
                
            section   = mesh.section(plane_normal=[0,0,1], plane_origin=mesh.centroid)
            hull      = mesh.convex_hull

            volume_ok = hull.volume > 0.0
            if not volume_ok:
                g.log.error('zero hull volume for %s', mesh.metadata['file_name'])
            self.assertTrue(volume_ok)

            sample = mesh.sample(1000)
            even_sample = g.trimesh.sample.sample_surface_even(mesh, 100)
            self.assertTrue(sample.shape == (1000,3))
            g.log.info('finished testing meshes')
Exemple #35
0
 def test_fix_normals(self):
     for mesh in g.get_meshes(5):
         mesh.fix_normals()
Exemple #36
0
    def test_meshes(self):
        # make sure we can load everything we think we can
        formats = g.trimesh.available_formats()
        assert all(isinstance(i, str) for i in formats)
        assert all(len(i) > 0 for i in formats)
        assert all(i in formats for i in ['stl', 'ply', 'off', 'obj'])

        for mesh in g.get_meshes(raise_error=True):
            # log file name for debugging
            file_name = mesh.metadata['file_name']
            g.log.info('Testing %s', file_name)

            start = {mesh.md5(), mesh.crc()}
            assert len(mesh.faces) > 0
            assert len(mesh.vertices) > 0

            assert len(mesh.edges) > 0
            assert len(mesh.edges_unique) > 0
            assert len(mesh.edges_sorted) > 0
            assert len(mesh.edges_face) > 0
            assert isinstance(mesh.euler_number, int)

            # check bounding primitives
            assert mesh.bounding_box.volume > 0.0
            assert mesh.bounding_primitive.volume > 0.0

            # none of these should have mutated anything
            assert start == {mesh.md5(), mesh.crc()}

            # run processing, again
            mesh.process()

            # still shouldn't have changed anything
            assert start == {mesh.md5(), mesh.crc()}

            if not (mesh.is_watertight and mesh.is_winding_consistent):
                continue

            assert len(mesh.facets) == len(mesh.facets_area)
            assert len(mesh.facets) == len(mesh.facets_normal)
            assert len(mesh.facets) == len(mesh.facets_boundary)

            if len(mesh.facets) != 0:
                faces = mesh.facets[mesh.facets_area.argmax()]
                outline = mesh.outline(faces)
                # check to make sure we can generate closed paths
                # on a Path3D object
                test = outline.paths

            smoothed = mesh.smoothed()

            assert mesh.volume > 0.0

            section = mesh.section(plane_normal=[0, 0, 1],
                                   plane_origin=mesh.centroid)

            sample = mesh.sample(1000)
            even_sample = g.trimesh.sample.sample_surface_even(mesh, 100)
            assert sample.shape == (1000, 3)
            g.log.info('finished testing meshes')

            # make sure vertex kdtree and triangles rtree exist

            t = mesh.kdtree
            assert hasattr(t, 'query')
            g.log.info('Creating triangles tree')
            r = mesh.triangles_tree
            assert hasattr(r, 'intersection')
            g.log.info('Triangles tree ok')

            # face angles should have same
            assert mesh.face_angles.shape == mesh.faces.shape
            assert len(mesh.vertices) == len(mesh.vertex_defects)
            assert len(mesh.principal_inertia_components) == 3

            # we should have built up a bunch of stuff into
            # our cache, so make sure all numpy arrays cached are
            # finite
            for name, cached in mesh._cache.cache.items():
                # only check numpy arrays
                if not isinstance(cached, g.np.ndarray):
                    continue

                # only check int, float, and bool
                if cached.dtype.kind not in 'ibf':
                    continue

                # there should never be NaN values
                if g.np.isnan(cached).any():
                    raise ValueError('NaN values in %s/%s', file_name, name)

                # fields allowed to have infinite values
                if name in ['face_adjacency_radius']:
                    continue

                # make sure everything is finite
                if not g.np.isfinite(cached).all():
                    raise ValueError('inf values in %s/%s', file_name, name)

            # some memory issues only show up when you copy the mesh a bunch
            # specifically, if you cache c- objects then deepcopy the mesh this
            # generally segfaults randomly
            copy_count = 200
            g.log.info('Attempting to copy mesh %d times', copy_count)
            for i in range(copy_count):
                copied = mesh.copy()
                assert copied.is_empty == mesh.is_empty
                #t = copied.triangles_tree
                c = copied.kdtree
                copied.apply_transform(
                    g.trimesh.transformations.rotation_matrix(
                        g.np.degrees(i), [0, 1, 1]))
            g.log.info('Multiple copies done')

            if not g.np.allclose(copied.identifier, mesh.identifier):
                raise ValueError('copied identifier changed!')

            # ...still shouldn't have changed anything
            assert start == {mesh.md5(), mesh.crc()}
Exemple #37
0
    def test_export(self):
        export_types = list(
            g.trimesh.exchange.export._mesh_exporters.keys())

        meshes = list(g.get_meshes(8))
        # make sure we've got something with texture
        meshes.append(g.get_mesh('fuze.obj'))

        for mesh in meshes:
            # disregard texture
            mesh.merge_vertices(textured=False)
            for file_type in export_types:
                export = mesh.export(file_type=file_type)
                if export is None:
                    raise ValueError('Exporting mesh %s to %s resulted in None!',
                                     mesh.metadata['file_name'],
                                     file_type)

                assert len(export) > 0

                if file_type in [
                        'dae',     # collada, no native importers
                        'collada',  # collada, no native importers
                        'msgpack',  # kind of flaky, but usually works
                        'drc']:    # DRC is not a lossless format
                    g.log.warning(
                        'no native loaders implemented for collada!')
                    continue

                g.log.info('Export/import testing on %s',
                           mesh.metadata['file_name'])

                # if export is string or bytes wrap as pseudo file object
                if isinstance(export, str) or isinstance(export, bytes):
                    file_obj = g.io_wrap(export)
                else:
                    file_obj = export

                loaded = g.trimesh.load(file_obj=file_obj,
                                        file_type=file_type)

                # if we exported as GLTF/dae it will come back as a Scene
                if isinstance(loaded, g.trimesh.Scene) and isinstance(
                        mesh, g.trimesh.Trimesh):
                    assert len(loaded.geometry) == 1
                    loaded = next(iter(loaded.geometry.values()))

                if (not g.trimesh.util.is_shape(loaded._data['faces'], (-1, 3)) or
                    not g.trimesh.util.is_shape(loaded._data['vertices'], (-1, 3)) or
                        loaded.faces.shape != mesh.faces.shape):
                    g.log.error('Export -> import for %s on %s wrong shape!',
                                file_type,
                                mesh.metadata['file_name'])

                if loaded.vertices is None:
                    g.log.error('Export -> import for %s on %s gave None for vertices!',
                                file_type,
                                mesh.metadata['file_name'])

                if loaded.faces.shape != mesh.faces.shape:
                    raise ValueError('export cycle {} on {} gave faces {}->{}!'.format(
                        file_type,
                        mesh.metadata['file_name'],
                        str(mesh.faces.shape),
                        str(loaded.faces.shape)))

                if loaded.vertices.shape != mesh.vertices.shape:
                    raise ValueError('export cycle {} on {} gave vertices {}->{}!'.format(
                        file_type,
                        mesh.metadata['file_name'],
                        mesh.vertices.shape,
                        loaded.vertices.shape))

                # try exporting/importing certain file types by name
                if file_type in ['obj', 'stl', 'ply', 'off']:
                    temp = g.tempfile.NamedTemporaryFile(suffix='.' + file_type,
                                                         delete=False)
                    # windows throws permissions errors if you keep it open
                    temp.close()

                    mesh.export(temp.name)
                    load = g.trimesh.load(temp.name)
                    # manual cleanup
                    g.os.remove(temp.name)

                    assert mesh.faces.shape == load.faces.shape
                    assert mesh.vertices.shape == load.vertices.shape

            # if we're not on linux don't run meshlab tests
            if not g.is_linux:
                continue
            # formats exportable by trimesh and importable by meshlab
            # make sure things we export can be loaded by meshlab
            both = set(g.meshlab_formats).intersection(
                set(export_types))

            # additional options to pass to exporters to try to ferret
            # out combinations which lead to invalid output
            kwargs = {'ply': [{'vertex_normal': True,
                               'encoding': 'ascii'},
                              {'vertex_normal': True,
                               'encoding': 'binary'},
                              {'vertex_normal': False,
                               'encoding': 'ascii'},
                              {'vertex_normal': False,
                               'encoding': 'binary'}],
                      'stl': [{'file_type': 'stl'},
                              {'file_type': 'stl_ascii'}]}

            # make sure input mesh has garbage removed
            mesh._validate = True
            # since we're going to be looking for exact export
            # counts remove anything small/degenerate again
            mesh.process()

            # run through file types supported by both meshlab and trimesh
            for file_type in both:
                # pull different exporter options for the format
                if file_type in kwargs:
                    options = kwargs[file_type]
                else:
                    options = [{}]

                # try each combination of options
                for option in options:
                    temp = g.tempfile.NamedTemporaryFile(
                        suffix='.' + file_type,
                        delete=False)
                    temp_off = g.tempfile.NamedTemporaryFile(
                        suffix='.off',
                        delete=False)
                    # windows throws permissions errors if you keep it open
                    temp.close()
                    temp_off.close()
                    # write over the tempfile
                    option['file_obj'] = temp.name
                    mesh.export(**option)

                    # will raise CalledProcessError if meshlab
                    # can't successfully import the file
                    try:
                        # have meshlab take the export and convert it into
                        # an OFF file, which is basically the simplest format
                        # that uses by- reference vertices
                        # meshlabserver requires X so wrap it with XVFB
                        cmd = 'xvfb-run -a -s "-screen 0 800x600x24" meshlabserver '
                        cmd += '-i {} -o {}'.format(temp.name, temp_off.name)
                        g.subprocess.check_call(cmd, shell=True)
                    except g.subprocess.CalledProcessError as E:
                        # log the options that produced the failure
                        g.log.error('failed to export {}'.format(
                            option))
                        # raise the error again
                        raise E

                    # load meshlabs export back into trimesh
                    r = g.trimesh.load(temp_off.name)

                    # we should have the same number of vertices and faces
                    assert len(r.vertices) == len(mesh.vertices)
                    assert len(r.faces) == len(mesh.faces)

                    # manual cleanup
                    g.os.remove(temp.name)
                    g.os.remove(temp_off.name)
Exemple #38
0
    def test_export(self):
        export_types = list(g.trimesh.exchange.export._mesh_exporters.keys())

        meshes = list(g.get_meshes(8))
        # make sure we've got something with texture
        meshes.append(g.get_mesh('fuze.obj'))

        for mesh in meshes:
            # disregard texture
            mesh.merge_vertices(textured=False)
            for file_type in export_types:
                export = mesh.export(file_type=file_type)
                if export is None:
                    raise ValueError(
                        'Exporting mesh %s to %s resulted in None!',
                        mesh.metadata['file_name'], file_type)

                assert len(export) > 0

                if file_type in [
                        'dae',  # collada, no native importers
                        'collada',  # collada, no native importers
                        'msgpack',  # kind of flaky, but usually works
                        'drc'
                ]:  # DRC is not a lossless format
                    g.log.warning('no native loaders implemented for collada!')
                    continue

                g.log.info('Export/import testing on %s',
                           mesh.metadata['file_name'])

                # if export is string or bytes wrap as pseudo file object
                if isinstance(export, str) or isinstance(export, bytes):
                    file_obj = g.io_wrap(export)
                else:
                    file_obj = export

                loaded = g.trimesh.load(file_obj=file_obj, file_type=file_type)

                # if we exported as GLTF/dae it will come back as a Scene
                if isinstance(loaded, g.trimesh.Scene) and isinstance(
                        mesh, g.trimesh.Trimesh):
                    assert len(loaded.geometry) == 1
                    loaded = next(iter(loaded.geometry.values()))

                if (not g.trimesh.util.is_shape(loaded._data['faces'],
                                                (-1, 3)) or
                        not g.trimesh.util.is_shape(loaded._data['vertices'],
                                                    (-1, 3))
                        or loaded.faces.shape != mesh.faces.shape):
                    g.log.error('Export -> import for %s on %s wrong shape!',
                                file_type, mesh.metadata['file_name'])

                if loaded.vertices is None:
                    g.log.error(
                        'Export -> import for %s on %s gave None for vertices!',
                        file_type, mesh.metadata['file_name'])

                if loaded.faces.shape != mesh.faces.shape:
                    raise ValueError(
                        'export cycle {} on {} gave faces {}->{}!'.format(
                            file_type, mesh.metadata['file_name'],
                            str(mesh.faces.shape), str(loaded.faces.shape)))

                if loaded.vertices.shape != mesh.vertices.shape:
                    raise ValueError(
                        'export cycle {} on {} gave vertices {}->{}!'.format(
                            file_type, mesh.metadata['file_name'],
                            mesh.vertices.shape, loaded.vertices.shape))

                # try exporting/importing certain file types by name
                if file_type in ['obj', 'stl', 'ply', 'off']:
                    temp = g.tempfile.NamedTemporaryFile(suffix='.' +
                                                         file_type,
                                                         delete=False)
                    # windows throws permissions errors if you keep it open
                    temp.close()

                    mesh.export(temp.name)
                    load = g.trimesh.load(temp.name)
                    # manual cleanup
                    g.os.remove(temp.name)

                    assert mesh.faces.shape == load.faces.shape
                    assert mesh.vertices.shape == load.vertices.shape

            # if we're not on linux don't run meshlab tests
            if not g.is_linux:
                continue
            # formats exportable by trimesh and importable by meshlab
            # make sure things we export can be loaded by meshlab
            both = set(g.meshlab_formats).intersection(set(export_types))

            # additional options to pass to exporters to try to ferret
            # out combinations which lead to invalid output
            kwargs = {
                'ply': [{
                    'vertex_normal': True,
                    'encoding': 'ascii'
                }, {
                    'vertex_normal': True,
                    'encoding': 'binary'
                }, {
                    'vertex_normal': False,
                    'encoding': 'ascii'
                }, {
                    'vertex_normal': False,
                    'encoding': 'binary'
                }],
                'stl': [{
                    'file_type': 'stl'
                }, {
                    'file_type': 'stl_ascii'
                }]
            }

            # make sure input mesh has garbage removed
            mesh._validate = True
            # since we're going to be looking for exact export
            # counts remove anything small/degenerate again
            mesh.process()

            # run through file types supported by both meshlab and trimesh
            for file_type in both:
                # pull different exporter options for the format
                if file_type in kwargs:
                    options = kwargs[file_type]
                else:
                    options = [{}]

                # try each combination of options
                for option in options:
                    temp = g.tempfile.NamedTemporaryFile(suffix='.' +
                                                         file_type,
                                                         delete=False)
                    temp_off = g.tempfile.NamedTemporaryFile(suffix='.off',
                                                             delete=False)
                    # windows throws permissions errors if you keep it open
                    temp.close()
                    temp_off.close()
                    # write over the tempfile
                    option['file_obj'] = temp.name
                    mesh.export(**option)

                    # will raise CalledProcessError if meshlab
                    # can't successfully import the file
                    try:
                        # have meshlab take the export and convert it into
                        # an OFF file, which is basically the simplest format
                        # that uses by- reference vertices
                        # meshlabserver requires X so wrap it with XVFB
                        cmd = 'xvfb-run -a -s "-screen 0 800x600x24" meshlabserver '
                        cmd += '-i {} -o {}'.format(temp.name, temp_off.name)
                        g.subprocess.check_call(cmd, shell=True)
                    except g.subprocess.CalledProcessError as E:
                        # log the options that produced the failure
                        g.log.error('failed to export {}'.format(option))
                        # raise the error again
                        raise E

                    # load meshlabs export back into trimesh
                    r = g.trimesh.load(temp_off.name)

                    # we should have the same number of vertices and faces
                    assert len(r.vertices) == len(mesh.vertices)
                    assert len(r.faces) == len(mesh.faces)

                    # manual cleanup
                    g.os.remove(temp.name)
                    g.os.remove(temp_off.name)
Exemple #39
0
    def test_meshes(self):
        # make sure we can load everything we think we can
        formats = g.trimesh.available_formats()
        assert all(isinstance(i, str) for i in formats)
        assert all(len(i) > 0 for i in formats)
        assert all(i in formats for i in ['stl', 'ply', 'off', 'obj'])

        for mesh in g.get_meshes(raise_error=True):
            # log file name for debugging
            file_name = mesh.metadata['file_name']

            # ply files can return PointCloud objects
            if file_name.startswith('points_'):
                continue

            g.log.info('Testing %s', file_name)
            start = {mesh.md5(), mesh.crc()}
            assert len(mesh.faces) > 0
            assert len(mesh.vertices) > 0

            # make sure vertex normals match vertices and are valid
            assert mesh.vertex_normals.shape == mesh.vertices.shape
            assert g.np.isfinite(mesh.vertex_normals).all()

            # should be one per vertex
            assert len(mesh.vertex_faces) == len(mesh.vertices)

            # check some edge properties
            assert len(mesh.edges) > 0
            assert len(mesh.edges_unique) > 0
            assert len(mesh.edges_sorted) == len(mesh.edges)
            assert len(mesh.edges_face) == len(mesh.edges)

            # check edges_unique
            assert len(mesh.edges) == len(mesh.edges_unique_inverse)
            assert g.np.allclose(mesh.edges_sorted,
                                 mesh.edges_unique[mesh.edges_unique_inverse])
            assert len(mesh.edges_unique) == len(mesh.edges_unique_length)

            # euler number should be an integer
            assert isinstance(mesh.euler_number, int)

            # check bounding primitives
            assert mesh.bounding_box.volume > 0.0
            assert mesh.bounding_primitive.volume > 0.0

            # none of these should have mutated anything
            assert start == {mesh.md5(), mesh.crc()}

            # run processing, again
            mesh.process()

            # still shouldn't have changed anything
            assert start == {mesh.md5(), mesh.crc()}

            if not (mesh.is_watertight and mesh.is_winding_consistent):
                continue

            assert len(mesh.facets) == len(mesh.facets_area)
            assert len(mesh.facets) == len(mesh.facets_normal)
            assert len(mesh.facets) == len(mesh.facets_boundary)

            if len(mesh.facets) != 0:
                faces = mesh.facets[mesh.facets_area.argmax()]
                outline = mesh.outline(faces)
                # check to make sure we can generate closed paths
                # on a Path3D object
                test = outline.paths  # NOQA

            smoothed = mesh.smoothed()  # NOQA

            assert mesh.volume > 0.0

            section = mesh.section(
                plane_normal=[0, 0, 1],  # NOQA
                plane_origin=mesh.centroid)

            sample = mesh.sample(1000)
            even_sample = g.trimesh.sample.sample_surface_even(mesh,
                                                               100)  # NOQA
            assert sample.shape == (1000, 3)
            g.log.info('finished testing meshes')

            # make sure vertex kdtree and triangles rtree exist

            t = mesh.kdtree
            assert hasattr(t, 'query')
            g.log.info('Creating triangles tree')
            r = mesh.triangles_tree
            assert hasattr(r, 'intersection')
            g.log.info('Triangles tree ok')

            # face angles should have same
            assert mesh.face_angles.shape == mesh.faces.shape
            assert len(mesh.vertices) == len(mesh.vertex_defects)
            assert len(mesh.principal_inertia_components) == 3

            # collect list of cached properties that are writeable
            writeable = []
            # we should have built up a bunch of stuff into
            # our cache, so make sure all numpy arrays cached
            # are read-only and not crazy
            for name, cached in mesh._cache.cache.items():
                # only check numpy arrays
                if not isinstance(cached, g.np.ndarray):
                    continue

                # nothing in the cache should be writeable
                if cached.flags['WRITEABLE']:
                    raise ValueError('{} is writeable!'.format(name))

                # only check int, float, and bool
                if cached.dtype.kind not in 'ibf':
                    continue

                # there should never be NaN values
                if g.np.isnan(cached).any():
                    raise ValueError('NaN values in %s/%s', file_name, name)

                # fields allowed to have infinite values
                if name in ['face_adjacency_radius']:
                    continue

                # make sure everything is finite
                if not g.np.isfinite(cached).all():
                    raise ValueError('inf values in %s/%s', file_name, name)

            # ...still shouldn't have changed anything
            assert start == {mesh.md5(), mesh.crc()}

            # log the names of properties we need to make read-only
            if len(writeable) > 0:
                # TODO : all cached values should be read-only
                g.log.error('cached properties writeable: {}'.format(
                    ', '.join(writeable)))
Exemple #40
0
    def test_meshes(self):
        # make sure we can load everything we think we can
        formats = g.trimesh.available_formats()
        assert all(isinstance(i, str) for i in formats)
        assert all(len(i) > 0 for i in formats)
        assert all(i in formats for i in ['stl', 'ply', 'off', 'obj'])

        for mesh in g.get_meshes(raise_error=True):
            g.log.info('Testing %s', mesh.metadata['file_name'])

            start = {mesh.md5(), mesh.crc()}
            assert len(mesh.faces) > 0
            assert len(mesh.vertices) > 0

            assert len(mesh.edges) > 0
            assert len(mesh.edges_unique) > 0
            assert len(mesh.edges_sorted) > 0
            assert len(mesh.edges_face) > 0
            assert isinstance(mesh.euler_number, int)

            # check bounding primitives
            assert mesh.bounding_box.volume > 0.0
            assert mesh.bounding_primitive.volume > 0.0

            # none of these should have mutated anything
            assert start == {mesh.md5(), mesh.crc()}

            # run processing, again
            mesh.process()

            # still shouldn't have changed anything
            assert start == {mesh.md5(), mesh.crc()}

            if not (mesh.is_watertight and mesh.is_winding_consistent):
                continue

            assert len(mesh.facets) == len(mesh.facets_area)
            assert len(mesh.facets) == len(mesh.facets_normal)
            assert len(mesh.facets) == len(mesh.facets_boundary)

            if len(mesh.facets) != 0:
                faces = mesh.facets[mesh.facets_area.argmax()]
                outline = mesh.outline(faces)
                # check to make sure we can generate closed paths
                # on a Path3D object
                test = outline.paths

            smoothed = mesh.smoothed()

            assert mesh.volume > 0.0

            section = mesh.section(plane_normal=[0, 0, 1],
                                   plane_origin=mesh.centroid)

            sample = mesh.sample(1000)
            even_sample = g.trimesh.sample.sample_surface_even(mesh, 100)
            assert sample.shape == (1000, 3)
            g.log.info('finished testing meshes')

            # make sure vertex kdtree and triangles rtree exist

            t = mesh.kdtree
            assert hasattr(t, 'query')
            g.log.info('Creating triangles tree')
            r = mesh.triangles_tree
            assert hasattr(r, 'intersection')
            g.log.info('Triangles tree ok')

            # some memory issues only show up when you copy the mesh a bunch
            # specifically, if you cache c- objects then deepcopy the mesh this
            # generally segfaults randomly
            copy_count = 200
            g.log.info('Attempting to copy mesh %d times', copy_count)
            for i in range(copy_count):
                copied = mesh.copy()
                assert copied.is_empty == mesh.is_empty
                #t = copied.triangles_tree
                c = copied.kdtree
                copied.apply_transform(
                    g.trimesh.transformations.rotation_matrix(
                        g.np.degrees(i), [0, 1, 1]))
            g.log.info('Multiple copies done')

            if not g.np.allclose(copied.identifier, mesh.identifier):
                raise ValueError('copied identifier changed!')

            # ...still shouldn't have changed anything
            assert start == {mesh.md5(), mesh.crc()}
Exemple #41
0
 def setUp(self):
     meshes = [g.get_mesh(i) for i in ['large_block.STL',
                                       'featuretype.STL']]
     self.meshes = g.np.append(meshes, list(g.get_meshes(5)))
Exemple #42
0
 def test_projections(self):
     for m in g.get_meshes(4):
         assert (len(m.face_adjacency_projections) == (len(
             m.face_adjacency)))
Exemple #43
0
 def setUp(self):
     meshes = [g.get_mesh(i) for i in ['large_block.STL',
                                       'featuretype.STL']]
     self.meshes = g.np.append(meshes, g.get_meshes(5))
Exemple #44
0
 def test_projections(self):
     # check the vertex projection onto adjacent face plane
     # this is used to calculate convexity
     for m in g.get_meshes(4):
         assert (len(m.face_adjacency_projections) ==
                 (len(m.face_adjacency)))
Exemple #45
0
    def test_meshes(self):
        # make sure we can load everything we think we can
        formats = g.trimesh.available_formats()
        assert all(isinstance(i, str) for i in formats)
        assert all(len(i) > 0 for i in formats)
        assert all(i in formats
                   for i in ['stl', 'ply', 'off', 'obj'])

        for mesh in g.get_meshes(raise_error=True):
            # log file name for debugging
            file_name = mesh.metadata['file_name']
            g.log.info('Testing %s', file_name)

            start = {mesh.md5(), mesh.crc()}
            assert len(mesh.faces) > 0
            assert len(mesh.vertices) > 0

            assert len(mesh.edges) > 0
            assert len(mesh.edges_unique) > 0
            assert len(mesh.edges_sorted) > 0
            assert len(mesh.edges_face) > 0
            assert isinstance(mesh.euler_number, int)

            # check bounding primitives
            assert mesh.bounding_box.volume > 0.0
            assert mesh.bounding_primitive.volume > 0.0

            # none of these should have mutated anything
            assert start == {mesh.md5(), mesh.crc()}

            # run processing, again
            mesh.process()

            # still shouldn't have changed anything
            assert start == {mesh.md5(), mesh.crc()}

            if not (mesh.is_watertight and
                    mesh.is_winding_consistent):
                continue

            assert len(mesh.facets) == len(mesh.facets_area)
            assert len(mesh.facets) == len(mesh.facets_normal)
            assert len(mesh.facets) == len(mesh.facets_boundary)

            if len(mesh.facets) != 0:
                faces = mesh.facets[mesh.facets_area.argmax()]
                outline = mesh.outline(faces)
                # check to make sure we can generate closed paths
                # on a Path3D object
                test = outline.paths  # NOQA

            smoothed = mesh.smoothed()  # NOQA

            assert mesh.volume > 0.0

            section = mesh.section(plane_normal=[0, 0, 1],  # NOQA
                                   plane_origin=mesh.centroid)

            sample = mesh.sample(1000)
            even_sample = g.trimesh.sample.sample_surface_even(mesh, 100)  # NOQA
            assert sample.shape == (1000, 3)
            g.log.info('finished testing meshes')

            # make sure vertex kdtree and triangles rtree exist

            t = mesh.kdtree
            assert hasattr(t, 'query')
            g.log.info('Creating triangles tree')
            r = mesh.triangles_tree
            assert hasattr(r, 'intersection')
            g.log.info('Triangles tree ok')

            # face angles should have same
            assert mesh.face_angles.shape == mesh.faces.shape
            assert len(mesh.vertices) == len(mesh.vertex_defects)
            assert len(mesh.principal_inertia_components) == 3

            # we should have built up a bunch of stuff into
            # our cache, so make sure all numpy arrays cached are
            # finite
            for name, cached in mesh._cache.cache.items():
                # only check numpy arrays
                if not isinstance(cached, g.np.ndarray):
                    continue

                # only check int, float, and bool
                if cached.dtype.kind not in 'ibf':
                    continue

                # there should never be NaN values
                if g.np.isnan(cached).any():
                    raise ValueError('NaN values in %s/%s',
                                     file_name, name)

                # fields allowed to have infinite values
                if name in ['face_adjacency_radius']:
                    continue

                # make sure everything is finite
                if not g.np.isfinite(cached).all():
                    raise ValueError('inf values in %s/%s',
                                     file_name, name)

            # some memory issues only show up when you copy the mesh a bunch
            # specifically, if you cache c- objects then deepcopy the mesh this
            # generally segfaults randomly
            copy_count = 200
            g.log.info('Attempting to copy mesh %d times', copy_count)
            for i in range(copy_count):
                copied = mesh.copy()
                assert copied.is_empty == mesh.is_empty
                # t = copied.triangles_tree
                c = copied.kdtree  # NOQA
                copied.apply_transform(
                    g.trimesh.transformations.rotation_matrix(
                        g.np.degrees(i),
                        [0, 1, 1]))
            g.log.info('Multiple copies done')

            if not g.np.allclose(copied.identifier,
                                 mesh.identifier):
                raise ValueError('copied identifier changed!')

            # ...still shouldn't have changed anything
            assert start == {mesh.md5(), mesh.crc()}
Exemple #46
0
    def test_permutate(self):
        def close(a, b):
            if len(a) == len(b) == 0:
                return False
            if a.shape != b.shape:
                return False
            return g.np.allclose(a, b)

        def make_assertions(mesh, test, rigid=False):
            if (close(test.face_adjacency,
                      mesh.face_adjacency) and
                    len(mesh.faces) > MIN_FACES):
                g.log.error(
                    'face_adjacency unchanged: {}'.format(
                        str(test.face_adjacency)))
                raise ValueError(
                    'face adjacency of %s the same after permutation!',
                    mesh.metadata['file_name'])

            if (close(test.face_adjacency_edges,
                      mesh.face_adjacency_edges) and
                    len(mesh.faces) > MIN_FACES):
                g.log.error(
                    'face_adjacency_edges unchanged: {}'.format(
                        str(test.face_adjacency_edges)))
                raise ValueError(
                    'face adjacency edges of %s the same after permutation!',
                    mesh.metadata['file_name'])

            assert not close(test.faces,
                             mesh.faces)
            assert not close(test.vertices,
                             mesh.vertices)
            assert not test.md5() == mesh.md5()

            # rigid transforms don't change area or volume
            if rigid:
                assert g.np.allclose(mesh.area, test.area)

                # volume is very dependent on meshes being watertight and sane
                if (mesh.is_watertight and
                    test.is_watertight and
                    mesh.is_winding_consistent and
                        test.is_winding_consistent):
                    assert g.np.allclose(mesh.volume, test.volume, rtol=.05)

        for mesh in g.get_meshes(5):
            if len(mesh.faces) < MIN_FACES:
                continue
            # warp the mesh to be a unit cube
            mesh.vertices /= mesh.extents
            original = mesh.copy()

            for i in range(5):
                mesh = original.copy()
                noise = g.trimesh.permutate.noise(mesh,
                                                  magnitude=mesh.scale / 50.0)
                # make sure that if we permutate vertices with no magnitude
                # area and volume remain the same
                no_noise = g.trimesh.permutate.noise(mesh, magnitude=0.0)

                transform = g.trimesh.permutate.transform(mesh)
                tessellate = g.trimesh.permutate.tessellation(mesh)

                make_assertions(mesh, noise, rigid=False)
                make_assertions(mesh, no_noise, rigid=True)
                make_assertions(mesh, transform, rigid=True)
                make_assertions(mesh, tessellate, rigid=True)

            # make sure permutate didn't alter the original mesh
            assert original.md5() == mesh.md5()
Exemple #47
0
 def test_base(self):
     for mesh in g.get_meshes(1):
         tess = mesh.permutate.tessellation()  # NOQA
         noise = mesh.permutate.noise()
         noise = mesh.permutate.noise(magnitude=mesh.scale / 10)  # NOQA
         transform = mesh.permutate.transform()  # NOQA
Exemple #48
0
 def test_projections(self):
     # check the vertex projection onto adjacent face plane
     # this is used to calculate convexity
     for m in g.get_meshes(4):
         assert (len(m.face_adjacency_projections) == (len(
             m.face_adjacency)))