Example #1
0
    def test_setRigidBody(self):
        """
        Set and retrieve object attributes like position, velocity,
        acceleration, and rotation.
        """
        # Instantiate a Leonard.
        leo = getLeonard()

        # Test constants.
        body_new = {
            'imass': 2,
            'scale': 3,
            'cshapes': {'csempty': getCSEmpty()},
            'position': (1, 2, 5),
            'velocityLin': (8, 9, 10.5),
            'velocityRot': (9, 10, 11.5),
            'rotation': (11, 12.5, 13, 13.5)
        }

        # Create a test body.
        id_1 = 0
        body = getRigidBody(cshapes={'csempty': getCSEmpty()})

        # Add the object to the DB with ID=0.
        assert leoAPI.addCmdSpawn([(id_1, body)]).ok
        leo.processCommandsAndSync()

        # Modify the state vector for body with id_1.
        assert leoAPI.addCmdModifyBodyState(id_1, body_new).ok
        leo.processCommandsAndSync()

        # Query the body again and verify the changes are in effect.
        ret = leo.allBodies[id_1]
        assert ret.imass == body_new['imass']
        assert ret.scale == body_new['scale']
        assert np.array_equal(ret.position, body_new['position'])
        assert np.array_equal(ret.velocityLin, body_new['velocityLin'])
        assert np.array_equal(ret.velocityRot, body_new['velocityRot'])
        assert np.array_equal(ret.rotation, body_new['rotation'])

        # Query the AABB, update the collision shapes, and verify that the new
        # AABBs are in effect.
        assert leo.allAABBs[id_1] == {}

        # Modify the body state by adding a collision shape.
        body_new = {'cshapes': {'cssphere': getCSSphere(radius=1)}}
        assert body_new is not None
        assert leoAPI.addCmdModifyBodyState(id_1, body_new).ok
        leo.processCommandsAndSync()
        assert leo.allAABBs[id_1] == {'cssphere': [0, 0, 0, 1, 1, 1]}

        # Modify the body state by adding a collision shape.
        cs_a = getCSSphere(radius=1, pos=(1, 2, 3))
        cs_b = getCSSphere(radius=2, pos=(4, 5, 6))
        cshapes = {'1': cs_a, '2': getCSEmpty(), '3': cs_b}
        body_new = {'cshapes': cshapes}
        assert leoAPI.addCmdModifyBodyState(id_1, body_new).ok
        leo.processCommandsAndSync()
        correct = {'1': [1, 2, 3, 1, 1, 1], '3': [4, 5, 6, 2, 2, 2]}
        assert leo.allAABBs[id_1] == correct
Example #2
0
    def test_getset_object(self):
        """
        Send/retrieve object to/from Bullet and verify the integrity.
        """
        # Define a set of collision shapes.
        pos = (0, 1, 2)
        rot = (0, 0, 0, 1)
        cshapes = {'1': getCSEmpty(pos, rot), '2': getCSSphere(pos, rot)}
        del pos, rot

        # Create an object and serialise it.
        obj_a = getRigidBody(
            scale=3.5,
            imass=4.5,
            cshapes=cshapes,
            restitution=5.5,
            rotation=(0, 1, 0, 0),
            position=(0.2, 0.4, 0.6),
            velocityLin=(0.8, 1.0, 1.2),
            velocityRot=(1.4, 1.6, 1.8))
        assert obj_a is not None

        # Instantiate Bullet engine.
        sim = azrael.bullet_api.PyBulletDynamicsWorld(1)

        # Request an invalid object ID.
        ret = sim.getRigidBodyData(0)
        assert not ret.ok

        # Send object to Bullet and request it back.
        sim.setRigidBodyData(0, obj_a)
        ret = sim.getRigidBodyData(0)
        assert (ret.ok, ret.data) == (True, obj_a)
