// 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; } } }