public void TestInit1() { var context = new PhysicalContext <int>(0.1, 1); var entity = new PointMass(new AxisStatus(1, 3), new AxisStatus(0, 0), new AxisStatus(0, 0), 1); context.AddEntity(0, entity); var tracker = new ContextTracker <int, double>(context, c => c[0].X.Position); AreEqual(1, tracker[0]); AreEqual(0ul, tracker.ObservationBeginTick); ThrowsException <ArgumentOutOfRangeException>(() => tracker[2]); context.Tick(0.5); AreEqual(1, tracker[0]); var pos05 = context[0].X.Position; AreEqual(pos05, tracker[5]); AreEqual(pos05, tracker.GetApproximately(0.5)); context.Tick(0.5); AreEqual(1, tracker[0]); AreEqual(pos05, tracker.GetApproximately(0.5)); AreEqual(pos05, tracker.GetApproximately(0.478)); AreEqual(pos05, tracker.GetApproximately(0.505)); AreEqual(context[0].X.Position, tracker[context.Ticks]); }
public void TestDispose() { var context = new PhysicalContext <int>(0.1, 2); var entity = new PointMass(0, 0, 0, 1); context.AddEntity(0, entity, c => new Force(1, 0, 0)); context.AddEntity(1, entity, c => new Force(1, 0, 0)); context.Tick(1); var tracker = new ContextTracker <int, int>(context, c => (int)Math.Round(c[0].X.Velocity + c[1].X.Velocity)); AreEqual(2, tracker[context.Ticks]); AreEqual(10ul, tracker.ObservationBeginTick); ThrowsException <ArgumentOutOfRangeException>(() => tracker.GetApproximately(0)); ThrowsException <ArgumentOutOfRangeException>(() => tracker.GetApproximately(2)); context.Tick(1); AreEqual(2, tracker.GetApproximately(1)); AreEqual(1, tracker.ObservationBeginTime); AreEqual(4, tracker[context.Ticks]); AreEqual(20ul, tracker.LastObservedTick); tracker.Dispose(); context.Tick(1); AreEqual(20ul, tracker.LastObservedTick); ThrowsException <ArgumentOutOfRangeException>(() => tracker[context.Ticks]); }
static void Main(string[] args) { //PhysicalContext is the core class represents set of entities and //set of force evaluation laws binded to it. //Type parameter represents the type of keys used to adding and retrieving entities. var context = new PhysicalContext <string> ( timePerTick: dt, //The time, that is considered to be as small, as force values are uniform. //The smaller timePerTick is, the better precision we get. //Be aware, time for evaluating state of entities after certain period of time //is proportional to (timePerTick)^-1. capacity: 1 //Number of entities required to be added to context. ); //Adding entity var freeFallEntity = new PointMass ( x: new AxisStatus(3.4), //Position = 3.4, zero velocity y: new AxisStatus(0, 10), //Zero position, velocity = 10 z: new AxisStatus(1.1, 2), //Position = 1.1, Velocity = 2 mass: 77.7 ); context.AddEntity ( "freeFallEntity", //key freeFallEntity, //entity c => //Only force that have impact on this entity - new Force ( xComponent: 0, yComponent: c["freeFallEntity"].Mass * freeFallAcceleration, zComponent: 0 ) //This force is always vertical and equal to mass of entity multiplied //by free fall acceleration. ); Console.WriteLine($"Start state is \n{context["freeFallEntity"]}"); //Evaluating the state of context after 1 second. context.Tick(timeSpan: 1); Console.WriteLine($"\nState of entity after 1 second is \n{context["freeFallEntity"]}"); //If you want to record some data while context is updating, //you may subscribe to OnTick event or better use class derived from ContextObserver. var yPositionTracker = new ContextTracker <string, double> ( context, c => c["freeFallEntity"].Y.Position ); context.Tick(1); //Context tracker implements IReadonlyDictionary. Console.WriteLine($"\nOn tick 10345 y position is {yPositionTracker[10345]}"); Console.WriteLine($"\nOn time 1.27 y position is {yPositionTracker.GetApproximately(1.27)}"); //Throws exception because tracker has started recording when time is context //was already 1.00. //Console.WriteLine($"On time 0.4 y position is {yPositionTracker.GetApproximately(0.4)}"); //Throws exception because tracker hasn't record data yet because time in context is 2.00. //Console.WriteLine($"On time 2.7 y position is {yPositionTracker.GetApproximately(2.7)}"); //Don't forget to dispose tracker when you don't need it anymore. //It will increase performance because tracker doesn't record data anymore. yPositionTracker.Dispose(); context.Tick(1); //Time is context is 3.0, but tracker is already disposed and doesn't record data anymore. Console.WriteLine ( $"\nTracker records data during time span {yPositionTracker.ObservationBeginTime} - {yPositionTracker.LastObservationTime}" + $"\nAverage y position is {yPositionTracker.Sum(record => record.Value) / yPositionTracker.Count}" ); //However, if you want to record only max, min, average value etc, //better use ContextDependentValue, because it doesn't use a lot of memory //to record value in each tick. var maxYVelocity = new ContextDependentValue <string, double> ( startValue: context["freeFallEntity"].Y.Velocity, observableContext: context, newValueFunc: (c, oldValue) => c["freeFallEntity"].Velocity > oldValue ? new double?(c["freeFallEntity"].Velocity) : null //null value means that there is no reason to change value ); context.Tick(1); Console.WriteLine ( $"\nMax y velocity in time span {maxYVelocity.ObservationBeginTime} - " + $"{maxYVelocity.LastObservationTime} is {maxYVelocity.Value}" ); //So, lets create something more complex. //What about the spring pendulum with air resistance? var pendulumContext = new PhysicalContext <PendulumEntities>(dt, 2); var axis = new PointMass(0, 0, 0, 0); var mass = new PointMass(1, 0, 0, 0); pendulumContext.AddEntity(PendulumEntities.Axis, axis); pendulumContext.AddEntity ( PendulumEntities.Mass, mass, c => new Force(0, c[PendulumEntities.Mass].Mass * freeFallAcceleration, 0), //Gravity HookesLaw.GetLaw //Mechanix.Laws contains amount of static classes to help force evaluating. ( PendulumEntities.Mass, PendulumEntities.Axis, 1, //undeformed length of spring 10 //elasticity coefficient ) ); //Or set of elastic balls. var ballsContext = new PhysicalContext <int>(dt, 100); var random = new Random(); const double radius = 1; const double elasticity = 100; for (int i = 0; i < 100; ++i) { var ball = new PointMass ( new AxisStatus(random.NextDouble(), random.NextDouble()), new AxisStatus(random.NextDouble(), random.NextDouble()), new AxisStatus(random.NextDouble(), random.NextDouble()), random.NextDouble() ); ballsContext.AddEntity ( i, ball, c => { var force = Force.Zero; foreach (var pair in c) { if (pair.Key != i) { force += RadialCollisionLaw.Eval ( c[i], pair.Value, radius, elasticity ); } } return(force); } ); } }