Example #1
0
class TestAlign(unittest.TestCase):
    DELTA = 0.01

    def setUp(self):
        """Runs before each unit test.
        Sets up AmpObject object using "stl_file_4.stl" "stl_file_5.stl".
        """
        from ampscan.core import AmpObject
        # Load 2 spheres with radius 1, and 1.2
        stl_path = get_path("stl_file_5.stl")  # R=1
        self.amp1 = AmpObject(stl_path)
        stl_path = get_path("stl_file_4.stl")  # R=1.2
        self.amp2 = AmpObject(stl_path)
        stl_path = get_path("stl_file.stl")
        self.amp3 = AmpObject(stl_path)
        self.amp4 = AmpObject(stl_path)

    def test_align(self):
        """Test that objects that are already centered on origin are aligned correctly"""
        al = align(self.amp1, self.amp2).m

        # Both objects are already centered, so should be close to origin (allowing for some inaccuracy)
        self.assertAlmostEqual(al.vert.mean(axis=0)[0],
                               0,
                               delta=TestAlign.DELTA)
        self.assertAlmostEqual(al.vert.mean(axis=0)[1],
                               0,
                               delta=TestAlign.DELTA)
        self.assertAlmostEqual(al.vert.mean(axis=0)[2],
                               0,
                               delta=TestAlign.DELTA)

    def test_align_points(self):
        """Test that the shape can be aligned to -5mm in z axis"""
        mv = [[0, 0, 5], [5, 0, 5], [0, 5, 5]]
        sv = [[0, 0, 0], [5, 0, 0], [0, 5, 0]]
        al = align(self.amp1, self.amp2, mv=mv, sv=sv, method='contPoints').m
        zMax = self.amp1.vert[:, 2].max() - 5
        # Both objects are already centered, so should be close to origin (allowing for some inaccuracy)
        self.assertAlmostEqual(al.vert[:, 2].max(),
                               zMax,
                               delta=TestAlign.DELTA)

    def test_align_idx(self):
        """Test that the shape can be using idxPoints"""
        self.amp4.rotateAng([5, 5, 5], ang='deg')
        al = align(self.amp3,
                   self.amp4,
                   mv=[0, 1, 2, 3],
                   sv=[0, 1, 2, 3],
                   method='idxPoints')
        all(
            self.assertAlmostEqual(al.m.vert[i, 0], al.s.vert[i, 0], delta=0.1)
            for i in range(al.s.vert.shape[0]))

    def test_align_invert(self):
        """Test for the inverted ICP. The transformations should be the same."""
        al = align(self.amp1, self.amp2, inverse=False)

        al_inv = align(self.amp2, self.amp1, inverse=True)

        print(al.R)
        print(al_inv.R)

        print(al.T)
        print(al_inv.T)
