Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vrld/HC
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: JustMog/HC
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 6 commits
  • 4 files changed
  • 1 contributor

Commits on May 8, 2022

  1. add capsule collider

    Mog authored and Mog committed May 8, 2022

    Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    4b27661 View commit details
  2. unify filled and unfilled capsule line segments

    Mog authored and Mog committed May 8, 2022

    Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    92febe4 View commit details
  3. Document capsule collider

    Mog authored and Mog committed May 8, 2022
    Copy the full SHA
    4be3144 View commit details
  4. Update readme

    Mog authored and Mog committed May 8, 2022
    Copy the full SHA
    a7c415b View commit details
  5. Optimise capsule bbox calculation

    Mog authored and Mog committed May 8, 2022

    Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    726c655 View commit details

Commits on May 12, 2022

  1. make removed shapes still useable

    Mog authored and Mog committed May 12, 2022

    Unverified

    This user has not yet uploaded their public signing key.
    Copy the full SHA
    f529893 View commit details
Showing with 162 additions and 8 deletions.
  1. +19 −5 README.md
  2. +16 −0 docs/Shapes.rst
  3. +10 −3 init.lua
  4. +117 −0 shapes.lua
24 changes: 19 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
# This repository is no longer maintained
## General Purpose 2D Collision Detection System

If you do maintain an active fork, please open a PR to add it to this readme.
Documentation and examples here: http://hc.readthedocs.org/

All other issues reports and pull requests will not be attended.
### Additional features

## General Purpose 2D Collision Detection System
- Capsule collider

Documentation and examples here: http://hc.readthedocs.org/
Useage:
```lua
local shapes = require 'HC.shapes'
local cap = shapes.CapsuleShape(x, y, radius, length)

-- or

local world = HC.new(cellSize)
local cap = world:capsule(x, y, radius, length)

```

### Differences

- Removed shapes no longer have their tranformation functions obliterated for no reason, so they can still be used and re-registered later.
16 changes: 16 additions & 0 deletions docs/Shapes.rst
Original file line number Diff line number Diff line change
@@ -293,6 +293,15 @@ Draw the circle shape either filled or as an outline and with the specified numb

.. class:: PointShape

.. class:: CapsuleShape

.. function:: CapsuleShape:draw(mode, segments)

:param DrawMode mode: How to draw the shape. Either 'line' or 'fill'.
:param number segments: The number of segments to draw each rounded end of the capsule with.

Draw the capsule shape either filled or as an outline and with the specified number of segments per rounded end.

.. function:: newPolygonShape(...)

:param numbers ...: Vertices of the :class:`Polygon`.
@@ -308,3 +317,10 @@ Draw the circle shape either filled or as an outline and with the specified numb

:param numbers x, y: Position of the point.
:returns: :class:`PointShape`.

.. function:: newCapsuleShape

:param numbers x, y: Position of the point.
:param number radius: Radius of the rounded ends of the capsule. Also its halfwidth.
:param number len: length of the capsule from the center of one rounded end to the other. total height = len + radius*2.
:returns: :class:`CapsuleShape`.
13 changes: 10 additions & 3 deletions init.lua
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ end
local newPolygonShape = Shapes.newPolygonShape
local newCircleShape = Shapes.newCircleShape
local newPointShape = Shapes.newPointShape
local newCapsuleShape = Shapes.newCapsuleShape

local HC = {}
function HC:init(cell_size)
@@ -74,9 +75,10 @@ end
function HC:remove(shape)
self._hash:remove(shape, shape:bbox())
for _, f in ipairs({'move', 'rotate', 'scale'}) do
shape[f] = function()
error(f.."() called on a removed shape")
end
shape[f] = nil --fall back to original class definition
-- shape[f] = function()
-- error(f.."() called on a removed shape")
-- end
end
return self
end
@@ -98,6 +100,10 @@ function HC:point(x,y)
return self:register(newPointShape(x,y))
end

function HC:capsule(x,y, radius, len)
return self:register(newCapsuleShape(x,y, radius, len))
end

