using System; using Azimuth; using UnitTestSharp; namespace Konis.UnitTests { public class ParticleMotionTests : TestFixture { private static ParticleStore StoreOf(int columnCount, params Particle[] particles) { var store = new ParticleStore(capacity: particles.Length, columnCount: columnCount); foreach (Particle particle in particles) { store.Add(particle); } return store; } private static Particle MovingAt(float velocityX, float velocityY, float velocityZ) { return new Particle { VelocityX = velocityX, VelocityY = velocityY, VelocityZ = velocityZ }; } public class ApplyGravityTests : TestFixture { public void Constant_PullsEveryParticleDownByTheSameAmount() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(10, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(0, 0, 10), MovingAt(0, 0, -2)); ParticleMotion.ApplyGravity(store, gravity, Axis.Z, seconds: 0.5f); CheckEqual(5f, store.Items[0].VelocityZ); CheckEqual(-7f, store.Items[1].VelocityZ); } public void PerParticle_PullsEachParticleByItsOwnGravity() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(new Variation(0, 8), ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(0, 0, 10), MovingAt(0, 0, 10)); store.Columns[0][0] = 2; store.Columns[0][1] = 4; ParticleMotion.ApplyGravity(store, gravity, Axis.Z, seconds: 0.5f); CheckEqual(9f, store.Items[0].VelocityZ); CheckEqual(8f, store.Items[1].VelocityZ); } public void Zero_LeavesEveryVelocityAlone() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(Variation.None, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(1, 2, 3)); ParticleMotion.ApplyGravity(store, gravity, Axis.Z, seconds: 1f); CheckEqual(3f, store.Items[0].VelocityZ); } public void NegativeGravity_PushesUp() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(-4, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(0, 0, 0)); ParticleMotion.ApplyGravity(store, gravity, Axis.Z, seconds: 0.5f); CheckEqual(2f, store.Items[0].VelocityZ); } public void LeavesTheSidewaysVelocityAlone() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(10, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(1, 2, 3)); ParticleMotion.ApplyGravity(store, gravity, Axis.Z, seconds: 1f); CheckEqual(1f, store.Items[0].VelocityX); CheckEqual(2f, store.Items[0].VelocityY); } public void Constant_PullsOnlyTheUpAxisX() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(10, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(1, 2, 3)); ParticleMotion.ApplyGravity(store, gravity, Axis.X, seconds: 1f); CheckEqual(-9f, store.Items[0].VelocityX); CheckEqual(2f, store.Items[0].VelocityY); CheckEqual(3f, store.Items[0].VelocityZ); } public void Constant_PullsOnlyTheUpAxisY() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(10, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(1, 2, 3)); ParticleMotion.ApplyGravity(store, gravity, Axis.Y, seconds: 1f); CheckEqual(1f, store.Items[0].VelocityX); CheckEqual(-8f, store.Items[0].VelocityY); CheckEqual(3f, store.Items[0].VelocityZ); } public void PerParticle_PullsOnlyTheUpAxisX() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(new Variation(0, 8), ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(1, 2, 3)); store.Columns[0][0] = 4; ParticleMotion.ApplyGravity(store, gravity, Axis.X, seconds: 0.5f); CheckEqual(-1f, store.Items[0].VelocityX); CheckEqual(2f, store.Items[0].VelocityY); CheckEqual(3f, store.Items[0].VelocityZ); } public void PerParticle_PullsOnlyTheUpAxisY() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(new Variation(0, 8), ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(1, 2, 3)); store.Columns[0][0] = 4; ParticleMotion.ApplyGravity(store, gravity, Axis.Y, seconds: 0.5f); CheckEqual(1f, store.Items[0].VelocityX); CheckEqual(0f, store.Items[0].VelocityY); CheckEqual(3f, store.Items[0].VelocityZ); } public void ConstantWithAnUndefinedAxis_Throws() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(10, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(1, 2, 3)); CheckThrow(typeof(ArgumentOutOfRangeException)); ParticleMotion.ApplyGravity(store, gravity, (Axis)7, seconds: 1f); } public void PerParticleWithAnUndefinedAxis_Throws() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(new Variation(0, 8), ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(1, 2, 3)); CheckThrow(typeof(ArgumentOutOfRangeException)); ParticleMotion.ApplyGravity(store, gravity, (Axis)7, seconds: 1f); } public void OnlyTouchesLiveParticles() { int columnCount = 0; ParticleChannel gravity = ParticleChannel.Of(new Variation(0, 8), ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(0, 0, 10), MovingAt(0, 0, 10)); store.RemoveAt(1); ParticleMotion.ApplyGravity(store, gravity, Axis.Z, seconds: 1f); CheckEqual(10f, store.Items[1].VelocityZ); } } public class ApplyDragTests : TestFixture { public void Constant_ScalesEveryVelocityComponentByTheSameFactor() { int columnCount = 0; ParticleChannel drag = ParticleChannel.Of(0.5, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(2, 4, 6)); ParticleMotion.ApplyDrag(store, drag, seconds: 1f); CheckEqual(1f, store.Items[0].VelocityX); CheckEqual(2f, store.Items[0].VelocityY); CheckEqual(3f, store.Items[0].VelocityZ); } public void PerParticle_ScalesEachParticleByItsOwnFactor() { int columnCount = 0; ParticleChannel drag = ParticleChannel.Of(new Variation(0, 1), ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(4, 4, 4), MovingAt(4, 4, 4)); store.Columns[0][0] = 0.5f; store.Columns[0][1] = 0.25f; ParticleMotion.ApplyDrag(store, drag, seconds: 1f); CheckEqual(2f, store.Items[0].VelocityX); CheckEqual(3f, store.Items[1].VelocityX); CheckEqual(3f, store.Items[1].VelocityZ); } public void Zero_LeavesEveryVelocityAlone() { int columnCount = 0; ParticleChannel drag = ParticleChannel.Of(Variation.None, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(1, 2, 3)); ParticleMotion.ApplyDrag(store, drag, seconds: 1f); CheckEqual(1f, store.Items[0].VelocityX); } public void ConstantDragThatWouldReverseTheVelocity_StopsTheParticle() { int columnCount = 0; ParticleChannel drag = ParticleChannel.Of(4, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(2, 4, 6)); ParticleMotion.ApplyDrag(store, drag, seconds: 1f); CheckEqual(0f, store.Items[0].VelocityX); CheckEqual(0f, store.Items[0].VelocityY); CheckEqual(0f, store.Items[0].VelocityZ); } public void PerParticleDragThatWouldReverseTheVelocity_StopsTheParticle() { int columnCount = 0; ParticleChannel drag = ParticleChannel.Of(new Variation(0, 8), ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(2, 4, 6)); store.Columns[0][0] = 4; ParticleMotion.ApplyDrag(store, drag, seconds: 1f); CheckEqual(0f, store.Items[0].VelocityX); CheckEqual(0f, store.Items[0].VelocityZ); } public void OnlyTouchesLiveParticles() { int columnCount = 0; ParticleChannel drag = ParticleChannel.Of(0.5, ref columnCount); ParticleStore store = StoreOf(columnCount, MovingAt(2, 2, 2), MovingAt(2, 2, 2)); store.RemoveAt(1); ParticleMotion.ApplyDrag(store, drag, seconds: 1f); CheckEqual(2f, store.Items[1].VelocityX); } } public class IntegrateTests : TestFixture { public void MovesEachParticleAlongItsVelocityAndAgesIt() { var particle = new Particle { PositionX = 1, PositionY = 2, PositionZ = 3, VelocityX = 10, VelocityY = 20, VelocityZ = 30, Age = 1, }; ParticleStore store = StoreOf(0, particle); ParticleMotion.Integrate(store, seconds: 0.5f); CheckEqual(6f, store.Items[0].PositionX); CheckEqual(12f, store.Items[0].PositionY); CheckEqual(18f, store.Items[0].PositionZ); CheckEqual(1.5f, store.Items[0].Age); } public void OnlyTouchesLiveParticles() { ParticleStore store = StoreOf(0, MovingAt(1, 1, 1), MovingAt(1, 1, 1)); store.RemoveAt(1); ParticleMotion.Integrate(store, seconds: 1f); CheckEqual(0f, store.Items[1].PositionX); CheckEqual(0f, store.Items[1].Age); } } public class RemoveExpiredTests : TestFixture { private static Particle Labeled(float label, float age, float lifetime) { return new Particle { PositionX = label, Age = age, Lifetime = lifetime }; } public void RemovesParticlesAtOrPastTheirLifetimeAndReturnsHowManyThatWas() { ParticleStore store = StoreOf(0, Labeled(0, 0.5f, 1), Labeled(1, 1, 1), Labeled(2, 2, 1)); CheckEqual(2, ParticleMotion.RemoveExpired(store)); CheckEqual(1, store.Count); CheckEqual(0f, store.Items[0].PositionX); } public void KeepsTheParticleSwappedIntoAGap() { ParticleStore store = StoreOf(0, Labeled(0, 2, 1), Labeled(1, 0, 1), Labeled(2, 2, 1), Labeled(3, 0, 1)); CheckEqual(2, ParticleMotion.RemoveExpired(store)); CheckEqual(2, store.Count); CheckEqual(3f, store.Items[0].PositionX); CheckEqual(1f, store.Items[1].PositionX); } public void MovesColumnEntriesWithTheirParticles() { ParticleStore store = StoreOf(1, Labeled(0, 2, 1), Labeled(1, 0, 1), Labeled(2, 0, 1)); store.Columns[0][1] = 10; store.Columns[0][2] = 20; ParticleMotion.RemoveExpired(store); CheckEqual(20f, store.Columns[0][0]); CheckEqual(10f, store.Columns[0][1]); } public void WhenEveryParticleHasExpired_EmptiesTheStore() { ParticleStore store = StoreOf(0, Labeled(0, 1, 1), Labeled(1, 1, 1)); CheckEqual(2, ParticleMotion.RemoveExpired(store)); CheckEqual(0, store.Count); } public void WhenNoneHaveExpired_RemovesNothing() { ParticleStore store = StoreOf(0, Labeled(0, 0, 1), Labeled(1, 0.5f, 1)); CheckEqual(0, ParticleMotion.RemoveExpired(store)); CheckEqual(2, store.Count); } public void OfAnEmptyStore_RemovesNothing() { CheckEqual(0, ParticleMotion.RemoveExpired(StoreOf(0))); } } } }