using Newtonsoft.Json; using RhythmBase.Converters; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; namespace RhythmBase.Components { /// /// A point whose horizontal and vertical coordinates are nullable /// [JsonConverter(typeof(RDPointsConverter))] [DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")] public struct RDPointI(int? x, int? y) : IRDVortex { /// /// Initializes a new instance of the struct with the specified size. /// /// The size to initialize the point with. public RDPointI(RDSizeI sz) : this(sz.Width, sz.Height) { } /// /// Initializes a new instance of the struct with the specified nullable size. /// /// The nullable size to initialize the point with. public RDPointI(RDSizeN sz) : this( (int)Math.Round((double)sz.Width), (int)Math.Round((double)sz.Height)) { } /// /// Gets a value indicating whether this point is empty. /// public readonly bool IsEmpty => X == null && Y == null; /// /// Gets or sets the X coordinate of this point. /// public int? X { get; set; } = x; /// /// Gets or sets the Y coordinate of this point. /// public int? Y { get; set; } = y; /// /// Offsets this point by the specified point. /// /// The point to offset by. public void Offset(RDPointI p) { X += p.X; Y += p.Y; } /// /// Offsets this point by the specified amounts. /// /// The amount to offset the X coordinate. /// The amount to offset the Y coordinate. public void Offset(int? dx, int? dy) { X += dx; Y += dy; } /// /// Returns a new point that is the ceiling of the specified point. /// /// The point to ceiling. /// A new point that is the ceiling of the specified point. public static RDPointI Ceiling(RDPoint value) => new( (value.X == null) ? null : (int)Math.Ceiling((double)value.X), (value.Y == null) ? null : (int)Math.Ceiling((double)value.Y) ); /// /// Adds the specified size to the specified point. /// /// The point to add to. /// The size to add. /// A new point that is the sum of the specified point and size. public static RDPointI Add(RDPointI pt, RDSizeI sz) => new( pt.X + sz.Width, pt.Y + sz.Height ); /// /// Returns a new point that is the truncated value of the specified point. /// /// The point to truncate. /// A new point that is the truncated value of the specified point. public static RDPointI Truncate(RDPoint value) => new( (value.X == null) ? null : (int)Math.Truncate((double)value.X), (value.Y == null) ? null : (int)Math.Truncate((double)value.Y) ); /// /// Subtracts the specified size from the specified point. /// /// The point to subtract from. /// The size to subtract. /// A new point that is the difference of the specified point and size. public static RDPointI Subtract(RDPointI pt, RDSizeI sz) => new( pt.X - sz.Width, pt.Y - sz.Height ); /// /// Returns a new point that is the rounded value of the specified point. /// /// The point to round. /// A new point that is the rounded value of the specified point. public static RDPointI Round(RDPoint value) => new( ((value.X == null) ? null : (int)Math.Round((double)value.X.Value)), ((value.Y == null) ? null : (int)Math.Round((double)value.Y.Value)) ); /// /// Multiplies this point by the specified matrix. /// /// The matrix to multiply by. /// A new point that is the result of the multiplication. /// Thrown when the matrix is not a 2x2 matrix. public readonly RDPoint MultipyByMatrix(float[,] matrix) { if (matrix.Rank == 2 && matrix.Length == 4) { int? num = X; float? num2 = (num == null ? null : num) * matrix[0, 0]; num = Y; float? x = num2 + ((num == null ? null : num) * matrix[1, 0]); num = X; float? num3 = (num == null ? null : num) * matrix[0, 1]; num = Y; RDPoint MultipyByMatrix = new(x, num3 + ((num != null) ? num : null) * matrix[1, 1]); return MultipyByMatrix; } throw new Exception("Matrix not match, 2*2 matrix expected."); } /// /// Rotates this point by the specified angle. /// /// The angle to rotate by. /// A new point that is the result of the rotation. public readonly RDPoint 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)-(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 RDPoint Rotate(RDPointN pivot, float angle) => ((RDPoint)this - new RDSizeN(pivot)).Rotate(angle) + new RDSizeN(pivot); /// public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is RDPointI e && Equals(e); /// public override readonly int GetHashCode() => HashCode.Combine(X, Y); /// public override readonly string ToString() => $"[{(X?.ToString()) ?? "null"},{(Y?.ToString()) ?? "null"}]"; /// public readonly bool Equals(RDPointI other) => other.X == X && other.Y == Y; /// public static RDPointI operator +(RDPointI pt, RDSizeI sz) => Add(pt, sz); /// public static RDPointI operator -(RDPointI pt, RDSizeI sz) => Subtract(pt, sz); /// public static RDPointI operator *(RDPointI pt, int? x) => new(pt.X * x, pt.Y * x); /// public static RDPointI operator /(RDPointI pt, int? x) => new(pt.X / x, pt.Y / x); /// public static bool operator ==(RDPointI left, RDPointI right) => left.Equals(right); /// public static bool operator !=(RDPointI left, RDPointI right) => !left.Equals(right); /// /// Implicitly converts an to an . /// /// The to convert. /// A new with the same coordinates as the input . public static implicit operator RDPoint(RDPointI p) => new(p.X, p.Y); /// /// Implicitly converts an to an . /// /// The to convert. /// A new with the same coordinates as the input . public static implicit operator RDPointE(RDPointI p) => new(p.X, p.Y); /// /// Explicitly converts an to an . /// /// The to convert. /// A new with the same dimensions as the input . public static explicit operator RDSizeI(RDPointI p) => new(p); private readonly string GetDebuggerDisplay() => ToString(); } }