Module tests.moog.physics.test_collisions
Tests for moog/physics/collisions.py.
To run this test, navigate to this directory and run
$ pytest test_collisions.py --capture=tee-sys
Note: The –capture=tee-sys routes print statements to stdout, which is useful for debugging.
Alternatively, to run this test and any others, navigate to any parent directory and simply run
$ pytest --capture=tee-sys
This will run all test_* files in children directories.
Classes
class MatplotlibUI (image_size=128, anti_aliasing=3)
-
Expand source code
class MatplotlibUI(): """Matplotlib UI. This can be used to visualize test conditions. """ def __init__(self, image_size=128, anti_aliasing=3): """Constructor.""" plt.ion() self._fig, self._ax = plt.subplots() self._ax.axis('off') self._imshow = self._ax.imshow( np.zeros((image_size, image_size, 3)), interpolation='none') self._renderer = pil_renderer.PILRenderer( image_size=(image_size, image_size), anti_aliasing=anti_aliasing) self._ax.set_xticks([]) self._ax.set_yticks([]) plt.show(block=False) plt.pause(0.1) def _render(self, state): """Renderer a state (ordereddict of iterables of sprites).""" self._imshow.set_data(self._renderer(state)) plt.draw() plt.pause(0.1) def _simulate_video(self, sprites, force, steps, symmetric): """Simulate and display video.""" for _ in range(steps): self._render(collections.OrderedDict([('', sprites)])) _apply_pairwise_force(sprites, force, symmetric=symmetric) for s in sprites: s.update_pos_from_vel(delta_t=1.) # Print the true positions, velocities, and angular velocities of the # sprites, rounded to 4 decimal places. for i, s in enumerate(sprites): print('') print(f'Sprite {i}') print(f'position: [{s.position[0]:.4f}, {s.position[1]:.4f}]') print(f'velocity: [{s.velocity[0]:.4f}, {s.velocity[1]:.4f}]') print(f'angle_vel: {s.angle_vel:.4f}')
Matplotlib UI.
This can be used to visualize test conditions.
Constructor.
class TestCollisions
-
Expand source code
class TestCollisions(): """Test collisions.""" @pytest.mark.parametrize( ('init_pos_0, init_vel_0, out_pos_0, out_vel_0, out_pos_1, out_vel_1, ' 'elasticity, symmetric'), [ # Head-on, asymmetric ([0.5, 0.35], [0., 0.], [0.5, 0.35], [0., 0.], [0.5, 0.4827], [0., 0.01], 1., False), # Head-on, symmetric ([0.5, 0.35], [0., 0.], [0.5, 0.3287], [0., -0.01], [0.5, 0.4613], [0., 0.], 1., True), # Head-on, symmetric, semi-elastic ([0.5, 0.35], [0., 0.], [0.5, 0.3337], [0., -0.0075], [0.5, 0.4563], [0., -0.0025], 0.5, True), # Head-on, symmetric, inelastic ([0.5, 0.35], [0., 0.], [0.5, 0.3387], [0., -0.005], [0.5, 0.4513], [0., -0.005], 0., True), # Head-on, symmetric, both moving ([0.5, 0.35], [0., 0.01], [0.5, 0.3287], [0., -0.01], [0.5, 0.5213], [0., 0.01], 1., True), # Offset, asymmetric ([0.44, 0.37], [0., 0.], [0.44, 0.37], [0., 0.], [0.5217, 0.4699], [0.0095, 0.0031], 1., False), # Offset, symmetric ([0.44, 0.37], [0., 0.], [0.4291, 0.3550], [-0.0048, -0.0065], [0.5109, 0.4550], [0.0048, -0.0035], 1., True), # Offset, symmetric, semi-elastic ([0.44, 0.37], [0., 0.], [0.4315, 0.3583], [-0.0036, -0.0049], [0.5085, 0.4517], [0.0036, -0.0051], 0.5, True), # Offset, symmetric, both moving ([0.44, 0.37], [0., 0.01], [0.4006, 0.3758], [-0.0095, -0.0031], [0.5394, 0.4942], [0.0095, 0.0031], 1., True), # Offset, symmetric, both moving, diagonal motion ([0.43, 0.36], [0.015, 0.01], [0.4793, 0.3286], [0.0051, -0.0123], [0.5407, 0.5314], [0.0099, 0.0123], 1., True), ] ) def testCirclesSameMass(self, init_pos_0, init_vel_0, out_pos_0, out_vel_0, out_pos_1, out_vel_1, elasticity, symmetric, steps=6, plot=False): """Two circles with same size and mass colliding. Set plot = True to display videos of the test conditions. """ force = collisions.Collision( elasticity=elasticity, symmetric=symmetric, update_angle_vel=False) sprite_0 = sprite.Sprite( x=init_pos_0[0], y=init_pos_0[1], scale=0.1, shape='circle', x_vel=init_vel_0[0], y_vel=init_vel_0[1], c1=255) sprite_1 = sprite.Sprite( x=0.5, y=0.5, scale=0.1, shape='circle', y_vel=-0.01, c0=255) if plot: MatplotlibUI()._simulate_video( [sprite_0, sprite_1], force, steps, symmetric) else: for _ in range(steps): _apply_pairwise_force( [sprite_0, sprite_1], force, symmetric=symmetric) sprite_0.update_pos_from_vel(delta_t=1.) sprite_1.update_pos_from_vel(delta_t=1.) assert np.allclose(sprite_0.position, out_pos_0, atol=_ATOL) assert np.allclose(sprite_0.velocity, out_vel_0, atol=_ATOL) assert np.allclose(sprite_1.position, out_pos_1, atol=_ATOL) assert np.allclose(sprite_1.velocity, out_vel_1, atol=_ATOL) @pytest.mark.parametrize( 'init_pos_0, init_vel_0, out_pos_0, out_vel_0, out_pos_1, out_vel_1', [ # Head-on ([0.5, 0.35], [0., 0.], [0.5, 0.3220], [0., -0.0133], [0.5, 0.4547], [0., -0.0033]), # Head-on, semi-elastic ([0.5, 0.35], [0., 0.], [0.5, 0.3220], [0., -0.0133], [0.5, 0.4547], [0., -0.0033]), # Offset, symmetric, both moving ([0.44, 0.37], [0., 0.01], [0.3879, 0.3583], [-0.0127, -0.0075], [0.5267, 0.4768], [0.0063, -0.0013]), # Offset, symmetric, both moving, diagonal motion ([0.43, 0.36], [0.015, 0.01], [0.4661, 0.2989], [0.0018, -0.0197], [0.5275, 0.5017], [0.0066, 0.0048]), ] ) def testCirclesDifferentMass(self, init_pos_0, init_vel_0, out_pos_0, out_vel_0, out_pos_1, out_vel_1, steps=6, plot=False): """Two circles with same size and different massed colliding. Set plot = True to display videos of the test conditions. """ force = collisions.Collision( elasticity=1., symmetric=True, update_angle_vel=False) sprite_0 = sprite.Sprite( x=init_pos_0[0], y=init_pos_0[1], scale=0.1, shape='circle', x_vel=init_vel_0[0], y_vel=init_vel_0[1], c1=255) sprite_1 = sprite.Sprite( x=0.5, y=0.5, scale=0.1, shape='circle', y_vel=-0.01, c0=255, mass=2.) if plot: MatplotlibUI()._simulate_video( [sprite_0, sprite_1], force, steps, symmetric=True) else: for _ in range(steps): _apply_pairwise_force( [sprite_0, sprite_1], force, symmetric=True) sprite_0.update_pos_from_vel(delta_t=1.) sprite_1.update_pos_from_vel(delta_t=1.) assert np.allclose(sprite_0.position, out_pos_0, atol=_ATOL) assert np.allclose(sprite_0.velocity, out_vel_0, atol=_ATOL) assert np.allclose(sprite_1.position, out_pos_1, atol=_ATOL) assert np.allclose(sprite_1.velocity, out_vel_1, atol=_ATOL) @pytest.mark.parametrize( ('init_angle_vel_0, out_pos_0, out_vel_0, out_angle_vel_0, out_pos_1, ' 'out_vel_1, out_angle_vel_1, elasticity, update_angle_vel'), [ # No angular velocity, elastic, no update_angle_vel (0., [0.5064, 0.6776], [-0.0044, 0.0024], 0., [0.6369, 0.5358], [0.0044, -0.0024], 0., 1., False), # No angular velocity, elastic (0., [0.5411, 0.6689], [0.0025, 0.0006], -0.0911, [0.6022, 0.5444], [-0.0025, -0.0006], 0.0362, 1., True), # No angular velocity, semi-elastic (0., [0.5442, 0.6681], [0.0031, 0.0005], -0.0683, [0.5991, 0.5452], [-0.0031, -0.0005], 0.0271, 0.5, True), # Positive angular velocity, elastic (0.1, [0.4950, 0.6804], [-0.0021, 0.0018], -0.1215, [0.6483, 0.5329], [0.0021, -0.0018], 0.0720, 1., True), # Negative angular velocity, elastic (-0.02, [0.5486, 0.6670], [0.0035, 0.0004], -0.0800, [0.5947, 0.5463], [-0.0035, -0.0004], 0.0250, 1., True), ] ) def testTriangles(self, init_angle_vel_0, out_pos_0, out_vel_0, out_angle_vel_0, out_pos_1, out_vel_1, out_angle_vel_1, elasticity, update_angle_vel, steps=10, plot=False): """Two irregular triangles. Set plot = True to display videos of the test conditions. """ force = collisions.Collision( elasticity=elasticity, symmetric=True, update_angle_vel=update_angle_vel) shape_0 = np.array([[1, 1], [1, 3], [-2, -2]]) sprite_0 = sprite.Sprite( x=0.5, y=0, scale=0.05, shape=shape_0, x_vel=0.005, y_vel=0., c0=255, angle=1., angle_vel=init_angle_vel_0) shape_1 = np.array([[2, 1], [0, 1], [-1, -3]]) sprite_1 = sprite.Sprite( x=0.31, y=0.88, scale=0.05, shape=shape_1, x_vel=-0.005, y_vel=0., c1=255) if plot: MatplotlibUI()._simulate_video( [sprite_0, sprite_1], force, steps, symmetric=True) else: for _ in range(steps): _apply_pairwise_force( [sprite_0, sprite_1], force, symmetric=True) sprite_0.update_pos_from_vel(delta_t=1.) sprite_1.update_pos_from_vel(delta_t=1.) assert np.allclose(sprite_0.position, out_pos_0, atol=_ATOL) assert np.allclose(sprite_0.velocity, out_vel_0, atol=_ATOL) assert np.allclose(sprite_0.angle_vel, out_angle_vel_0, atol=_ATOL) assert np.allclose(sprite_1.position, out_pos_1, atol=_ATOL) assert np.allclose(sprite_1.velocity, out_vel_1, atol=_ATOL) assert np.allclose(sprite_1.angle_vel, out_angle_vel_1, atol=_ATOL)
Test collisions.
Methods
def testCirclesDifferentMass(self,
init_pos_0,
init_vel_0,
out_pos_0,
out_vel_0,
out_pos_1,
out_vel_1,
steps=6,
plot=False)-
Expand source code
@pytest.mark.parametrize( 'init_pos_0, init_vel_0, out_pos_0, out_vel_0, out_pos_1, out_vel_1', [ # Head-on ([0.5, 0.35], [0., 0.], [0.5, 0.3220], [0., -0.0133], [0.5, 0.4547], [0., -0.0033]), # Head-on, semi-elastic ([0.5, 0.35], [0., 0.], [0.5, 0.3220], [0., -0.0133], [0.5, 0.4547], [0., -0.0033]), # Offset, symmetric, both moving ([0.44, 0.37], [0., 0.01], [0.3879, 0.3583], [-0.0127, -0.0075], [0.5267, 0.4768], [0.0063, -0.0013]), # Offset, symmetric, both moving, diagonal motion ([0.43, 0.36], [0.015, 0.01], [0.4661, 0.2989], [0.0018, -0.0197], [0.5275, 0.5017], [0.0066, 0.0048]), ] ) def testCirclesDifferentMass(self, init_pos_0, init_vel_0, out_pos_0, out_vel_0, out_pos_1, out_vel_1, steps=6, plot=False): """Two circles with same size and different massed colliding. Set plot = True to display videos of the test conditions. """ force = collisions.Collision( elasticity=1., symmetric=True, update_angle_vel=False) sprite_0 = sprite.Sprite( x=init_pos_0[0], y=init_pos_0[1], scale=0.1, shape='circle', x_vel=init_vel_0[0], y_vel=init_vel_0[1], c1=255) sprite_1 = sprite.Sprite( x=0.5, y=0.5, scale=0.1, shape='circle', y_vel=-0.01, c0=255, mass=2.) if plot: MatplotlibUI()._simulate_video( [sprite_0, sprite_1], force, steps, symmetric=True) else: for _ in range(steps): _apply_pairwise_force( [sprite_0, sprite_1], force, symmetric=True) sprite_0.update_pos_from_vel(delta_t=1.) sprite_1.update_pos_from_vel(delta_t=1.) assert np.allclose(sprite_0.position, out_pos_0, atol=_ATOL) assert np.allclose(sprite_0.velocity, out_vel_0, atol=_ATOL) assert np.allclose(sprite_1.position, out_pos_1, atol=_ATOL) assert np.allclose(sprite_1.velocity, out_vel_1, atol=_ATOL)
Two circles with same size and different massed colliding.
Set plot = True to display videos of the test conditions.
def testCirclesSameMass(self,
init_pos_0,
init_vel_0,
out_pos_0,
out_vel_0,
out_pos_1,
out_vel_1,
elasticity,
symmetric,
steps=6,
plot=False)-
Expand source code
@pytest.mark.parametrize( ('init_pos_0, init_vel_0, out_pos_0, out_vel_0, out_pos_1, out_vel_1, ' 'elasticity, symmetric'), [ # Head-on, asymmetric ([0.5, 0.35], [0., 0.], [0.5, 0.35], [0., 0.], [0.5, 0.4827], [0., 0.01], 1., False), # Head-on, symmetric ([0.5, 0.35], [0., 0.], [0.5, 0.3287], [0., -0.01], [0.5, 0.4613], [0., 0.], 1., True), # Head-on, symmetric, semi-elastic ([0.5, 0.35], [0., 0.], [0.5, 0.3337], [0., -0.0075], [0.5, 0.4563], [0., -0.0025], 0.5, True), # Head-on, symmetric, inelastic ([0.5, 0.35], [0., 0.], [0.5, 0.3387], [0., -0.005], [0.5, 0.4513], [0., -0.005], 0., True), # Head-on, symmetric, both moving ([0.5, 0.35], [0., 0.01], [0.5, 0.3287], [0., -0.01], [0.5, 0.5213], [0., 0.01], 1., True), # Offset, asymmetric ([0.44, 0.37], [0., 0.], [0.44, 0.37], [0., 0.], [0.5217, 0.4699], [0.0095, 0.0031], 1., False), # Offset, symmetric ([0.44, 0.37], [0., 0.], [0.4291, 0.3550], [-0.0048, -0.0065], [0.5109, 0.4550], [0.0048, -0.0035], 1., True), # Offset, symmetric, semi-elastic ([0.44, 0.37], [0., 0.], [0.4315, 0.3583], [-0.0036, -0.0049], [0.5085, 0.4517], [0.0036, -0.0051], 0.5, True), # Offset, symmetric, both moving ([0.44, 0.37], [0., 0.01], [0.4006, 0.3758], [-0.0095, -0.0031], [0.5394, 0.4942], [0.0095, 0.0031], 1., True), # Offset, symmetric, both moving, diagonal motion ([0.43, 0.36], [0.015, 0.01], [0.4793, 0.3286], [0.0051, -0.0123], [0.5407, 0.5314], [0.0099, 0.0123], 1., True), ] ) def testCirclesSameMass(self, init_pos_0, init_vel_0, out_pos_0, out_vel_0, out_pos_1, out_vel_1, elasticity, symmetric, steps=6, plot=False): """Two circles with same size and mass colliding. Set plot = True to display videos of the test conditions. """ force = collisions.Collision( elasticity=elasticity, symmetric=symmetric, update_angle_vel=False) sprite_0 = sprite.Sprite( x=init_pos_0[0], y=init_pos_0[1], scale=0.1, shape='circle', x_vel=init_vel_0[0], y_vel=init_vel_0[1], c1=255) sprite_1 = sprite.Sprite( x=0.5, y=0.5, scale=0.1, shape='circle', y_vel=-0.01, c0=255) if plot: MatplotlibUI()._simulate_video( [sprite_0, sprite_1], force, steps, symmetric) else: for _ in range(steps): _apply_pairwise_force( [sprite_0, sprite_1], force, symmetric=symmetric) sprite_0.update_pos_from_vel(delta_t=1.) sprite_1.update_pos_from_vel(delta_t=1.) assert np.allclose(sprite_0.position, out_pos_0, atol=_ATOL) assert np.allclose(sprite_0.velocity, out_vel_0, atol=_ATOL) assert np.allclose(sprite_1.position, out_pos_1, atol=_ATOL) assert np.allclose(sprite_1.velocity, out_vel_1, atol=_ATOL)
Two circles with same size and mass colliding.
Set plot = True to display videos of the test conditions.
def testTriangles(self,
init_angle_vel_0,
out_pos_0,
out_vel_0,
out_angle_vel_0,
out_pos_1,
out_vel_1,
out_angle_vel_1,
elasticity,
update_angle_vel,
steps=10,
plot=False)-
Expand source code
@pytest.mark.parametrize( ('init_angle_vel_0, out_pos_0, out_vel_0, out_angle_vel_0, out_pos_1, ' 'out_vel_1, out_angle_vel_1, elasticity, update_angle_vel'), [ # No angular velocity, elastic, no update_angle_vel (0., [0.5064, 0.6776], [-0.0044, 0.0024], 0., [0.6369, 0.5358], [0.0044, -0.0024], 0., 1., False), # No angular velocity, elastic (0., [0.5411, 0.6689], [0.0025, 0.0006], -0.0911, [0.6022, 0.5444], [-0.0025, -0.0006], 0.0362, 1., True), # No angular velocity, semi-elastic (0., [0.5442, 0.6681], [0.0031, 0.0005], -0.0683, [0.5991, 0.5452], [-0.0031, -0.0005], 0.0271, 0.5, True), # Positive angular velocity, elastic (0.1, [0.4950, 0.6804], [-0.0021, 0.0018], -0.1215, [0.6483, 0.5329], [0.0021, -0.0018], 0.0720, 1., True), # Negative angular velocity, elastic (-0.02, [0.5486, 0.6670], [0.0035, 0.0004], -0.0800, [0.5947, 0.5463], [-0.0035, -0.0004], 0.0250, 1., True), ] ) def testTriangles(self, init_angle_vel_0, out_pos_0, out_vel_0, out_angle_vel_0, out_pos_1, out_vel_1, out_angle_vel_1, elasticity, update_angle_vel, steps=10, plot=False): """Two irregular triangles. Set plot = True to display videos of the test conditions. """ force = collisions.Collision( elasticity=elasticity, symmetric=True, update_angle_vel=update_angle_vel) shape_0 = np.array([[1, 1], [1, 3], [-2, -2]]) sprite_0 = sprite.Sprite( x=0.5, y=0, scale=0.05, shape=shape_0, x_vel=0.005, y_vel=0., c0=255, angle=1., angle_vel=init_angle_vel_0) shape_1 = np.array([[2, 1], [0, 1], [-1, -3]]) sprite_1 = sprite.Sprite( x=0.31, y=0.88, scale=0.05, shape=shape_1, x_vel=-0.005, y_vel=0., c1=255) if plot: MatplotlibUI()._simulate_video( [sprite_0, sprite_1], force, steps, symmetric=True) else: for _ in range(steps): _apply_pairwise_force( [sprite_0, sprite_1], force, symmetric=True) sprite_0.update_pos_from_vel(delta_t=1.) sprite_1.update_pos_from_vel(delta_t=1.) assert np.allclose(sprite_0.position, out_pos_0, atol=_ATOL) assert np.allclose(sprite_0.velocity, out_vel_0, atol=_ATOL) assert np.allclose(sprite_0.angle_vel, out_angle_vel_0, atol=_ATOL) assert np.allclose(sprite_1.position, out_pos_1, atol=_ATOL) assert np.allclose(sprite_1.velocity, out_vel_1, atol=_ATOL) assert np.allclose(sprite_1.angle_vel, out_angle_vel_1, atol=_ATOL)
Two irregular triangles.
Set plot = True to display videos of the test conditions.