diff --git a/content/api/AABB.md b/content/api/AABB.md index d84b66a..2fb2ed3 100644 --- a/content/api/AABB.md +++ b/content/api/AABB.md @@ -8,22 +8,38 @@ weight: 6 ## Properties -##### **public Vector2 Min { get; private set; }** +##### **public Vector2 Min** The minimum point of the AABB. -##### **public Vector2 Max { get; private set; }** +##### **public Vector2 Max** The maximum point of the AABB. -##### **public float Width { get; }** +##### **public float Width** The width of the AABB. -##### **public float Height { get; }** +##### **public float Height** The height of the AABB. +##### **public float Right** + +The right point of the AABB. + +##### **public float Left** + +The left point of the AABB. + +##### **public float Top** + +The top point of the AABB. + +##### **public float Bottom** + +The bottom point of the AABB. + ## Methods ##### **public static AABB FromVertices(IEnumerable vertices)** @@ -33,3 +49,7 @@ Can be used to generate an AABB from an arbitrary collection of vertices. It is ##### **public static AABB Transformed(AABB aabb, Transform2D transform)** Efficiently transforms an AABB. Used internally by SpatialHash to quickly transform a shape's AABB. + +##### **public static bool TestOverlap(AABB a, AABB b)** + +Efficiently checks if two AABBs overlap. diff --git a/content/api/EPA2D.md b/content/api/EPA2D.md deleted file mode 100644 index bb9373c..0000000 --- a/content/api/EPA2D.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: "EPA2D" -date: 2019-09-15T11:23:43-07:00 -weight: 30 ---- - -*EPA2D* is a static class containing a single method for computing the penetration vector of two overlapping shapes. - -## Methods - -##### **public static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, Simplex simplex)** - -Given two sets of shapes and transforms and an initial simplex, computes a penetration vector. When motion along the penetration vector is applied, the shape-transforms are guaranteed to become separated. - -**Example:** - -```cs -var squareA = new Rectangle(-1, -1, 1, 1); -var transformA = Transform2D.DefaultTransform; -var squareB = new Rectangle(-1, -1, 1, 1); -var transformB = new Transform2D(new Vector2(1.5f, 0)); - -var (result, simplex) = GJK2D.FindCollisionSimplex(squareA, transformA, squareB, transformB); - -if (result) -{ - var intersection = EPA2D.Intersect(squareA, transformA, squareB, transformB, simplex); -} -``` - -In this example, the resulting penetration vector will be (1, 0). \ No newline at end of file diff --git a/content/api/GJK2D.md b/content/api/GJK2D.md deleted file mode 100644 index c6ee413..0000000 --- a/content/api/GJK2D.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -title: "GJK2D" -date: 2019-09-15T11:19:13-07:00 -weight: 20 ---- - -*GJK2D* is a static class containing methods for narrow-phase collision detection. - -## Methods - -##### **public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)** - -Tests if the two shape-transform pairs are overlapping. - -**Example:** -```cs -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)); - -var result = GJK2D.TestCollision(circleA, transformA, circleB, transformB); -``` - -In this example, these transformed circles are indeed overlapping, so *result* will be true. - -##### **public static (bool, Simplex) FindCollisionSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)** - -Tests if the two shape-transform pairs are overlapping, and if so returns a Simplex that can be used by the EPA algorithm to determine a minimum separating vector. - -**Example:** -```cs -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)); - -var (result, simplex) = GJK2D.TestCollision(circleA, transformA, circleB, transformB); -``` - -In this example, these transformed circles are indeed overlapping, so *result* will be true and *simplex* will contain the Simplex that can be used by EPA to determine a minimum separating vector. \ No newline at end of file diff --git a/content/api/IHasAABB2D.md b/content/api/IHasAABB2D.md new file mode 100644 index 0000000..e53ff61 --- /dev/null +++ b/content/api/IHasAABB2D.md @@ -0,0 +1,26 @@ +--- +title: "IHasAABB2D" +date: 2020-07-18T12:30:21-07:00 +weight: 6 +--- + +*IHasAABB2D* is an interface that allows for spatial hashing to be performed. + +## Properties + +##### **public AABB { get; }** + +An axis-aligned bounding box for the shape. This is stored so we can efficiently transform the AABB when necessary. + +##### **AABB TransformedAABB(Transform2D transform)** + +A method which returns the axis-aligned bounding box for the transformed shape. + +For example, the TransformedAABB method for Bonk's *Circle* implementation: + +```cs +public AABB TransformedAABB(Transform2D Transform2D) +{ + return AABB.Transformed(AABB, transform2D); +} +``` diff --git a/content/api/IShape2D.md b/content/api/IShape2D.md index 42bc485..114517d 100644 --- a/content/api/IShape2D.md +++ b/content/api/IShape2D.md @@ -4,16 +4,10 @@ date: 2019-09-15T11:06:50-07:00 weight: 5 --- -An *IShape2D* is an interface that, when implemented, allows for spatial hashing and the computation of Minkowski Differences. +An *IShape2D* is an interface that, when implemented, allows for computation of Minkowski Differences. Your *IShape2D* implementations should be structs for memory efficiency purposes. -## Properties - -##### **public AABB { get; }** - -An axis-aligned bounding box for the shape. This is stored so we can efficiently transform the AABB when necessary. - ## Methods ##### **Vector2 Support(Vector2 direction, Transform2D transform)** @@ -30,18 +24,3 @@ public Vector2 Support(Vector2 direction, Transform2D transform) return Vector2.Transform(Vector2.Normalize(direction) * Radius, transform.TransformMatrix); } ``` - ---- - -##### **AABB TransformedAABB(Transform2D transform)** - -A method which returns the axis-aligned bounding box for the transformed shape. - -For example, the AABB method for Bonk's *Circle* implementation: - -```cs -public AABB TransformedAABB(Transform2D Transform2D) -{ - return AABB.Transformed(AABB, transform2D); -} -``` diff --git a/content/api/NarrowPhase.md b/content/api/NarrowPhase.md new file mode 100644 index 0000000..2453152 --- /dev/null +++ b/content/api/NarrowPhase.md @@ -0,0 +1,93 @@ +--- +title: "NarrowPhase" +date: 2019-09-15T11:19:13-07:00 +weight: 20 +--- + +*NarrowPhase* is a static class containing methods for narrow-phase collision detection. + +Note that all the **TestCollision** overloads automatically employ fast path checks. + +## Methods + +##### **public static bool TestCollision(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)** + +Tests if the two shape-transform pairs are overlapping. + +**Example:** +```cs +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)); + +var result = GJK2D.TestCollision(circleA, transformA, circleB, transformB); +``` + +In this example, these transformed circles are indeed overlapping, so *result* will be true. + +--- + +##### **public static bool TestCollision(IHasAABB2D hasBoundingBoxA, Transform2D transformA, IHasAABB2D hasBoundingBoxB, Transform2D transformB)** + +Detects whether two AABB-transform pairs are overlapping. + +--- + +##### **public static bool TestCollision(MultiShape multiShape, Transform2D multiShapeTransform, IShape2D shape, Transform2D shapeTransform)** + +Tests if a multishape-transform and shape-transform pair are overlapping. + +--- + +##### **public static bool TestCollision(IShape2D shape, Transform2D shapeTransform, MultiShape multiShape, Transform2D multiShapeTransform)** + +Tests if a multishape-transform and shape-transform pair are overlapping. + +--- + +##### **public static bool TestCollision(MultiShape multiShapeA, Transform2D transformA, MultiShape multiShapeB, Transform2D transformB)** + +Tests if two multishape-transform pairs are overlapping. + +--- + +##### **public static bool TestRectangleOverlap(Rectangle rectangleA, Transform2D transformA, Rectangle rectangleB, Transform2D transformB)** + +Fast path for axis-aligned rectangles. Assumes that the transforms have zero rotation. If the transforms have non-zero rotation this will be inaccurate. + +--- + +##### **public static bool TestPointRectangleOverlap(Point point, Transform2D pointTransform, Rectangle rectangle, Transform2D rectangleTransform)** + +Fast path for overlapping point and axis-aligned rectangle. The rectangle transform must have non-zero rotation. + +--- + +##### **public static bool TestCircleOverlap(Circle circleA, Transform2D transformA, Circle circleB, Transform2D transformB)** + +Fast path for overlapping circles. The circles must have uniform scaling. + +--- + +##### **public static (bool, Simplex) FindCollisionSimplex(IShape2D shapeA, Transform2D transformA, IShape2D shapeB, Transform2D transformB)** + +Tests if the two shape-transform pairs are overlapping, and if so returns a Simplex that can be used by the EPA algorithm to determine a minimum separating vector. + +**Example:** +```cs +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)); + +var (result, simplex) = GJK2D.TestCollision(circleA, transformA, circleB, transformB); +``` + +In this example, these transformed circles are indeed overlapping, so *result* will be true and *simplex* will contain the Simplex that can be used by EPA to determine a minimum separating vector. + +--- + +##### **public unsafe static Vector2 Intersect(IShape2D shapeA, Transform2D Transform2DA, IShape2D shapeB, Transform2D Transform2DB, Simplex2D simplex)** + +Returns a minimum separating vector in the direction from A to B. This information can be used to push two overlapping shape-transforms apart. diff --git a/content/api/SpatialHash.md b/content/api/SpatialHash.md index 2750e7c..cf635a8 100644 --- a/content/api/SpatialHash.md +++ b/content/api/SpatialHash.md @@ -24,9 +24,9 @@ var hash = new SpatialHash(16); --- -##### **public void Insert(T id, IShape2D shape, Transform2D Transform2D)** +##### **public void Insert(T id, IHasAABB2D shape, Transform2D Transform2D)** -Given an ID, a shape, and corresponding transform, inserts into the spatial hash. Uses the shape's AABB to insert into appropriate hash cells. +Given an ID, an object that has an AABB, and corresponding transform, inserts into the spatial hash. Uses the transformed AABB to insert into appropriate hash cells. **Example:** @@ -45,9 +45,9 @@ hash.Insert(1, rect, rectTransform); --- -##### **public IEnumerable<(T, IShape2D, Transform2D)> Retrieve(T id, IShape2D shape, Transform2D Transform2D)** +##### **public IEnumerable<(T, IHasAABB2D, Transform2D)> Retrieve(T id, IHasAABB2D shape, Transform2D Transform2D)** -Given an ID, a shape, and corresponding transform, retrieves a set of potential collisions from the spatial hash. +Given an ID, an object with an AABB, and corresponding transform, retrieves a set of potential collisions from the spatial hash. **Example:** @@ -72,6 +72,10 @@ If a potential colliding shape-transform has the same ID as the one passed into --- +##### **public IEnumerable<(T, IHasAABB2D, Transform2D)> Retrieve(AABB aabb)** + +Retrieves objects from the AABB based on a pre-transformed AABB. + ##### **public void Clear()** Empties the spatial hash. Useful when the spatial hash contains dynamic data that needs to be updated. diff --git a/content/api/SweepTest.md b/content/api/SweepTest.md new file mode 100644 index 0000000..56cee5a --- /dev/null +++ b/content/api/SweepTest.md @@ -0,0 +1,11 @@ +--- +title: "SweepTest" +date: 2020-07-18T13:07:01-07:00 +weight: 30 +--- + +*SweepTest* is a static class containing methods for performing sweep testing. + +##### **public static SweepResult Test(SpatialHash spatialHash, Rectangle rectangle, Transform2D transform, Vector2 ray) where T : IEquatable** + +Efficiently performs a sweep test on and against rectangles. Returns the position 1 pixel before overlap occurs. Useful for things like platforming collision. diff --git a/content/narrow_phase/EPA2D.md b/content/narrow_phase/EPA2D.md index 55437c1..7c47641 100644 --- a/content/narrow_phase/EPA2D.md +++ b/content/narrow_phase/EPA2D.md @@ -4,9 +4,9 @@ date: 2019-09-14T22:54:35-07:00 weight: 20 --- -If an overlap has occurred, often we wish to determine the penetration vector, which is the shortest possible vector by which one of the shapes could be moved in order for them to stop overlapping. For this purpose, Bonk implements the Expanding Polytope Algorithm. +If an overlap has occurred, sometimes we wish to determine the penetration vector, which is the shortest possible vector by which one of the shapes could be moved in order for them to stop overlapping. For this purpose, Bonk implements the Expanding Polytope Algorithm. -To use this feature, you must pass the shapes and transforms as well as the simplex that is returned from the *GJK2D.TestCollision* method. +To use this feature, you must pass the shapes and transforms as well as the simplex that is returned from the **NarrowPhase.FindCollisionSimplex** method. ```cs var squareA = new Rectangle(-1, -1, 1, 1); @@ -14,11 +14,10 @@ var transformA = Transform2D.DefaultTransform; var squareB = new Rectangle(-1, -1, 1, 1); var transformB = new Transform2D(new Vector2(1.5f, 0)); -var (result, simplex) = GJK2D.TestCollision(squareA, transformA, squareB, transformB); +var (result, simplex) = NarrowPhase.FindCollisionSimplex(squareA, transformA, squareB, transformB); if (result) { - var intersection = EPA2D.Intersect(squareA, transformA, squareB, transformB, simplex); + var intersection = NarrowPhase.Intersect(squareA, transformA, squareB, transformB, simplex); } ``` - diff --git a/content/narrow_phase/GJK2D.md b/content/narrow_phase/GJK2D.md index b112fba..17adec2 100644 --- a/content/narrow_phase/GJK2D.md +++ b/content/narrow_phase/GJK2D.md @@ -4,9 +4,9 @@ date: 2019-09-14T22:47:55-07:00 weight: 10 --- -Bonk uses the Gilbert-Johnson-Keerthi, or GJK, algorithm to perform narrow phase collision detection. +Bonk uses the Gilbert-Johnson-Keerthi, or GJK, algorithm to perform narrow phase collision detection. -To accurately check a collision, you must pass two sets of shapes and transforms to the *GJK2D.TestCollision* method. Remember that a transform operates on the vertices of a shape: it moves, rotates, and scales them in 2D space. +To accurately check a collision, you must pass two sets of shapes and transforms to the **NarrowPhase.TestCollision** method. Remember that a transform operates on the vertices of a shape: it moves, rotates, and scales them in 2D space. ```cs var circleA = new Circle(2); @@ -14,7 +14,7 @@ var transformA = new Transform2D(new Vector2(-1, -1)); var circleB = new Circle(2); var transformB = new Transform2D(new Vector2(1, 1)); -var (result, simplex) = GJK2D.TestCollision(circleA, transformA, circleB, transformB); +var result = NarrowPhase.TestCollision(circleA, transformA, circleB, transformB); ``` -Note that this method returns two variables. The first is whether an overlap has occurred. The second is the termination simplex. You can think of the termination simplex as a key that helps you find the penetration vector with *EPA2D*. Note that if there was no collision, the value of the simplex is irrelevant. \ No newline at end of file +The GJK algorithm can also return a *termination simplex*. You can think of the termination simplex as a key that helps you find the penetration vector with **NarrowPhase.Intersect**. We have a related method, **NarrowPhase.FindCollisionSimplex** which returns two variables. The first is whether an overlap has occurred. The second is the termination simplex. Note that if there was no collision, the value of the simplex is irrelevant. diff --git a/content/narrow_phase/SweepTest.md b/content/narrow_phase/SweepTest.md new file mode 100644 index 0000000..fbfd34d --- /dev/null +++ b/content/narrow_phase/SweepTest.md @@ -0,0 +1,32 @@ +--- +title: "Sweep Test" +date: 2020-07-18T12:37:29-07:00 +weight: 15 +--- + +Sweep testing is an efficient way to check collision positions on moving objects. + +At the moment, Bonk only implements sweep testing on Rectangles. + +Example: + +```cs +var rectangle = new Rectangle(-2, -2, 4, 4); +var transform = new Transform2D(new Position2D(-6, 0)); + +var otherRectangle = new Rectangle(-2, -2, 4, 4); +var otherTransform = new Transform2D(new Position2D(6, 0)); + +var farthestRectangle = new Rectangle(-2, -2, 4, 4); +var farthestTransform = new Transform2D(new Position2D(12, 0)); + +var downRectangle = new Rectangle(-6, -2, 12, 4); +var downTransform = new Transform2D(new Position2D(-6, 20)); + +var spatialHash = new SpatialHash(16); +spatialHash.Insert(1, otherRectangle, otherTransform); +spatialHash.Insert(2, farthestRectangle, farthestTransform); +spatialHash.Insert(3, downRectangle, downTransform); + +var result = SweepTest.Test(spatialHash, rectangle, transform, new Vector2(12, 0)); // hits +``` diff --git a/content/shapes/line.md b/content/shapes/line.md index 71ed035..9f7f270 100644 --- a/content/shapes/line.md +++ b/content/shapes/line.md @@ -10,4 +10,4 @@ To define a Line, give a start and end position. var start = new Position2D(-1, 1); var end = new Position2D(1, 1); var line = new Line(start, end); -``` \ No newline at end of file +``` diff --git a/content/shapes/polygon.md b/content/shapes/polygon.md index 4961d3e..70dd0bd 100644 --- a/content/shapes/polygon.md +++ b/content/shapes/polygon.md @@ -18,7 +18,7 @@ This polygon, for example, is a triangle. Be careful not to define a concave Polygon, as this will cause the results of collision detection to be incorrect. Support for concave polygons is planned for a future release. -Polygons are considered equal if they define the same vertices, regardless of order. They can even be equivalent to Rectangles: +Polygons are considered equal if they define the same vertices, regardless of order. ```cs var a = new Polygon( @@ -28,7 +28,12 @@ var a = new Polygon( new Position2D(-1, 1) ); -var b = new Rectangle(-1, -1, 1, 1); +var b = new Polygon( + new Position2D(-1, 1), + new Position2D(1, 1), + new Position2D(1, -1), + new Position2D(-1, -1) +); a == b; // true! ``` diff --git a/content/shapes/rectangle.md b/content/shapes/rectangle.md index 124c2e9..3fd1d6a 100644 --- a/content/shapes/rectangle.md +++ b/content/shapes/rectangle.md @@ -7,10 +7,9 @@ weight: 30 To define a rectangle, give a minimum X and Y and a maximum X and Y. ```cs -var minX = -1; -var minY = -1; -var maxX = 1; -var maxY = 1; -var rectangle = new Rectangle(minX, minY, maxX, maxY); +var left = -1; +var top = -1; +var width = 2; +var height = 2; +var rectangle = new Rectangle(left, top, width, height); ``` -