Example #2
0
class TestCore(unittest.TestCase):
    ACCURACY = 5  # The number of decimal places to value accuracy for - needed due to floating point inaccuracies

    def setUp(self):
        """Runs before each unit test.
        Sets up the AmpObject object using "stl_file.stl".
        """
        from ampscan.core import AmpObject
        stl_path = get_path("stl_file.stl")
        self.amp = AmpObject(stl_path)

    def test_centre(self):
        """Test the centre method of AmpObject"""

        # Translate the mesh
        self.amp.translate([1, 0, 0])
        # Recenter the mesh
        self.amp.centre()
        centre = self.amp.vert.mean(axis=0)

        # Check that the mesh is centred correctly (to at least the number of decimal places of ACCURACY)
        self.assertTrue(
            all(centre[i] < (10**-TestCore.ACCURACY) for i in range(3)))

    def test_centre_static(self):

        with self.assertRaises(TypeError):
            self.amp.centreStatic(1)
        with self.assertRaises(TypeError):
            self.amp.centreStatic([])

        # Import second shape
        from ampscan.core import AmpObject
        stl_path = get_path("stl_file_2.stl")
        amp2 = AmpObject(stl_path)

        self.amp.centreStatic(amp2)

        for i in range(3):
            # This method has a large degree of error so, it's only testing to 2 dp
            self.assertAlmostEqual(
                self.amp.vert.mean(axis=0)[i],
                amp2.vert.mean(axis=0)[i], 2)

    def test_rotate_ang(self):
        """Tests the rotateAng method of AmpObject"""

        # Test rotation on random node
        n = randrange(len(self.amp.vert))
        rot = [0, 0, np.pi / 3]
        before = self.amp.vert[n].copy()
        self.amp.rotateAng(rot)
        after_vert_pos = self.amp.vert[n].copy()
        # Use 2D rotation matrix formula to test rotate method on z axis
        expected = [
            np.cos(rot[2]) * before[0] - np.sin(rot[2]) * before[1],
            np.sin(rot[2]) * before[0] + np.cos(rot[2]) * before[1], before[2]
        ]
        # Check all coordinate dimensions are correct
        all(
            self.assertAlmostEqual(expected[i], after_vert_pos[i],
                                   TestCore.ACCURACY) for i in range(3))

        # Check single floats cause TypeError
        with self.assertRaises(TypeError):
            self.amp.rotateAng(7)

        # Check dictionaries cause TypeError
        with self.assertRaises(TypeError):
            self.amp.rotateAng(dict())

        # Tests that incorrect number of elements causes ValueError
        with self.assertRaises(ValueError):
            self.amp.rotateAng(rot, "test")
        with self.assertRaises(ValueError):
            self.amp.rotateAng(rot, [])

    def test_rotate(self):
        """Tests the rotate method of AmpObject"""
        # A test rotation and translation using list
        m = [[1, 0, 0], [0, np.sqrt(3) / 2, 1 / 2],
             [0, -1 / 2, np.sqrt(3) / 2]]
        self.amp.rotate(m)

        # Check single floats cause TypeError
        with self.assertRaises(TypeError):
            self.amp.rotate(7)

        # Check dictionaries cause TypeError
        with self.assertRaises(TypeError):
            self.amp.rotate(dict())

        # Check invalid dimensions cause ValueError
        with self.assertRaises(ValueError):
            self.amp.rotate([])
        with self.assertRaises(ValueError):
            self.amp.rotate([[0, 0, 1]])
        with self.assertRaises(ValueError):
            self.amp.rotate([[], [], []])

    def test_translate(self):
        """Test translating method of AmpObject"""

        # Check that everything has been translated correctly to a certain accuracy
        start = self.amp.vert.mean(axis=0).copy()
        self.amp.translate([1, -1, 0])
        end = self.amp.vert.mean(axis=0).copy()
        self.assertAlmostEqual(start[0] + 1, end[0], places=TestCore.ACCURACY)
        self.assertAlmostEqual(start[1] - 1, end[1], places=TestCore.ACCURACY)
        self.assertAlmostEqual(start[2], end[2], places=TestCore.ACCURACY)

        # Check that translating raises TypeError when translating with an invalid type
        with self.assertRaises(TypeError):
            self.amp.translate("")

        # Check that translating raises ValueError when translating with 2 dimensions
        with self.assertRaises(ValueError):
            self.amp.translate([0, 0])

        # Check that translating raises ValueError when translating with 4 dimensions
        with self.assertRaises(ValueError):
            self.amp.translate([0, 0, 0, 0])

    def test_rigid_transform(self):
        """Test the rigid transform method of AmpObject"""

        # Test if no transform is applied, vertices aren't affected
        before_vert = self.amp.vert.copy()
        self.amp.rigidTransform(R=None, T=None)
        all(
            self.assertEqual(self.amp.vert[y][x], before_vert[y][x])
            for y in range(len(self.amp.vert))
            for x in range(len(self.amp.vert[0])))

        # A test rotation and translation
        m = [[1, 0, 0], [0, np.sqrt(3) / 2, 1 / 2],
             [0, -1 / 2, np.sqrt(3) / 2]]
        self.amp.rigidTransform(R=m, T=[1, 0, -1])

        # Check that translating raises TypeError when translating with an invalid type
        with self.assertRaises(TypeError):
            self.amp.rigidTransform(T=dict())

        # Check that rotating raises TypeError when translating with an invalid type
        with self.assertRaises(TypeError):
            self.amp.rigidTransform(R=7)

    def test_rot_matrix(self):
        """Tests the rotMatrix method in AmpObject"""

        # Tests that a transformation by 0 in all axis is 0 matrix
        all(
            self.amp.rotMatrix([0, 0, 0])[y][x] == 0 for x in range(3)
            for y in range(3))

        expected = [[1, 0, 0], [0, np.sqrt(3) / 2, 1 / 2],
                    [0, -1 / 2, np.sqrt(3) / 2]]
        all(
            self.amp.rotMatrix([np.pi / 6, 0, 0])[y][x] == expected[y][x]
            for x in range(3) for y in range(3))

        # Tests that string passed into rot causes TypeError
        with self.assertRaises(TypeError):
            self.amp.rotMatrix(" ")
        with self.assertRaises(TypeError):
            self.amp.rotMatrix(dict())

        # Tests that incorrect number of elements causes ValueError
        with self.assertRaises(ValueError):
            self.amp.rotMatrix([0, 1])
        with self.assertRaises(ValueError):
            self.amp.rotMatrix([0, 1, 3, 0])

    def test_flip(self):
        """Tests the flip method in AmpObject"""
        # Check invalid axis types cause TypeError
        with self.assertRaises(TypeError):
            self.amp.flip(" ")
        with self.assertRaises(TypeError):
            self.amp.flip(dict())

        # Check invalid axis values cause ValueError
        with self.assertRaises(ValueError):
            self.amp.flip(-1)
        with self.assertRaises(ValueError):
            self.amp.flip(3)