Example #3
0
    def test_skipEmpty(self):
        """
        Verify that _skipEmptyBodies removes all bodies that have a) exactly
        one collision shape and b) that collision shape is empty.
        """
        # Convenience: some collision shapes.
        empty = getCSEmpty()
        sphere = getCSSphere(radius=1)

        # Create several bodies with various collision shape combinations.
        bodies = {
            1: getRigidBody(cshapes={'foo': empty}),
            2: getRigidBody(cshapes={'bar': sphere}),
            3: getRigidBody(cshapes={'foo': empty, 'bar': sphere}),
            4: getRigidBody(cshapes={'foo': empty, 'bar': empty})
        }

        # Shallow copy of the original dictionary for the comparison
        # afterwards.
        bodies_copy = dict(bodies)
        ret = azrael.leonard._skipEmptyBodies(bodies_copy)

        # Verify that the function has no side effect (ie that it does not
        # alter the dictionary we pass in).
        assert bodies == bodies_copy

        # The function must have removed the first body.
        assert ret == {2: bodies[2], 3: bodies[3]}
    def test_getset_object(self):
        """
        Send/retrieve object to/from Bullet and verify the integrity.
        """
        aid = '0'

        # Define a set of collision shapes.
        pos = (0, 1, 2)
        rot = (0, 0, 0, 1)
        cshapes = {'1': getCSEmpty(pos, rot), '2': getCSSphere(pos, rot)}
        del pos, rot

        # Create a test object.
        obj_a = getRigidBody(scale=3.5,
                             imass=4.5,
                             cshapes=cshapes,
                             restitution=5.5,
                             rotation=(0, 1, 0, 0),
                             position=(0.2, 0.4, 0.6),
                             velocityLin=(0.8, 1.0, 1.2),
                             velocityRot=(1.4, 1.6, 1.8))
        assert obj_a is not None

        # Instantiate Bullet engine.
        sim = azrael.bullet_api.PyBulletDynamicsWorld(1)

        # Request an invalid object ID.
        ret = sim.getRigidBodyData(0)
        assert not ret.ok

        # Send object to Bullet and request it back.
        sim.setRigidBodyData(aid, obj_a)
        ret = sim.getRigidBodyData(aid)
        assert ret.ok and self.isBulletRbsEqual(ret.data, obj_a)
Example #5
0
    def setup_method(self, method):
        # Reset the database.
        azrael.database.init()

        # Flush the model database.
        self.dibbler.reset()

        # Insert default objects. None of them has an actual geometry but
        # their collision shapes are: none, sphere, box.
        clerk = azrael.clerk.Clerk()

        frag = {'NoName': getFragRaw()}
        rbs_empty = getRigidBody(cshapes={'csempty': getCSEmpty()})
        rbs_sphere = getRigidBody(cshapes={'cssphere': getCSSphere()})
        rbs_box = getRigidBody(cshapes={'csbox': getCSBox()})
        t1 = getTemplate('_templateEmpty', rbs=rbs_empty, fragments=frag)
        t2 = getTemplate('_templateSphere', rbs=rbs_sphere, fragments=frag)
        t3 = getTemplate('_templateBox', rbs=rbs_box, fragments=frag)
        ret = clerk.addTemplates([t1, t2, t3])
        assert ret.ok
