// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace SixLabors.Fonts {
///
/// Stores a set of four single precision floating points that represent the location and size of a rectangle.
///
public readonly struct FontRectangle : IEquatable
{
///
/// Represents a that has X, Y, Width, and Height values set to zero.
///
public static readonly FontRectangle Empty;
///
/// Initializes a new instance of the struct.
///
/// The horizontal position of the rectangle.
/// The vertical position of the rectangle.
/// The width of the rectangle.
/// The height of the rectangle.
public FontRectangle(float x, float y, float width, float height)
{
this.X = x;
this.Y = y;
this.Width = width;
this.Height = height;
}
///
/// Initializes a new instance of the struct.
///
///
/// The which specifies the rectangles point in a two-dimensional plane.
///
///
/// The which specifies the rectangles height and width.
///
public FontRectangle(Vector2 point, Vector2 size)
: this(point.X, point.Y, size.X, size.Y)
{
}
///
/// Initializes a new instance of the structure using the specified bounding box.
///
/// The bounding box that defines the position and size of the rectangle.
internal FontRectangle(in Bounds bound)
{
this.X = bound.Min.X;
this.Y = bound.Min.Y;
Vector2 size = bound.Max - bound.Min;
this.Width = size.X;
this.Height = size.Y;
}
///
/// Gets the x-coordinate of this .
///
public float X { get; }
///
/// Gets the y-coordinate of this .
///
public float Y { get; }
///
/// Gets the width of this .
///
public float Width { get; }
///
/// Gets the height of this .
///
public float Height { get; }
///
/// Gets the coordinates of the upper-left corner of the rectangular region represented by this .
///
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly Vector2 Location
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(this.X, this.Y);
}
///
/// Gets the size of this .
///
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly Vector2 Size
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(this.Width, this.Height);
}
///
/// Gets a value indicating whether this is empty.
///
[EditorBrowsable(EditorBrowsableState.Never)]
public readonly bool IsEmpty => (this.Width <= 0) || (this.Height <= 0);
///
/// Gets the y-coordinate of the top edge of this .
///
public readonly float Top => this.Y;
///
/// Gets the x-coordinate of the right edge of this .
///
public float Right
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.X + this.Width;
}
///
/// Gets the y-coordinate of the bottom edge of this .
///
public float Bottom
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => this.Y + this.Height;
}
///
/// Gets the x-coordinate of the left edge of this .
///
public float Left => this.X;
///
/// 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 ==(in FontRectangle left, in FontRectangle 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 !=(in FontRectangle left, in FontRectangle right) => !left.Equals(right);
///
/// Creates a new with the specified location and size.
/// The left coordinate of the rectangle.
/// The top coordinate of the rectangle.
/// The right coordinate of the rectangle.
/// The bottom coordinate of the rectangle.
/// The .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once InconsistentNaming
public static FontRectangle FromLTRB(float left, float top, float right, float bottom) => new(left, top, right - left, bottom - top);
///
/// Returns the center point of the given .
///
/// The rectangle.
/// The .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Center(in FontRectangle rectangle) => new(rectangle.Left + (rectangle.Width / 2), rectangle.Top + (rectangle.Height / 2));
///
/// Creates a rectangle that represents the intersection between and
/// . If there is no intersection, an empty rectangle is returned.
///
/// The first rectangle.
/// The second rectangle.
/// The .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static FontRectangle Intersect(in FontRectangle a, in FontRectangle b)
{
float x1 = MathF.Max(a.X, b.X);
float x2 = MathF.Min(a.Right, b.Right);
float y1 = MathF.Max(a.Y, b.Y);
float y2 = MathF.Min(a.Bottom, b.Bottom);
if (x2 >= x1 && y2 >= y1)
{
return new FontRectangle(x1, y1, x2 - x1, y2 - y1);
}
return Empty;
}
///
/// Creates a new from the given
/// that is inflated by the specified amount.
///
/// The rectangle.
/// The amount to inflate the width by.
/// The amount to inflate the height by.
/// A new .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static FontRectangle Inflate(in FontRectangle rectangle, float x, float y)
=> rectangle.Inflate(x, y);
///
/// Creates a new by transforming the given rectangle by the given matrix.
///
/// The source rectangle.
/// The transformation matrix.
/// A transformed .
public static FontRectangle Transform(in FontRectangle rectangle, Matrix3x2 matrix)
{
Vector2 bottomRight = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix);
Vector2 topLeft = Vector2.Transform(rectangle.Location, matrix);
Vector2 size = bottomRight - topLeft;
return new FontRectangle(topLeft, size);
}
///
/// Creates a rectangle that represents the union between and .
///
/// The first rectangle.
/// The second rectangle.
/// The .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static FontRectangle Union(in FontRectangle a, in FontRectangle b)
{
float x1 = MathF.Min(a.X, b.X);
float x2 = MathF.Max(a.Right, b.Right);
float y1 = MathF.Min(a.Y, b.Y);
float y2 = MathF.Max(a.Bottom, b.Bottom);
return new FontRectangle(x1, y1, x2 - x1, y2 - y1);
}
///
/// Deconstructs this rectangle into four floats.
///
/// The out value for X.
/// The out value for Y.
/// The out value for the width.
/// The out value for the height.
public void Deconstruct(out float x, out float y, out float width, out float height)
{
x = this.X;
y = this.Y;
width = this.Width;
height = this.Height;
}
///
/// Creates a FontRectangle that represents the intersection between this FontRectangle and the .
///
/// The rectangle.
/// New representing the intersections between the two rectangles.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FontRectangle Intersect(in FontRectangle rectangle)
=> Intersect(rectangle, this);
///
/// Creates a new inflated by the specified amount.
///
/// The width.
/// The height.
/// New representing the inflated rectangle
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FontRectangle Inflate(float width, float height)
=> new(
this.X - width,
this.Y - height,
this.Width + (2 * width),
this.Height + (2 * height));
///
/// Creates a new inflated by the specified amount.
///
/// The size.
/// New representing the inflated rectangle
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FontRectangle Inflate(Vector2 size) => this.Inflate(size.X, size.Y);
///
/// Determines if the specified point is contained within the rectangular region defined by
/// this .
///
/// The x-coordinate of the given point.
/// The y-coordinate of the given point.
/// The .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(float x, float y) => this.X <= x && x < this.Right && this.Y <= y && y < this.Bottom;
///
/// Determines if the specified point is contained within the rectangular region defined by this .
///
/// The point.
/// The .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(Vector2 point) => this.Contains(point.X, point.Y);
///
/// Determines if the rectangular region represented by is entirely contained
/// within the rectangular region represented by this .
///
/// The rectangle.
/// The .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Contains(in FontRectangle rectangle) =>
(this.X <= rectangle.X) && (rectangle.Right <= this.Right) &&
(this.Y <= rectangle.Y) && (rectangle.Bottom <= this.Bottom);
///
/// Determines if the specified intersects the rectangular region defined by
/// this .
///
/// The other rectangle.
/// The .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IntersectsWith(in FontRectangle rectangle) =>
(rectangle.X < this.Right) && (this.X < rectangle.Right) &&
(rectangle.Y < this.Bottom) && (this.Y < rectangle.Bottom);
///
/// Adjusts the location of this rectangle by the specified amount.
///
/// The point.
/// New representing the offset rectangle.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FontRectangle Offset(Vector2 point) => this.Offset(point.X, point.Y);
///
/// Adjusts the location of this rectangle by the specified amount.
///
/// The amount to offset the x-coordinate.
/// The amount to offset the y-coordinate.
/// New representing the inflated rectangle.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public FontRectangle Offset(float dx, float dy)
=> new(this.X + dx, this.Y + dy, this.Width, this.Height);
///
public override int GetHashCode()
=> HashCode.Combine(this.X, this.Y, this.Width, this.Height);
///
public override string ToString()
=> $"FontRectangle [ X={this.X}, Y={this.Y}, Width={this.Width}, Height={this.Height} ]";
///
public override bool Equals(object? obj)
=> obj is FontRectangle other
&& this.Equals(other);
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(FontRectangle other)
=> this.X.Equals(other.X)
&& this.Y.Equals(other.Y)
&& this.Width.Equals(other.Width)
&& this.Height.Equals(other.Height);
}
}