forked from MoonsideGames/MoonTools.Bonk
				
			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