Example #6
0
    def test_create_fetch_template(self, client_type):
        """
        Add a new object to the templateID DB and query it again.
        """
        # Get the client for this test.
        client = self.clients[client_type]

        # Request an invalid ID.
        assert not client.getTemplates(['blah']).ok

        # Clerk has default objects. This one has an empty collision shape...
        name_1 = '_templateEmpty'
        ret = client.getTemplates([name_1])
        assert ret.ok and (len(ret.data) == 1)
        assert ret.data[name_1]['template'].rbs.cshapes == {'csempty': getCSEmpty()}

        # ... this one is a sphere...
        name_2 = '_templateSphere'
        ret = client.getTemplates([name_2])
        assert ret.ok and (len(ret.data) == 1)
        assert ret.data[name_2]['template'].rbs.cshapes == {'cssphere': getCSSphere()}

        # ... and this one is a box.
        name_3 = '_templateBox'
        ret = client.getTemplates([name_3])
        assert ret.ok and (len(ret.data) == 1)
        assert ret.data[name_3]['template'].rbs.cshapes == {'csbox': getCSBox()}

        # Retrieve all three again but with a single call.
        ret = client.getTemplates([name_1, name_2, name_3])
        assert ret.ok
        assert set(ret.data.keys()) == set((name_1, name_2, name_3))
        assert ret.data[name_2]['template'].rbs.cshapes == {'cssphere': getCSSphere()}
        assert ret.data[name_3]['template'].rbs.cshapes == {'csbox': getCSBox()}
        assert ret.data[name_1]['template'].rbs.cshapes == {'csempty': getCSEmpty()}

        # Add a new object template.
        frag = {'bar': getFragRaw()}
        body = getRigidBody()
        temp_name = 't1'
        temp_orig = getTemplate(temp_name, rbs=body, fragments=frag)
        assert client.addTemplates([temp_orig]).ok

        # Fetch the just added template again and verify its content (skip the
        # geometry because it contains only meta information and will be
        # checked afterwards).
        ret = client.getTemplates([temp_name])
        assert ret.ok and (len(ret.data) == 1)
        temp_out = ret.data[temp_name]['template']
        assert temp_out.boosters == temp_orig.boosters
        assert temp_out.factories == temp_orig.factories
        assert temp_out.rbs == temp_orig.rbs

        # Fetch the geometry from the web server and verify it.
        ret = client.getTemplateGeometry(ret.data[temp_name])
        assert ret.ok
        assert ret.data['bar'] == frag['bar'].fragdata
        del ret, temp_out, temp_orig

        # Define a new object with two boosters and one factory unit.
        # The 'boosters' and 'factories' arguments are a list of named
        # tuples. Their first argument is the unit ID (Azrael does not
        # automatically assign any).
        boosters = {
            '0': types.Booster(pos=(0, 0, 0), direction=(0, 0, 1),
                               minval=0, maxval=0.5, force=0),
            '1': types.Booster(pos=(0, 0, 0), direction=(0, 0, 1),
                               minval=0, maxval=0.5, force=0),
        }
        factories = {
            '0': types.Factory(pos=(0, 0, 0), direction=(0, 0, 1),
                               templateID='_templateBox',
                               exit_speed=(0.1, 0.5))
        }

        # Attempt to query the geometry of a non-existing object.
        assert client.getFragments([1]) == (True, None, {1: None})

        # Define a new template, add it to Azrael, spawn it, and record its
        # object ID.
        body = getRigidBody(cshapes={'csbox': getCSBox()})
        temp = getTemplate('t2',
                           rbs=body,
                           fragments=frag,
                           boosters=boosters,
                           factories=factories)
        assert client.addTemplates([temp]).ok
        init = {'templateID': temp.aid,
                'rbs': {'position': (0, 0, 0)}}
        ret = client.spawn([init])
        assert ret.ok and len(ret.data) == 1
        objID = ret.data[0]

        # Retrieve- and verify the geometry of the just spawned object.
        ret = client.getFragments([objID])
        assert ret.ok
        assert ret.data[objID]['bar']['fragtype'] == 'RAW'

        # Retrieve the entire template and verify the CS and geometry, and
        # number of boosters/factories.
        ret = client.getTemplates([temp.aid])
        assert ret.ok and (len(ret.data) == 1)
        t_data = ret.data[temp.aid]['template']
        assert t_data.rbs == body
        assert t_data.boosters == temp.boosters
        assert t_data.factories == temp.factories

        # Fetch the geometry from the Web server and verify it is correct.
        ret = client.getTemplateGeometry(ret.data[temp.aid])
        assert ret.ok
        assert ret.data['bar'] == frag['bar'].fragdata
