initial commit
						commit
						3d1aa25a1e
					
				|  | @ -0,0 +1,2 @@ | |||
| bin/ | ||||
| obj/ | ||||
|  | @ -0,0 +1,40 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Microsoft.Xna.Framework; | ||||
| using MoonTools.Core.Structs; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public struct AABB | ||||
|     { | ||||
|         public int MinX { get; private set; } | ||||
|         public int MinY { get; private set; } | ||||
|         public int MaxX { get; private set; } | ||||
|         public int MaxY { get; private set; } | ||||
| 
 | ||||
|         public int Width { get { return MaxX - MinX; } } | ||||
|         public int Height { get { return MaxY - MinY; } } | ||||
| 
 | ||||
|         public static AABB FromTransform2DedVertices(IEnumerable<Position2D> vertices, Transform2D transform) | ||||
|         { | ||||
|             var Transform2DedVertices = vertices.Select(vertex => Vector2.Transform(vertex, transform.TransformMatrix)); | ||||
| 
 | ||||
|             return new AABB | ||||
|             { | ||||
|                 MinX = (int)Math.Round(Transform2DedVertices.Min(vertex => vertex.X)), | ||||
|                 MinY = (int)Math.Round(Transform2DedVertices.Min(vertex => vertex.Y)), | ||||
|                 MaxX = (int)Math.Round(Transform2DedVertices.Max(vertex => vertex.X)), | ||||
|                 MaxY = (int)Math.Round(Transform2DedVertices.Max(vertex => vertex.Y)) | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         public AABB(int minX, int minY, int maxX, int maxY) | ||||
|         { | ||||
|             MinX = minX; | ||||
|             MinY = minY; | ||||
|             MaxX = maxX; | ||||
|             MaxY = maxY; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,20 @@ | |||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|   <PropertyGroup> | ||||
|     <Version>1.0.0</Version> | ||||
|     <TargetFramework>netstandard2.0</TargetFramework> | ||||
|     <Description>.NET Core Collision Detection for MonoGame</Description> | ||||
|     <PackageId>MoonTools.Core.Bonk</PackageId> | ||||
|     <RootNamespace>MoonTools.Core.Bonk</RootNamespace> | ||||
|     <Company>Moonside Games</Company> | ||||
|     <Authors>Evan Hemsley</Authors> | ||||
|     <Copyright>Evan Hemsley 2019</Copyright> | ||||
|     <Product>MoonTools.Core.Bonk</Product> | ||||
|     <GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||||
|     <AssemblyName>MoonTools.Core.Bonk</AssemblyName> | ||||
|     <PackageLicenseExpression>LGPL-3.0-only</PackageLicenseExpression> | ||||
|     <PackageProjectUrl>https://github.com/MoonsideGames/MoonTools.Core.Bonk</PackageProjectUrl> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="MoonTools.Core.Structs" Version="1.0.1"/> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
|  | @ -0,0 +1,41 @@ | |||
| using System; | ||||
| using Microsoft.Xna.Framework; | ||||
| using MoonTools.Core.Structs; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public struct Circle : IShape2D, IEquatable<IShape2D> | ||||
|     { | ||||
|         public int Radius { get; private set; } | ||||
| 
 | ||||
|         public Circle(int radius) | ||||
|         { | ||||
|             Radius = radius; | ||||
|         } | ||||
| 
 | ||||
|         public Vector2 Support(Vector2 direction, Transform2D transform) | ||||
|         { | ||||
|             return Vector2.Transform(Vector2.Normalize(direction) * Radius, transform.TransformMatrix); | ||||
|         } | ||||
| 
 | ||||
|         public AABB AABB(Transform2D Transform2D) | ||||
|         { | ||||
|             return new AABB( | ||||
|                 Transform2D.Position.X - Radius, | ||||
|                 Transform2D.Position.Y - Radius, | ||||
|                 Transform2D.Position.X + Radius, | ||||
|                 Transform2D.Position.Y + Radius | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         public bool Equals(IShape2D other) | ||||
|         { | ||||
|             if (other is Circle circle) | ||||
|             { | ||||
|                 return Radius == circle.Radius; | ||||
|             } | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,102 @@ | |||
| /* | ||||
|  * Implementation of the Expanding Polytope Algorithm | ||||
|  * as based on the following blog post: | ||||
|  * https://blog.hamaluik.ca/posts/building-a-collision-engine-part-2-2d-penetration-vectors/ | ||||
|  */ | ||||
| 
 | ||||
| using Microsoft.Xna.Framework; | ||||
| using MoonTools.Core.Structs; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     enum PolygonWinding | ||||
|     { | ||||
|         Clockwise, | ||||
|         CounterClockwise | ||||
|     } | ||||
| 
 | ||||
|     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<Vector2> givenSimplexVertices) | ||||
|         { | ||||
|             var simplexVertices = new SimplexVertices(new Vector2?[36]); | ||||
| 
 | ||||
|             foreach (var vertex in givenSimplexVertices) | ||||
|             { | ||||
|                 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); | ||||
|             var e2 = (simplexVertices[0].X - simplexVertices[2].X) * (simplexVertices[0].Y + simplexVertices[2].Y); | ||||
|             var winding = e0 + e1 + e2 >= 0 ? PolygonWinding.Clockwise : PolygonWinding.CounterClockwise; | ||||
| 
 | ||||
|             Vector2 intersection = default; | ||||
| 
 | ||||
|             for (int i = 0; i < 32; i++) | ||||
|             { | ||||
|                 var edge = FindClosestEdge(winding, simplexVertices); | ||||
|                 var support = CalculateSupport(shapeA, Transform2DA, shapeB, Transform2DB, edge.normal); | ||||
|                 var distance = Vector2.Dot(support, edge.normal); | ||||
| 
 | ||||
|                 intersection = edge.normal; | ||||
|                 intersection *= distance; | ||||
| 
 | ||||
|                 if (Math.Abs(distance - edge.distance) <= float.Epsilon) | ||||
|                 { | ||||
|                     return intersection; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     simplexVertices.Insert(edge.index, support); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return intersection; | ||||
|         } | ||||
| 
 | ||||
|         private static Edge FindClosestEdge(PolygonWinding winding, SimplexVertices simplexVertices) | ||||
|         { | ||||
|             var closestDistance = float.PositiveInfinity; | ||||
|             var closestNormal = Vector2.Zero; | ||||
|             var closestIndex = 0; | ||||
| 
 | ||||
|             for (int i = 0; i < simplexVertices.Count; i++) | ||||
|             { | ||||
|                 var j = i + 1; | ||||
|                 if (j >= simplexVertices.Count) { j = 0; } | ||||
|                 Vector2 edge = simplexVertices[j] - simplexVertices[i]; | ||||
| 
 | ||||
|                 Vector2 norm; | ||||
|                 if (winding == PolygonWinding.Clockwise) | ||||
|                 { | ||||
|                     norm = new Vector2(edge.Y, -edge.X); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     norm = new Vector2(-edge.Y, edge.X); | ||||
|                 } | ||||
|                 norm.Normalize(); | ||||
| 
 | ||||
|                 var dist = Vector2.Dot(norm, simplexVertices[i]); | ||||
| 
 | ||||
|                 if (dist < closestDistance) | ||||
|                 { | ||||
|                     closestDistance = dist; | ||||
|                     closestNormal = norm; | ||||
|                     closestIndex = j; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return new Edge(closestDistance, closestNormal, closestIndex); | ||||
|         } | ||||
| 
 | ||||
|         private static Vector2 CalculateSupport(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, Vector2 direction) | ||||
|         { | ||||
|             return shapeA.Support(direction, Transform2DA) - shapeB.Support(-direction, Transform2DB); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,17 @@ | |||
| using Microsoft.Xna.Framework; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public struct Edge | ||||
|     { | ||||
|         public float distance; | ||||
|         public Vector2 normal; | ||||
|         public int index; | ||||
| 
 | ||||
|         public Edge(float distance, Vector2 normal, int index) { | ||||
|             this.distance = distance; | ||||
|             this.normal = normal; | ||||
|             this.index = index; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,126 @@ | |||
| /* | ||||
|  * 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 MoonTools.Core.Structs; | ||||
| using System; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public static class GJK2D | ||||
|     { | ||||
|         private enum SolutionStatus | ||||
|         { | ||||
|             NoIntersection, | ||||
|             Intersection, | ||||
|             StillSolving | ||||
|         } | ||||
| 
 | ||||
|         public static ValueTuple<bool, SimplexVertices> TestCollision(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB) | ||||
|         { | ||||
|             var vertices = new SimplexVertices(new Vector2?[] { null, null, null, null }); | ||||
| 
 | ||||
|             const SolutionStatus solutionStatus = SolutionStatus.StillSolving; | ||||
|             var direction = Transform2DB.Position - Transform2DA.Position; | ||||
| 
 | ||||
|             var result = (solutionStatus, direction); | ||||
| 
 | ||||
|             while (result.solutionStatus == SolutionStatus.StillSolving) | ||||
|             { | ||||
|                 result = EvolveSimplex(shapeA, Transform2DA, shapeB, Transform2DB, vertices, result.direction); | ||||
|             } | ||||
| 
 | ||||
|             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) | ||||
|             { | ||||
|                 case 0: | ||||
|                     if (direction == Vector2.Zero) | ||||
|                     { | ||||
|                         direction = Vector2.UnitX; | ||||
|                     } | ||||
|                     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); | ||||
|                     } | ||||
|                     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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| using System; | ||||
| using Microsoft.Xna.Framework; | ||||
| using MoonTools.Core.Structs; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public interface IShape2D : IEquatable<IShape2D> | ||||
|     { | ||||
|         // A Support function for a Minkowski sum. | ||||
|         // A Support function gives the point on the edge of a shape based on a direction. | ||||
|         Vector2 Support(Vector2 direction, Transform2D transform); | ||||
| 
 | ||||
|         AABB AABB(Transform2D transform); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,41 @@ | |||
| using System; | ||||
| using Microsoft.Xna.Framework; | ||||
| using MoonTools.Core.Structs; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public struct Line : IShape2D, IEquatable<IShape2D> | ||||
|     { | ||||
|         private Position2D[] vertices; | ||||
| 
 | ||||
|         public Line(Position2D start, Position2D end) | ||||
|         { | ||||
|             vertices = new Position2D[2] { start, end }; | ||||
|         } | ||||
| 
 | ||||
|         public Vector2 Support(Vector2 direction, Transform2D transform) | ||||
|         { | ||||
|             var Transform2DedStart = Vector2.Transform(vertices[0], transform.TransformMatrix); | ||||
|             var Transform2DedEnd = Vector2.Transform(vertices[1], transform.TransformMatrix); | ||||
|             return Vector2.Dot(Transform2DedStart, direction) > Vector2.Dot(Transform2DedEnd, direction) ? | ||||
|                 Transform2DedStart : | ||||
|                 Transform2DedEnd; | ||||
|         } | ||||
| 
 | ||||
|         public AABB AABB(Transform2D Transform2D) | ||||
|         { | ||||
|             return Bonk.AABB.FromTransform2DedVertices(vertices, Transform2D); | ||||
|         } | ||||
| 
 | ||||
|         public bool Equals(IShape2D other) | ||||
|         { | ||||
|             if (other is Line) | ||||
|             { | ||||
|                 var otherLine = (Line)other; | ||||
|                 return vertices[0].ToVector2() == otherLine.vertices[0].ToVector2() && vertices[1].ToVector2() == otherLine.vertices[1].ToVector2(); | ||||
|             } | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,60 @@ | |||
| using System; | ||||
| using Microsoft.Xna.Framework; | ||||
| using MoonTools.Core.Structs; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public struct Polygon : IShape2D, IEquatable<IShape2D> | ||||
|     { | ||||
|         public Position2D[] Vertices { get; private set; } | ||||
| 
 | ||||
|         // vertices are local to the origin | ||||
|         public Polygon(params Position2D[] vertices) | ||||
|         { | ||||
|             Vertices = vertices; | ||||
|         } | ||||
| 
 | ||||
|         public Vector2 Support(Vector2 direction, Transform2D transform) | ||||
|         { | ||||
|             var furthest = float.NegativeInfinity; | ||||
|             var furthestVertex = Vector2.Transform(Vertices[0], transform.TransformMatrix); | ||||
| 
 | ||||
|             foreach (var vertex in Vertices) | ||||
|             { | ||||
|                 var Transform2DedVertex = Vector2.Transform(vertex, transform.TransformMatrix); | ||||
|                 var distance = Vector2.Dot(Transform2DedVertex, direction); | ||||
|                 if (distance > furthest) | ||||
|                 { | ||||
|                     furthest = distance; | ||||
|                     furthestVertex = Transform2DedVertex; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return furthestVertex; | ||||
|         } | ||||
| 
 | ||||
|         public AABB AABB(Transform2D Transform2D) | ||||
|         { | ||||
|             return Bonk.AABB.FromTransform2DedVertices(Vertices, Transform2D); | ||||
|         } | ||||
| 
 | ||||
|         public bool Equals(IShape2D other) | ||||
|         { | ||||
|             if (other is Polygon) | ||||
|             { | ||||
|                 var otherPolygon = (Polygon)other; | ||||
| 
 | ||||
|                 if (Vertices.Length != otherPolygon.Vertices.Length) { return false; } | ||||
| 
 | ||||
|                 for (int i = 0; i < Vertices.Length; i++) | ||||
|                 { | ||||
|                     if (Vertices[i].ToVector2() != otherPolygon.Vertices[i].ToVector2()) { return false;} | ||||
|                 } | ||||
| 
 | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,69 @@ | |||
| using System; | ||||
| using Microsoft.Xna.Framework; | ||||
| using MoonTools.Core.Structs; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public struct Rectangle : IShape2D, IEquatable<IShape2D> | ||||
|     { | ||||
|         public int MinX { get; } | ||||
|         public int MinY { get; } | ||||
|         public int MaxX { get; } | ||||
|         public int MaxY { get; } | ||||
| 
 | ||||
|         private Position2D[] vertices; | ||||
| 
 | ||||
|         public Rectangle(int minX, int minY, int maxX, int maxY) | ||||
|         { | ||||
|             MinX = minX; | ||||
|             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 Transform2DedVertex = Vector2.Transform(v, transform.TransformMatrix); | ||||
|                 var distance = Vector2.Dot(Transform2DedVertex, direction); | ||||
|                 if (distance > furthestDistance) | ||||
|                 { | ||||
|                     furthestDistance = distance; | ||||
|                     furthestVertex = Transform2DedVertex; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return furthestVertex; | ||||
|         } | ||||
| 
 | ||||
|         public AABB AABB(Transform2D Transform2D) | ||||
|         { | ||||
|             return Bonk.AABB.FromTransform2DedVertices(vertices, Transform2D); | ||||
|         } | ||||
| 
 | ||||
|         public bool Equals(IShape2D other) | ||||
|         { | ||||
|             if (other is Rectangle rectangle) | ||||
|             { | ||||
|                 return MinX == rectangle.MinX && | ||||
|                     MinY == rectangle.MinY && | ||||
|                     MaxX == rectangle.MaxX && | ||||
|                     MaxY == rectangle.MaxY; | ||||
|             } | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,91 @@ | |||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using Microsoft.Xna.Framework; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public struct SimplexVertices : IEnumerable<Vector2> | ||||
|     { | ||||
|         public Vector2?[] vertices; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Make sure to pass in all nulls | ||||
|         /// </summary> | ||||
|         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<Vector2> 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(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,85 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using MoonTools.Core.Structs; | ||||
| 
 | ||||
| namespace MoonTools.Core.Bonk | ||||
| { | ||||
|     public class SpatialHash<T> where T : IEquatable<T> | ||||
|     { | ||||
|         private readonly int cellSize; | ||||
| 
 | ||||
|         private readonly Dictionary<int, Dictionary<int, HashSet<(IShape2D, Transform2D)>>> hashDictionary = new Dictionary<int, Dictionary<int, HashSet<(IShape2D, Transform2D)>>>(); | ||||
|         private readonly Dictionary<(IShape2D, Transform2D), T> IDLookup = new Dictionary<(IShape2D, Transform2D), T>(); | ||||
| 
 | ||||
|         public SpatialHash(int cellSize) | ||||
|         { | ||||
|             this.cellSize = cellSize; | ||||
|         } | ||||
| 
 | ||||
|         public (int, int) Hash(int x, int y) | ||||
|         { | ||||
|             return ((int)Math.Floor((float)x / cellSize), (int)Math.Floor((float)y / cellSize)); | ||||
|         } | ||||
| 
 | ||||
|         public void Insert(T id, IShape2D shape, Transform2D Transform2D) | ||||
|         { | ||||
|             var box = shape.AABB(Transform2D); | ||||
|             var minHash = Hash(box.MinX, box.MinY); | ||||
|             var maxHash = Hash(box.MaxX, box.MaxY); | ||||
| 
 | ||||
|             for (int i = minHash.Item1; i <= maxHash.Item1; i++) | ||||
|             { | ||||
|                 for (int j = minHash.Item2; j <= maxHash.Item2; j++) | ||||
|                 { | ||||
|                     if (!hashDictionary.ContainsKey(i)) | ||||
|                     { | ||||
|                         hashDictionary.Add(i, new Dictionary<int, HashSet<(IShape2D, Transform2D)>>()); | ||||
|                     } | ||||
| 
 | ||||
|                     if (!hashDictionary[i].ContainsKey(j)) | ||||
|                     { | ||||
|                         hashDictionary[i].Add(j, new HashSet<(IShape2D, Transform2D)>()); | ||||
|                     } | ||||
| 
 | ||||
|                     hashDictionary[i][j].Add((shape, Transform2D)); | ||||
|                     IDLookup[(shape, Transform2D)] = id; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public IEnumerable<(T, IShape2D, Transform2D)> Retrieve(T id, IShape2D shape, Transform2D Transform2D) | ||||
|         { | ||||
|             var box = shape.AABB(Transform2D); | ||||
|             var minHash = Hash(box.MinX, box.MinY); | ||||
|             var maxHash = Hash(box.MaxX, box.MaxY); | ||||
| 
 | ||||
|             for (int i = minHash.Item1; i <= maxHash.Item1; i++) | ||||
|             { | ||||
|                 for (int j = minHash.Item2; j <= maxHash.Item2; j++) | ||||
|                 { | ||||
|                     if (hashDictionary.ContainsKey(i) && hashDictionary[i].ContainsKey(j)) | ||||
|                     { | ||||
|                         foreach (var (otherShape, otherTransform2D) in hashDictionary[i][j]) | ||||
|                         { | ||||
|                             var otherID = IDLookup[(otherShape, otherTransform2D)]; | ||||
|                             if (!id.Equals(otherID)) { yield return (otherID, otherShape, otherTransform2D); } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public void Clear() | ||||
|         { | ||||
|             foreach (var innerDict in hashDictionary.Values) | ||||
|             { | ||||
|                 foreach (var set in innerDict.Values) | ||||
|                 { | ||||
|                     set.Clear(); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             IDLookup.Clear(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,70 @@ | |||
| using NUnit.Framework; | ||||
| using FluentAssertions; | ||||
| 
 | ||||
| using Microsoft.Xna.Framework; | ||||
| using System; | ||||
| using MoonTools.Core.Structs; | ||||
| using MoonTools.Core.Bonk; | ||||
| 
 | ||||
| namespace Tests | ||||
| { | ||||
|     public class EPA2DTest | ||||
|     { | ||||
|         [Test] | ||||
|         public void RectangleOverlap() | ||||
|         { | ||||
|             var squareA = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); | ||||
|             var transformA = Transform2D.DefaultTransform; | ||||
|             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); | ||||
| 
 | ||||
|             Assert.That(test.Item1, Is.True); | ||||
| 
 | ||||
|             var intersection = EPA2D.Intersect(squareA, transformA, squareB, transformB, test.Item2); | ||||
| 
 | ||||
|             intersection.X.Should().Be(1f); | ||||
|             intersection.Y.Should().Be(0); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void CircleOverlap() | ||||
|         { | ||||
|             var circleA = new Circle(2); | ||||
|             var transformA = Transform2D.DefaultTransform; | ||||
|             var circleB = new Circle(1); | ||||
|             var transformB = new Transform2D(new Vector2(1, 1)); | ||||
| 
 | ||||
|             var test = GJK2D.TestCollision(circleA, transformA, circleB, transformB); | ||||
| 
 | ||||
|             Assert.That(test.Item1, Is.True); | ||||
| 
 | ||||
|             var intersection = EPA2D.Intersect(circleA, transformA, circleB, transformB, test.Item2); | ||||
| 
 | ||||
|             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); | ||||
| 
 | ||||
|             intersection.X.Should().BeApproximately(ix, 0.01f); | ||||
|             intersection.Y.Should().BeApproximately(iy, 0.01f); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void LineRectangleOverlap() | ||||
|         { | ||||
|             var line = new Line(new Position2D(-4, -4), new Position2D(4, 4)); | ||||
|             var transformA = Transform2D.DefaultTransform; | ||||
|             var square = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); | ||||
|             var transformB = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             var test = GJK2D.TestCollision(line, transformA, square, transformB); | ||||
| 
 | ||||
|             Assert.That(test.Item1, Is.True); | ||||
| 
 | ||||
|             var intersection = EPA2D.Intersect(line, transformA, square, transformB, test.Item2); | ||||
| 
 | ||||
|             intersection.X.Should().Be(-1); | ||||
|             intersection.Y.Should().Be(1); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,201 @@ | |||
| using NUnit.Framework; | ||||
| using MoonTools.Core.Bonk; | ||||
| using MoonTools.Core.Structs; | ||||
| using Microsoft.Xna.Framework; | ||||
| 
 | ||||
| namespace Tests | ||||
| { | ||||
|     public class GJK2DTest | ||||
|     { | ||||
|         [Test] | ||||
|         public void LineLineOverlapping() | ||||
|         { | ||||
|             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); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void LineLineNotOverlapping() | ||||
|         { | ||||
|             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); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void CircleCircleOverlapping() | ||||
|         { | ||||
|             var circleA = new Circle(2); | ||||
|             var transformA = new Transform2D(new Vector2(-1, -1)); | ||||
|             var circleB = new Circle(2); | ||||
|             var transformB = new Transform2D(new Vector2(1, 1)); | ||||
| 
 | ||||
|             Assert.IsTrue(GJK2D.TestCollision(circleA, transformA, circleB, transformB).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void CircleCircleNotOverlapping() | ||||
|         { | ||||
|             var circleA = new Circle(2); | ||||
|             var transformA = new Transform2D(new Vector2(-5, -5)); | ||||
|             var circleB = new Circle(2); | ||||
|             var transformB = new Transform2D(new Vector2(5, 5)); | ||||
| 
 | ||||
|             Assert.IsFalse(GJK2D.TestCollision(circleA, transformA, circleB, transformB).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void PolygonPolygonOverlapping() | ||||
|         { | ||||
|             var shapeA = new Polygon( | ||||
|                 new Position2D(-1, 1), new Position2D(1, 1), | ||||
|                 new Position2D(-1, -1), new Position2D(1, -1) | ||||
|             ); | ||||
| 
 | ||||
|             var transformA = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             var shapeB = new Polygon( | ||||
|                 new Position2D(-1, 1), new Position2D(1, 1), | ||||
|                 new Position2D(-1, -1), new Position2D(1, -1) | ||||
|             ); | ||||
| 
 | ||||
|             var transformB = new Transform2D(new Vector2(0.5f, 0.5f)); | ||||
| 
 | ||||
|             Assert.IsTrue(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void PolygonPolygonNotOverlapping() | ||||
|         { | ||||
|             var shapeA = new Polygon(new Position2D(0, 0), | ||||
|                 new Position2D(-1, 1), new Position2D(1, 1), | ||||
|                 new Position2D(-1, -1), new Position2D(1, -1) | ||||
|             ); | ||||
| 
 | ||||
|             var transformA = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             var shapeB = new Polygon( | ||||
|                 new Position2D(-1, 1), new Position2D(1, 1), | ||||
|                 new Position2D(-1, -1), new Position2D(1, -1) | ||||
|             ); | ||||
| 
 | ||||
|             var transformB = new Transform2D(new Vector2(5, 0)); | ||||
| 
 | ||||
|             Assert.IsFalse(GJK2D.TestCollision(shapeA, transformA, shapeB, transformB).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void LinePolygonOverlapping() | ||||
|         { | ||||
|             var line = new Line(new Position2D(-1, -1), new Position2D(1, 1)); | ||||
| 
 | ||||
|             var transformA = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             var polygon = new Polygon( | ||||
|                 new Position2D(-1, -1), new Position2D(1, -1), | ||||
|                 new Position2D(1, 1), new Position2D(-1, 1) | ||||
|             ); | ||||
| 
 | ||||
|             var transformB = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             Assert.IsTrue(GJK2D.TestCollision(line, transformA, polygon, transformB).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void LinePolygonNotOverlapping() | ||||
|         { | ||||
|             var line = new Line(new Position2D(-5, 5), new Position2D(-5, 5)); | ||||
| 
 | ||||
|             var transformA = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             var polygon = new Polygon(new Position2D(0, 0), | ||||
|                 new Position2D(-1, -1), new Position2D(1, -1), | ||||
|                 new Position2D(1, 1), new Position2D(-1, 1) | ||||
|             ); | ||||
| 
 | ||||
|             var transformB = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             Assert.IsFalse(GJK2D.TestCollision(line, transformA, polygon, transformB).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void LineCircleOverlapping() | ||||
|         { | ||||
|             var line = new Line(new Position2D(-1, -1), new Position2D(1, 1)); | ||||
|             var transformA = Transform2D.DefaultTransform; | ||||
|             var circle = new Circle(1); | ||||
|             var transformB = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             Assert.IsTrue(GJK2D.TestCollision(line, transformA, circle, transformB).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void LineCircleNotOverlapping() | ||||
|         { | ||||
|             var line = new Line(new Position2D(-5, -5), new Position2D(-4, -4)); | ||||
|             var transformA = Transform2D.DefaultTransform; | ||||
|             var circle = new Circle(1); | ||||
|             var transformB = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             Assert.IsFalse(GJK2D.TestCollision(line, transformA, circle, transformB).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void CirclePolygonOverlapping() | ||||
|         { | ||||
|             var circle = new Circle(1); | ||||
|             var transformA = new Transform2D(new Vector2(0.25f, 0)); | ||||
| 
 | ||||
|             var square = new Polygon( | ||||
|                 new Position2D(-1, -1), new Position2D(1, -1), | ||||
|                 new Position2D(1, 1), new Position2D(-1, 1) | ||||
|             ); | ||||
| 
 | ||||
|             var transformB = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             Assert.IsTrue(GJK2D.TestCollision(circle, transformA, square, transformB).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void CirclePolygonNotOverlapping() | ||||
|         { | ||||
|             var circle = new Circle(1); | ||||
|             var circleTransform = new Transform2D(new Vector2(5, 0)); | ||||
| 
 | ||||
|             var square = new Polygon(new Position2D(0, 0), | ||||
|                 new Position2D(-1, -1), new Position2D(1, -1), | ||||
|                 new Position2D(1, 1), new Position2D(-1, 1) | ||||
|             ); | ||||
|             var squareTransform = Transform2D.DefaultTransform; | ||||
| 
 | ||||
|             Assert.IsFalse(GJK2D.TestCollision(circle, circleTransform, square, squareTransform).Item1); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void RotatedRectanglesOverlapping() | ||||
|         { | ||||
|             var rectangleA = new MoonTools.Core.Bonk.Rectangle(-1, -1, 2, 2); | ||||
|             var transformA = new Transform2D(new Vector2(-1, 0), -90f); | ||||
| 
 | ||||
|             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); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void RectanglesTouching() | ||||
|         { | ||||
|             var rectangleA = new MoonTools.Core.Bonk.Rectangle(-1, -1, 1, 1); | ||||
|             var transformA = new Transform2D(new Position2D(-1, 0)); | ||||
| 
 | ||||
|             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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,75 @@ | |||
| using FluentAssertions; | ||||
| using Microsoft.Xna.Framework; | ||||
| using NUnit.Framework; | ||||
| using MoonTools.Core.Structs; | ||||
| using MoonTools.Core.Bonk; | ||||
| 
 | ||||
| namespace Tests | ||||
| { | ||||
|     public class SpatialHashTest | ||||
|     { | ||||
|         [Test] | ||||
|         public void InsertAndRetrieve() | ||||
|         { | ||||
|             var spatialHash = new SpatialHash<int>(16); | ||||
| 
 | ||||
|             var rectA = new MoonTools.Core.Bonk.Rectangle(-2, -2, 2, 2); | ||||
|             var rectATransform = new Transform2D(new Vector2(-8, -8)); | ||||
| 
 | ||||
|             var rectB = new MoonTools.Core.Bonk.Rectangle(-2, -2, 2, 2); | ||||
|             var rectBTransform = new Transform2D(new Vector2(8, 8)); | ||||
| 
 | ||||
|             var rectC = new MoonTools.Core.Bonk.Rectangle(-2, -2, 2, 2); | ||||
|             var rectCTransform = new Transform2D(new Vector2(24, -4)); | ||||
| 
 | ||||
|             var rectD = new MoonTools.Core.Bonk.Rectangle(-2, -2, 2, 2); | ||||
|             var rectDTransform = new Transform2D(new Vector2(24, 24)); | ||||
| 
 | ||||
|             var circleA = new MoonTools.Core.Bonk.Circle(2); | ||||
|             var circleATransform = new Transform2D(new Vector2(24, -8)); | ||||
| 
 | ||||
|             var circleB = new MoonTools.Core.Bonk.Circle(8); | ||||
|             var circleBTransform = new Transform2D(new Vector2(16, 16)); | ||||
| 
 | ||||
|             var line = new MoonTools.Core.Bonk.Line(new Position2D(20, -4), new Position2D(22, -12)); | ||||
|             var lineTransform = new Transform2D(new Vector2(0, 0)); | ||||
| 
 | ||||
|             spatialHash.Insert(0, rectA, rectATransform); | ||||
|             spatialHash.Insert(1, rectB, rectBTransform); | ||||
|             spatialHash.Insert(2, rectC, rectCTransform); | ||||
|             spatialHash.Insert(3, rectD, rectDTransform); | ||||
|             spatialHash.Insert(4, circleA, circleATransform); | ||||
|             spatialHash.Insert(1, circleB, circleBTransform); | ||||
|             spatialHash.Insert(6, line, lineTransform); | ||||
| 
 | ||||
|             spatialHash.Retrieve(0, rectA, rectATransform).Should().BeEmpty(); | ||||
|             spatialHash.Retrieve(1, rectB, rectBTransform).Should().NotContain((1, circleB, circleBTransform)); | ||||
|             spatialHash.Retrieve(2, rectC, rectCTransform).Should().Contain((6, line, lineTransform)).And.Contain((4, circleA, circleATransform)); | ||||
|             spatialHash.Retrieve(3, rectD, rectDTransform).Should().Contain((1, circleB, circleBTransform)); | ||||
| 
 | ||||
|             spatialHash.Retrieve(4, circleA, circleATransform).Should().Contain((6, line, lineTransform)).And.Contain((2, rectC, rectCTransform)); | ||||
|             spatialHash.Retrieve(1, circleB, circleBTransform).Should().NotContain((1, rectB, rectBTransform)).And.Contain((3, rectD, rectDTransform)); | ||||
| 
 | ||||
|             spatialHash.Retrieve(6, line, lineTransform).Should().Contain((4, circleA, circleATransform)).And.Contain((2, rectC, rectCTransform)); | ||||
|         } | ||||
| 
 | ||||
|         [Test] | ||||
|         public void Clear() | ||||
|         { | ||||
|             var spatialHash = new SpatialHash<int>(16); | ||||
| 
 | ||||
|             var rectA = new MoonTools.Core.Bonk.Rectangle(-2, -2, 2, 2); | ||||
|             var rectATransform = new Transform2D(new Vector2(-8, -8)); | ||||
| 
 | ||||
|             var rectB = new MoonTools.Core.Bonk.Rectangle(-2, -2, 2, 2); | ||||
|             var rectBTransform = new Transform2D(new Vector2(8, 8)); | ||||
| 
 | ||||
|             spatialHash.Insert(0, rectA, rectATransform); | ||||
|             spatialHash.Insert(1, rectB, rectBTransform); | ||||
| 
 | ||||
|             spatialHash.Clear(); | ||||
| 
 | ||||
|             spatialHash.Retrieve(0, rectA, rectATransform).Should().HaveCount(0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|   <PropertyGroup> | ||||
|     <TargetFramework>netcoreapp3.0</TargetFramework> | ||||
|     <IsPackable>false</IsPackable> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="nunit" Version="3.11.0"/> | ||||
|     <PackageReference Include="NUnit3TestAdapter" Version="3.12.0"/> | ||||
|     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0"/> | ||||
|     <PackageReference Include="FluentAssertions" Version="5.9.0"/> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\Bonk\Bonk.csproj"/> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
|  | @ -0,0 +1,48 @@ | |||
|  | ||||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
| # Visual Studio 15 | ||||
| VisualStudioVersion = 15.0.26124.0 | ||||
| MinimumVisualStudioVersion = 15.0.26124.0 | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bonk", "Bonk\Bonk.csproj", "{F5349EC2-5BA2-4051-A1BB-48649344739B}" | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{866BDF4C-96C3-4DA9-B461-E01BF2146D19}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Any CPU = Debug|Any CPU | ||||
| 		Debug|x64 = Debug|x64 | ||||
| 		Debug|x86 = Debug|x86 | ||||
| 		Release|Any CPU = Release|Any CPU | ||||
| 		Release|x64 = Release|x64 | ||||
| 		Release|x86 = Release|x86 | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Debug|x64.Build.0 = Debug|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Debug|x86.ActiveCfg = Debug|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Debug|x86.Build.0 = Debug|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Release|x64.ActiveCfg = Release|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{F5349EC2-5BA2-4051-A1BB-48649344739B}.Release|x86.Build.0 = Release|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Debug|x64.Build.0 = Debug|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Debug|x86.ActiveCfg = Debug|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Debug|x86.Build.0 = Debug|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Release|x64.ActiveCfg = Release|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{866BDF4C-96C3-4DA9-B461-E01BF2146D19}.Release|x86.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| EndGlobal | ||||
		Loading…
	
		Reference in New Issue