// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Numerics;
namespace SixLabors.Fonts {
///
/// Represents a rectangular clipping region as a convex quadrilateral.
/// Allows for transformation by rotation, skew, or non-uniform scaling,
/// resulting in non-axis-aligned edges.
///
public readonly struct ClipQuad
{
///
/// Initializes a new instance of the struct.
///
/// The top-left corner of the quadrilateral.
/// The top-right corner of the quadrilateral.
/// The bottom-right corner of the quadrilateral.
/// The bottom-left corner of the quadrilateral.
public ClipQuad(Vector2 topLeft, Vector2 topRight, Vector2 bottomRight, Vector2 bottomLeft)
{
this.TopLeft = topLeft;
this.TopRight = topRight;
this.BottomRight = bottomRight;
this.BottomLeft = bottomLeft;
}
///
/// Gets the top-left corner of the quadrilateral.
///
public Vector2 TopLeft { get; }
///
/// Gets the top-right corner of the quadrilateral.
///
public Vector2 TopRight { get; }
///
/// Gets the bottom-right corner of the quadrilateral.
///
public Vector2 BottomRight { get; }
///
/// Gets the bottom-left corner of the quadrilateral.
///
public Vector2 BottomLeft { get; }
///
/// Creates a from an axis-aligned and an optional transform.
///
/// The bounds representing the untransformed rectangular area.
/// An optional transform to apply. If omitted, no transform is applied.
/// A representing the transformed rectangle.
internal static ClipQuad FromBounds(in Bounds bounds, in Matrix3x2 transform)
{
Vector2 tl = Vector2.Transform(bounds.Min, transform);
Vector2 tr = Vector2.Transform(new Vector2(bounds.Max.X, bounds.Min.Y), transform);
Vector2 br = Vector2.Transform(bounds.Max, transform);
Vector2 bl = Vector2.Transform(new Vector2(bounds.Min.X, bounds.Max.Y), transform);
return new ClipQuad(tl, tr, br, bl);
}
///
/// Determines whether the quadrilateral is axis-aligned within a small tolerance.
///
/// The tolerance for comparing parallel edges, typically a small epsilon.
///
/// if opposite edges are parallel and of equal length; otherwise, .
///
public bool IsAxisAligned(float tolerance = 1E-4F)
{
Vector2 top = this.TopRight - this.TopLeft;
Vector2 bottom = this.BottomRight - this.BottomLeft;
Vector2 left = this.BottomLeft - this.TopLeft;
Vector2 right = this.BottomRight - this.TopRight;
bool horizontalParallel = MathF.Abs(Vector2.Dot(Vector2.Normalize(top), Vector2.Normalize(bottom)) - 1F) < tolerance;
bool verticalParallel = MathF.Abs(Vector2.Dot(Vector2.Normalize(left), Vector2.Normalize(right)) - 1F) < tolerance;
return horizontalParallel && verticalParallel;
}
}
}