Example #7
0
    def test_compute_AABB(self):
        """
        Create some collision shapes and verify that 'computeAABBs' returns the
        correct results.
        """
        # Convenience.
        computeAABBs = azrael.leo_api.computeAABBs

        # Empty set of Collision shapes.
        assert computeAABBs({}) == (True, None, {})

        # Cubes with different side lengths. The algorithm must always pick
        # the largest side length times sqrt(3)
        for size in (0, 0.5, 1, 2):
            # The three side lengths used for the cubes.
            s1, s2, s3 = size, 2 * size, 3 * size

            # The AABB dimensions must always be the largest side lengths time
            # sqrt(3) to accommodate all rotations. However, Azreal adds some
            # slack and uses sqrt(3.1).
            v = s3 * np.sqrt(3.1)
            correct = (1, 2, 3, v, v, v)
            pos = correct[:3]

            cs = getCSBox(dim=(s1, s2, s3), pos=pos)
            assert computeAABBs({'1': cs}) == (True, None, {'1': correct})

            cs = getCSBox(dim=(s2, s3, s1), pos=pos)
            assert computeAABBs({'2': cs}) == (True, None, {'2': correct})

            cs = getCSBox(dim=(s3, s1, s2), pos=pos)
            assert computeAABBs({'3': cs}) == (True, None, {'3': correct})

        # The AABB for a sphere must always exactly bound the sphere.
        for radius in (0, 0.5, 1, 2):
            correct = (0, 0, 0, radius, radius, radius)
            cs = getCSSphere(radius=radius)
            assert computeAABBs({'': cs}) == (True, None, {'': correct})

        # Sphere at origin but with a rotation: must remain at origin.
        pos, rot = (0, 0, 0), (np.sqrt(2), 0, 0, np.sqrt(2))
        correct = {'': (0, 0, 0, 1, 1, 1)}
        cs = getCSSphere(radius=1, pos=pos, rot=rot)
        assert computeAABBs({'': cs}) == (True, None, correct)

        # Sphere at y=1 and rotated 180degrees around x-axis. This must result
        # in a sphere at y=-1.
        pos, rot = (0, 1, 0), (1, 0, 0, 0)
        correct = {'': (0, -1, 0, 1, 1, 1)}
        cs = getCSSphere(radius=1, pos=pos, rot=rot)
        assert computeAABBs({'': cs}) == (True, None, correct)

        # Sphere at y=1 and rotated 90degrees around x-axis. This must move the
        # sphere onto the z-axis, ie to position (x, y, z) = (0, 0, 1). Due to
        # roundoff errors it will be necessary to test with np.allclose instead
        # for exact equality.
        pos = (0, 1, 0)
        rot = (1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2))
        correct = {'': (0, 0, 1, 1, 1, 1)}
        cs = getCSSphere(radius=1, pos=pos, rot=rot)
        ret = computeAABBs({'': cs})
        assert ret.ok
        assert np.allclose(ret.data[''], correct[''])

        # Use an empty shape. This must not return any AABB.
        cs = getCSEmpty()
        assert computeAABBs({'': cs}) == (True, None, {})

        # Pass in multiple collision shapes, namely [box, empty, sphere]. This
        # must return 2 collision shapes because the empty one is skipped.
        cs = {'1': getCSSphere(), '2': getCSEmpty(), '3': getCSBox()}
        correct = {
            '1': (0, 0, 0, 1, 1, 1),
            '3': (0, 0, 0, np.sqrt(3.1), np.sqrt(3.1), np.sqrt(3.1))
        }
        assert computeAABBs(cs) == (True, None, correct)

        # Pass in invalid arguments. This must return with an error.
        assert not computeAABBs({'x': (1, 2)}).ok
Example #8
0
    def test_setRigidBody(self):
        """
        Set and retrieve object attributes like position, velocity,
        acceleration, and rotation.
        """
        # Instantiate a Leonard.
        leo = getLeonard()

        # Test constants.
        body_new = {
            'imass': 2,
            'scale': 3,
            'cshapes': {
                'csempty': getCSEmpty()
            },
            'position': (1, 2, 5),
            'velocityLin': (8, 9, 10.5),
            'velocityRot': (9, 10, 11.5),
            'rotation': (11, 12.5, 13, 13.5)
        }

        # Create a test body.
        id_1 = '0'
        body = getRigidBody(cshapes={'csempty': getCSEmpty()})

        # Add the object to the DB with ID=0.
        assert leoAPI.addCmdSpawn([(id_1, body)]).ok
        leo.processCommandsAndSync()

        # Modify the state vector for body with id_1.
        assert leoAPI.addCmdModifyBodyState(id_1, body_new).ok
        leo.processCommandsAndSync()

        # Query the body again and verify the changes are in effect.
        ret = leo.allBodies[id_1]
        assert ret.imass == body_new['imass']
        assert ret.scale == body_new['scale']
        assert np.array_equal(ret.position, body_new['position'])
        assert np.array_equal(ret.velocityLin, body_new['velocityLin'])
        assert np.array_equal(ret.velocityRot, body_new['velocityRot'])
        assert np.array_equal(ret.rotation, body_new['rotation'])

        # Query the AABB, update the collision shapes, and verify that the new
        # AABBs are in effect.
        assert leo.allAABBs[id_1] == {}

        # Modify the body state by adding a collision shape.
        body_new = {'cshapes': {'cssphere': getCSSphere(radius=1)}}
        assert body_new is not None
        assert leoAPI.addCmdModifyBodyState(id_1, body_new).ok
        leo.processCommandsAndSync()
        assert leo.allAABBs[id_1] == {'cssphere': [0, 0, 0, 1, 1, 1]}

        # Modify the body state by adding a collision shape.
        cs_a = getCSSphere(radius=1, pos=(1, 2, 3))
        cs_b = getCSSphere(radius=2, pos=(4, 5, 6))
        cshapes = {'1': cs_a, '2': getCSEmpty(), '3': cs_b}
        body_new = {'cshapes': cshapes}
        assert leoAPI.addCmdModifyBodyState(id_1, body_new).ok
        leo.processCommandsAndSync()
        correct = {'1': [1, 2, 3, 1, 1, 1], '3': [4, 5, 6, 2, 2, 2]}
        assert leo.allAABBs[id_1] == correct