-- collision detection
function HC:neighbors(shape)
local neighbors = self._hash:inSameCells(shape:bbox())
@@ -143,6 +149,7 @@ return setmetatable({
rectangle = function(...) return instance:rectangle(...) end,
circle = function(...) return instance:circle(...) end,
point = function(...) return instance:point(...) end,
capsule = function(...) return instance:capsule(...) end,

neighbors = function(...) return instance:neighbors(...) end,
collisions = function(...) return instance:collisions(...) end,
117 changes: 117 additions & 0 deletions shapes.lua
Original file line number Diff line number Diff line change
@@ -99,6 +99,14 @@ function PointShape:init(x,y)
self._pos = {x = x, y = y}
end

local CapsuleShape = {}
function CapsuleShape:init(x,y, radius, len)
Shape.init(self, 'capsule')
self._pos = {x = x, y = y}
self._len = len
self._radius = radius
end

--
-- collision functions
--
@@ -119,6 +127,19 @@ function CircleShape:support(dx,dy)
vector.mul(self._radius, vector.normalize(dx,dy)))
end

function CapsuleShape:support(dx, dy)
dx, dy = vector.normalize(dx, dy)
local rx, ry = vector.rotate(-self._rotation, dx, dy)

local resX, resY = 0, self._len/2
if vector.dot(rx, ry, 0, 1) < 0 then resY = resY * -1 end

resX, resY = vector.add(resX, resY, vector.mul(self._radius, rx, ry))
resX, resY = vector.rotate(self._rotation, resX, resY)

return vector.add(resX, resY, self._pos.x, self._pos.y)
end

-- collision dispatching:
-- let circle shape or compund shape handle the collision
function ConvexPolygonShape:collidesWith(other)
@@ -185,6 +206,18 @@ function PointShape:collidesWith(other)
return other:contains(self._pos.x, self._pos.y), 0,0
end

function CapsuleShape:collidesWith(other)
if self == other then return false end
if other._type == 'point' then
return other:collidesWith(self)
end
if other._type == 'compound' then
local collide, sx,sy = other:collidesWith(self)
return collide, sx and -sx, sy and -sy
end
return GJK(self, other)
end

--
-- point location/ray intersection
--
@@ -204,6 +237,16 @@ function PointShape:contains(x,y)
return x == self._pos.x and y == self._pos.y
end

function CapsuleShape:contains(x,y)
x = x - self._pos.x
y = y - self._pos.y
x, y = vector.rotate(-self._rotation, x, y)
if math.abs(x) < self._radius and math.abs(y) < self._len/2 then
return true
end
return vector.len2(x, y-self._len/2) < self._radius * self._radius
or vector.len2(x, y+self._len/2) < self._radius * self._radius
end

function ConcavePolygonShape:intersectsRay(x,y, dx,dy)
return self._polygon:intersectsRay(x,y, dx,dy)
@@ -266,6 +309,14 @@ function PointShape:intersectionsWithRay(x,y, dx,dy)
return intersects and {t} or {}
end

function CapsuleShape:intersectsRay(x,y, dx,dy)
error("CapsuleShape:intersectsRay not yet implemented", 2)
end

function CapsuleShape:intersectionsWithRay(x,y, dx,dy)
error("CapsuleShape:intersectionsWithRay not yet implemented", 2)
end

--
-- auxiliary
--
@@ -285,6 +336,10 @@ function PointShape:center()
return self._pos.x, self._pos.y
end

function CapsuleShape:center()
return self._pos.x, self._pos.y
end

function ConvexPolygonShape:outcircle()
local cx,cy = self:center()
return cx,cy, self._polygon._radius
@@ -304,6 +359,10 @@ function PointShape:outcircle()
return self._pos.x, self._pos.y, 0
end

function CapsuleShape:outcircle()
return self._pos.x, self._pos.y, self._len/2 + self._radius
end

function ConvexPolygonShape:bbox()
return self._polygon:bbox()
end
@@ -323,6 +382,19 @@ function PointShape:bbox()
return x,y,x,y
end

function CapsuleShape:bbox()
local ax, ay = vector.rotate(self._rotation, 0, -self._len/2)
local bx, by = vector.add(-ax, -ay, self._pos.x, self._pos.y)
ax, ay = vector.add(ax, ay, self._pos.x, self._pos.y)

local left = math.min(ax, bx) - self._radius
local top = math.min(ay, by) - self._radius
local right = math.max(ax, bx) + self._radius
local bottom = math.max(ay, by) + self._radius

return left, top, right, bottom
end


function ConvexPolygonShape:move(x,y)
self._polygon:move(x,y)
@@ -345,6 +417,11 @@ function PointShape:move(x,y)
self._pos.y = self._pos.y + y
end

function CapsuleShape:move(x,y)
self._pos.x = self._pos.x + x
self._pos.y = self._pos.y + y
end


function ConcavePolygonShape:rotate(angle,cx,cy)
Shape.rotate(self, angle)
@@ -374,6 +451,18 @@ function PointShape:rotate(angle, cx,cy)
self._pos.x,self._pos.y = vector.add(cx,cy, vector.rotate(angle, self._pos.x-cx, self._pos.y-cy))
end

function CapsuleShape:rotate(angle, cx,cy)
Shape.rotate(self, angle)
if not (cx and cy) then return end
local startX, startY = cx, cy
cx, cy = vector.sub(cx, cy, self._pos.x, self._pos.y)
cx, cy = vector.rotate(angle, cx, cy)
cx, cy = vector.add(cx, cy, self._pos.x, self._pos.y)
cx, cy = vector.sub(cx, cy, startX, startY)
self:move(-cx, -cy)

end


function ConcavePolygonShape:scale(s)
assert(type(s) == "number" and s > 0, "Invalid argument. Scale must be greater than 0")
@@ -400,6 +489,11 @@ function PointShape:scale()
-- nothing
end

function CapsuleShape:scale(s)
assert(type(s) == "number" and s > 0, "Invalid argument. Scale must be greater than 0")
self._radius = self._radius * s
self._len = self._len * s
end

function ConvexPolygonShape:draw(mode)
mode = mode or 'line'
@@ -426,12 +520,29 @@ function PointShape:draw()
(love.graphics.points or love.graphics.point)(self:center())
end

function CapsuleShape:draw(mode, segments)
love.graphics.push()
love.graphics.translate(self._pos.x, self._pos.y)
love.graphics.rotate(self._rotation)

if mode == "line" then
love.graphics.line(-self._radius,-self._len/2, -self._radius, self._len/2)
love.graphics.line(self._radius,-self._len/2, self._radius, self._len/2)
else
love.graphics.rectangle("fill", -self._radius,-self._len/2, self._radius*2, self._len)
end
love.graphics.arc(mode, "open", 0, -self._len/2, self._radius, 0, -math.pi, segments)
love.graphics.arc(mode, "open", 0, self._len/2, self._radius, 0, math.pi, segments)

love.graphics.pop()
end

Shape = common_local.class('Shape', Shape)
ConvexPolygonShape = common_local.class('ConvexPolygonShape', ConvexPolygonShape, Shape)
ConcavePolygonShape = common_local.class('ConcavePolygonShape', ConcavePolygonShape, Shape)
CircleShape = common_local.class('CircleShape', CircleShape, Shape)
PointShape = common_local.class('PointShape', PointShape, Shape)
CapsuleShape = common_local.class('CapsuleShape', CapsuleShape, Shape)

local function newPolygonShape(polygon, ...)
-- create from coordinates if needed
@@ -456,13 +567,19 @@ local function newPointShape(...)
return common_local.instance(PointShape, ...)
end

local function newCapsuleShape(...)
return common_local.instance(CapsuleShape, ...)
end

return {
ConcavePolygonShape = ConcavePolygonShape,
ConvexPolygonShape = ConvexPolygonShape,
CircleShape = CircleShape,
PointShape = PointShape,
CapsuleShape = CapsuleShape,
newPolygonShape = newPolygonShape,
newCircleShape = newCircleShape,
newPointShape = newPointShape,
newCapsuleShape = newCapsuleShape,
}