commit ad2e5e1d47919f10a81b33d49a13ba0ec977e06b
parent 998f16dd6aef77607c0fc3118f889f5d30d2de60
Author: Mikolaj Lenczewski <33129490+EnderRifter@users.noreply.github.com>
Date: Tue, 2 Jul 2019 07:45:06 +0100
Input handling was completely refactored into a separate class, body shape generation was improved through the use of delegates, and orbit tracers now work as intended.
Diffstat:
7 files changed, 259 insertions(+), 21 deletions(-)
diff --git a/StarSim/StarSim/Program.cs b/StarSim/StarSim/Program.cs
@@ -68,7 +68,7 @@ namespace StarSim
bodyPositionUpdater = BodyUpdater.UpdateBodiesBruteForce;
#endif
bodyDrawer = new Drawer(window, ref bodies, ref bodyShapeMap);
- inputHandler = new InputHandler(ref bodyDrawer);
+ inputHandler = new InputHandler(ref bodies, ref bodyDrawer);
Rng = new Random();
}
diff --git a/StarSim/StarSimLib/Constants.cs b/StarSim/StarSimLib/Constants.cs
@@ -60,7 +60,7 @@ namespace StarSimLib
/// <summary>
/// The number of previous positions that will be stored by a body
/// </summary>
- public const int StoredPreviousPositionCount = 100;
+ public const int StoredPreviousPositionCount = 30;
/// <summary>
/// The time step for the simulation.
diff --git a/StarSim/StarSimLib/Graphics/Drawer.cs b/StarSim/StarSimLib/Graphics/Drawer.cs
@@ -94,6 +94,12 @@ namespace StarSimLib.Graphics
private readonly Dictionary<Body, CircleShape> managedBodyShapeMap;
/// <summary>
+ /// Maps a <see cref="Body"/> to the <see cref="VertexArray"/> that holds its orbit tracer vertices, and
+ /// is drawn to the screen behind the <see cref="Body"/> instance.
+ /// </summary>
+ private readonly Dictionary<Body, VertexArray> managedBodyTracerVertexArrayMap;
+
+ /// <summary>
/// The offset that has to be applied to the positions of a <see cref="CircleShape"/>, so that they appear
/// to be in the centre of the render target and not the top left corner.
/// </summary>
@@ -190,6 +196,14 @@ namespace StarSimLib.Graphics
managedBodies = bodies;
managedBodyShapeMap = bodyShapeMap;
+ managedBodyTracerVertexArrayMap = new Dictionary<Body, VertexArray>();
+
+ foreach (Body body in bodies)
+ {
+ // create a vertex array to store orbit tracer points for this body
+ managedBodyTracerVertexArrayMap.Add(body,
+ new VertexArray(PrimitiveType.LineStrip, Constants.StoredPreviousPositionCount));
+ }
}
/// <summary>
@@ -314,20 +328,39 @@ namespace StarSimLib.Graphics
// which is used as the new position of the shape
CircleShape shape = managedBodyShapeMap[body];
- foreach (Vector4d previousPosition in body.PreviousPositions)
+ // get the vertex array used to store orbit tracer points for this body
+ VertexArray orbitTracerVertexArray = managedBodyTracerVertexArrayMap[body];
+
+ // we need to clear the vertex array, to ensure that no garbage values are present. otherwise
+ // there are lines from the origin to the first actually desired position
+ orbitTracerVertexArray.Clear();
+
+ // we only attempt to draw tracers if the body instance in question should record its previous position
+ // otherwise, we skip the relatively expensive rendering code
+ if (body.RecordPreviousPositions)
{
- // project previous position onto the screen
- Vector4d pointScreenPosition = ProjectPoint(previousPosition);
+ Vector4d[] orbitTracerPositions = body.PreviousPositions.ToArray();
- CircleShape previousPositionShape = new CircleShape(0.5f)
+ for (uint i = 0; i < orbitTracerPositions.Length; i++)
{
- Position = new Vector2f(
+ Vector4d previousPosition = orbitTracerPositions[i];
+
+ // project previous position onto the screen
+ Vector4d pointScreenPosition = ProjectPoint(previousPosition);
+
+ // convert the final position into a Vector2f, useable by the SFML.NET Vertex struct
+ Vector2f finalOrbitTracerPosition = new Vector2f(
(float)(pointScreenPosition.X * renderTarget.Size.X / 2 + originOffset.X),
- (float)(pointScreenPosition.Y * renderTarget.Size.Y / 2 + originOffset.Y)),
- FillColor = Color.Cyan
- };
+ (float)(pointScreenPosition.Y * renderTarget.Size.Y / 2 + originOffset.Y));
+
+ // append the new vertex to the orbit tracer array
+ orbitTracerVertexArray.Append(new Vertex(finalOrbitTracerPosition, Color.Cyan));
+ }
- previousPositionShape.Draw(renderTarget, RenderStates.Default);
+ // single call to VertexArray.Draw() makes use of hardware acceleration without causing delays,
+ // as modern graphics processing units are optimised to render many vertices simultaneously, instead
+ // of rendering many vertices sequentially
+ orbitTracerVertexArray.Draw(renderTarget, RenderStates.Default);
}
Vector4d screenPosition = ProjectPoint(body.Position);
diff --git a/StarSim/StarSimLib/Physics/Body.cs b/StarSim/StarSimLib/Physics/Body.cs
@@ -16,6 +16,12 @@ namespace StarSimLib.Physics
"Body {0,2}.{1,-4}: Pos-{2}, Vel-{3} Mass-{4,3}";
/// <summary>
+ /// Sample rate for the previous position. Used to improve performance and get a longer orbit tracer tail
+ /// for less computation. The previous position will be saved once every 20 sampling opportunities.
+ /// </summary>
+ private const int PositionSampleRate = 20;
+
+ /// <summary>
/// Backing field for the <see cref="PreviousPositions"/> property.
/// </summary>
private readonly Queue<Vector4d> previousPositions;
@@ -36,6 +42,12 @@ namespace StarSimLib.Physics
private Vector4d position;
/// <summary>
+ /// Counts the number of sampling opportunities that have gone by since the last position sample. Resets once
+ /// it reaches the value of <see cref="PositionSampleRate"/>.
+ /// </summary>
+ private int positionSampleCounter = 0;
+
+ /// <summary>
/// Backing field for the <see cref="Velocity"/> property.
/// </summary>
private Vector4d velocity;
@@ -105,6 +117,11 @@ namespace StarSimLib.Physics
}
/// <summary>
+ /// Whether to record previous positions of this instance, to render an orbit tracer behind it.
+ /// </summary>
+ public bool RecordPreviousPositions { get; set; }
+
+ /// <summary>
/// The current velocity of the <see cref="Body"/> in 3D space.
/// </summary>
public Vector4d Velocity
@@ -115,16 +132,23 @@ namespace StarSimLib.Physics
/// <summary>
/// Enqueues the current position on the <see cref="previousPositions"/> queue, to save it.
/// Will dequeue positions from the queue if the number of stored positions exceeds the
- /// value in <see cref="Constants.StoredPreviousPositionCount"/>.
+ /// value in <see cref="Constants.StoredPreviousPositionCount"/>. Will only enqueue the
+ /// current position if the <see cref="PositionSampleRate"/> is met.
/// </summary>
private void EnqueuePosition()
{
+ // if the sample rate limit has not yet been met, don't sample a position
+ if (++positionSampleCounter < PositionSampleRate) return;
+
previousPositions.Enqueue(position);
if (previousPositions.Count > Constants.StoredPreviousPositionCount)
{
previousPositions.Dequeue();
}
+
+ // reset the counter
+ positionSampleCounter = 0;
}
/// <summary>
@@ -166,6 +190,14 @@ namespace StarSimLib.Physics
}
/// <summary>
+ /// Clears the <see cref="Queue{T}"/> holding the previous positions.
+ /// </summary>
+ public void ClearPreviousPositionQueue()
+ {
+ previousPositions.Clear();
+ }
+
+ /// <summary>
/// Collides this instance with the given <see cref="Body"/> instance.
/// </summary>
/// <param name="otherBody">The other instance with which to collide.</param>
@@ -210,7 +242,12 @@ namespace StarSimLib.Physics
public void Update(double deltaTime)
{
velocity += deltaTime * force / mass;
- EnqueuePosition();
+
+ if (RecordPreviousPositions)
+ {
+ EnqueuePosition();
+ }
+
position += deltaTime * velocity;
}
@@ -222,7 +259,12 @@ namespace StarSimLib.Physics
public void Update(Vector4d forceVector, double deltaTime)
{
velocity += deltaTime * forceVector / mass;
- EnqueuePosition();
+
+ if (RecordPreviousPositions)
+ {
+ EnqueuePosition();
+ }
+
position += deltaTime * velocity;
}
diff --git a/StarSim/StarSimLib/Physics/BodyGenerator.cs b/StarSim/StarSimLib/Physics/BodyGenerator.cs
@@ -7,6 +7,20 @@ using StarSimLib.Data_Structures;
namespace StarSimLib.Physics
{
/// <summary>
+ /// Takes the mass of a <see cref="Body"/> instance and returns a colour for the <see cref="CircleShape"/>
+ /// that will represent the <see cref="Body"/> of the given mass.
+ /// </summary>
+ /// <returns>The colour of the <see cref="CircleShape"/> for a <see cref="Body"/> of the given mass.</returns>
+ public delegate Color MassToColourDelegate(double mass);
+
+ /// <summary>
+ /// Takes the mass of a <see cref="Body"/> instance and returns a radius for the <see cref="CircleShape"/>
+ /// that will represent the <see cref="Body"/> of the given mass.
+ /// </summary>
+ /// <returns>The radius of the <see cref="CircleShape"/> for a <see cref="Body"/> of the given mass.</returns>
+ public delegate float MassToRadiusDelegate(double mass);
+
+ /// <summary>
/// Provides methods for generating <see cref="Body"/> instances.
/// </summary>
public static class BodyGenerator
@@ -17,6 +31,18 @@ namespace StarSimLib.Physics
private static readonly Random Rng = new Random();
/// <summary>
+ /// Default implementation of the <see cref="MassToColourDelegate"/>.
+ /// </summary>
+ public static readonly MassToColourDelegate DefaultColourDelegate =
+ mass => mass >= Constants.CentralBodyMass ? Color.Red : Color.White;
+
+ /// <summary>
+ /// Default implementation of the <see cref="MassToRadiusDelegate"/>.
+ /// </summary>
+ public static readonly MassToRadiusDelegate DefaultRadiusDelegate =
+ mass => mass >= Constants.CentralBodyMass ? 4f : 2f;
+
+ /// <summary>
/// The current generation of <see cref="Body"/> instances that the generator is on.
/// </summary>
public static uint CurrentGeneration { get; private set; }
@@ -62,15 +88,36 @@ namespace StarSimLib.Physics
/// <returns>
/// A <see cref="Dictionary{TKey,TValue}"/> mapping the given <see cref="Body"/> instances to their <see cref="CircleShape"/> instances.
/// </returns>
- public static Dictionary<Body, CircleShape> GenerateShapes(IEnumerable<Body> bodies)
+ public static Dictionary<Body, CircleShape> GenerateShapes(IEnumerable<Body> bodies) =>
+ GenerateShapes(bodies, DefaultRadiusDelegate, DefaultColourDelegate);
+
+ /// <summary>
+ /// Generates a <see cref="Dictionary{TKey,TValue}"/> mapping each given <see cref="Body"/> to a
+ /// <see cref="CircleShape"/> that represents the body instance in 2D space.
+ /// </summary>
+ /// <param name="bodies">The <see cref="Body"/> instances for which to generate the shapes.</param>
+ /// <param name="massToRadiusDelegate">
+ /// The function to use to derive the radius of the <see cref="CircleShape"/> that will represent a <see cref="Body"/>
+ /// instance from its mass.
+ /// </param>
+ /// <param name="massToColourDelegate">
+ /// The function to use to derive the colour of the <see cref="CircleShape"/> that will represent a <see cref="Body"/>
+ /// instance from its mass.
+ /// </param>
+ /// <returns>
+ /// A <see cref="Dictionary{TKey,TValue}"/> mapping the given <see cref="Body"/> instances to their <see cref="CircleShape"/> instances.
+ /// </returns>
+ public static Dictionary<Body, CircleShape> GenerateShapes(IEnumerable<Body> bodies,
+ MassToRadiusDelegate massToRadiusDelegate, MassToColourDelegate massToColourDelegate)
{
Dictionary<Body, CircleShape> bodyCircleShapeMap = new Dictionary<Body, CircleShape>();
foreach (Body body in bodies)
{
- CircleShape shape = body.Mass >= Constants.CentralBodyMass
- ? new CircleShape(4) { FillColor = Color.Red }
- : new CircleShape(1) { FillColor = Color.White };
+ CircleShape shape = new CircleShape(massToRadiusDelegate(body.Mass))
+ {
+ FillColor = massToColourDelegate(body.Mass)
+ };
shape.Origin = new Vector2f(shape.Radius, shape.Radius);
diff --git a/StarSim/StarSimLib/StarSimLib.xml b/StarSim/StarSimLib/StarSimLib.xml
@@ -602,6 +602,12 @@
screen at the <see cref="T:StarSimLib.Physics.Body"/>s position.
</summary>
</member>
+ <member name="F:StarSimLib.Graphics.Drawer.managedBodyTracerVertexArrayMap">
+ <summary>
+ Maps a <see cref="T:StarSimLib.Physics.Body"/> to the <see cref="T:SFML.Graphics.VertexArray"/> that holds its orbit tracer vertices, and
+ is drawn to the screen behind the <see cref="T:StarSimLib.Physics.Body"/> instance.
+ </summary>
+ </member>
<member name="F:StarSimLib.Graphics.Drawer.originOffset">
<summary>
The offset that has to be applied to the positions of a <see cref="T:SFML.Graphics.CircleShape"/>, so that they appear
@@ -727,6 +733,12 @@
Describes how the <see cref="T:StarSimLib.Physics.Body"/> instance should be formatted as a <see cref="T:System.String"/>.
</summary>
</member>
+ <member name="F:StarSimLib.Physics.Body.PositionSampleRate">
+ <summary>
+ Sample rate for the previous position. Used to improve performance and get a longer orbit tracer tail
+ for less computation. The previous position will be saved once every 20 sampling opportunities.
+ </summary>
+ </member>
<member name="F:StarSimLib.Physics.Body.previousPositions">
<summary>
Backing field for the <see cref="P:StarSimLib.Physics.Body.PreviousPositions"/> property.
@@ -747,6 +759,12 @@
Backing field for the <see cref="P:StarSimLib.Physics.Body.Position"/> property.
</summary>
</member>
+ <member name="F:StarSimLib.Physics.Body.positionSampleCounter">
+ <summary>
+ Counts the number of sampling opportunities that have gone by since the last position sample. Resets once
+ it reaches the value of <see cref="F:StarSimLib.Physics.Body.PositionSampleRate"/>.
+ </summary>
+ </member>
<member name="F:StarSimLib.Physics.Body.velocity">
<summary>
Backing field for the <see cref="P:StarSimLib.Physics.Body.Velocity"/> property.
@@ -792,6 +810,11 @@
A <see cref="T:System.Collections.Generic.Queue`1"/> containing previous positions of the body.
</summary>
</member>
+ <member name="P:StarSimLib.Physics.Body.RecordPreviousPositions">
+ <summary>
+ Whether to record previous positions of this instance, to render an orbit tracer behind it.
+ </summary>
+ </member>
<member name="P:StarSimLib.Physics.Body.Velocity">
<summary>
The current velocity of the <see cref="T:StarSimLib.Physics.Body"/> in 3D space.
@@ -801,7 +824,8 @@
<summary>
Enqueues the current position on the <see cref="F:StarSimLib.Physics.Body.previousPositions"/> queue, to save it.
Will dequeue positions from the queue if the number of stored positions exceeds the
- value in <see cref="F:StarSimLib.Constants.StoredPreviousPositionCount"/>.
+ value in <see cref="F:StarSimLib.Constants.StoredPreviousPositionCount"/>. Will only enqueue the
+ current position if the <see cref="F:StarSimLib.Physics.Body.PositionSampleRate"/> is met.
</summary>
</member>
<member name="M:StarSimLib.Physics.Body.GetForceBetween(StarSimLib.Physics.Body,StarSimLib.Physics.Body)">
@@ -819,6 +843,11 @@
</summary>
<param name="otherBody">The other body to calculate the force between.</param>
</member>
+ <member name="M:StarSimLib.Physics.Body.ClearPreviousPositionQueue">
+ <summary>
+ Clears the <see cref="T:System.Collections.Generic.Queue`1"/> holding the previous positions.
+ </summary>
+ </member>
<member name="M:StarSimLib.Physics.Body.Collide(StarSimLib.Physics.Body)">
<summary>
Collides this instance with the given <see cref="T:StarSimLib.Physics.Body"/> instance.
@@ -855,6 +884,20 @@
<member name="M:StarSimLib.Physics.Body.ToString">
<inheritdoc />
</member>
+ <member name="T:StarSimLib.Physics.MassToColourDelegate">
+ <summary>
+ Takes the mass of a <see cref="T:StarSimLib.Physics.Body"/> instance and returns a colour for the <see cref="T:SFML.Graphics.CircleShape"/>
+ that will represent the <see cref="T:StarSimLib.Physics.Body"/> of the given mass.
+ </summary>
+ <returns>The colour of the <see cref="T:SFML.Graphics.CircleShape"/> for a <see cref="T:StarSimLib.Physics.Body"/> of the given mass.</returns>
+ </member>
+ <member name="T:StarSimLib.Physics.MassToRadiusDelegate">
+ <summary>
+ Takes the mass of a <see cref="T:StarSimLib.Physics.Body"/> instance and returns a radius for the <see cref="T:SFML.Graphics.CircleShape"/>
+ that will represent the <see cref="T:StarSimLib.Physics.Body"/> of the given mass.
+ </summary>
+ <returns>The radius of the <see cref="T:SFML.Graphics.CircleShape"/> for a <see cref="T:StarSimLib.Physics.Body"/> of the given mass.</returns>
+ </member>
<member name="T:StarSimLib.Physics.BodyGenerator">
<summary>
Provides methods for generating <see cref="T:StarSimLib.Physics.Body"/> instances.
@@ -865,6 +908,16 @@
Caches a random number generator to use for all randomised positions and velocities.
</summary>
</member>
+ <member name="F:StarSimLib.Physics.BodyGenerator.DefaultColourDelegate">
+ <summary>
+ Default implementation of the <see cref="T:StarSimLib.Physics.MassToColourDelegate"/>.
+ </summary>
+ </member>
+ <member name="F:StarSimLib.Physics.BodyGenerator.DefaultRadiusDelegate">
+ <summary>
+ Default implementation of the <see cref="T:StarSimLib.Physics.MassToRadiusDelegate"/>.
+ </summary>
+ </member>
<member name="P:StarSimLib.Physics.BodyGenerator.CurrentGeneration">
<summary>
The current generation of <see cref="T:StarSimLib.Physics.Body"/> instances that the generator is on.
@@ -888,6 +941,24 @@
A <see cref="T:System.Collections.Generic.Dictionary`2"/> mapping the given <see cref="T:StarSimLib.Physics.Body"/> instances to their <see cref="T:SFML.Graphics.CircleShape"/> instances.
</returns>
</member>
+ <member name="M:StarSimLib.Physics.BodyGenerator.GenerateShapes(System.Collections.Generic.IEnumerable{StarSimLib.Physics.Body},StarSimLib.Physics.MassToRadiusDelegate,StarSimLib.Physics.MassToColourDelegate)">
+ <summary>
+ Generates a <see cref="T:System.Collections.Generic.Dictionary`2"/> mapping each given <see cref="T:StarSimLib.Physics.Body"/> to a
+ <see cref="T:SFML.Graphics.CircleShape"/> that represents the body instance in 2D space.
+ </summary>
+ <param name="bodies">The <see cref="T:StarSimLib.Physics.Body"/> instances for which to generate the shapes.</param>
+ <param name="massToRadiusDelegate">
+ The function to use to derive the radius of the <see cref="T:SFML.Graphics.CircleShape"/> that will represent a <see cref="T:StarSimLib.Physics.Body"/>
+ instance from its mass.
+ </param>
+ <param name="massToColourDelegate">
+ The function to use to derive the colour of the <see cref="T:SFML.Graphics.CircleShape"/> that will represent a <see cref="T:StarSimLib.Physics.Body"/>
+ instance from its mass.
+ </param>
+ <returns>
+ A <see cref="T:System.Collections.Generic.Dictionary`2"/> mapping the given <see cref="T:StarSimLib.Physics.Body"/> instances to their <see cref="T:SFML.Graphics.CircleShape"/> instances.
+ </returns>
+ </member>
<member name="T:StarSimLib.Physics.UpdateDelegate">
<summary>
Updates the given <see cref="T:StarSimLib.Physics.Body"/> collections individual bodies, using the given delta time.
@@ -940,10 +1011,18 @@
The renderer used to display the <see cref="T:StarSimLib.Physics.Body"/> instances on the screen.
</summary>
</member>
- <member name="M:StarSimLib.UI.InputHandler.#ctor(StarSimLib.Graphics.Drawer@)">
+ <member name="F:StarSimLib.UI.InputHandler.managedBodies">
+ <summary>
+ Holds all the <see cref="T:StarSimLib.Physics.Body"/> instances that should be simulated.
+ </summary>
+ </member>
+ <member name="M:StarSimLib.UI.InputHandler.#ctor(StarSimLib.Physics.Body[]@,StarSimLib.Graphics.Drawer@)">
<summary>
Initialises a new instance of the <see cref="T:StarSimLib.UI.InputHandler"/> class.
</summary>
+ <param name="bodies">
+ A reference to the <see cref="T:StarSimLib.Physics.Body"/> instances which should be managed by this instance.
+ </param>
<param name="drawer">
A reference to the renderer used to display the <see cref="T:StarSimLib.Physics.Body"/> instances on the screen.
</param>
@@ -953,6 +1032,12 @@
Whether the simulation is paused at any given time.
</summary>
</member>
+ <member name="P:StarSimLib.UI.InputHandler.RecordOrbitTracers">
+ <summary>
+ Whether to record previous <see cref="T:StarSimLib.Physics.Body"/> instance positions, in order to render an orbit tracer
+ behind each body instance.
+ </summary>
+ </member>
<member name="M:StarSimLib.UI.InputHandler.HandleKeyPressed(System.Object,SFML.Window.KeyEventArgs)">
<summary>
Handles key presses.
diff --git a/StarSim/StarSimLib/UI/InputHandler.cs b/StarSim/StarSimLib/UI/InputHandler.cs
@@ -16,13 +16,22 @@ namespace StarSimLib.UI
private readonly Drawer bodyDrawer;
/// <summary>
+ /// Holds all the <see cref="Body"/> instances that should be simulated.
+ /// </summary>
+ private readonly Body[] managedBodies;
+
+ /// <summary>
/// Initialises a new instance of the <see cref="InputHandler"/> class.
/// </summary>
+ /// <param name="bodies">
+ /// A reference to the <see cref="Body"/> instances which should be managed by this instance.
+ /// </param>
/// <param name="drawer">
/// A reference to the renderer used to display the <see cref="Body"/> instances on the screen.
/// </param>
- public InputHandler(ref Drawer drawer)
+ public InputHandler(ref Body[] bodies, ref Drawer drawer)
{
+ managedBodies = bodies;
bodyDrawer = drawer;
}
@@ -32,6 +41,12 @@ namespace StarSimLib.UI
public bool IsSimulationPaused { get; set; }
/// <summary>
+ /// Whether to record previous <see cref="Body"/> instance positions, in order to render an orbit tracer
+ /// behind each body instance.
+ /// </summary>
+ public bool RecordOrbitTracers { get; set; }
+
+ /// <summary>
/// Handles key presses.
/// </summary>
/// <param name="sender">The <see cref="Window"/> that sent the event.</param>
@@ -47,6 +62,22 @@ namespace StarSimLib.UI
IsSimulationPaused = !IsSimulationPaused;
break;
+ case Keyboard.Key.T:
+ // toggle orbit tracer recording
+ RecordOrbitTracers = !RecordOrbitTracers;
+
+ // every simulated body must be individually set to record tracers
+ foreach (Body body in managedBodies)
+ {
+ // we want to clear the screen of any orbit tracers that still exist when we turn orbit tracing
+ // off, so we must clear the previous position queues of every managed object. this has the
+ // effect of clearing the vertex arrays holding the orbit tracer vertices
+ body.RecordPreviousPositions = RecordOrbitTracers;
+ body.ClearPreviousPositionQueue();
+ }
+
+ break;
+
case Keyboard.Key.W:
// rotate the view north
bodyDrawer.Rotate(RotationDirection.North, Constants.EulerRotationStep);