//
// Copyright 2021–2022 alterNERDtive.
//
// This file is part of EDNA.
//
// EDNA is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// EDNA is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with EDNA. If not, see <https://www.gnu.org/licenses/>.
//
using System;
namespace alterNERDtive.Edna
{
///
/// A location in the galaxy, represented by coordinates and a value for
/// precision (all in ly).
///
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Either this or wrong class/struct order 🤷")]
public struct Coordinates
{
///
/// Initializes a new instance of the struct.
///
/// The x coordinate.
/// The y coordinate.
/// The z coordinate.
/// The available precision.
public Coordinates(double x, double y, double z, int precision)
=> (this.X, this.Y, this.Z, this.Precision) = (x, y, z, precision);
///
/// Initializes a new instance of the struct.
///
/// The x coordinate.
/// The y coordinate.
/// The z coordinate.
public Coordinates(double x, double y, double z)
=> (this.X, this.Y, this.Z, this.Precision) = (x, y, z, 0);
///
/// Initializes a new instance of the struct.
///
/// An EDTS system/location to convert.
public Coordinates(Edts.StarSystem edtsSystem)
=> (this.X, this.Y, this.Z, this.Precision)
= ((int)edtsSystem.Position.X, (int)edtsSystem.Position.Y, (int)edtsSystem.Position.Z, (int)edtsSystem.Uncertainty);
///
/// Initializes a new instance of the struct.
///
/// An EDSM system to convert.
public Coordinates(Edsm.ApiSystem edsmSystem)
=> (this.X, this.Y, this.Z, this.Precision)
= (edsmSystem.Coords.Value.X, edsmSystem.Coords.Value.Y, edsmSystem.Coords.Value.Z, 0);
///
/// Initializes a new instance of the struct.
///
/// A set of EDSM coordinates to convert.
public Coordinates(Edsm.Coordinates edsmCoords)
=> (this.X, this.Y, this.Z, this.Precision)
= (edsmCoords.X, edsmCoords.Y, edsmCoords.Z, 0);
///
/// Gets the x coordinate.
///
public double X { get; }
///
/// Gets the y coordinate.
///
public double Y { get; }
///
/// Gets the z coordinate.
///
public double Z { get; }
///
/// Gets the precision to which the location can be calculated. This is
/// an actual ± for distance, not a precision _per axis_ as for a Location.
///
public int Precision { get; }
public static bool operator ==(Coordinates a, Coordinates b)
=> a.X == b.X && a.Y == b.Y && a.Z == b.Z && a.Precision == 0 && b.Precision == 0;
public static bool operator !=(Coordinates a, Coordinates b)
=> !(a == b);
///
public override bool Equals(object o)
=> o is Coordinates location && this == location;
///
public override int GetHashCode()
{
return Tuple.Create(this.X, this.Y, this.Z, this.Precision).GetHashCode();
}
///
/// The distance to another location.
///
/// The other location.
/// The distance between both locations.
public Distance DistanceTo(Coordinates location)
{
if (this == location && this.Precision == 0)
{
return new Distance(value: 0);
}
else
{
// Precision actually adds up weird. Since Location precision is
// per each individual axis, not a radius, we’re dealing with a
// cube, not a sphere.
//
// Therefore for a precision p the system can be √(p²+p²+p²) =
// √(3p²) = √3*p light years from the exact coordinates.
//
// For a distance between two Locations with precision p,q these
// add up as √3(p+q). This is only the worst case scenario and
// can be less depending on the angle, but worst case is fine
// here.
return new Distance(
value: Math.Sqrt(
Math.Pow(this.X - location.X, 2)
+ Math.Pow(this.Y - location.Y, 2)
+ Math.Pow(this.Z - location.Z, 2)),
precision: (int)Math.Ceiling(Math.Sqrt(3) * (this.Precision + location.Precision)));
}
}
}
///
/// A distance between two objects in the galaxy, represented by the
/// distance itself and a value for precision.
///
public struct Distance
{
///
/// Initializes a new instance of the struct.
///
/// The distance, with absolute precision.
public Distance(double value)
: this(value: value, precision: 0)
{
}
///
/// Initializes a new instance of the struct.
///
/// The distance.
/// The available precision.
public Distance(double value, int precision)
{
(this.Value, this.Precision) = (value, precision);
}
///
/// Gets the distance value in ly.
///
public double Value { get; }
///
/// Gets the available precision in ly.
///
public double Precision { get; }
}
///
/// An object that can be located in the galaxy.
///
public abstract class Locatable
{
///
/// Gets the object’s location in the galaxy.
///
public Coordinates Coordinates { get; private set; }
///
/// The distance to another Locatable object.
///
/// The other Locatable object.
/// The distance between both objects.
public Distance DistanceTo(Locatable locatable)
{
return this.Coordinates.DistanceTo(locatable.Coordinates);
}
///
/// The distance to a given Location.
///
/// The Location to compare to.
/// The distance to said Location.
public Distance DistanceTo(Coordinates location)
{
return this.Coordinates.DistanceTo(location);
}
}
}