optimize polygon equality
parent
097790a41f
commit
2dca5f716c
|
@ -1,5 +1,4 @@
|
||||||
using System.Linq;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
@ -9,34 +8,32 @@ namespace MoonTools.Core.Bonk
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A Shape defined by an arbitrary collection of vertices.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct Polygon : IShape2D, IEquatable<Polygon>
|
public struct Polygon : IShape2D, IEquatable<Polygon>
|
||||||
{
|
{
|
||||||
private ImmutableArray<Position2D> _vertices;
|
public ImmutableArray<Position2D> Vertices { get; private set; }
|
||||||
public AABB AABB { get; }
|
public AABB AABB { get; }
|
||||||
|
|
||||||
public IEnumerable<Position2D> Vertices { get { return _vertices; } }
|
public int VertexCount { get { return Vertices.Length; } }
|
||||||
|
|
||||||
public int VertexCount { get { return _vertices.Length; } }
|
|
||||||
|
|
||||||
// vertices are local to the origin
|
// vertices are local to the origin
|
||||||
public Polygon(IEnumerable<Position2D> vertices)
|
public Polygon(IEnumerable<Position2D> vertices)
|
||||||
{
|
{
|
||||||
_vertices = vertices.ToImmutableArray();
|
Vertices = vertices.ToImmutableArray();
|
||||||
AABB = AABB.FromVertices(vertices);
|
AABB = AABB.FromVertices(vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Polygon(ImmutableArray<Position2D> vertices)
|
public Polygon(ImmutableArray<Position2D> vertices)
|
||||||
{
|
{
|
||||||
_vertices = vertices;
|
Vertices = vertices;
|
||||||
AABB = AABB.FromVertices(vertices);
|
AABB = AABB.FromVertices(vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2 Support(Vector2 direction, Transform2D transform)
|
public Vector2 Support(Vector2 direction, Transform2D transform)
|
||||||
{
|
{
|
||||||
var maxDotProduct = float.NegativeInfinity;
|
var maxDotProduct = float.NegativeInfinity;
|
||||||
var maxVertex = _vertices[0].ToVector2();
|
var maxVertex = Vertices[0].ToVector2();
|
||||||
foreach (var vertex in Vertices)
|
foreach (var vertex in Vertices)
|
||||||
{
|
{
|
||||||
var transformed = Vector2.Transform(vertex, transform.TransformMatrix);
|
var transformed = Vector2.Transform(vertex, transform.TransformMatrix);
|
||||||
|
@ -67,11 +64,22 @@ namespace MoonTools.Core.Bonk
|
||||||
|
|
||||||
public bool Equals(Polygon other)
|
public bool Equals(Polygon other)
|
||||||
{
|
{
|
||||||
var q = from a in _vertices
|
if (VertexCount != other.VertexCount) { return false; }
|
||||||
join b in other.Vertices on a equals b
|
|
||||||
select a;
|
|
||||||
|
|
||||||
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)
|
public bool Equals(Rectangle rectangle)
|
||||||
|
|
|
@ -10,51 +10,66 @@ namespace MoonTools.Core.Bonk
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct Rectangle : IShape2D, IEquatable<Rectangle>
|
public struct Rectangle : IShape2D, IEquatable<Rectangle>
|
||||||
{
|
{
|
||||||
public int MinX { get; }
|
/// <summary>
|
||||||
public int MinY { get; }
|
/// The minimum position of the rectangle. Note that we assume y-down coordinates.
|
||||||
public int MaxX { get; }
|
/// </summary>
|
||||||
public int MaxY { get; }
|
/// <value></value>
|
||||||
|
public Position2D Min { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum position of the rectangle. Note that we assume y-down coordinates.
|
||||||
|
/// </summary>
|
||||||
|
/// <value></value>
|
||||||
|
public Position2D Max { get; }
|
||||||
public AABB AABB { 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<Position2D> Vertices
|
public IEnumerable<Position2D> Vertices
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
yield return new Position2D(MinX, MinY);
|
yield return new Position2D(Min.X, Min.Y);
|
||||||
yield return new Position2D(MinX, MaxY);
|
yield return new Position2D(Min.X, Max.Y);
|
||||||
yield return new Position2D(MaxX, MinY);
|
yield return new Position2D(Max.X, Min.Y);
|
||||||
yield return new Position2D(MaxX, MaxY);
|
yield return new Position2D(Max.X, Max.Y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle(int minX, int minY, int maxX, int maxY)
|
public Rectangle(int minX, int minY, int maxX, int maxY)
|
||||||
{
|
{
|
||||||
MinX = minX;
|
Min = new Position2D(minX, minY);
|
||||||
MinY = minY;
|
Max = new Position2D(maxX, maxY);
|
||||||
MaxX = maxX;
|
|
||||||
MaxY = maxY;
|
|
||||||
|
|
||||||
AABB = new AABB(minX, minY, maxX, maxY);
|
AABB = new AABB(minX, minY, maxX, maxY);
|
||||||
|
Width = Max.X - Min.X;
|
||||||
|
Height = Max.Y - Min.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Vector2 Support(Vector2 direction)
|
private Vector2 Support(Vector2 direction)
|
||||||
{
|
{
|
||||||
if (direction.X >= 0 && direction.Y >= 0)
|
if (direction.X >= 0 && direction.Y >= 0)
|
||||||
{
|
{
|
||||||
return new Vector2(MaxX, MaxY);
|
return Max;
|
||||||
}
|
}
|
||||||
else if (direction.X >= 0 && direction.Y < 0)
|
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)
|
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)
|
else if (direction.X < 0 && direction.Y < 0)
|
||||||
{
|
{
|
||||||
return new Vector2(MinX, MinY);
|
return new Vector2(Min.X, Min.Y);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -87,10 +102,7 @@ namespace MoonTools.Core.Bonk
|
||||||
|
|
||||||
public bool Equals(Rectangle other)
|
public bool Equals(Rectangle other)
|
||||||
{
|
{
|
||||||
return MinX == other.MinX &&
|
return Min == other.Min && Max == other.Max;
|
||||||
MinY == other.MinY &&
|
|
||||||
MaxX == other.MaxX &&
|
|
||||||
MaxY == other.MaxY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(Polygon other)
|
public bool Equals(Polygon other)
|
||||||
|
@ -100,7 +112,7 @@ namespace MoonTools.Core.Bonk
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine(MinX, MinY, MaxX, MaxY);
|
return HashCode.Combine(Min, Max);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(Rectangle a, Rectangle b)
|
public static bool operator ==(Rectangle a, Rectangle b)
|
||||||
|
@ -112,5 +124,15 @@ namespace MoonTools.Core.Bonk
|
||||||
{
|
{
|
||||||
return !(a == b);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Linq;
|
using MoonTools.Core.Structs;
|
||||||
|
|
||||||
namespace MoonTools.Core.Bonk
|
namespace MoonTools.Core.Bonk
|
||||||
{
|
{
|
||||||
|
@ -6,11 +6,20 @@ namespace MoonTools.Core.Bonk
|
||||||
{
|
{
|
||||||
public static bool Equals(Polygon polygon, Rectangle rectangle)
|
public static bool Equals(Polygon polygon, Rectangle rectangle)
|
||||||
{
|
{
|
||||||
var q = from a in polygon.Vertices
|
if (polygon.VertexCount != 4) { return false; }
|
||||||
join b in rectangle.Vertices on a equals b
|
|
||||||
select a;
|
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -288,45 +288,48 @@ namespace Tests
|
||||||
public void PolygonRectangleEqual()
|
public void PolygonRectangleEqual()
|
||||||
{
|
{
|
||||||
var a = new Polygon(ImmutableArray.Create(
|
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),
|
||||||
new Position2D(-1, 1)
|
new Position2D(-1, 1)
|
||||||
));
|
));
|
||||||
|
|
||||||
var b = new Rectangle(-1, -1, 1, 1);
|
var b = new Rectangle(-1, -1, 1, 1);
|
||||||
|
|
||||||
a.Equals(b).Should().BeTrue();
|
a.Equals(b).Should().BeTrue();
|
||||||
|
b.Equals(a).Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void PolygonRectangleNotEqual()
|
public void PolygonRectangleNotEqual()
|
||||||
{
|
{
|
||||||
var a = new Polygon(ImmutableArray.Create(
|
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(1, 1),
|
||||||
new Position2D(-2, 1)
|
new Position2D(-2, 1)
|
||||||
));
|
));
|
||||||
|
|
||||||
var b = new Rectangle(-1, -1, 1, 1);
|
var b = new Rectangle(-1, -1, 1, 1);
|
||||||
|
|
||||||
a.Equals(b).Should().BeFalse();
|
a.Equals(b).Should().BeFalse();
|
||||||
|
b.Equals(a).Should().BeFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void PolygonRectangleEqualOperator()
|
public void PolygonRectangleEqualOperator()
|
||||||
{
|
{
|
||||||
var a = new Polygon(ImmutableArray.Create(
|
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),
|
||||||
new Position2D(-1, 1)
|
new Position2D(-1, 1)
|
||||||
));
|
));
|
||||||
|
|
||||||
var b = new Rectangle(-1, -1, 1, 1);
|
var b = new Rectangle(-1, -1, 1, 1);
|
||||||
|
|
||||||
(a == b).Should().BeTrue();
|
(a == b).Should().BeTrue();
|
||||||
|
(b == a).Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -342,6 +345,7 @@ namespace Tests
|
||||||
var b = new Rectangle(-1, -1, 1, 1);
|
var b = new Rectangle(-1, -1, 1, 1);
|
||||||
|
|
||||||
(a != b).Should().BeTrue();
|
(a != b).Should().BeTrue();
|
||||||
|
(b != a).Should().BeTrue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue