StarSim

StarSim.git
git clone git://git.lenczewski.org/StarSim.git
Log | Files | Refs | README | LICENSE

commit 41fc6f39d7b79bef1375d0e5af80488fd3814ef3
parent 1359975637532a7180e3a333224f484534975d55
Author: Mikolaj Lenczewski <33129490+EnderRifter@users.noreply.github.com>
Date:   Wed, 19 Jun 2019 12:01:44 +0100

Working now

Diffstat:
MStarSim/StarSim/Program.cs | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
MStarSim/StarSimLib/Body.cs | 140++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
AStarSim/StarSimLib/Constants.cs | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AStarSim/StarSimLib/ExampleBody.cs | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AStarSim/StarSimLib/Extensions/Vector3fExtensions.cs | 18++++++++++++++++++
AStarSim/StarSimLib/OrbitGenerator.cs | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AStarSim/StarSimLib/Vector3d.cs | 363+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 871 insertions(+), 4 deletions(-)

diff --git a/StarSim/StarSim/Program.cs b/StarSim/StarSim/Program.cs @@ -1,12 +1,171 @@ using System; +using System.Collections.Generic; +using System.Linq; +using SFML.Graphics; +using SFML.System; +using SFML.Window; +using StarSimLib; namespace StarSim { - class Program + internal class Program { - static void Main(string[] args) + /// <summary> + /// Caches a random number generator to use for all randomised positions and velocities. + /// </summary> + private static readonly Random RNG = new Random(); + + /// <summary> + /// The current generation of <see cref="Body"/>s that we have. Increments each time the bodies are regenerated + /// via <see cref="GenerateBodies(int, bool)"/>. + /// </summary> + private static uint _generation; + + /// <summary> + /// Holds all the <see cref="Body"/> instances that should be simulated. + /// </summary> + private static Body[] _bodies; + + /// <summary> + /// Maps a <see cref="Body"/> to the <see cref="CircleShape"/> that represents it, and is drawn to the + /// screen at the <see cref="Body"/>s position. + /// </summary> + private static Dictionary<Body, CircleShape> BodyShapeMap; + + private static void Main(string[] args) { Console.WriteLine("Hello World!"); + + GenerateBodies(10, true); + + Console.ReadLine(); + + RenderWindow window = new RenderWindow(VideoMode.DesktopMode, "N-Body Simulator", Styles.Default); + window.SetFramerateLimit(Constants.FrameRate); + window.Closed += (sender, eventArgs) => ((RenderWindow)sender).Close(); + window.KeyPressed += HandleKeyPressed; + + // caches the delta between the screen origin (top left corner) and the world origin, + // which should be the centre of the screen + uint wx = window.Size.X, wy = window.Size.Y; + uint dx = wx / 2, dy = wy / 2; + Vector2u originOffset = new Vector2u(dx, dy); + + DrawBodies(_bodies, window, originOffset); + + while (window.IsOpen) + { + window.Clear(); + window.DispatchEvents(); + + UpdateBodiesBruteForce(_bodies, Constants.TimeStep); + DrawBodies(_bodies, window, originOffset); + + window.Display(); + } + } + + private static void GenerateBodies(int bodyCount = 2, bool centralAttractor = false) + { + _generation++; + + uint id = 0; + _bodies = new Body[bodyCount]; + BodyShapeMap = new Dictionary<Body, CircleShape>(); + + Console.WriteLine($"=========== Generation {_generation} ==========="); + for (int i = 0; i < _bodies.Length; i++) + { + float mass = (float)(RNG.NextDouble() * Constants.SolarMass); + + Vector3d position = OrbitGenerator.RandomPosition(); + Vector3d velocity = new Vector3d(); //OrbitGenerator.RandomOrbit(position); + + _bodies[i] = new Body(position, velocity, mass, _generation, id); + BodyShapeMap.Add(_bodies[i], new CircleShape(4) { FillColor = Color.White }); + + Console.WriteLine(_bodies[i]); + id++; + } + + if (centralAttractor && bodyCount >= 2) + { + BodyShapeMap.Remove(_bodies[0]); + _bodies[0] = new Body(new Vector3d(), new Vector3d(), Constants.CentralBodyMass, _generation, 0); + BodyShapeMap.Add(_bodies[0], new CircleShape(4) { FillColor = Color.Red }); + } + } + + /// <summary> + /// Updates the positions of all the given <see cref="Body"/>s with O(n^2) time complexity, with the given time step. + /// </summary> + /// <param name="bodies">The collection of <see cref="Body"/>s whose positions to update.</param> + /// <param name="deltaTime">The time step.</param> + private static void UpdateBodiesBruteForce(IEnumerable<Body> bodies, double deltaTime) + { + IEnumerable<Body> bodyEnumerable = bodies as Body[] ?? bodies.ToArray(); + Vector3d forceVector = new Vector3d(); + + foreach (Body body in bodyEnumerable) + { + // resets the force vector to avoid another instantiation and allocation + forceVector.X = 0; + forceVector.Y = 0; + forceVector.Z = 0; + + // use LINQ expression as it is more concise; sum attraction vectors for all other bodies + forceVector = bodyEnumerable.Where(b => b != body).Aggregate(forceVector, (current, b) => current + Body.GetForceBetween(body, b)); + + body.Update(forceVector, deltaTime); + } + } + + /// <summary> + /// Draws each <see cref="Body"/> in the given <see cref="IEnumerable{T}"/> to the given window. + /// </summary> + /// <param name="bodies">The collection of <see cref="Body"/>s to draw.</param> + /// <param name="renderTarget">The target to draw the <see cref="Body"/>s to.</param> + /// <param name="originOffset"> + /// The offset that has to be applied to the positions of the <see cref="CircleShape"/>, so that they appear + /// to be in the centre of the screen and not the top left corner. + /// </param> + private static void DrawBodies(IEnumerable<Body> bodies, RenderWindow renderTarget, Vector2u originOffset) + { + foreach (Body body in bodies) + { + // get the cached shape and projects the current bodies 3D position down to a 2D position, + // which is used as the new position of the shape. + CircleShape shape = BodyShapeMap[body]; + + shape.Position = new Vector2f( + (float)(body.Position.X * Constants.UniverseScalingFactor + originOffset.X), + (float)(body.Position.Y * Constants.UniverseScalingFactor + originOffset.Y) + ); + + shape.Draw(renderTarget, RenderStates.Default); + } + } + + /// <summary> + /// Handles key presses. + /// </summary> + /// <param name="sender">The <see cref="RenderWindow"/> that sent the event.</param> + /// <param name="eventArgs">The <see cref="KeyEventArgs"/> about the key press.</param> + private static void HandleKeyPressed(object sender, KeyEventArgs eventArgs) + { + switch (eventArgs.Code) + { + case Keyboard.Key.Space: + UpdateBodiesBruteForce(_bodies, Constants.TimeStep); + break; + + case Keyboard.Key.G: + GenerateBodies(10, true); + break; + + default: + break; + } } } -} +} +\ No newline at end of file diff --git a/StarSim/StarSimLib/Body.cs b/StarSim/StarSimLib/Body.cs @@ -1,6 +1,144 @@ -namespace StarSimLib +using SFML.System; +using System; +using StarSimLib.Extensions; + +namespace StarSimLib { + /// <summary> + /// Represents a stellar body. + /// </summary> public class Body { + #region Variables + + /// <summary> + /// Describes how the <see cref="Body"/> instance should be formatted as a <see cref="string"/>. + /// </summary> + private const string BodyFormatString = + "Body {0,2}.{1,-4}: Pos-({2:D,-3}, {3:D,-3}, {4:D,-3}), Vel-({5:D,-3}, {6:D,-3}, {7:D,-3}), Mass-{8:D,3}"; + + /// <summary> + /// The generation that this body belongs to. + /// </summary> + public readonly uint Generation; + + /// <summary> + /// The unique id for this body. + /// </summary> + public readonly uint Id; + + #endregion Variables + + #region Properties + + /// <summary> + /// The current position of the <see cref="Body"/> in 3D space. + /// </summary> + public Vector3d Position { get; private set; } + + /// <summary> + /// The current velocity of the <see cref="Body"/> in 3D space. + /// </summary> + public Vector3d Velocity { get; private set; } + + /// <summary> + /// The mass of the <see cref="Body"/>. + /// </summary> + public double Mass { get; private set; } + + #endregion Properties + + #region Constructors + + /// <summary> + /// Initialises a new instance of the <see cref="Body"/> class. + /// </summary> + /// <param name="position">The starting position of the <see cref="Body"/>.</param> + /// <param name="velocity">The starting velocity of the <see cref="Body"/>.</param> + /// <param name="mass">The starting mass of the <see cref="Body"/>.</param> + /// <param name="generation">The generation that this <see cref="Body"/> belongs to.</param> + /// <param name="id">The unique id for this body.</param> + public Body(Vector3d position, Vector3d velocity, double mass, uint generation = 1, uint id = 1) + { + Generation = generation; + Id = id; + + Position = position; + Velocity = velocity; + Mass = mass; + } + + #endregion Constructors + + #region Methods + + #region Overrides of Object + + /// <inheritdoc /> + public override string ToString() + { + return string.Format(BodyFormatString, Generation, Id, Position.X, Position.Y, Position.Z, Velocity.X, Velocity.Y, Velocity.Z, Mass); + } + + #endregion Overrides of Object + + /// <summary> + /// Updates the <see cref="Position"/> of the current body using the given force vector and time step. + /// </summary> + /// <param name="forceVector">The sum vector of all forces due to other <see cref="Body"/>s.</param> + /// <param name="deltaTime">The time step.</param> + public void Update(Vector3d forceVector, double deltaTime) + { + Velocity += deltaTime * forceVector / Mass; + Position += deltaTime * Velocity; + } + + /// <summary> + /// Finds and returns the distance between the current <see cref="Body"/> instance and the given <see cref="Body"/>. + /// In other words, it finds the magnitude of the translation vector between the two <see cref="Body"/> instances. + /// </summary> + /// <param name="body">The other <see cref="Body"/> instance to which to calculate the distance.</param> + /// <param name="displacement">The vector showing the displacement of the current body from the given one.</param> + /// <returns>The distance to the other <see cref="Body"/> as a <see cref="float"/>.</returns> + public double DistanceTo(Body body, out Vector3d displacement) + { + double dpx = body.Position.X - Position.X, + dpy = body.Position.Y - Position.Y, + dpz = body.Position.Z - Position.Z; + + displacement = new Vector3d(dpx, dpy, dpz); + + return Math.Sqrt(dpx * dpx + dpy * dpy + dpz * dpz); + } + + /// <summary> + /// Gets the force vector for the attraction between <see cref="Body"/> A and <see cref="Body"/> B. + /// </summary> + /// <param name="a">The first <see cref="Body"/> instance.</param> + /// <param name="b">The second <see cref="Body"/> instance.</param> + /// <returns></returns> + public static Vector3d GetForceBetween(Body a, Body b) + { + // Inlines the Body.DistanceTo(Body) as the position deltas need to be cached for later, + // as well as to gain a small performance increase + double dx = b.Position.X - a.Position.X, + dy = b.Position.Y - a.Position.Y, + dz = b.Position.Z - a.Position.Z; + + // The distance between two bodies can be found via taking the magnitude of their displacements, + // as shown here via pythagoras + double distance = Math.Sqrt(dx * dx + dy * dy + dz * dz); + + double numerator = Constants.G * a.Mass * b.Mass; + double denominator = distance * distance + Constants.SofteningFactor2; + + // Using the equation Force = Gravitational Constant * Mass(a) * Mass(b) / distance(a, b)^2 + // with a softening factor, we get the attraction force vector between the 2 bodies + double force = numerator / denominator; + + return new Vector3d(force * dx / distance, force * dy / distance, force * dz / distance); + } + + #endregion Methods } } \ No newline at end of file diff --git a/StarSim/StarSimLib/Constants.cs b/StarSim/StarSimLib/Constants.cs @@ -0,0 +1,60 @@ +using SFML.Graphics; + +namespace StarSimLib +{ + /// <summary> + /// Holds common constants and helper functions. + /// </summary> + public static class Constants + { + /// <summary> + /// The frame rate limit for the simulation. + /// </summary> + public const uint FrameRate = 60; + + /// <summary> + /// The number of seconds that each frame represents. + /// </summary> + public const double SecondsPerFrame = 1e7f; + + /// <summary> + /// The default time step for the simulation. + /// </summary> + public const double TimeStep = SecondsPerFrame * FrameRate; + + /// <summary> + /// The gravitational constant (m^3 kg^-1 s^-2). + /// </summary> + public const double G = 6.673e-11f; + + /// <summary> + /// The mass of the sun (1.98892e30f). + /// </summary> + public const double SolarMass = 1.98892e30f; + + /// <summary> + /// Softens the force between <see cref="Body"/>s to avoid infinities. + /// </summary> + public const double SofteningFactor = 3e4f; + + /// <summary> + /// The square of the <see cref="SofteningFactor"/>. + /// </summary> + public const double SofteningFactor2 = SofteningFactor * SofteningFactor; + + /// <summary> + /// The maximum radius within which <see cref="Body"/>s will be placed (1e18f). + /// </summary> + public const double UniverseSize = 1e18f; + + /// <summary> + /// Factor by which to multiply the position of <see cref="Body"/>s to scale them to the screen for displaying. + /// </summary> + public const double UniverseScalingFactor = 2500 / UniverseSize; + + /// <summary> + /// The mass of the central body, if it is included. + /// </summary> + public const double CentralBodyMass = SolarMass * 1e6f; + } +} +\ No newline at end of file diff --git a/StarSim/StarSimLib/ExampleBody.cs b/StarSim/StarSimLib/ExampleBody.cs @@ -0,0 +1,68 @@ +using System; + +namespace StarSimLib +{ + public class ExampleBody + { + private static readonly double G = 6.673e-11; // gravitational constant + private static readonly double solarmass = 1.98892e30; + + public double pos_x, pos_y; + public double vel_x, vel_y; + public double force_x, force_y; + public double mass; + + public ExampleBody(double px, double py, double vx, double vy, double mass) + { + pos_x = px; + pos_y = py; + vel_x = vx; + vel_y = vy; + this.mass = mass; + } + + public void update(double delta_time) + { + vel_x += delta_time * force_x / mass; + vel_y += delta_time * force_y / mass; + pos_x += delta_time * vel_x; + pos_y += delta_time * vel_y; + } + + public double distanceTo(ExampleBody b) + { + double dx = pos_x - b.pos_x; + double dy = pos_y - b.pos_y; + return Math.Sqrt(dx * dx + dy * dy); + } + + public void resetForce() + { + force_x = 0.0; + force_y = 0.0; + } + + public void addForce(ExampleBody b) + { + ExampleBody a = this; + double softening_factor = 3E4; + double dx = b.pos_x - a.pos_x; + double dy = b.pos_y - a.pos_y; + double dist = Math.Sqrt(dx * dx + dy * dy); + double force = (G * a.mass * b.mass) / (dist * dist + softening_factor * softening_factor); + + a.force_x += force * dx / dist; + a.force_y += force * dy / dist; + } + + #region Overrides of Object + + /// <inheritdoc /> + public override string ToString() + { + return $"Body @ ({pos_x}, {pos_y}) with velocity ({vel_x}, {vel_y})"; + } + + #endregion Overrides of Object + } +} +\ No newline at end of file diff --git a/StarSim/StarSimLib/Extensions/Vector3fExtensions.cs b/StarSim/StarSimLib/Extensions/Vector3fExtensions.cs @@ -0,0 +1,17 @@ +using System; +using SFML.System; + +namespace StarSimLib.Extensions +{ + /// <summary> + /// Provides additional functionality to the <see cref="Vector3f"/> struct. + /// </summary> + public static class Vector3fExtensions + { + public static float Magnitude(this Vector3f vector) + { + // returns the magnitude of the vector, via pythagoras + return (float)Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y + vector.Z + vector.Z); + } + } +} +\ No newline at end of file diff --git a/StarSim/StarSimLib/OrbitGenerator.cs b/StarSim/StarSimLib/OrbitGenerator.cs @@ -0,0 +1,57 @@ +using System; +using SFML.System; +using StarSimLib.Extensions; + +namespace StarSimLib +{ + /// <summary> + /// Initialises positions, velocities, and orbits for bodies. + /// </summary> + public static class OrbitGenerator + { + /// <summary> + /// Random number generator. + /// </summary> + private static readonly Random Rng = new Random(); + + /// <summary> + /// Returns a random position within the universe sphere (see <see cref="Constants.UniverseSize"/>). + /// </summary> + /// <returns>A 3D position vector.</returns> + public static Vector3d RandomPosition() + { + double RandomPos() + { + return Constants.UniverseSize * Math.Exp(-1.8) * (.5 - Rng.NextDouble()); + } + + return new Vector3d(RandomPos(), RandomPos(), RandomPos()); + } + + /// <summary> + /// Returns a randomised orbit around a central, heavy mass. + /// </summary> + /// <param name="positionVector">The position vector for the body for which to generate the orbit.</param> + /// <returns>The magnitude of the velocity of the orbit.</returns> + public static Vector3d RandomOrbit(Vector3d positionVector) + { + Vector3d position = positionVector; + + // F = G m1 - m2 / distance + double distanceToCentralBody = positionVector.Magnitude(); + double numerator = Constants.G * Constants.SolarMass; + double velocityMagnitude = Math.Sqrt(numerator / distanceToCentralBody); + + double absAngle = Math.Atan(Math.Abs(position.X / position.Y)); + double velocityTheta = Math.PI / 2 - absAngle; + double velocityPhi = Rng.NextDouble() * Math.PI; + + double vx = -1 * Math.Sign(position.Y) * Math.Cos(velocityTheta) * velocityMagnitude; + double vy = Math.Sign(position.X) * Math.Sin(velocityTheta) * velocityMagnitude; + double vz = 0; + + // Randomly orient the orbit + return !(Rng.NextDouble() <= 0.5f) ? new Vector3d(vx, vy, vz) : new Vector3d(-vx, -vy, -vz); + } + } +} +\ No newline at end of file diff --git a/StarSim/StarSimLib/Vector3d.cs b/StarSim/StarSimLib/Vector3d.cs @@ -0,0 +1,362 @@ +using System; +using System.Data.Common; +using System.Transactions; + +namespace StarSimLib +{ + /// <summary> + /// A 3D vector with <see cref="double"/> components. + /// </summary> + public struct Vector3d + { + /// <summary> + /// The x component of the vector. + /// </summary> + public double X; + + /// <summary> + /// The y component of the vector. + /// </summary> + public double Y; + + /// <summary> + /// The z component of the vector. + /// </summary> + public double Z; + + /// <summary> + /// Initialises a new instance of the <see cref="Vector3d"/> struct. Sets Z to 0. + /// </summary> + /// <param name="x">The x component.</param> + /// <param name="y">The y component.</param> + public Vector3d(double x, double y) + { + X = x; + Y = y; + Z = 0; + } + + /// <summary> + /// Initialises a new instance of the <see cref="Vector3d"/> struct. + /// </summary> + /// <param name="x">The x component.</param> + /// <param name="y">The y component.</param> + /// <param name="z">The z component.</param> + public Vector3d(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + + #region Operator Overloads + + /// <summary> + /// Implements the addition operator for 2 <see cref="Vector3d"/>s. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The other <see cref="Vector3d"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator +(Vector3d vector, Vector3d vector2) + { + vector.X += vector2.X; + vector.Y += vector2.Y; + vector.Z += vector2.Z; + + return vector; + } + + /// <summary> + /// Implements the addition operator for a <see cref="Vector3d"/> and a (X, Y, Z) tuple. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The 3D tuple.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator +(Vector3d vector, (double X, double Y, double Z) vector2) + { + (double x, double y, double z) = vector2; + + vector.X += x; + vector.Y += y; + vector.Z += z; + + return vector; + } + + /// <summary> + /// Implements the addition operator for a <see cref="Vector3d"/> and a (X, Y, Z) tuple. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The 3D tuple.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator +((double X, double Y, double Z) vector2, Vector3d vector) => vector + vector2; + + /// <summary> + /// Implements the addition operator for a <see cref="Vector3d"/> and a scalar <see cref="double"/> value. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="scalar">The scalar <see cref="double"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator +(Vector3d vector, double scalar) + { + vector.X += scalar; + vector.Y += scalar; + vector.Z += scalar; + + return vector; + } + + /// <summary> + /// Implements the addition operator for a <see cref="Vector3d"/> and a scalar <see cref="double"/> value. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="scalar">The scalar <see cref="double"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator +(double scalar, Vector3d vector) => vector + scalar; + + /// <summary> + /// Implements the subtraction operator for 2 <see cref="Vector3d"/>s. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The other <see cref="Vector3d"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator -(Vector3d vector, Vector3d vector2) + { + vector.X -= vector2.X; + vector.Y -= vector2.Y; + vector.Z -= vector2.Z; + + return vector; + } + + /// <summary> + /// Implements the subtraction operator for a <see cref="Vector3d"/> and a (X, Y, Z) tuple. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The 3D tuple.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator -(Vector3d vector, (double X, double Y, double Z) vector2) + { + (double x, double y, double z) = vector2; + + vector.X -= x; + vector.Y -= y; + vector.Z -= z; + + return vector; + } + + /// <summary> + /// Implements the subtraction operator for a <see cref="Vector3d"/> and a (X, Y, Z) tuple. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The 3D tuple.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator -((double X, double Y, double Z) vector2, Vector3d vector) => vector - vector2; + + /// <summary> + /// Implements the subtraction operator for a <see cref="Vector3d"/> and a scalar <see cref="double"/> value. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="scalar">The scalar <see cref="double"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator -(Vector3d vector, double scalar) + { + vector.X -= scalar; + vector.Y -= scalar; + vector.Z -= scalar; + + return vector; + } + + /// <summary> + /// Implements the subtraction operator for a <see cref="Vector3d"/> and a scalar <see cref="double"/> value. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="scalar">The scalar <see cref="double"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator -(double scalar, Vector3d vector) => vector - scalar; + + /// <summary> + /// Implements the multiplication operator for 2 <see cref="Vector3d"/>s. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The other <see cref="Vector3d"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator *(Vector3d vector, Vector3d vector2) + { + vector.X *= vector2.X; + vector.Y *= vector2.Y; + vector.Z *= vector2.Z; + + return vector; + } + + /// <summary> + /// Implements the multiplication operator for a <see cref="Vector3d"/> and a (X, Y, Z) tuple. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The 3D tuple.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator *(Vector3d vector, (double X, double Y, double Z) vector2) + { + (double x, double y, double z) = vector2; + + vector.X *= x; + vector.Y *= y; + vector.Z *= z; + + return vector; + } + + /// <summary> + /// Implements the multiplication operator for a <see cref="Vector3d"/> and a (X, Y, Z) tuple. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The 3D tuple.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator *((double X, double Y, double Z) vector2, Vector3d vector) => vector * vector2; + + /// <summary> + /// Implements the multiplication operator for a <see cref="Vector3d"/> and a scalar <see cref="double"/> value. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="scalar">The scalar <see cref="double"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator *(Vector3d vector, double scalar) + { + vector.X *= scalar; + vector.Y *= scalar; + vector.Z *= scalar; + + return vector; + } + + /// <summary> + /// Implements the multiplication operator for a <see cref="Vector3d"/> and a scalar <see cref="double"/> value. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="scalar">The scalar <see cref="double"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator *(double scalar, Vector3d vector) => vector * scalar; + + /// <summary> + /// Implements the division operator for 2 <see cref="Vector3d"/>s. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The other <see cref="Vector3d"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator /(Vector3d vector, Vector3d vector2) + { + vector.X /= vector2.X; + vector.Y /= vector2.Y; + vector.Z /= vector2.Z; + + return vector; + } + + /// <summary> + /// Implements the division operator for a <see cref="Vector3d"/> and a (X, Y, Z) tuple. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The 3D tuple.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator /(Vector3d vector, (double X, double Y, double Z) vector2) + { + (double x, double y, double z) = vector2; + + vector.X /= x; + vector.Y /= y; + vector.Z /= z; + + return vector; + } + + /// <summary> + /// Implements the division operator for a <see cref="Vector3d"/> and a (X, Y, Z) tuple. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="vector2">The 3D tuple.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator /((double X, double Y, double Z) vector2, Vector3d vector) => vector / vector2; + + /// <summary> + /// Implements the division operator for a <see cref="Vector3d"/> and a scalar <see cref="double"/> value. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="scalar">The scalar <see cref="double"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator /(Vector3d vector, double scalar) + { + vector.X /= scalar; + vector.Y /= scalar; + vector.Z /= scalar; + + return vector; + } + + /// <summary> + /// Implements the division operator for a <see cref="Vector3d"/> and a scalar <see cref="double"/> value. + /// </summary> + /// <param name="vector">The original <see cref="Vector3d"/>.</param> + /// <param name="scalar">The scalar <see cref="double"/>.</param> + /// <returns>The new <see cref="Vector3d"/>.</returns> + public static Vector3d operator /(double scalar, Vector3d vector) => vector / scalar; + + #endregion Operator Overloads + + /// <summary> + /// Returns the absolute value of this <see cref="Vector3d"/>. + /// </summary> + /// <returns>The absolute value (magnitude) of this <see cref="Vector3d"/>, as a <see cref="double"/>.</returns> + public double Abs() + { + return Math.Sqrt(X * X + Y * Y + Z * Z); + } + + /// <summary> + /// Returns the magnitude of this <see cref="Vector3d"/>. + /// </summary> + /// <returns>The magnitude (absolute value) of this <see cref="Vector3d"/>, as a <see cref="double"/>.</returns> + public double Magnitude() + { + return Abs(); + } + + /// <summary> + /// Holds unit vectors. + /// </summary> + public static class UnitVectors + { + /// <summary> + /// The positive unit vector for the X axis. + /// </summary> + public static readonly Vector3d Left = new Vector3d(1, 0, 0); + + /// <summary> + /// The positive unit vector for the Y axis. + /// </summary> + public static readonly Vector3d Up = new Vector3d(0, 1, 0); + + /// <summary> + /// The positive unit vector for the Z axis. + /// </summary> + public static readonly Vector3d Forwards = new Vector3d(0, 0, 1); + + /// <summary> + /// The negative unit vector for the X axis. + /// </summary> + public static readonly Vector3d Right = new Vector3d(-1, 0, 0); + + /// <summary> + /// The negative unit vector for the Y axis. + /// </summary> + public static readonly Vector3d Down = new Vector3d(0, -1, 0); + + /// <summary> + /// The negative unit vector for the Z axis. + /// </summary> + public static readonly Vector3d Backwards = new Vector3d(0, 0, -1); + } + } +} +\ No newline at end of file