Example #9
0
    def test_compute_AABB(self):
        """
        Create some collision shapes and verify that 'computeAABBs' returns the
        correct results.
        """
        # Convenience.
        computeAABBs = azrael.leo_api.computeAABBs

        # Empty set of Collision shapes.
        assert computeAABBs({}) == (True, None, {})

        # Cubes with different side lengths. The algorithm must always pick
        # the largest side length times sqrt(3)
        for size in (0, 0.5, 1, 2):
            # The three side lengths used for the cubes.
            s1, s2, s3 = size, 2 * size, 3 * size

            # The AABB dimensions must always be the largest side lengths time
            # sqrt(3) to accommodate all rotations. However, Azreal adds some
            # slack and uses sqrt(3.1).
            v = s3 * np.sqrt(3.1)
            correct = (1, 2, 3, v, v, v)
            pos = correct[:3]

            cs = getCSBox(dim=(s1, s2, s3), pos=pos)
            assert computeAABBs({'1': cs}) == (True, None, {'1': correct})

            cs = getCSBox(dim=(s2, s3, s1), pos=pos)
            assert computeAABBs({'2': cs}) == (True, None, {'2': correct})

            cs = getCSBox(dim=(s3, s1, s2), pos=pos)
            assert computeAABBs({'3': cs}) == (True, None, {'3': correct})

        # The AABB for a sphere must always exactly bound the sphere.
        for radius in (0, 0.5, 1, 2):
            correct = (0, 0, 0, radius, radius, radius)
            cs = getCSSphere(radius=radius)
            assert computeAABBs({'': cs}) == (True, None, {'': correct})

        # Sphere at origin but with a rotation: must remain at origin.
        pos, rot = (0, 0, 0), (np.sqrt(2), 0, 0, np.sqrt(2))
        correct = {'': (0, 0, 0, 1, 1, 1)}
        cs = getCSSphere(radius=1, pos=pos, rot=rot)
        assert computeAABBs({'': cs}) == (True, None, correct)

        # Sphere at y=1 and rotated 180degrees around x-axis. This must result
        # in a sphere at y=-1.
        pos, rot = (0, 1, 0), (1, 0, 0, 0)
        correct = {'': (0, -1, 0, 1, 1, 1)}
        cs = getCSSphere(radius=1, pos=pos, rot=rot)
        assert computeAABBs({'': cs}) == (True, None, correct)

        # Sphere at y=1 and rotated 90degrees around x-axis. This must move the
        # sphere onto the z-axis, ie to position (x, y, z) = (0, 0, 1). Due to
        # roundoff errors it will be necessary to test with np.allclose instead
        # for exact equality.
        pos = (0, 1, 0)
        rot = (1 / np.sqrt(2), 0, 0, 1 / np.sqrt(2))
        correct = {'': (0, 0, 1, 1, 1, 1)}
        cs = getCSSphere(radius=1, pos=pos, rot=rot)
        ret = computeAABBs({'': cs})
        assert ret.ok
        assert np.allclose(ret.data[''], correct[''])

        # Use an empty shape. This must not return any AABB.
        cs = getCSEmpty()
        assert computeAABBs({'': cs}) == (True, None, {})

        # Pass in multiple collision shapes, namely [box, empty, sphere]. This
        # must return 2 collision shapes because the empty one is skipped.
        cs = {'1': getCSSphere(), '2': getCSEmpty(), '3': getCSBox()}
        correct = {
            '1': (0, 0, 0, 1, 1, 1),
            '3': (0, 0, 0, np.sqrt(3.1), np.sqrt(3.1), np.sqrt(3.1))
        }
        assert computeAABBs(cs) == (True, None, correct)

        # Pass in invalid arguments. This must return with an error.
        assert not computeAABBs({'x': (1, 2)}).ok