using Newtonsoft.Json;
using RhythmBase.Converters;
using RhythmBase.Extensions;
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 RDPoint(float? x, float? y) : IRDVortex, IRDVortex
{
///
/// Initializes a new instance of the struct with the specified size.
///
/// The size to initialize the point with.
public RDPoint(RDSize sz) : this(sz.Width, 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 the point.
///
public float? X { get; set; } = x;
///
/// Gets or sets the Y coordinate of the point.
///
public float? Y { get; set; } = y;
///
/// Offsets the point by the specified point.
///
/// The point to offset by.
public void Offset(RDPoint p)
{
X += p.X;
Y += p.Y;
}
///
/// Offsets the point by the specified amounts.
///
/// The amount to offset the X coordinate.
/// The amount to offset the Y coordinate.
public void Offset(float? dx, float? dy)
{
X += dx;
Y += dy;
}
///
/// Adds the specified size to the point.
///
/// The point to add to.
/// The size to add.
/// A new point that is the result of the addition.
public static RDPoint Add(RDPoint pt, RDSizeI sz) => new(
pt.X + sz.Width, pt.Y + sz.Height
);
///
/// Adds the specified size to the point.
///
/// The point to add to.
/// The size to add.
/// A new point that is the result of the addition.
public static RDPoint Add(RDPoint pt, RDSize sz) => new(
pt.X + sz.Width, pt.Y + sz.Height
);
///
/// Subtracts the specified size from the point.
///
/// The point to subtract from.
/// The size to subtract.
/// A new point that is the result of the subtraction.
public static RDPoint Subtract(RDPoint pt, RDSizeI sz) => new(
pt.X - sz.Width, pt.Y - sz.Height
);
///
/// Subtracts the specified size from the point.
///
/// The point to subtract from.
/// The size to subtract.
/// A new point that is the result of the subtraction.
public static RDPoint Subtract(RDPoint pt, RDSize sz) => new(
pt.X - sz.Width, pt.Y - sz.Height
);
///
/// Multiplies the 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)
{
RDPoint 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 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) => (this - new RDSizeN(pivot)).Rotate(angle) + new RDSizeN(pivot);
///
public override readonly bool Equals([NotNullWhen(true)] object? obj) => obj is RDPoint 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?.ToString() ?? "null"}, {Y?.ToString() ?? "null"}]";
///
public readonly bool Equals(RDPoint other) => other.X.NullableEquals(X) && other.Y.NullableEquals(Y);
///
public static RDPoint operator +(RDPoint pt, RDSizeI sz) => Add(pt, sz);
///
public static RDPoint operator +(RDPoint pt, RDSize sz) => Add(pt, sz);
///
public static RDPoint operator -(RDPoint pt, RDSizeI sz) => Subtract(pt, sz);
///
public static RDPoint operator -(RDPoint pt, RDSize sz) => Subtract(pt, sz);
///
public static RDPoint operator *(RDPoint pt, float? x) => new(pt.X * x, pt.Y * x);
///
public static RDPoint operator /(RDPoint pt, float? x) => new(pt.X / x, pt.Y / x);
///
public static bool operator ==(RDPoint left, RDPoint right) => left.Equals(right);
///
public static bool operator !=(RDPoint left, RDPoint 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 RDPointE(RDPoint p) => new(p.X, p.Y);
///
/// Explicitly converts an to an .
///
/// The to convert.
/// A new with the same coordinates as the input .
public static explicit operator RDSize(RDPoint p) => new(p.X, p.Y);
private readonly string GetDebuggerDisplay() => ToString();
}
}