// // 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); } } }