// // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. // namespace SixLabors.Primitives { using System; using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; /// /// Represents an ordered pair of integer x- and y-coordinates that defines a point in /// a two-dimensional plane. /// /// /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance, /// as it avoids the need to create new values for modification operations. /// public struct Point : IEquatable { /// /// Represents a that has X and Y values set to zero. /// public static readonly Point Empty = default(Point); /// /// Represents a that has X and Y values set to zero. /// public static readonly Point Zero = new Point(0, 0); /// /// Initializes a new instance of the struct. /// /// The horizontal and vertical position of the point. public Point(int value) : this() { this.X = LowInt16(value); this.Y = HighInt16(value); } /// /// Initializes a new instance of the struct. /// /// The horizontal position of the point. /// The vertical position of the point. public Point(int x, int y) : this() { this.X = x; this.Y = y; } /// /// Initializes a new instance of the struct from the given . /// /// The size public Point(Size size) { this.X = size.Width; this.Y = size.Height; } /// /// Gets or sets the x-coordinate of this . /// public int X { get; set; } /// /// Gets or sets the y-coordinate of this . /// public int Y { get; set; } /// /// Gets a value indicating whether this is empty. /// [EditorBrowsable(EditorBrowsableState.Never)] public bool IsEmpty => this.Equals(Empty); /// /// Creates a with the coordinates of the specified . /// /// The point [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator PointF(Point point) => new PointF(point.X, point.Y); /// /// Creates a with the coordinates of the specified . /// /// The point [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Vector2(Point point) => new Vector2(point.X, point.Y); /// /// Creates a with the coordinates of the specified . /// /// The point [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Size(Point point) => new Size(point.X, point.Y); /// /// Negates the given point by multiplying all values by -1. /// /// The source point. /// The negated point. public static Point operator -(Point value) => new Point(-value.X, -value.Y); /// /// Translates a by a given . /// /// The point on the left hand of the operand. /// The size on the right hand of the operand. /// /// The /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point operator +(Point point, Size size) => Add(point, size); /// /// Translates a by the negative of a given . /// /// The point on the left hand of the operand. /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point operator -(Point point, Size size) => Subtract(point, size); /// /// Multiplies by a producing . /// /// Multiplier of type . /// Multiplicand of type . /// Product of type . public static Point operator *(int left, Point right) => Multiply(right, left); /// /// Multiplies by a producing . /// /// Multiplicand of type . /// Multiplier of type . /// Product of type . public static Point operator *(Point left, int right) => Multiply(left, right); /// /// Divides by a producing . /// /// Dividend of type . /// Divisor of type . /// Result of type . public static Point operator /(Point left, int right) => new Point(left.X / right, left.Y / right); /// /// Compares two objects for equality. /// /// The on the left side of the operand. /// The on the right side of the operand. /// /// True if the current left is equal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Point left, Point right) => left.Equals(right); /// /// Compares two objects for inequality. /// /// The on the left side of the operand. /// The on the right side of the operand. /// /// True if the current left is unequal to the parameter; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Point left, Point right) => !left.Equals(right); /// /// Translates a by the negative of a given . /// /// The point on the left hand of the operand. /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Add(Point point, Size size) => new Point(unchecked(point.X + size.Width), unchecked(point.Y + size.Height)); /// /// Translates a by the negative of a given value /// /// The point on the left hand of the operand. /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Multiply(Point point, int value) => new Point(unchecked(point.X * value), unchecked(point.Y * value)); /// /// Translates a by the negative of a given . /// /// The point on the left hand of the operand. /// The size on the right hand of the operand. /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Subtract(Point point, Size size) => new Point(unchecked(point.X - size.Width), unchecked(point.Y - size.Height)); /// /// Converts a to a by performing a ceiling operation on all the coordinates. /// /// The point /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Ceiling(PointF point) => new Point(unchecked((int)MathF.Ceiling(point.X)), unchecked((int)MathF.Ceiling(point.Y))); /// /// Converts a to a by performing a round operation on all the coordinates. /// /// The point /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Round(PointF point) => new Point(unchecked((int)MathF.Round(point.X)), unchecked((int)MathF.Round(point.Y))); /// /// Converts a to a by performing a truncate operation on all the coordinates. /// /// The point /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Truncate(PointF point) => new Point(unchecked((int)point.X), unchecked((int)point.Y)); /// /// Converts a to a by performing a round operation on all the coordinates. /// /// The vector /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Round(Vector2 vector) => new Point(unchecked((int)MathF.Round(vector.X)), unchecked((int)MathF.Round(vector.Y))); /// /// Rotates a point around the given rotation matrix. /// /// The point to rotate /// Rotation matrix used /// The rotated [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Rotate(Point point, System.Numerics.Matrix3x2 rotation) => Round(Vector2.Transform(new Vector2(point.X, point.Y), rotation)); /// /// Skews a point using the given skew matrix. /// /// The point to rotate /// Rotation matrix used /// The rotated [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Skew(Point point, System.Numerics.Matrix3x2 skew) => Round(Vector2.Transform(new Vector2(point.X, point.Y), skew)); /// /// Translates this by the specified amount. /// /// The amount to offset the x-coordinate. /// The amount to offset the y-coordinate. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(int dx, int dy) { unchecked { this.X += dx; this.Y += dy; } } /// /// Translates this by the specified amount. /// /// The used offset this . [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Offset(Point point) => this.Offset(point.X, point.Y); /// public override int GetHashCode() => this.GetHashCode(this); /// public override string ToString() { if (this.IsEmpty) { return "Point [ Empty ]"; } return $"Point [ X={this.X}, Y={this.Y} ]"; } /// public override bool Equals(object obj) => obj is Point && this.Equals((Point)obj); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Point other) => this.X == other.X && this.Y == other.Y; private static short HighInt16(int n) => unchecked((short)((n >> 16) & 0xffff)); private static short LowInt16(int n) => unchecked((short)(n & 0xffff)); private int GetHashCode(Point point) => HashHelpers.Combine(point.X.GetHashCode(), point.Y.GetHashCode()); /// /// Transforms a point by the given matrix. /// /// The source point /// The transformation matrix. /// public static PointF Transform(Point position, Matrix3x2 matrix) { return Vector2.Transform(position, matrix); } } }