using Newtonsoft.Json; using RhythmBase.Converters; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace RhythmBase.Components { /// /// A point whose horizontal and vertical coordinates are non-nullable /// [JsonConverter(typeof(RDPointsConverter))] [DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] public struct RDPointNI(int x, int y) : IRDVortex { /// /// Initializes a new instance of the struct with the specified size. /// /// The size to initialize the point with. public RDPointNI(RDSizeNI sz) : this(sz.Width, sz.Height) { } /// /// Gets or sets the X coordinate of the point. /// public int X { get; set; } = x; /// /// Gets or sets the Y coordinate of the point. /// public int Y { get; set; } = y; /// /// Offsets the point by the specified point. /// /// The point to offset by. public void Offset(RDPointNI p) { X += p.X; Y += p.Y; } /// /// Offsets the point by the specified size. /// /// The size to offset by. public void Offset(RDSizeNI p) { X += p.Width; Y += p.Height; } /// /// Offsets the point by the specified horizontal and vertical amounts. /// /// The horizontal offset. /// The vertical offset. public void Offset(int dx, int dy) { X += dx; Y += dy; } /// /// Returns a new point with coordinates rounded up to the nearest integer values. /// /// The point to round up. /// A new point with coordinates rounded up. public static RDPointNI Ceiling(RDPointN value) => new( (int)Math.Ceiling((double)value.X), (int)Math.Ceiling((double)value.Y) ); /// /// Adds the specified size to the point. /// /// The point to add to. /// The size to add. /// A new point with the size added. public static RDPointNI Add(RDPointNI pt, RDSizeNI sz) => new( pt.X + sz.Width, pt.Y + sz.Height ); /// /// Returns a new point with coordinates truncated to the nearest integer values. /// /// The point to truncate. /// A new point with coordinates truncated. public static RDPointNI Truncate(RDPointN value) => new( (int)Math.Truncate((double)value.X), (int)Math.Truncate((double)value.Y) ); /// /// Subtracts the specified size from the point. /// /// The point to subtract from. /// The size to subtract. /// A new point with the size subtracted. public static RDPointNI Subtract(RDPointNI pt, RDSizeNI sz) => new( pt.X - sz.Width, pt.Y - sz.Height ); /// /// Returns a new point with coordinates rounded to the nearest integer values. /// /// The point to round. /// A new point with coordinates rounded. public static RDPointNI Round(RDPointN value) => new( (int)Math.Round((double)value.X), (int)Math.Round((double)value.Y) ); /// /// Multiplies the point by the specified 2x2 matrix. /// /// The 2x2 matrix to multiply by. /// A new point resulting from the matrix multiplication. /// Thrown when the matrix is not a 2x2 matrix. public readonly RDPointN MultipyByMatrix(float[,] matrix) { if (matrix.Rank == 2 && matrix.Length == 4) { RDPointN MultipyByMatrix = new(X * matrix[0, 0] + (Y * matrix[1, 0]), (X * matrix[0, 1]) + Y * matrix[1, 1]); return MultipyByMatrix; } throw new Exception("Matrix not match, 2*2 matrix expected."); } /// /// Rotate. /// public readonly RDPointN Rotate(float angle) { float[,] array = new float[2, 2]; array[0, 0] = (float)Math.Cos((double)angle); array[0, 1] = (float)Math.Sin((double)angle); array[1, 0] = -(float)Math.Sin((double)angle); array[1, 1] = (float)Math.Cos((double)angle); return MultipyByMatrix(array); } /// /// Rotate at a given pivot. /// /// Given pivot. /// Angle. /// public readonly RDPointN Rotate(RDPointN pivot, float angle) => ((RDPointN)this - new RDSizeN(pivot)).Rotate(angle) + new RDSizeN(pivot); /// public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is RDPointNI e && Equals(e); /// public override readonly int GetHashCode() { HashCode h = default; h.Add(X); h.Add(Y); return h.ToHashCode(); } /// public override readonly string ToString() => $"[{X}, {Y}]"; /// public readonly bool Equals(RDPointNI other) => other.X == X && other.Y == Y; /// public static RDPointNI operator +(RDPointNI pt, RDSizeNI sz) => Add(pt, sz); /// public static RDPointNI operator -(RDPointNI pt, RDSizeNI sz) => Subtract(pt, sz); /// public static RDPointNI operator *(RDPointNI pt, int x) => new(pt.X * x, pt.Y * x); /// public static RDPointNI operator /(RDPointNI pt, int x) => new(pt.X / x, pt.Y / x); /// public static bool operator ==(RDPointNI left, RDPointNI right) => left.Equals(right); /// public static bool operator !=(RDPointNI left, RDPointNI right) => !left.Equals(right); /// /// Implicitly converts an to an . /// /// The to convert. /// An with the same coordinates. public static implicit operator RDPointN(RDPointNI p) => new(p.X, p.Y); /// /// Implicitly converts an to an . /// /// The to convert. /// An with the same coordinates. public static implicit operator RDPointI(RDPointNI p) => new(new int?(p.X), new int?(p.Y)); /// /// Implicitly converts an to an . /// /// The to convert. /// An with the same coordinates. public static implicit operator RDPointE(RDPointNI p) => new(p.X, p.Y); /// /// Explicitly converts an to an . /// /// The to convert. /// An with the same dimensions. public static explicit operator RDSizeNI(RDPointNI p) => new(p.X, p.Y); private readonly string GetDebuggerDisplay() => ToString(); } }