// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.ComponentModel; using System.Numerics; using System.Runtime.CompilerServices; namespace SixLabors.Primitives { /// /// 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); /// /// 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 value 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 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))); /// /// 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)); /// /// Transforms a point by a specified 3x2 matrix. /// /// The point to transform /// The transformation matrix used /// The transformed [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point Transform(Point point, Matrix3x2 matrix) => Round(Vector2.Transform(new Vector2(point.X, point.Y), matrix)); /// /// 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() { return HashHelpers.Combine(this.X.GetHashCode(), this.Y.GetHashCode()); } /// public override string ToString() { return $"Point [ X={this.X}, Y={this.Y} ]"; } /// public override bool Equals(object obj) => obj is Point other && this.Equals(other); /// [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)); } }