// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Tests; public class PointFTests { private static readonly ApproximateFloatComparer ApproximateFloatComparer = new(1e-6f); [Fact] public void CanReinterpretCastFromVector2() { Vector2 vector = new(1, 2); PointF point = Unsafe.As(ref vector); Assert.Equal(vector.X, point.X); Assert.Equal(vector.Y, point.Y); } [Fact] public void DefaultConstructorTest() { Assert.Equal(default, PointF.Empty); } [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MinValue)] [InlineData(float.MaxValue, float.MaxValue)] [InlineData(float.MinValue, float.MaxValue)] [InlineData(0.0, 0.0)] public void NonDefaultConstructorTest(float x, float y) { PointF p1 = new(x, y); Assert.Equal(x, p1.X); Assert.Equal(y, p1.Y); } [Fact] public void IsEmptyDefaultsTest() { Assert.True(PointF.Empty.IsEmpty); Assert.True(default(PointF).IsEmpty); Assert.True(new PointF(0, 0).IsEmpty); } [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MinValue)] [InlineData(float.MaxValue, float.MaxValue)] public void IsEmptyRandomTest(float x, float y) { Assert.False(new PointF(x, y).IsEmpty); } [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MinValue)] [InlineData(float.MaxValue, float.MaxValue)] [InlineData(0, 0)] public void CoordinatesTest(float x, float y) { PointF p = new(x, y); Assert.Equal(x, p.X); Assert.Equal(y, p.Y); p.X = 10; Assert.Equal(10, p.X); p.Y = -10.123f; Assert.Equal(-10.123, p.Y, 3); } [Theory] [InlineData(float.MaxValue, float.MinValue, int.MaxValue, int.MinValue)] [InlineData(float.MinValue, float.MaxValue, int.MinValue, int.MaxValue)] [InlineData(0, 0, 0, 0)] public void ArithmeticTestWithSize(float x, float y, int x1, int y1) { PointF p = new(x, y); Size s = new(x1, y1); PointF addExpected = new(x + x1, y + y1); PointF subExpected = new(x - x1, y - y1); Assert.Equal(addExpected, p + s); Assert.Equal(subExpected, p - s); Assert.Equal(addExpected, PointF.Add(p, s)); Assert.Equal(subExpected, PointF.Subtract(p, s)); } [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MaxValue)] [InlineData(0, 0)] public void ArithmeticTestWithSizeF(float x, float y) { PointF p = new(x, y); SizeF s = new(y, x); PointF addExpected = new(x + y, y + x); PointF subExpected = new(x - y, y - x); Assert.Equal(addExpected, p + s); Assert.Equal(subExpected, p - s); Assert.Equal(addExpected, PointF.Add(p, s)); Assert.Equal(subExpected, PointF.Subtract(p, s)); } [Fact] public void RotateTest() { PointF p = new(13, 17); Matrix3x2 matrix = Matrix3x2Extensions.CreateRotationDegrees(45, PointF.Empty); PointF pout = PointF.Transform(p, matrix); Assert.Equal(-2.82842732F, pout.X, ApproximateFloatComparer); Assert.Equal(21.2132034F, pout.Y, ApproximateFloatComparer); } [Fact] public void SkewTest() { PointF p = new(13, 17); Matrix3x2 matrix = Matrix3x2Extensions.CreateSkewDegrees(45, 45, PointF.Empty); PointF pout = PointF.Transform(p, matrix); Assert.Equal(new PointF(30, 30), pout); } [Fact] public void TransformMatrix4x4_AffineMatchesMatrix3x2() { PointF p = new(13, 17); Matrix3x2 m3 = Matrix3x2Extensions.CreateRotationDegrees(45, PointF.Empty); Matrix4x4 m4 = new(m3); PointF r3 = PointF.Transform(p, m3); PointF r4 = PointF.Transform(p, m4); Assert.Equal(r3.X, r4.X, ApproximateFloatComparer); Assert.Equal(r3.Y, r4.Y, ApproximateFloatComparer); } [Fact] public void TransformMatrix4x4_Identity() { PointF p = new(42.5F, -17.3F); PointF result = PointF.Transform(p, Matrix4x4.Identity); Assert.Equal(p.X, result.X, ApproximateFloatComparer); Assert.Equal(p.Y, result.Y, ApproximateFloatComparer); } [Fact] public void TransformMatrix4x4_Translation() { PointF p = new(10, 20); Matrix4x4 m = Matrix4x4.CreateTranslation(5, -3, 0); PointF result = PointF.Transform(p, m); Assert.Equal(15F, result.X, ApproximateFloatComparer); Assert.Equal(17F, result.Y, ApproximateFloatComparer); } [Fact] public void TransformMatrix4x4_Scale() { PointF p = new(10, 20); Matrix4x4 m = Matrix4x4.CreateScale(2, 3, 1); PointF result = PointF.Transform(p, m); Assert.Equal(20F, result.X, ApproximateFloatComparer); Assert.Equal(60F, result.Y, ApproximateFloatComparer); } [Fact] public void TransformMatrix4x4_Projective() { // A taper matrix with M14 != 0 produces W != 1, requiring perspective divide. PointF p = new(100, 50); Matrix4x4 m = Matrix4x4.Identity; m.M14 = 0.005F; // perspective component PointF result = PointF.Transform(p, m); // W = x*M14 + M44 = 100*0.005 + 1 = 1.5 // X = x*M11 + M41 = 100, Y = y*M22 + M42 = 50 // result = (100/1.5, 50/1.5) Assert.Equal(100F / 1.5F, result.X, ApproximateFloatComparer); Assert.Equal(50F / 1.5F, result.Y, ApproximateFloatComparer); } [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MaxValue)] [InlineData(float.MinValue, float.MinValue)] [InlineData(float.MaxValue, float.MaxValue)] [InlineData(0, 0)] public void EqualityTest(float x, float y) { PointF pLeft = new(x, y); PointF pRight = new(y, x); if (x == y) { Assert.True(pLeft == pRight); Assert.False(pLeft != pRight); Assert.True(pLeft.Equals(pRight)); Assert.True(pLeft.Equals((object)pRight)); Assert.Equal(pLeft.GetHashCode(), pRight.GetHashCode()); return; } Assert.True(pLeft != pRight); Assert.False(pLeft == pRight); Assert.False(pLeft.Equals(pRight)); Assert.False(pLeft.Equals((object)pRight)); } [Fact] public void EqualityTest_NotPointF() { PointF point = new(0, 0); Assert.False(point.Equals(null)); Assert.False(point.Equals(0)); // If PointF implements IEquatable (e.g. in .NET Core), then structs that are implicitly // convertible to var can potentially be equal. // See https://github.com/dotnet/corefx/issues/5255. bool expectsImplicitCastToPointF = typeof(IEquatable).IsAssignableFrom(point.GetType()); Assert.Equal(expectsImplicitCastToPointF, point.Equals(new Point(0, 0))); Assert.False(point.Equals((object)new Point(0, 0))); // No implicit cast } [Fact] public void GetHashCodeTest() { PointF point = new(10, 10); Assert.Equal(point.GetHashCode(), new PointF(10, 10).GetHashCode()); Assert.NotEqual(point.GetHashCode(), new PointF(20, 10).GetHashCode()); Assert.NotEqual(point.GetHashCode(), new PointF(10, 20).GetHashCode()); } [Fact] public void ToStringTest() { PointF p = new(5.1F, -5.123F); Assert.Equal(string.Format(CultureInfo.CurrentCulture, "PointF [ X={0}, Y={1} ]", p.X, p.Y), p.ToString()); } [Theory] [InlineData(float.MaxValue, float.MinValue)] [InlineData(float.MinValue, float.MinValue)] [InlineData(float.MaxValue, float.MaxValue)] [InlineData(0, 0)] public void DeconstructTest(float x, float y) { PointF p = new(x, y); (float deconstructedX, float deconstructedY) = p; Assert.Equal(x, deconstructedX); Assert.Equal(y, deconstructedY); } }