From 420e617cec548a153ece22cd120f69476ae3752c Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 25 Oct 2019 03:46:47 -0700 Subject: [PATCH 1/8] gc-related optimizations --- .gitignore | 3 +- Bonk/Bonk.csproj | 1 + Bonk/EPA2D.cs | 12 +-- Bonk/GJK2D.cs | 161 +++++++++++++++----------------------- Bonk/Line.cs | 22 ++++-- Bonk/Rectangle.cs | 38 ++++----- Bonk/Vector2Extensions.cs | 18 +++++ Test/EPA2DTest.cs | 18 ++--- Test/GJK2DTest.cs | 29 +++---- Test/Test.csproj | 2 +- 10 files changed, 145 insertions(+), 159 deletions(-) create mode 100644 Bonk/Vector2Extensions.cs diff --git a/.gitignore b/.gitignore index cbbd0b5..5ed3d3e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ bin/ -obj/ \ No newline at end of file +obj/ +.vscode \ No newline at end of file diff --git a/Bonk/Bonk.csproj b/Bonk/Bonk.csproj index 4a5064c..7999b43 100644 --- a/Bonk/Bonk.csproj +++ b/Bonk/Bonk.csproj @@ -16,5 +16,6 @@ + \ No newline at end of file diff --git a/Bonk/EPA2D.cs b/Bonk/EPA2D.cs index 2219f26..931be03 100644 --- a/Bonk/EPA2D.cs +++ b/Bonk/EPA2D.cs @@ -5,6 +5,7 @@ */ using Microsoft.Xna.Framework; +using MoonTools.Core.Bonk.Extensions; using MoonTools.Core.Structs; using System; using System.Collections.Generic; @@ -20,14 +21,15 @@ namespace MoonTools.Core.Bonk public static class EPA2D { // vector returned gives direction from A to B - public static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, IEnumerable givenSimplexVertices) + public static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, (Func, Vector2, Vector2) givenSimplexVertices) { var simplexVertices = new SimplexVertices(new Vector2?[36]); - foreach (var vertex in givenSimplexVertices) - { - simplexVertices.Add(vertex); - } + var (simplexSupport, a, b) = givenSimplexVertices; + simplexVertices.Add(simplexSupport(a)); + simplexVertices.Add(simplexSupport(b)); + simplexVertices.Add(simplexSupport(-a)); + simplexVertices.Add(simplexSupport(-b)); var e0 = (simplexVertices[1].X - simplexVertices[0].X) * (simplexVertices[1].Y + simplexVertices[0].Y); var e1 = (simplexVertices[2].X - simplexVertices[1].X) * (simplexVertices[2].Y + simplexVertices[1].Y); diff --git a/Bonk/GJK2D.cs b/Bonk/GJK2D.cs index 76b23e2..ea814bc 100644 --- a/Bonk/GJK2D.cs +++ b/Bonk/GJK2D.cs @@ -1,126 +1,87 @@ -/* - * Implementation of the GJK collision algorithm - * Based on some math blogs - * https://blog.hamaluik.ca/posts/building-a-collision-engine-part-1-2d-gjk-collision-detection/ - * and some code from https://github.com/kroitor/gjk.c - */ - -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using MoonTools.Core.Structs; using System; +using MoonTools.Core.Bonk.Extensions; namespace MoonTools.Core.Bonk { public static class GJK2D { - private enum SolutionStatus + public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) { - NoIntersection, - Intersection, - StillSolving + return OriginInside(MinkowskiDifference(shapeA, transformA, shapeB, transformB)); } - public static ValueTuple TestCollision(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB) + public static (bool, (Func, Vector2, Vector2)) CollisionAndSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) { - var vertices = new SimplexVertices(new Vector2?[] { null, null, null, null }); + var support = MinkowskiDifference(shapeA, transformA, shapeB, transformB); + var result = OriginInsideWithSimplex(support); + return (result.Item1, (support, result.Item2, result.Item3)); + } - const SolutionStatus solutionStatus = SolutionStatus.StillSolving; - var direction = Transform2DB.Position - Transform2DA.Position; + private static Func MinkowskiDifference(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) + { + return direction => shapeA.Support(direction, transformA) - shapeB.Support(-direction, transformB); + } - var result = (solutionStatus, direction); + private static bool OriginInside(Func support) + { + var a = support(Vector2.UnitX); + var b = support(-a); - while (result.solutionStatus == SolutionStatus.StillSolving) + return Vector2.Dot(a, b) > 0 ? false : CheckSimplex(support, a, b); + } + + private static (bool, Vector2, Vector2) OriginInsideWithSimplex(Func support) + { + var a = support(Vector2.UnitX); + var b = support(-a); + + return Vector2.Dot(a, b) > 0 ? (false, a, b) : Simplex(support, a, b); + } + + private static bool CheckSimplex(Func support, Vector2 a, Vector2 b) + { + var axb = a.Cross(b); + var c = support((b - a).Perpendicular()); + var axc = a.Cross(c); + var bxc = b.Cross(c); + var cxb = -bxc; + + return (b - a) == Vector2.Zero || (axb.Y > 0 != bxc.Y > 0 ? CheckSimplex(support, b, c) : (axc.Y > 0 != cxb.Y > 0 ? CheckSimplex(support, a, c) : true)); + } + + private static (bool, Vector2, Vector2) Simplex(Func support, Vector2 a, Vector2 b) + { + if ((b - a) == Vector2.Zero) { - result = EvolveSimplex(shapeA, Transform2DA, shapeB, Transform2DB, vertices, result.direction); + return (false, a, b); } - - return ValueTuple.Create(result.solutionStatus == SolutionStatus.Intersection, vertices); - } - - private static (SolutionStatus, Vector2) EvolveSimplex(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, SimplexVertices vertices, Vector2 direction) - { - switch(vertices.Count) + else { - case 0: - if (direction == Vector2.Zero) + var c = support((b - a).Perpendicular()); + var axb = a.Cross(b); + var bxc = b.Cross(c); + + if (axb.Y > 0 != bxc.Y > 0) + { + return Simplex(support, b, c); + } + else + { + var axc = a.Cross(c); + var cxb = -bxc; + + if (axc.Y > 0 != cxb.Y > 0) { - direction = Vector2.UnitX; + return Simplex(support, a, b); } - break; - - case 1: - direction *= -1; - break; - - case 2: - var ab = vertices[1] - vertices[0]; - var a0 = vertices[0] * -1; - - direction = TripleProduct(ab, a0, ab); - if (direction == Vector2.Zero) - { - direction = Perpendicular(ab); - } - break; - - case 3: - var c0 = vertices[2] * -1; - var bc = vertices[1] - vertices[2]; - var ca = vertices[0] - vertices[2]; - - var bcNorm = TripleProduct(ca, bc, bc); - var caNorm = TripleProduct(bc, ca, ca); - - // the origin is outside line bc - // get rid of a and add a new support in the direction of bcNorm - if (Vector2.Dot(bcNorm, c0) > 0) - { - vertices.RemoveAt(0); - direction = bcNorm; - } - // the origin is outside line ca - // get rid of b and add a new support in the direction of caNorm - else if (Vector2.Dot(caNorm, c0) > 0) - { - vertices.RemoveAt(1); - direction = caNorm; - } - // the origin is inside both ab and ac, - // so it must be inside the triangle! else { - return (SolutionStatus.Intersection, direction); + return (true, a, b); } - break; + } } - - return (AddSupport(shapeA, Transform2DA, shapeB, Transform2DB, vertices, direction) ? - SolutionStatus.StillSolving : - SolutionStatus.NoIntersection, direction); - } - - private static bool AddSupport(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, SimplexVertices vertices, Vector2 direction) - { - var newVertex = shapeA.Support(direction, Transform2DA) - shapeB.Support(-direction, Transform2DB); - vertices.Add(newVertex); - return Vector2.Dot(direction, newVertex) >= 0; - } - - private static Vector2 TripleProduct(Vector2 a, Vector2 b, Vector2 c) - { - var A = new Vector3(a.X, a.Y, 0); - var B = new Vector3(b.X, b.Y, 0); - var C = new Vector3(c.X, c.Y, 0); - - var first = Vector3.Cross(A, B); - var second = Vector3.Cross(first, C); - - return new Vector2(second.X, second.Y); - } - - private static Vector2 Perpendicular(Vector2 v) - { - return new Vector2(v.Y, -v.X); } } } diff --git a/Bonk/Line.cs b/Bonk/Line.cs index e35dd6e..62456d3 100644 --- a/Bonk/Line.cs +++ b/Bonk/Line.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Microsoft.Xna.Framework; using MoonTools.Core.Structs; @@ -6,17 +7,28 @@ namespace MoonTools.Core.Bonk { public struct Line : IShape2D, IEquatable { - private Position2D[] vertices; + private Position2D v0; + private Position2D v1; + + private IEnumerable vertices + { + get + { + yield return v0; + yield return v0; + } + } public Line(Position2D start, Position2D end) { - vertices = new Position2D[2] { start, end }; + v0 = start; + v1 = end; } public Vector2 Support(Vector2 direction, Transform2D transform) { - var TransformedStart = Vector2.Transform(vertices[0], transform.TransformMatrix); - var TransformedEnd = Vector2.Transform(vertices[1], transform.TransformMatrix); + var TransformedStart = Vector2.Transform(v0, transform.TransformMatrix); + var TransformedEnd = Vector2.Transform(v1, transform.TransformMatrix); return Vector2.Dot(TransformedStart, direction) > Vector2.Dot(TransformedEnd, direction) ? TransformedStart : TransformedEnd; @@ -32,7 +44,7 @@ namespace MoonTools.Core.Bonk if (other is Line) { var otherLine = (Line)other; - return vertices[0].ToVector2() == otherLine.vertices[0].ToVector2() && vertices[1].ToVector2() == otherLine.vertices[1].ToVector2(); + return v0.ToVector2() == otherLine.v0.ToVector2() && v1.ToVector2() == otherLine.v1.ToVector2(); } return false; diff --git a/Bonk/Rectangle.cs b/Bonk/Rectangle.cs index e880119..fb44184 100644 --- a/Bonk/Rectangle.cs +++ b/Bonk/Rectangle.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.Xna.Framework; using MoonTools.Core.Structs; +using MoreLinq; namespace MoonTools.Core.Bonk { @@ -11,7 +14,16 @@ namespace MoonTools.Core.Bonk public int MaxX { get; } public int MaxY { get; } - private Position2D[] vertices; + private 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); + } + } public Rectangle(int minX, int minY, int maxX, int maxY) { @@ -19,33 +31,11 @@ namespace MoonTools.Core.Bonk MinY = minY; MaxX = maxX; MaxY = maxY; - - vertices = new Position2D[4] - { - new Position2D(minX, minY), - new Position2D(minX, maxY), - new Position2D(maxX, minY), - new Position2D(maxX, maxY) - }; } public Vector2 Support(Vector2 direction, Transform2D transform) { - var furthestDistance = float.NegativeInfinity; - var furthestVertex = Vector2.Transform(vertices[0], transform.TransformMatrix); - - foreach (var v in vertices) - { - var TransformedVertex = Vector2.Transform(v, transform.TransformMatrix); - var distance = Vector2.Dot(TransformedVertex, direction); - if (distance > furthestDistance) - { - furthestDistance = distance; - furthestVertex = TransformedVertex; - } - } - - return furthestVertex; + return vertices.Select(vertex => Vector2.Transform(vertex, transform.TransformMatrix)).MaxBy(transformed => Vector2.Dot(transformed, direction)).First(); } public AABB AABB(Transform2D Transform2D) diff --git a/Bonk/Vector2Extensions.cs b/Bonk/Vector2Extensions.cs new file mode 100644 index 0000000..f411b4a --- /dev/null +++ b/Bonk/Vector2Extensions.cs @@ -0,0 +1,18 @@ +using Microsoft.Xna.Framework; + +namespace MoonTools.Core.Bonk.Extensions +{ + public static class Vector2Extensions + { + public static Vector2 Cross(this Vector2 a, Vector2 b) + { + var vec3 = Vector3.Cross(new Vector3(a.X, a.Y, 0), new Vector3(b.X, b.Y, 0)); + return new Vector2(vec3.X, vec3.Y); + } + + public static Vector2 Perpendicular(this Vector2 v) + { + return new Vector2(v.Y, -v.X); + } + } +} \ No newline at end of file diff --git a/Test/EPA2DTest.cs b/Test/EPA2DTest.cs index df35325..4459b9c 100644 --- a/Test/EPA2DTest.cs +++ b/Test/EPA2DTest.cs @@ -18,11 +18,11 @@ namespace Tests var squareB = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); var transformB = new Transform2D(new Vector2(1.5f, 0)); - var test = GJK2D.TestCollision(squareA, transformA, squareB, transformB); + var (result, simplex) = GJK2D.CollisionAndSimplex(squareA, transformA, squareB, transformB); - Assert.That(test.Item1, Is.True); + result.Should().BeTrue(); - var intersection = EPA2D.Intersect(squareA, transformA, squareB, transformB, test.Item2); + var intersection = EPA2D.Intersect(squareA, transformA, squareB, transformB, simplex); intersection.X.Should().Be(1f); intersection.Y.Should().Be(0); @@ -36,11 +36,11 @@ namespace Tests var circleB = new Circle(1); var transformB = new Transform2D(new Vector2(1, 1)); - var test = GJK2D.TestCollision(circleA, transformA, circleB, transformB); + var (result, simplex) = GJK2D.CollisionAndSimplex(circleA, transformA, circleB, transformB); - Assert.That(test.Item1, Is.True); + result.Should().BeTrue(); - var intersection = EPA2D.Intersect(circleA, transformA, circleB, transformB, test.Item2); + var intersection = EPA2D.Intersect(circleA, transformA, circleB, transformB, simplex); var ix = circleA.Radius * (float)Math.Cos(Math.PI / 4) - (circleB.Radius * (float)Math.Cos(5 * Math.PI / 4) + transformB.Position.X); var iy = circleA.Radius * (float)Math.Sin(Math.PI / 4) - (circleB.Radius * (float)Math.Sin(5 * Math.PI / 4) + transformB.Position.Y); @@ -57,11 +57,11 @@ namespace Tests var square = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); var transformB = Transform2D.DefaultTransform; - var test = GJK2D.TestCollision(line, transformA, square, transformB); + var (result, simplex) = GJK2D.CollisionAndSimplex(line, transformA, square, transformB); - Assert.That(test.Item1, Is.True); + result.Should().BeTrue(); - var intersection = EPA2D.Intersect(line, transformA, square, transformB, test.Item2); + var intersection = EPA2D.Intersect(line, transformA, square, transformB, simplex); intersection.X.Should().Be(-1); intersection.Y.Should().Be(1); diff --git a/Test/GJK2DTest.cs b/Test/GJK2DTest.cs index 81a868a..b054e15 100644 --- a/Test/GJK2DTest.cs +++ b/Test/GJK2DTest.cs @@ -2,6 +2,7 @@ using MoonTools.Core.Bonk; using MoonTools.Core.Structs; using Microsoft.Xna.Framework; +using FluentAssertions; namespace Tests { @@ -13,7 +14,7 @@ namespace Tests var lineA = new Line(new Position2D(-1, -1), new Position2D(1, 1)); var lineB = new Line(new Position2D(-1, 1), new Position2D(1, -1)); - Assert.IsTrue(GJK2D.TestCollision(lineA, Transform2D.DefaultTransform, lineB, Transform2D.DefaultTransform).Item1); + GJK2D.TestCollision(lineA, Transform2D.DefaultTransform, lineB, Transform2D.DefaultTransform).Should().BeTrue(); } [Test] @@ -22,7 +23,7 @@ namespace Tests var lineA = new Line(new Position2D(0, 1), new Position2D(1, 0)); var lineB = new Line(new Position2D(-1, -1), new Position2D(-2, -2)); - Assert.IsFalse(GJK2D.TestCollision(lineA, Transform2D.DefaultTransform, lineB, Transform2D.DefaultTransform).Item1); + GJK2D.TestCollision(lineA, Transform2D.DefaultTransform, lineB, Transform2D.DefaultTransform).Should().BeFalse(); } [Test] @@ -33,7 +34,7 @@ namespace Tests var circleB = new Circle(2); var transformB = new Transform2D(new Vector2(1, 1)); - Assert.IsTrue(GJK2D.TestCollision(circleA, transformA, circleB, transformB).Item1); + Assert.IsTrue(GJK2D.TestCollision(circleA, transformA, circleB, transformB)); } [Test] @@ -44,7 +45,7 @@ namespace Tests var circleB = new Circle(2); var transformB = new Transform2D(new Vector2(5, 5)); - Assert.IsFalse(GJK2D.TestCollision(circleA, transformA, circleB, transformB).Item1); + Assert.IsFalse(GJK2D.TestCollision(circleA, transformA, circleB, transformB)); } [Test] @@ -64,7 +65,7 @@ namespace Tests var transformB = new Transform2D(new Vector2(0.5f, 0.5f)); - Assert.IsTrue(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Item1); + Assert.IsTrue(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB)); } [Test] @@ -84,7 +85,7 @@ namespace Tests var transformB = new Transform2D(new Vector2(5, 0)); - Assert.IsFalse(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Item1); + Assert.IsFalse(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB)); } [Test] @@ -101,7 +102,7 @@ namespace Tests var transformB = Transform2D.DefaultTransform; - Assert.IsTrue(GJK2D.TestCollision(line, transformA, polygon, transformB).Item1); + Assert.IsTrue(GJK2D.TestCollision(line, transformA, polygon, transformB)); } [Test] @@ -118,7 +119,7 @@ namespace Tests var transformB = Transform2D.DefaultTransform; - Assert.IsFalse(GJK2D.TestCollision(line, transformA, polygon, transformB).Item1); + Assert.IsFalse(GJK2D.TestCollision(line, transformA, polygon, transformB)); } [Test] @@ -129,7 +130,7 @@ namespace Tests var circle = new Circle(1); var transformB = Transform2D.DefaultTransform; - Assert.IsTrue(GJK2D.TestCollision(line, transformA, circle, transformB).Item1); + Assert.IsTrue(GJK2D.TestCollision(line, transformA, circle, transformB)); } [Test] @@ -140,7 +141,7 @@ namespace Tests var circle = new Circle(1); var transformB = Transform2D.DefaultTransform; - Assert.IsFalse(GJK2D.TestCollision(line, transformA, circle, transformB).Item1); + Assert.IsFalse(GJK2D.TestCollision(line, transformA, circle, transformB)); } [Test] @@ -156,7 +157,7 @@ namespace Tests var transformB = Transform2D.DefaultTransform; - Assert.IsTrue(GJK2D.TestCollision(circle, transformA, square, transformB).Item1); + Assert.IsTrue(GJK2D.TestCollision(circle, transformA, square, transformB)); } [Test] @@ -171,7 +172,7 @@ namespace Tests ); var squareTransform = Transform2D.DefaultTransform; - Assert.IsFalse(GJK2D.TestCollision(circle, circleTransform, square, squareTransform).Item1); + Assert.IsFalse(GJK2D.TestCollision(circle, circleTransform, square, squareTransform)); } [Test] @@ -183,7 +184,7 @@ namespace Tests var rectangleB = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); var transformB = new Transform2D(new Vector2(1, 0)); - Assert.IsTrue(GJK2D.TestCollision(rectangleA, transformA, rectangleB, transformB).Item1); + Assert.IsTrue(GJK2D.TestCollision(rectangleA, transformA, rectangleB, transformB)); } [Test] @@ -195,7 +196,7 @@ namespace Tests var rectangleB = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); var transformB = new Transform2D(new Vector2(1, 0)); - Assert.IsTrue(GJK2D.TestCollision(rectangleA, transformA, rectangleB, transformB).Item1); + Assert.IsTrue(GJK2D.TestCollision(rectangleA, transformA, rectangleB, transformB)); } } } diff --git a/Test/Test.csproj b/Test/Test.csproj index 505ac85..31e8e2e 100644 --- a/Test/Test.csproj +++ b/Test/Test.csproj @@ -1,6 +1,6 @@ - netcoreapp2.2 + netcoreapp3.0 false From 2a715e71dbef6e29c5dd3dad3d97a2f303936161 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 25 Oct 2019 04:07:13 -0700 Subject: [PATCH 2/8] todo notes --- Bonk/EPA2D.cs | 1 + Bonk/GJK2D.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Bonk/EPA2D.cs b/Bonk/EPA2D.cs index 931be03..e4edad5 100644 --- a/Bonk/EPA2D.cs +++ b/Bonk/EPA2D.cs @@ -18,6 +18,7 @@ namespace MoonTools.Core.Bonk CounterClockwise } + // TODO: convert SimplexVertices to PooledList public static class EPA2D { // vector returned gives direction from A to B diff --git a/Bonk/GJK2D.cs b/Bonk/GJK2D.cs index ea814bc..e6c06a9 100644 --- a/Bonk/GJK2D.cs +++ b/Bonk/GJK2D.cs @@ -5,6 +5,7 @@ using MoonTools.Core.Bonk.Extensions; namespace MoonTools.Core.Bonk { + // TODO: get rid of minkowski closure for GC purposes public static class GJK2D { public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) From 20872021fc1f93980e77207a6cc81543f96976e9 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 25 Oct 2019 12:37:25 -0700 Subject: [PATCH 3/8] even better gc optimization --- Bonk/EPA2D.cs | 11 +++-- Bonk/GJK2D.cs | 115 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 104 insertions(+), 22 deletions(-) diff --git a/Bonk/EPA2D.cs b/Bonk/EPA2D.cs index e4edad5..6b3e1a5 100644 --- a/Bonk/EPA2D.cs +++ b/Bonk/EPA2D.cs @@ -22,15 +22,14 @@ namespace MoonTools.Core.Bonk public static class EPA2D { // vector returned gives direction from A to B - public static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, (Func, Vector2, Vector2) givenSimplexVertices) + public static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, Simplex givenSimplex) { var simplexVertices = new SimplexVertices(new Vector2?[36]); - var (simplexSupport, a, b) = givenSimplexVertices; - simplexVertices.Add(simplexSupport(a)); - simplexVertices.Add(simplexSupport(b)); - simplexVertices.Add(simplexSupport(-a)); - simplexVertices.Add(simplexSupport(-b)); + foreach (var vertex in givenSimplex.Vertices) + { + simplexVertices.Add(vertex); + } var e0 = (simplexVertices[1].X - simplexVertices[0].X) * (simplexVertices[1].Y + simplexVertices[0].Y); var e1 = (simplexVertices[2].X - simplexVertices[1].X) * (simplexVertices[2].Y + simplexVertices[1].Y); diff --git a/Bonk/GJK2D.cs b/Bonk/GJK2D.cs index e6c06a9..31a8783 100644 --- a/Bonk/GJK2D.cs +++ b/Bonk/GJK2D.cs @@ -2,43 +2,126 @@ using MoonTools.Core.Structs; using System; using MoonTools.Core.Bonk.Extensions; +using System.Collections.Generic; namespace MoonTools.Core.Bonk { - // TODO: get rid of minkowski closure for GC purposes + public struct MinkowskiDifference : IEquatable + { + private IShape2D shapeA; + private Transform2D transformA; + private IShape2D shapeB; + private Transform2D transformB; + + public MinkowskiDifference(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) + { + this.shapeA = shapeA; + this.transformA = transformA; + this.shapeB = shapeB; + this.transformB = transformB; + } + + public bool Equals(MinkowskiDifference other) + { + return + shapeA == other.shapeA && + transformA.Equals(other.transformA) && + shapeB == other.shapeB && + transformB.Equals(other.transformB); + } + + public Vector2 Support(Vector2 direction) + { + return shapeA.Support(direction, transformA) - shapeB.Support(-direction, transformB); + } + } + + public struct Simplex : IShape2D + { + MinkowskiDifference minkowskiDifference; + Vector2 directionA; + Vector2 directionB; + + public Simplex(MinkowskiDifference minkowskiDifference, Vector2 directionA, Vector2 directionB) + { + this.minkowskiDifference = minkowskiDifference; + this.directionA = directionA; + this.directionB = directionB; + } + + public IEnumerable Vertices + { + get + { + yield return (Position2D)Support(directionA); + yield return (Position2D)Support(directionB); + yield return (Position2D)Support(-(directionB - directionA).Perpendicular()); + } + } + + public AABB AABB(Transform2D transform) + { + return Bonk.AABB.FromTransformedVertices(Vertices, transform); + } + + public bool Equals(IShape2D other) + { + if (other is Simplex polytope) + { + return minkowskiDifference.Equals(polytope.minkowskiDifference) && + directionA == polytope.directionA && + directionB == polytope.directionB; + } + + return false; + } + + public Vector2 Support(Vector2 direction) + { + return minkowskiDifference.Support(direction); + } + + public Vector2 Support(Vector2 direction, Transform2D transform) + { + return Vector2.Transform(Support(direction), transform.TransformMatrix); + } + } + public static class GJK2D { public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) { - return OriginInside(MinkowskiDifference(shapeA, transformA, shapeB, transformB)); + var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB); + return OriginInside(minkowskiDifference); } - public static (bool, (Func, Vector2, Vector2)) CollisionAndSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) + public static (bool, Simplex) CollisionAndSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) { - var support = MinkowskiDifference(shapeA, transformA, shapeB, transformB); - var result = OriginInsideWithSimplex(support); - return (result.Item1, (support, result.Item2, result.Item3)); + var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB); + var (collision, a, b) = OriginInsideWithSimplex(minkowskiDifference); + var polytope = new Simplex(minkowskiDifference, a, b); + return (collision, polytope); } - private static Func MinkowskiDifference(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) + private static Vector2 MinkowskiDifference(Vector2 direction, IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) { - return direction => shapeA.Support(direction, transformA) - shapeB.Support(-direction, transformB); + return shapeA.Support(direction, transformA) - shapeB.Support(-direction, transformB); } - private static bool OriginInside(Func support) + private static bool OriginInside(MinkowskiDifference minkowskiDifference) { - var a = support(Vector2.UnitX); - var b = support(-a); + var a = minkowskiDifference.Support(Vector2.UnitX); + var b = minkowskiDifference.Support(-a); - return Vector2.Dot(a, b) > 0 ? false : CheckSimplex(support, a, b); + return Vector2.Dot(a, b) > 0 ? false : CheckSimplex(minkowskiDifference.Support, a, b); } - private static (bool, Vector2, Vector2) OriginInsideWithSimplex(Func support) + private static (bool, Vector2, Vector2) OriginInsideWithSimplex(MinkowskiDifference minkowskiDifference) { - var a = support(Vector2.UnitX); - var b = support(-a); + var a = minkowskiDifference.Support(Vector2.UnitX); + var b = minkowskiDifference.Support(-a); - return Vector2.Dot(a, b) > 0 ? (false, a, b) : Simplex(support, a, b); + return Vector2.Dot(a, b) > 0 ? (false, a, b) : Simplex(minkowskiDifference.Support, a, b); } private static bool CheckSimplex(Func support, Vector2 a, Vector2 b) From a53677ba02050ce7e149c851f701ceaa3a0e4d05 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 25 Oct 2019 12:42:39 -0700 Subject: [PATCH 4/8] restructure files --- Bonk/GJK2D.cs | 80 ---------------------------------- Bonk/MinkowskiDifference.cs | 36 +++++++++++++++ Bonk/{ => Shapes}/Circle.cs | 0 Bonk/{ => Shapes}/Line.cs | 0 Bonk/{ => Shapes}/Polygon.cs | 0 Bonk/{ => Shapes}/Rectangle.cs | 0 Bonk/Shapes/Simplex.cs | 58 ++++++++++++++++++++++++ Bonk/Vector2Extensions.cs | 6 +-- 8 files changed, 97 insertions(+), 83 deletions(-) create mode 100644 Bonk/MinkowskiDifference.cs rename Bonk/{ => Shapes}/Circle.cs (100%) rename Bonk/{ => Shapes}/Line.cs (100%) rename Bonk/{ => Shapes}/Polygon.cs (100%) rename Bonk/{ => Shapes}/Rectangle.cs (100%) create mode 100644 Bonk/Shapes/Simplex.cs diff --git a/Bonk/GJK2D.cs b/Bonk/GJK2D.cs index 31a8783..1d9ae3d 100644 --- a/Bonk/GJK2D.cs +++ b/Bonk/GJK2D.cs @@ -2,90 +2,10 @@ using MoonTools.Core.Structs; using System; using MoonTools.Core.Bonk.Extensions; -using System.Collections.Generic; namespace MoonTools.Core.Bonk { - public struct MinkowskiDifference : IEquatable - { - private IShape2D shapeA; - private Transform2D transformA; - private IShape2D shapeB; - private Transform2D transformB; - public MinkowskiDifference(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) - { - this.shapeA = shapeA; - this.transformA = transformA; - this.shapeB = shapeB; - this.transformB = transformB; - } - - public bool Equals(MinkowskiDifference other) - { - return - shapeA == other.shapeA && - transformA.Equals(other.transformA) && - shapeB == other.shapeB && - transformB.Equals(other.transformB); - } - - public Vector2 Support(Vector2 direction) - { - return shapeA.Support(direction, transformA) - shapeB.Support(-direction, transformB); - } - } - - public struct Simplex : IShape2D - { - MinkowskiDifference minkowskiDifference; - Vector2 directionA; - Vector2 directionB; - - public Simplex(MinkowskiDifference minkowskiDifference, Vector2 directionA, Vector2 directionB) - { - this.minkowskiDifference = minkowskiDifference; - this.directionA = directionA; - this.directionB = directionB; - } - - public IEnumerable Vertices - { - get - { - yield return (Position2D)Support(directionA); - yield return (Position2D)Support(directionB); - yield return (Position2D)Support(-(directionB - directionA).Perpendicular()); - } - } - - public AABB AABB(Transform2D transform) - { - return Bonk.AABB.FromTransformedVertices(Vertices, transform); - } - - public bool Equals(IShape2D other) - { - if (other is Simplex polytope) - { - return minkowskiDifference.Equals(polytope.minkowskiDifference) && - directionA == polytope.directionA && - directionB == polytope.directionB; - } - - return false; - } - - public Vector2 Support(Vector2 direction) - { - return minkowskiDifference.Support(direction); - } - - public Vector2 Support(Vector2 direction, Transform2D transform) - { - return Vector2.Transform(Support(direction), transform.TransformMatrix); - } - } public static class GJK2D { diff --git a/Bonk/MinkowskiDifference.cs b/Bonk/MinkowskiDifference.cs new file mode 100644 index 0000000..bee297a --- /dev/null +++ b/Bonk/MinkowskiDifference.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.Xna.Framework; +using MoonTools.Core.Structs; + +namespace MoonTools.Core.Bonk +{ + public struct MinkowskiDifference : IEquatable + { + private IShape2D shapeA; + private Transform2D transformA; + private IShape2D shapeB; + private Transform2D transformB; + + public MinkowskiDifference(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) + { + this.shapeA = shapeA; + this.transformA = transformA; + this.shapeB = shapeB; + this.transformB = transformB; + } + + public bool Equals(MinkowskiDifference other) + { + return + shapeA == other.shapeA && + transformA.Equals(other.transformA) && + shapeB == other.shapeB && + transformB.Equals(other.transformB); + } + + public Vector2 Support(Vector2 direction) + { + return shapeA.Support(direction, transformA) - shapeB.Support(-direction, transformB); + } + } +} \ No newline at end of file diff --git a/Bonk/Circle.cs b/Bonk/Shapes/Circle.cs similarity index 100% rename from Bonk/Circle.cs rename to Bonk/Shapes/Circle.cs diff --git a/Bonk/Line.cs b/Bonk/Shapes/Line.cs similarity index 100% rename from Bonk/Line.cs rename to Bonk/Shapes/Line.cs diff --git a/Bonk/Polygon.cs b/Bonk/Shapes/Polygon.cs similarity index 100% rename from Bonk/Polygon.cs rename to Bonk/Shapes/Polygon.cs diff --git a/Bonk/Rectangle.cs b/Bonk/Shapes/Rectangle.cs similarity index 100% rename from Bonk/Rectangle.cs rename to Bonk/Shapes/Rectangle.cs diff --git a/Bonk/Shapes/Simplex.cs b/Bonk/Shapes/Simplex.cs new file mode 100644 index 0000000..9e09334 --- /dev/null +++ b/Bonk/Shapes/Simplex.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using Microsoft.Xna.Framework; +using MoonTools.Core.Structs; +using MoonTools.Core.Bonk.Extensions; + +namespace MoonTools.Core.Bonk +{ + public struct Simplex : IShape2D + { + MinkowskiDifference minkowskiDifference; + Vector2 directionA; + Vector2 directionB; + + public Simplex(MinkowskiDifference minkowskiDifference, Vector2 directionA, Vector2 directionB) + { + this.minkowskiDifference = minkowskiDifference; + this.directionA = directionA; + this.directionB = directionB; + } + + public IEnumerable Vertices + { + get + { + yield return (Position2D)Support(directionA); + yield return (Position2D)Support(directionB); + yield return (Position2D)Support(-(directionB - directionA).Perpendicular()); + } + } + + public AABB AABB(Transform2D transform) + { + return Bonk.AABB.FromTransformedVertices(Vertices, transform); + } + + public bool Equals(IShape2D other) + { + if (other is Simplex polytope) + { + return minkowskiDifference.Equals(polytope.minkowskiDifference) && + directionA == polytope.directionA && + directionB == polytope.directionB; + } + + return false; + } + + public Vector2 Support(Vector2 direction) + { + return minkowskiDifference.Support(direction); + } + + public Vector2 Support(Vector2 direction, Transform2D transform) + { + return Vector2.Transform(Support(direction), transform.TransformMatrix); + } + } +} \ No newline at end of file diff --git a/Bonk/Vector2Extensions.cs b/Bonk/Vector2Extensions.cs index f411b4a..016a8cb 100644 --- a/Bonk/Vector2Extensions.cs +++ b/Bonk/Vector2Extensions.cs @@ -2,15 +2,15 @@ using Microsoft.Xna.Framework; namespace MoonTools.Core.Bonk.Extensions { - public static class Vector2Extensions + internal static class Vector2Extensions { - public static Vector2 Cross(this Vector2 a, Vector2 b) + internal static Vector2 Cross(this Vector2 a, Vector2 b) { var vec3 = Vector3.Cross(new Vector3(a.X, a.Y, 0), new Vector3(b.X, b.Y, 0)); return new Vector2(vec3.X, vec3.Y); } - public static Vector2 Perpendicular(this Vector2 v) + internal static Vector2 Perpendicular(this Vector2 v) { return new Vector2(v.Y, -v.X); } From a3eec5c01c2a6fc12d9d03793002be2710a4876f Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 25 Oct 2019 13:09:03 -0700 Subject: [PATCH 5/8] tasty restructuring --- Bonk/GJK2D.cs | 62 ++++++++++++++++-------------------------- Bonk/Shapes/Simplex.cs | 8 ++++++ Test/EPA2DTest.cs | 6 ++-- 3 files changed, 35 insertions(+), 41 deletions(-) diff --git a/Bonk/GJK2D.cs b/Bonk/GJK2D.cs index 1d9ae3d..417b071 100644 --- a/Bonk/GJK2D.cs +++ b/Bonk/GJK2D.cs @@ -1,75 +1,61 @@ using Microsoft.Xna.Framework; using MoonTools.Core.Structs; -using System; using MoonTools.Core.Bonk.Extensions; namespace MoonTools.Core.Bonk { - - public static class GJK2D { public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) { var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB); - return OriginInside(minkowskiDifference); - } - - public static (bool, Simplex) CollisionAndSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) - { - var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB); - var (collision, a, b) = OriginInsideWithSimplex(minkowskiDifference); - var polytope = new Simplex(minkowskiDifference, a, b); - return (collision, polytope); - } - - private static Vector2 MinkowskiDifference(Vector2 direction, IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) - { - return shapeA.Support(direction, transformA) - shapeB.Support(-direction, transformB); - } - - private static bool OriginInside(MinkowskiDifference minkowskiDifference) - { var a = minkowskiDifference.Support(Vector2.UnitX); var b = minkowskiDifference.Support(-a); - return Vector2.Dot(a, b) > 0 ? false : CheckSimplex(minkowskiDifference.Support, a, b); + return Vector2.Dot(a, b) > 0 ? false : CheckSimplex(new Simplex(minkowskiDifference, a, b)); } - private static (bool, Vector2, Vector2) OriginInsideWithSimplex(MinkowskiDifference minkowskiDifference) + private static bool CheckSimplex(Simplex simplex) { - var a = minkowskiDifference.Support(Vector2.UnitX); - var b = minkowskiDifference.Support(-a); + var a = simplex.DirectionA; + var b = simplex.DirectionB; - return Vector2.Dot(a, b) > 0 ? (false, a, b) : Simplex(minkowskiDifference.Support, a, b); - } - - private static bool CheckSimplex(Func support, Vector2 a, Vector2 b) - { var axb = a.Cross(b); - var c = support((b - a).Perpendicular()); + var c = simplex.Support((b - a).Perpendicular()); var axc = a.Cross(c); var bxc = b.Cross(c); var cxb = -bxc; - return (b - a) == Vector2.Zero || (axb.Y > 0 != bxc.Y > 0 ? CheckSimplex(support, b, c) : (axc.Y > 0 != cxb.Y > 0 ? CheckSimplex(support, a, c) : true)); + return (b - a) == Vector2.Zero || (axb.Y > 0 != bxc.Y > 0 ? CheckSimplex(simplex.WithDirections(b, c)) : (axc.Y > 0 != cxb.Y > 0 ? CheckSimplex(simplex.WithDirections(a, c)) : true)); } - private static (bool, Vector2, Vector2) Simplex(Func support, Vector2 a, Vector2 b) + public static (bool, Simplex) FindCollisionSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB) { + var minkowskiDifference = new MinkowskiDifference(shapeA, transformA, shapeB, transformB); + var a = minkowskiDifference.Support(Vector2.UnitX); + var b = minkowskiDifference.Support(-a); + + return Vector2.Dot(a, b) > 0 ? (false, default(Simplex)) : Simplex(new Simplex(minkowskiDifference, a, b)); + } + + private static (bool, Simplex) Simplex(Simplex simplex) + { + var a = simplex.DirectionA; + var b = simplex.DirectionB; + if ((b - a) == Vector2.Zero) { - return (false, a, b); + return (false, simplex.WithDirections(a, b)); } else { - var c = support((b - a).Perpendicular()); + var c = simplex.Support((b - a).Perpendicular()); var axb = a.Cross(b); var bxc = b.Cross(c); if (axb.Y > 0 != bxc.Y > 0) { - return Simplex(support, b, c); + return Simplex(simplex.WithDirections(b, c)); } else { @@ -78,11 +64,11 @@ namespace MoonTools.Core.Bonk if (axc.Y > 0 != cxb.Y > 0) { - return Simplex(support, a, b); + return Simplex(simplex.WithDirections(a, b)); } else { - return (true, a, b); + return (true, simplex.WithDirections(a, b)); } } } diff --git a/Bonk/Shapes/Simplex.cs b/Bonk/Shapes/Simplex.cs index 9e09334..3eb8dc2 100644 --- a/Bonk/Shapes/Simplex.cs +++ b/Bonk/Shapes/Simplex.cs @@ -11,6 +11,9 @@ namespace MoonTools.Core.Bonk Vector2 directionA; Vector2 directionB; + public Vector2 DirectionA { get { return directionA; } } + public Vector2 DirectionB { get { return directionB; } } + public Simplex(MinkowskiDifference minkowskiDifference, Vector2 directionA, Vector2 directionB) { this.minkowskiDifference = minkowskiDifference; @@ -18,6 +21,11 @@ namespace MoonTools.Core.Bonk this.directionB = directionB; } + public Simplex WithDirections(Vector2 a, Vector2 b) + { + return new Simplex(minkowskiDifference, a, b); + } + public IEnumerable Vertices { get diff --git a/Test/EPA2DTest.cs b/Test/EPA2DTest.cs index 4459b9c..422d78d 100644 --- a/Test/EPA2DTest.cs +++ b/Test/EPA2DTest.cs @@ -18,7 +18,7 @@ namespace Tests var squareB = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); var transformB = new Transform2D(new Vector2(1.5f, 0)); - var (result, simplex) = GJK2D.CollisionAndSimplex(squareA, transformA, squareB, transformB); + var (result, simplex) = GJK2D.FindCollisionSimplex(squareA, transformA, squareB, transformB); result.Should().BeTrue(); @@ -36,7 +36,7 @@ namespace Tests var circleB = new Circle(1); var transformB = new Transform2D(new Vector2(1, 1)); - var (result, simplex) = GJK2D.CollisionAndSimplex(circleA, transformA, circleB, transformB); + var (result, simplex) = GJK2D.FindCollisionSimplex(circleA, transformA, circleB, transformB); result.Should().BeTrue(); @@ -57,7 +57,7 @@ namespace Tests var square = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); var transformB = Transform2D.DefaultTransform; - var (result, simplex) = GJK2D.CollisionAndSimplex(line, transformA, square, transformB); + var (result, simplex) = GJK2D.FindCollisionSimplex(line, transformA, square, transformB); result.Should().BeTrue(); From 1c95dd1591848ebcf0a33a0e2d357889d2271bcb Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 25 Oct 2019 13:14:37 -0700 Subject: [PATCH 6/8] optimized EPA2D --- Bonk/Bonk.csproj | 1 + Bonk/EPA2D.cs | 14 +++---- Bonk/SimplexVertices.cs | 91 ----------------------------------------- 3 files changed, 8 insertions(+), 98 deletions(-) delete mode 100644 Bonk/SimplexVertices.cs diff --git a/Bonk/Bonk.csproj b/Bonk/Bonk.csproj index 7999b43..2110f49 100644 --- a/Bonk/Bonk.csproj +++ b/Bonk/Bonk.csproj @@ -17,5 +17,6 @@ + \ No newline at end of file diff --git a/Bonk/EPA2D.cs b/Bonk/EPA2D.cs index 6b3e1a5..9b04e43 100644 --- a/Bonk/EPA2D.cs +++ b/Bonk/EPA2D.cs @@ -4,11 +4,10 @@ * https://blog.hamaluik.ca/posts/building-a-collision-engine-part-2-2d-penetration-vectors/ */ +using Collections.Pooled; using Microsoft.Xna.Framework; -using MoonTools.Core.Bonk.Extensions; using MoonTools.Core.Structs; using System; -using System.Collections.Generic; namespace MoonTools.Core.Bonk { @@ -18,15 +17,14 @@ namespace MoonTools.Core.Bonk CounterClockwise } - // TODO: convert SimplexVertices to PooledList public static class EPA2D { // vector returned gives direction from A to B - public static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, Simplex givenSimplex) + public static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, Simplex simplex) { - var simplexVertices = new SimplexVertices(new Vector2?[36]); + var simplexVertices = new PooledList(36, ClearMode.Always); - foreach (var vertex in givenSimplex.Vertices) + foreach (var vertex in simplex.Vertices) { simplexVertices.Add(vertex); } @@ -57,10 +55,12 @@ namespace MoonTools.Core.Bonk } } + simplexVertices.Dispose(); + return intersection; } - private static Edge FindClosestEdge(PolygonWinding winding, SimplexVertices simplexVertices) + private static Edge FindClosestEdge(PolygonWinding winding, PooledList simplexVertices) { var closestDistance = float.PositiveInfinity; var closestNormal = Vector2.Zero; diff --git a/Bonk/SimplexVertices.cs b/Bonk/SimplexVertices.cs deleted file mode 100644 index b89b753..0000000 --- a/Bonk/SimplexVertices.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using Microsoft.Xna.Framework; - -namespace MoonTools.Core.Bonk -{ - public struct SimplexVertices : IEnumerable - { - public Vector2?[] vertices; - - /// - /// Make sure to pass in all nulls - /// - public SimplexVertices(Vector2?[] vertices) - { - this.vertices = vertices; - } - - public Vector2 this[int key] - { - get - { - if (!vertices[key].HasValue) { throw new IndexOutOfRangeException(); } - return vertices[key].Value; - } - set - { - vertices[key] = value; - } - } - - public int Count { - get - { - for (int i = 0; i < vertices.Length; i++) - { - if (!vertices[i].HasValue) { return i; } - } - return vertices.Length; - } - } - - public void Add(Vector2 vertex) - { - if (Count > vertices.Length - 1) { throw new IndexOutOfRangeException(); } - - vertices[Count] = vertex; - } - - public void Insert(int index, Vector2 vertex) - { - if (Count >= vertices.Length || index > vertices.Length - 1) { throw new IndexOutOfRangeException(); } - - var currentCount = Count; - - for (int i = currentCount - 1; i >= index; i--) - { - vertices[i + 1] = vertices[i]; - } - - vertices[index] = vertex; - } - - public IEnumerator GetEnumerator() - { - foreach (Vector2? vec in vertices) - { - if (!vec.HasValue) { yield break; } - yield return vec.Value; - } - } - - public void RemoveAt(int index) - { - if (index > vertices.Length - 1 || index > Count) { throw new ArgumentOutOfRangeException(); } - - for (int i = vertices.Length - 2; i >= index; i--) - { - vertices[i] = vertices[i + 1]; - } - - vertices[vertices.Length - 1] = null; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} \ No newline at end of file From ca0488f9d27fb46115fcb458cfc3747bb4f5c617 Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 25 Oct 2019 13:25:25 -0700 Subject: [PATCH 7/8] circleCI build with .NET Core 3 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d94c542..68e42d5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2.1 defaults: &defaults working_directory: ~/repo docker: - - image: mcr.microsoft.com/dotnet/core/sdk:2.2 + - image: mcr.microsoft.com/dotnet/core/sdk:3.0 environment: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1 From 8077de11f58a4532f09f7d0c7c3513a2a4de12de Mon Sep 17 00:00:00 2001 From: Evan Hemsley Date: Fri, 25 Oct 2019 13:39:12 -0700 Subject: [PATCH 8/8] make tests idiomatic --- Test/GJK2DTest.cs | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Test/GJK2DTest.cs b/Test/GJK2DTest.cs index f6e5ff8..0cc7196 100644 --- a/Test/GJK2DTest.cs +++ b/Test/GJK2DTest.cs @@ -25,7 +25,7 @@ namespace Tests var transform = new Transform2D(new Position2D(0, 0), 0f, new Vector2(2, 2)); - Assert.IsTrue(GJK2D.TestCollision(lineA, transform, lineB, transform).Item1); + GJK2D.TestCollision(lineA, transform, lineB, transform).Should().BeTrue(); } [Test] @@ -45,7 +45,7 @@ namespace Tests var transform = new Transform2D(new Position2D(0, 0), 0f, new Vector2(2, 2)); - Assert.IsFalse(GJK2D.TestCollision(lineA, transform, lineB, transform).Item1); + GJK2D.TestCollision(lineA, transform, lineB, transform).Should().BeFalse(); } [Test] @@ -56,7 +56,7 @@ namespace Tests var circleB = new Circle(2); var transformB = new Transform2D(new Vector2(1, 1)); - Assert.IsTrue(GJK2D.TestCollision(circleA, transformA, circleB, transformB)); + GJK2D.TestCollision(circleA, transformA, circleB, transformB).Should().BeTrue(); } [Test] @@ -67,7 +67,7 @@ namespace Tests var circleB = new Circle(2); var transformB = new Transform2D(new Vector2(3, 0), 0f, new Vector2(2, 2)); - Assert.IsTrue(GJK2D.TestCollision(circleA, transformA, circleB, transformB).Item1); + GJK2D.TestCollision(circleA, transformA, circleB, transformB).Should().BeTrue(); } [Test] @@ -78,7 +78,7 @@ namespace Tests var circleB = new Circle(2); var transformB = new Transform2D(new Vector2(5, 5)); - Assert.IsFalse(GJK2D.TestCollision(circleA, transformA, circleB, transformB)); + GJK2D.TestCollision(circleA, transformA, circleB, transformB).Should().BeFalse(); } [Test] @@ -89,7 +89,7 @@ namespace Tests var circleB = new Circle(2); var transformB = new Transform2D(new Vector2(5, 5), 0, new Vector2(0.2f, 0.2f)); - Assert.IsFalse(GJK2D.TestCollision(circleA, transformA, circleB, transformB).Item1); + GJK2D.TestCollision(circleA, transformA, circleB, transformB).Should().BeFalse(); } [Test] @@ -109,7 +109,7 @@ namespace Tests var transformB = new Transform2D(new Vector2(0.5f, 0.5f)); - Assert.IsTrue(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB)); + GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Should().BeTrue(); } [Test] @@ -129,7 +129,7 @@ namespace Tests var transformB = new Transform2D(new Vector2(3f, 0f), 0f, new Vector2(3f, 3f)); - Assert.IsTrue(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Item1); + GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Should().BeTrue(); } [Test] @@ -149,7 +149,7 @@ namespace Tests var transformB = new Transform2D(new Vector2(5, 0)); - Assert.IsFalse(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB)); + GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Should().BeFalse(); } [Test] @@ -163,13 +163,13 @@ namespace Tests var transformA = Transform2D.DefaultTransform; var shapeB = new Polygon( - new Position2D(-1, 1), new Position2D(1, 1), - new Position2D(-1, -1), new Position2D(1, -1) + new Position2D(-2, 2), new Position2D(2, 2), + new Position2D(-2, -2), new Position2D(2, -2) ); - var transformB = new Transform2D(new Vector2(2f, 0), 0f, new Vector2(0.2f, 0.2f)); + var transformB = new Transform2D(new Vector2(3f, 0), 0f, new Vector2(0.5f, 0.5f)); - Assert.IsFalse(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Item1); + GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Should().BeFalse(); } [Test] @@ -186,7 +186,7 @@ namespace Tests var transformB = Transform2D.DefaultTransform; - Assert.IsTrue(GJK2D.TestCollision(line, transformA, polygon, transformB)); + GJK2D.TestCollision(line, transformA, polygon, transformB).Should().BeTrue(); } [Test] @@ -203,7 +203,7 @@ namespace Tests var transformB = Transform2D.DefaultTransform; - Assert.IsFalse(GJK2D.TestCollision(line, transformA, polygon, transformB)); + GJK2D.TestCollision(line, transformA, polygon, transformB).Should().BeFalse(); } [Test] @@ -214,7 +214,7 @@ namespace Tests var circle = new Circle(1); var transformB = Transform2D.DefaultTransform; - Assert.IsTrue(GJK2D.TestCollision(line, transformA, circle, transformB)); + GJK2D.TestCollision(line, transformA, circle, transformB).Should().BeTrue(); } [Test] @@ -225,7 +225,7 @@ namespace Tests var circle = new Circle(1); var transformB = Transform2D.DefaultTransform; - Assert.IsFalse(GJK2D.TestCollision(line, transformA, circle, transformB)); + GJK2D.TestCollision(line, transformA, circle, transformB).Should().BeFalse(); } [Test] @@ -241,7 +241,7 @@ namespace Tests var transformB = Transform2D.DefaultTransform; - Assert.IsTrue(GJK2D.TestCollision(circle, transformA, square, transformB)); + GJK2D.TestCollision(circle, transformA, square, transformB).Should().BeTrue(); } [Test] @@ -256,7 +256,7 @@ namespace Tests ); var squareTransform = Transform2D.DefaultTransform; - Assert.IsFalse(GJK2D.TestCollision(circle, circleTransform, square, squareTransform)); + GJK2D.TestCollision(circle, circleTransform, square, squareTransform).Should().BeFalse(); } [Test] @@ -268,7 +268,7 @@ namespace Tests var rectangleB = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); var transformB = new Transform2D(new Vector2(1, 0)); - Assert.IsTrue(GJK2D.TestCollision(rectangleA, transformA, rectangleB, transformB)); + GJK2D.TestCollision(rectangleA, transformA, rectangleB, transformB).Should().BeTrue(); } [Test] @@ -280,7 +280,7 @@ namespace Tests var rectangleB = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); var transformB = new Transform2D(new Vector2(1, 0)); - Assert.IsTrue(GJK2D.TestCollision(rectangleA, transformA, rectangleB, transformB)); + GJK2D.TestCollision(rectangleA, transformA, rectangleB, transformB).Should().BeTrue(); } } }