{"id":13526079,"url":"https://github.com/Aweptimum/Strike","last_synced_at":"2025-04-01T06:31:10.553Z","repository":{"id":43270089,"uuid":"354097558","full_name":"Aweptimum/Strike","owner":"Aweptimum","description":"2D Collision Detection for Lua using the Separating-Axis Theorem","archived":false,"fork":false,"pushed_at":"2023-09-15T12:49:39.000Z","size":287,"stargazers_count":24,"open_issues_count":2,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-08-02T06:19:32.130Z","etag":null,"topics":["2d-game-framework","collision-detection","love2d","lua-library","separating-axis-theorem"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Aweptimum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-04-02T18:10:44.000Z","updated_at":"2024-07-06T02:44:26.000Z","dependencies_parsed_at":"2024-01-03T04:02:37.033Z","dependency_job_id":null,"html_url":"https://github.com/Aweptimum/Strike","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aweptimum%2FStrike","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aweptimum%2FStrike/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aweptimum%2FStrike/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Aweptimum%2FStrike/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Aweptimum","download_url":"https://codeload.github.com/Aweptimum/Strike/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222703737,"owners_count":17025838,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2d-game-framework","collision-detection","love2d","lua-library","separating-axis-theorem"],"created_at":"2024-08-01T06:01:25.032Z","updated_at":"2024-11-02T10:31:29.566Z","avatar_url":"https://github.com/Aweptimum.png","language":"Lua","funding_links":[],"categories":["Physics"],"sub_categories":[],"readme":"# Strike\nStrike (Separating-Axis Theorem Routines for Ill-tempered, Kaleidascopic Entities) is a 2D SAT Collision Detection library.\\\nMade primarily for the [LÖVE](https://github.com/love2d/love) community, but should be compatible with any Lua version (at least at time of writing)\n\n## Installation\nDownload or recurisvely clone Strike into your project's directory\n\nIf using git's command line to clone Strike, run either of the following commands to clone it:\\\n`git clone --recurse-submodules https://github.com/Aweptimum/Strike.git (git \u003e= 2.13)`\\\n`git clone --recursive https://github.com/Aweptimum/Strike.git (git \u003c 2.13)`\\\nIf using github desktop, it automatically resolves submodules, so no command-line needed\n\nThen require it:\n```lua\nlocal S = require 'Strike'\n```\n\n## Shapes\nAccessed via `S.hapes`, Shapes are objects representing convex geometry, mostly polygons and circles. They are not used for collision detection by themselves, but are at your disposal. The available ones are in the `/shapes` directory, but you can add your own shape definitions as well. With the exception of `Circle`, every shape is extended from `ConvexPolygon` and overrides its constructor. There are a few more things to note, but it's important to know that both object definitions and instantiation are handled by rxi's [classic](https://github.com/rxi/classic). classic's distinction is that the constructor definition,`:new`, does not return the object itself. The object's `__call` method handles that, which is in turn handled by classic.\n\nAn example of a shape structure\n```lua\nRect = {\n    vertices    = {},             -- list of {x,y} coords\n    convex      = true,           -- boolean\n    centroid    = {x = 0, y = 0}, -- {x, y} coordinate pair\n    radius      = 0,              -- radius of circumscribed circle\n    area        = 0               -- absolute/unsigned area of polygon\n}\n```\nTo actually define this, the `ConvexPolygon` definition can be extended and overriden with a new constructor. Example in [Defining Your Own Shapes](#defining-your-own-shapes)\n\nSee [Basic Colliders](#basic-colliders) down below for available Shapes.\n\nNow for some Shape methods:\n### Transforming Shapes\nAll of the below methods return `self` if you need to do some transformation chaining\n```lua\nshape:translate(dx, dy)\t\t  -- adds dx and dy to each shapes' points\nshape:translateTo(x,y)\t\t  -- translates centroid to position (and everything with it)\nshape:rotate(angle, refx, refy)\t  -- rotates by `angle` (radians) about a reference point (defaults to centroid)\nshape:rotateTo(angle, refx, refy) -- rotates the shape *to* an `angle` (radians) about a reference point (defaults to centroid)\nshape:scale(sf, refx, refy)\t  -- scales by factor `sf` with respect to a reference point (defaults to centroid)\n```\n\n### Querying Shapes\nUnder the hood, Strike's `Shape` class uses a Transform object, so directly accessing coordinates won't give expected values. Relevant getters return transformed coordinates, such as `getVertex`, so definitely use them.\n\n```lua\nshape:getArea()\t\t-- Returns the area of the shape\nshape:getCentroid()\t-- Returns the centroid of the shape as a table {x = x, y = y}\nshape:getAreaCentroid()\t-- Returns the area *and* the centroid of the shape\nshape:getRadius()\t-- Returns the radies of the shape's circumscribed circle\nshape:getBbox()\t\t-- Returns the minimum AABB dimensions of the shape as 4 numbers: minimum-x, minimum-y, width, height\nshape:unpack()\t\t-- Returns the args the shape was constructed with\n```\n#### More specific query methods:\n```lua\nshape:getEdge(i)\n```\nGiven an index, returns the corresponding numbered edge. Returns `nil` if OOB\n```lua\nshape:getVertex(i)\n```\nGiven an index, returns the corresponding numbered edge. Returns `nil` if OOB\n```lua\nshape:project(nx, ny)\n```\nGiven two normalized vector components, returns the minimum and maximum values of the shape's projection onto the vector\n```lua\nshape:containsPoint(point)\n```\nGiven a point ({x=x,y=y}), returns `true` if it is within bounds of the shape, else `false`\n```lua\nshape:rayIntersects(x,y, nx,ny)\n```\nGiven a ray-origin `x,y` and a normalized vector, returns `true` if it intersects the shape, else `false`\n```lua\nshape:rayInteresections(x,y, nx,ny, ts)\t-- Returns a table of numbers that are \n```\nSame args as `rayIntersects` w/ optional table to insert into. Returns a list of numbers representing lengths along normal `nx, ny`\n\n### Misc Shape Methods\nThere are a few more methods, specifically:\n```lua\nshape:copy(x,y, angle_rads) -- (returns a copy, go figure) w/ centroid located at `x, y` and the specified angle.\nshape:ipairs( ?shape2 )\t    -- Edge iterator. shape2 only necessary for Circles, which need another shape to get a useful edge\nshape:vecs( ?shape2 )\t    -- Vector iterator. shape2 only necessary for Circles, which need another shape to get a useful edge\n```\n## Colliders\nAccessed via `S.trikers`, Colliders are grab-bags of geometry that are used for collision detection. They can be composed of Shapes, but may also contain other Colliders (and their shapes). The only requirement is that *every shape* in the Collider is convex. As with Shapes, available Colliders are defined in the `/colliders` directory and auto-loaded in. You can define custom collider definitions for particular collections of geometry that you're fond of. Just look at the included `Capsule` definition for a simple example. The included collider objects are listed below.\n### Basic Colliders\nThese correspond to the available `S.hapes`, but are not Shapes themselves. Their constructors are identical to the referenced shapes, and are automatically defined as `Collider(shape_name(shape_args))`. They are a Collider that contains a single shape of the same name.\n#### Circle\n```lua\nc = S.trikers.Circle(x_pos, y_pos, radius, angle_rads)\n```\nCreates a circle centered on `{x_pos, y_pos}` with the given `radius`. `angle_rads` not really useful at the moment, might delete it.\n\n#### ConvexPolygon\n```lua\ncp = S.trikers.ConvexPolygon(x,y, ...)\n```\nTakes a vardiadic list of `x,y` pairs that describe a convex polygon.\nShould be in counter-clockwise winding order, but the constructor will automatically sort non-ccw input when it fails a convexity check.\n\n#### Edge\n```lua\ne = S.trikers.Edge(x1,y1, x2,y2)\n```\nTakes the two endpoints of an edge and... creates an edge\n\n#### Ellipse\n```lua\nel = S.trikers.Ellipse(x_pos, y_pos, a, b, segments, angle_rads)\n```\nCreates a **discretized** Ellipse centered at `{x_pos, y_pos}`, `a` and `b` are lengths of major/minor axes, segments is the number of edges to use to approximate the Ellipse, and `angle_rads` is the angled offset in radians.\n\n#### Rectangle\n```lua\nS.trikers.Rectangle(x_pos, y_pos, dx, dy, angle_rads)\n```\nCreates a rectangle centered at `{x_pos, y_pos}` with width `dx` and height `dy`, offset by `angle_rads` (radians).\n\n#### RegularPolygon\n```lua\nr = S.trikers.RegularPolygon(x_pos, y_pos, n, radius, angle_rads)\n```\nCreates a regular polygon centered at `{x_pos, y_pos}` with `n` sides. `radius` is the radius of the circumscribed circle, `angle_rads` is the angled offset in radians.\n\n### Composite Colliders\n#### Collider\n```lua\ncoll = S.trikers.Collider( S.hapes.Rectangle(...), S.hapes.Circle(...), S.trikers.Circle(...), ... )\n```\nCreates a collider object given a variadic amount of `S.hapes` or `S.trikers` that contains the specified geometry, though `S.hapes` make for a flatter object\n\n#### Capsule\n```lua\ncap = S.trikers.Capsule(x_pos, y_pos, dx, dy, angle_rads)\n```\nCreates a capsule centered at `{x_pos, y_pos}` with width `dx` and height `dy`, offset by `angle_rads` (radians). Circles are along vertical axis.\n\n#### Concave\n```lua\nconcave = S.trikers.Concave(x,y, ...)\n```\nTakes a vardiadic list of `x,y` pairs that describe a concave polygon.\nShould be in pseudo-counter-clockwise winding order. \n\n### Transforming Colliders\nThe base Collider class (and all colliders that extend it) have the below methods\nAll of them return `self` if you need to do some transformation chaining\n\n```lua\ncollider:translate(dx, dy)\t\t-- adds dx and dy to each shapes' points\ncollider:translateTo(x,y)\t\t-- translates centroid to position (and everything with it)\ncollider:rotate(angle, refx, refy)\t-- rotates by `angle` (radians) about a reference point (defaults to centroid)\ncollider:rotateTo(angle, refx, refy)\t-- rotates the shape *to* an `angle` (radians) about a reference point (defaults to centroid)\ncollider:scale(sf, refx, refy)\t\t-- scales by factor `sf` with respect to a reference point (defaults to centroid)\n```\n### Querying Colliders\n```lua\ncollider:getArea()\t\t-- Returns the area of the collider\ncollider:getCentroid()\t\t-- Returns the centroid of the collider as a table {x = x, y = y}\ncollider:getAreaCentroid()\t-- Returns the area *and* the centroid of the collider\ncollider:getRadius()\t\t-- Returns the radies of the collider's circumscribed circle\ncollider:getBbox()\t\t-- Returns the minimum AABB dimensions of the collider as 4 numbers: minimum-x, minimum-y, width, height\ncollider:unpack()\t\t-- Returns the the shapes a collider contains\n```\n#### More specific query methods:\n```lua\ncollider:project(nx, ny)\n```\nGiven two normalized vector components, returns the minimum and maximum values of the collider's projection onto the vector.\nNot super useful for concave colliders, but it's there.\n\nOf course, there's also ray-methods, but they're expounded upon in [Ray Intersection](#ray-intersection)\n\n### Manipulating Colliders\nOther useful methods include:\n```lua\ncollider:copy() \t\t-- returns a copy of the collider\ncollider:remove(index, ...) \t-- removes a shape at the specified index, can handle multiple indexes\n\t**uses table.remove internally, so as long as you don't have tens of thousands of shapes in a collider, you'll be fine! \ncollider:consolidate() \t\t-- will merge incident convex polygons together, makes for less iterations if applicable\n```\n\n### Collider Iterating\nThere's the expensive `:ipairs()` method which uses a coroutine\n```lua\nfor parent_collider, shape, shape_index in collider:ipairs() do\n\t-- stuff\nend\n```\n`Collider:ipairs()` is a flattened-list iterator that will return *all* Shapes, nested or not, contained within the 'root' Collider it's called from.\n`parent_collider` is the collider that contains `shape`, and `shape_index` is the index of `shape` within `parent_collider.shapes`.\\\nIf you wanted to remove a shape from a Collider that met some condition, calling `parent_collider:remove( shape_index )` would do it.\n\nAnd the cheaper `:elems()` method that returns only leaf nodes and does not use a coroutine\n```lua\nfor shape in collider:elems() do\n    -- stuff\nend\n```\n\n## Ray Intersection\nThere are two ray intersection functions: `rayIntersects` and `rayIntersections`. Both have the same arguments: a ray origin and a normalized vector. The current implementation also assumes infinite length. They are defined both at the `Shapes` level and at the `Collider` level.\n```lua\nCollider:rayIntersect(ion)s(x,y, dx,dy)\n```\n`Intersects` earlies out at the first intersection and returns true (else false).\n\n`Intersections` returns a list of key-value pairs, where the keys are references to the shape objects hit and the values are a table of lengths along the ray vector. \nIt looks like this:\n```lua\nhits = Collider:rayIntersections(0,0, 1,1)\n-- hits = {\n--\t\u003cshape1\u003e = {length-1, length-2},\n--\t\u003cshape2\u003e = {length-1},\n--\t\u003cshape3\u003e = {length-1, length-2}\n--\t}\n```\nBecause the keys are references, it is possible to iterate through the `hits` table using `pairs()` and operate on them individually.\n\nTo compute the intersection points, here's a sample loop:\n```lua\nfor shape, dists in pairs(hits) do\n\tfor _, dist in ipairs(dists) do\n\t\tlocal px, py = rx + dx*dist, ry + dy*dist -- coordinate math\n\t\tlove.graphics.points(px, py) -- draw with love\n\tend\nend\n```\n## MTV's\nMinimum Translating Vectors are an object that represent the penetration depth between two colliders. It sits under the `classes/` directory if you wish to use it. Here's the constructor:\n```lua\nMTV(dx, dy, colliderShape, collidedShape)\n```\nWhere `dx,dy` are the vector components and `collider/collided` are each a Collider for reference.\nThere are other fields that are not currently set from the constructor. An example of the contained fields is below:\n```lua\nMTV = {\n    x = 0,\n    y = 0,\n    collider = \u003creference-to-collider\u003e,\n    collided = \u003creference-to-collider\u003e,\n    colliderShape = \u003creference-to-collider-shape\u003e,\n    collidedShape = \u003creference-to-collided-shape\u003e,\n    edgeIndex = colliderShape-edge-index,\n    separating = boolean\n}\n```\nThe vector components are accessed via `mtv.x` and `mtv.y`. The `collider` field represents the collider that the mtv is oriented *from*. If you were to draw the mtv from the centroid of the collider object, it would point out of the shape, towards the collider it is currently intersecting. The `collided` field is a reference to that intersected collider, the one that the mtv would be pointing *towards*. This information is necessary to know the orientation of the mtv and for settling/resolving collisions; they can directly be operated on from the references in the mtv.\n\nThe `colliderShape` and `collidedShape` fields are references to the two actual shapes that generated the collision. `edgeIndex` is the actual index of the edge that generated the separating axis. The edge can be retrieved by calling `mtv.colliderShape:getEdge( mtv.edgeIndex )`.\n\nThe plan is to add a solver that can calculate the contact points between the two colliders given the information inside of a MTV alone. Similar to Box2D's manifolds.\n(very unsure of how to go about this, will need to play with clipping algorithms).\n\nThe MTV object has a camelCased setter for every single field (`setCollider` for `collider`). It's unlikely anyone will need to use them, but here they are:\n```lua\nMTV:setCollider(collider)\n\nMTV:setColliderShape(shape)\n\nMTV:setEdgeIndex(index)\n\nMTV:setCollided(collider)\n\nMTV:setCollidedShape(shape)\n```\nThe one practical instance method of interest might be `MTV:mag()/mag2` - it returns the magnitude/magnitude-squared of the separating vector.\n\n## Collision\n### Broad Phase\nHas both circle-circle and aabb-aabb intersection test functions - `S.ircle(collider1, collider2)` and `S.aabb(collider1, collider2)` respectively. Both return true on interesction, else false.\n### Narrow Phase (SAT)\nCalling `S.triking(collider1, collider2)` will check for collisions between the two given colliders and return an MTV (there is a collision) or false (no collision). \n\nThe underlying SAT algorithm for two convex shapes is exposed through `S.AT(shape1, shape2)`, and it similarly returns an `MTV`, but whether there is a collision or not. The MTV's boolean `separating` field can be examined. true = separating (no collision), false = overlapping (collision).  It's entirely possible to build your physics around Shapes + S.AT instead of using Colliders + S.triking if you don't need composite shapes or groups of geometry.\n\nIt's important to note that geometries contained in the same Collider do not collide with each other. This is relevant for how Strike unintentionally gets around [Ghost Collisions](#ghosting)\n### Resolution\nCalling `S.ettle(mtv)` will move the referenced colliders by half the magnitude of the mtv in opposite directions to one another.\n\n### Point of Contact\nCalling `S.ite(mtv)` will return a table with 1-2 contact points (`{{x=x_val, y=y_val}}`) that represent the position of the collision. For compound Colliders, it only checks for contact between the shapes that generated the MTV, which is currently the maximum of all potential MTV's.\n\n## In Love?\nIf you're running within [LÖVE](https://github.com/love2d/love), every included shape has an appropriate `:draw` function defined. Calling `collider:draw` will draw every single shape and collider contained.\n\n# Bit more in depth\n\n## Ghosting\nErin Catto wrote up a nice article on the subject of [ghost collisions](https://box2d.org/posts/2020/06/ghost-collisions/). The problem outlined is this: if two colliders intersect, and a third collider hits both at their intersection, not-nice things can happen. Strike has this problem as well. Box2D solves it with chain shapes, which store edges together and modify the collision logic to avoid bad resolution. Strike doesn't directly solve this. However, in the case of two edges intersecting at a common endpoint and a shape hitting that intersection, it seems to be circumvented by adding both edge colliders to a common collider. A minimum example is below:\n```lua\nlocal edges = {\n\tS.hapes.Edge(400,600, 600,600),\n\tS.hapes.Edge(600,600, 800,600)\n}\n-- vs\nlocal EDGE = S.trikers.Collider(\n\tS.hapes.Edge(400,600, 600,600),\n\tS.hapes.Edge(600,600, 800,600)\n)\n```\nThe first will produce ghosting, while the second does not. This is either because of extreme luck during testing or built into the collision detection logic on accident. Either way, it's a feature.\n\nTo make this explicit, a check for whether the MTV is headed *into* a Collider's centroid should probably be added somewhere in the logic for `S.triking`.\n\n## Defining Your Own Shapes\nYou can create shape definitions in the `/shapes` directory of Strike that will be loaded into `S.hapes`. There are a few rules to follow:\n1. The shape must be convex\n2. At least define `:new` and `:unpack`\n3. Create a vertex list and feed it to the super constructor\n\nAnd you should generally be fine.\n\nLet's use the example of the rectangle structure in the [Shapes](#shapes) section. We'll define a Rectangle object that can be used with Strike. There are a lot of methods that each shape needs to have, but the base ConvexPolygon object takes care of most of that. Generally, all you need to define is a constructor (`:new`) and \"deconstructor\" (`:unpack`).\n\nLet's say we create an imaginary file called `Rectangle.lua`.\n\nFirst, let's require Vector-light and ConvexPolygon so we can do some math and override the parent's behavior.\n(Vector-light is currently accessed through DeWallua, the triangulation library)\n```lua\nlocal Vec = _Require_relative(..., 'lib.DeWallua.vector-light',1) -- yes, this is pretty horrible\nlocal Polygon = _Require_relative(..., 'ConvexPolygon')\n\nlocal Rect = Polygon:extend()\n```\nThen, we define a constructor. All we need to do is create a list of points to feed to the `ConvexPolygon` constructor and it'll handle the rest. Even if your points are fed in ccw, `ConvexPolygon:new` will sort it properly for you. For rotation/scaling, you can call `:rotate` or `scale` to handle that. Anything else is up to you.\n```lua\nfunction Rect:new(x, y, dx, dy, angle)\n\tassert(dx, 'Rectangle constructor missing width/height')\n\tdy = dy or dx\n    self.dx, self.dy = dx, dy\n\tself.angle = angle or 0\n\tlocal hx, hy = dx/2, dy/2 -- halfsize\n\tRect.super.new(self,\n\t\tx - hx, y - hy,\n\t\tx + hx, y - hy,\n\t\tx + hx, y + hy,\n\t\tx - hx, y + hy\n\t)\n    -- Remember to rotate the shape!\n\tself:rotate(self.angle)\nend\n```\nYou might notice that the attributes in the constructor that are no longer needed are actually stored (dx, dy). This is so that we can unpack those values if need-be, such as copying arguments into a constructor call. For that, we define `unpack`:\n```lua\nfunction Rect:unpack()\n    local cx, cy = self:getCentroid(x,y) \n\treturn cx, cy, self.dx, self.dy, self.angle\nend\n```\nAnd the last thing we need to do is return our object for when it's required by Strike:\n```lua\nreturn Rect\n```\n\nIf you crack open Rectangle.lua, this is actually the entire file! Hopefully this is enough to get you started if you're in need of trapezoids, parallelograms, or anything else!\n\nOne last thing to touch on (and you may have wondered this already) - do I use this by calling `S.hapes.Rect` or `S.hapes.Rectangle`? The answer is that Strike stores each shape using the filename, so `S.hapes.Rectangle` it is.\n\n## Defining Your Own Colliders\nJust like Shapes, you can make your own ready-to-go Collider definitions. These follow the same rules as Shapes, with the exception of `:unpack` being unnecessary. You should, however, always call `self:calcAreaCentroid` and `self:calcRadius` at the end of your constructor.\n\nLet's make a Capsule!\n\nWell, a Capsule is basically a Rectangle with two Circles on either end, so let's start there. We'll require the base `Collider`, the `Circle`, and the `Rectangle` objects:\n```lua\nlocal Collider\t= _Require_relative(..., 'Collider')\nlocal Circle\t= _Require_relative(..., 'shapes.Circle', 1)\nlocal Rectangle\t= _Require_relative(..., 'shapes.Rectangle', 1)\n\nlocal Capsule = Collider:extend()\n```\n\nFor our constructor, we'll have an x-y position that will be the center, a width, a height, and an angle-offset. Let's put the circles on top and bottom. This means their radii is equal to half the width of the `Capsule`. Let's say that the height encompasses both circles and the rectangle, so the Rectangle's height = `height - 2*circle-radius`. Lastly, the `Circle`s will be positioned on the top and bottom edge of the `Rectangle`, so we can just add/subtract half the height accordingle. \nNow, we have enough information to create our `Capsule` constructor:\n```lua\nfunction Capsule:new(x, y, dx, dy, angle_rads)\n    self.shapes = {}\n    self.centroid = {x=0,y=0}\n    self.radius = 0\n    self.angle = angle_rads or 0\n    local hx, hy = dx/2, dy/2\n    self:add(\n        Circle(x, y-hy, hx),\n        Circle(x, y+hy, hx),\n        Rectangle(x,y,dx,dy)\n    )\n    self:calcAreaCentroid()\n    self:calcRadius()\n    self:rotate(self.angle)\nend\n```\nAll that's left is to return it\n```lua\nreturn Capsule\n```\nAnd that's it! \n\nBecause the Collider object assumes it only contains convex shapes and other colliders, you have a lot of flexibility in what you can construct.\n\n# Thanks\nI'd like to thank Max Cahill, MikuAuahDark, Potatonomicon, and radgeRayden for helping (knowingly or not) with some details :)\n\n# Contributing\nVery little in this library was done in the best way from the start, and it's been extensively rewritten as its author learned more about best practices. Still, there's further work to be done (the require structure is particularly bad). If a snippet makes you cringe, or there's a feature missing, feel free to fork, edit, test, and PR.\n\n## Out-Of-Scope Features\n* **Bit-Masking/Layering**\\\n\tI want to add it, but this is where Lua falls down a bit. Between Lua 5.1/5.2, LuaJIT, and Lua 5.3+, there's too much compatibility to consider.\\\n\tBest left to the user to implement it\n\n## TODO\n- [ ] Clean up (require structure is awful, might need to eschew vector-light)\n- [ ] Add class primitives? Line/Point classes specifically, although Points would better be represented as vectors\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAweptimum%2FStrike","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAweptimum%2FStrike","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAweptimum%2FStrike/lists"}