From 2dca5f716c3ea984ddb095bdfd9c0f332144a3e1 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Wed, 1 Jan 2020 20:01:05 -0800 Subject: [PATCH] optimize polygon equality --- Bonk/Shapes/Polygon.cs | 36 +++++++----- Bonk/Shapes/Rectangle.cs | 68 +++++++++++++++-------- Bonk/Shapes/RectanglePolygonComparison.cs | 19 +++++-- Test/Equality.cs | 16 ++++-- 4 files changed, 91 insertions(+), 48 deletions(-) diff --git a/Bonk/Shapes/Polygon.cs b/Bonk/Shapes/Polygon.cs index e68663b..90dc44c 100644 --- a/Bonk/Shapes/Polygon.cs +++ b/Bonk/Shapes/Polygon.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Numerics; @@ -9,34 +8,32 @@ namespace MoonTools.Core.Bonk { /// /// A Shape defined by an arbitrary collection of vertices. - /// NOTE: A Polygon must have more than 2 vertices, be convex, and should not have duplicate vertices. + /// NOTE: A Polygon must be defined in clockwise order, have more than 2 vertices, be convex, and have no duplicate vertices. /// public struct Polygon : IShape2D, IEquatable { - private ImmutableArray _vertices; + public ImmutableArray Vertices { get; private set; } public AABB AABB { get; } - public IEnumerable Vertices { get { return _vertices; } } - - public int VertexCount { get { return _vertices.Length; } } + public int VertexCount { get { return Vertices.Length; } } // vertices are local to the origin public Polygon(IEnumerable vertices) { - _vertices = vertices.ToImmutableArray(); + Vertices = vertices.ToImmutableArray(); AABB = AABB.FromVertices(vertices); } public Polygon(ImmutableArray vertices) { - _vertices = vertices; + Vertices = vertices; AABB = AABB.FromVertices(vertices); } public Vector2 Support(Vector2 direction, Transform2D transform) { var maxDotProduct = float.NegativeInfinity; - var maxVertex = _vertices[0].ToVector2(); + var maxVertex = Vertices[0].ToVector2(); foreach (var vertex in Vertices) { var transformed = Vector2.Transform(vertex, transform.TransformMatrix); @@ -67,11 +64,22 @@ namespace MoonTools.Core.Bonk public bool Equals(Polygon other) { - var q = from a in _vertices - join b in other.Vertices on a equals b - select a; + if (VertexCount != other.VertexCount) { return false; } - return _vertices.Length == other.VertexCount && q.Count() == _vertices.Length; + int? offset = null; + for (var i = 0; i < VertexCount; i++) + { + if (Vertices[0] == other.Vertices[i]) { offset = i; break; } + } + + if (!offset.HasValue) { return false; } + + for (var i = 0; i < VertexCount; i++) + { + if (Vertices[i] != other.Vertices[(i + offset.Value) % VertexCount]) { return false; } + } + + return true; } public bool Equals(Rectangle rectangle) diff --git a/Bonk/Shapes/Rectangle.cs b/Bonk/Shapes/Rectangle.cs index 5433ecc..cd0da32 100644 --- a/Bonk/Shapes/Rectangle.cs +++ b/Bonk/Shapes/Rectangle.cs @@ -10,51 +10,66 @@ namespace MoonTools.Core.Bonk /// public struct Rectangle : IShape2D, IEquatable { - public int MinX { get; } - public int MinY { get; } - public int MaxX { get; } - public int MaxY { get; } - + /// + /// The minimum position of the rectangle. Note that we assume y-down coordinates. + /// + /// + public Position2D Min { get; } + /// + /// The maximum position of the rectangle. Note that we assume y-down coordinates. + /// + /// + public Position2D Max { get; } public AABB AABB { get; } + public int Left { get { return Min.X; } } + public int Right { get { return Max.X; } } + public int Top { get { return Min.Y; } } + public int Bottom { get { return Max.Y; } } + + public int Width { get; } + public int Height { get; } + + public Position2D TopRight { get { return new Position2D(Right, Top); } } + public Position2D BottomLeft { get { return new Position2D(Left, Bottom); } } + public IEnumerable Vertices { get { - yield return new Position2D(MinX, MinY); - yield return new Position2D(MinX, MaxY); - yield return new Position2D(MaxX, MinY); - yield return new Position2D(MaxX, MaxY); + yield return new Position2D(Min.X, Min.Y); + yield return new Position2D(Min.X, Max.Y); + yield return new Position2D(Max.X, Min.Y); + yield return new Position2D(Max.X, Max.Y); } } public Rectangle(int minX, int minY, int maxX, int maxY) { - MinX = minX; - MinY = minY; - MaxX = maxX; - MaxY = maxY; - + Min = new Position2D(minX, minY); + Max = new Position2D(maxX, maxY); AABB = new AABB(minX, minY, maxX, maxY); + Width = Max.X - Min.X; + Height = Max.Y - Min.Y; } private Vector2 Support(Vector2 direction) { if (direction.X >= 0 && direction.Y >= 0) { - return new Vector2(MaxX, MaxY); + return Max; } else if (direction.X >= 0 && direction.Y < 0) { - return new Vector2(MaxX, MinY); + return new Vector2(Max.X, Min.Y); } else if (direction.X < 0 && direction.Y >= 0) { - return new Vector2(MinX, MaxY); + return new Vector2(Min.X, Max.Y); } else if (direction.X < 0 && direction.Y < 0) { - return new Vector2(MinX, MinY); + return new Vector2(Min.X, Min.Y); } else { @@ -87,10 +102,7 @@ namespace MoonTools.Core.Bonk public bool Equals(Rectangle other) { - return MinX == other.MinX && - MinY == other.MinY && - MaxX == other.MaxX && - MaxY == other.MaxY; + return Min == other.Min && Max == other.Max; } public bool Equals(Polygon other) @@ -100,7 +112,7 @@ namespace MoonTools.Core.Bonk public override int GetHashCode() { - return HashCode.Combine(MinX, MinY, MaxX, MaxY); + return HashCode.Combine(Min, Max); } public static bool operator ==(Rectangle a, Rectangle b) @@ -112,5 +124,15 @@ namespace MoonTools.Core.Bonk { return !(a == b); } + + public static bool operator ==(Rectangle a, Polygon b) + { + return a.Equals(b); + } + + public static bool operator !=(Rectangle a, Polygon b) + { + return !(a == b); + } } } diff --git a/Bonk/Shapes/RectanglePolygonComparison.cs b/Bonk/Shapes/RectanglePolygonComparison.cs index 34c7ef3..36821d7 100644 --- a/Bonk/Shapes/RectanglePolygonComparison.cs +++ b/Bonk/Shapes/RectanglePolygonComparison.cs @@ -1,4 +1,4 @@ -using System.Linq; +using MoonTools.Core.Structs; namespace MoonTools.Core.Bonk { @@ -6,11 +6,20 @@ namespace MoonTools.Core.Bonk { public static bool Equals(Polygon polygon, Rectangle rectangle) { - var q = from a in polygon.Vertices - join b in rectangle.Vertices on a equals b - select a; + if (polygon.VertexCount != 4) { return false; } - return polygon.VertexCount == 4 && q.Count() == 4; + int? minIndex = null; + for (var i = 0; i < 4; i++) + { + if (polygon.Vertices[i] == rectangle.Min) { minIndex = i; break; } + } + + if (!minIndex.HasValue) { return false; } + + return + polygon.Vertices[(minIndex.Value + 1) % 4] == rectangle.TopRight && + polygon.Vertices[(minIndex.Value + 2) % 4] == rectangle.Max && + polygon.Vertices[(minIndex.Value + 3) % 4] == rectangle.BottomLeft; } } } diff --git a/Test/Equality.cs b/Test/Equality.cs index bf303dd..e69c9e5 100644 --- a/Test/Equality.cs +++ b/Test/Equality.cs @@ -288,45 +288,48 @@ namespace Tests public void PolygonRectangleEqual() { var a = new Polygon(ImmutableArray.Create( - new Position2D(1, 1), - new Position2D(1, -1), new Position2D(-1, -1), + new Position2D(1, -1), + new Position2D(1, 1), new Position2D(-1, 1) )); var b = new Rectangle(-1, -1, 1, 1); a.Equals(b).Should().BeTrue(); + b.Equals(a).Should().BeTrue(); } [Test] public void PolygonRectangleNotEqual() { var a = new Polygon(ImmutableArray.Create( - new Position2D(2, 1), + new Position2D(-2, -1), new Position2D(1, -1), - new Position2D(-1, -1), + new Position2D(1, 1), new Position2D(-2, 1) )); var b = new Rectangle(-1, -1, 1, 1); a.Equals(b).Should().BeFalse(); + b.Equals(a).Should().BeFalse(); } [Test] public void PolygonRectangleEqualOperator() { var a = new Polygon(ImmutableArray.Create( - new Position2D(1, 1), - new Position2D(1, -1), new Position2D(-1, -1), + new Position2D(1, -1), + new Position2D(1, 1), new Position2D(-1, 1) )); var b = new Rectangle(-1, -1, 1, 1); (a == b).Should().BeTrue(); + (b == a).Should().BeTrue(); } [Test] @@ -342,6 +345,7 @@ namespace Tests var b = new Rectangle(-1, -1, 1, 1); (a != b).Should().BeTrue(); + (b != a).Should().BeTrue(); } }