Fork me on GitHub
hardoncollider^ top

The main module.

HardonCollider will automatically handle - but not resolve - collisions. It uses search data structure - a spatial hash - to quickly find colliding shapes.

A spatial hash is simply a grid that is laid over the whole scene in which a shape can occupy several cells. To find shapes that may be colliding, you simply need to look which shapes occupy the same cell. You can specify the cell size in the init function.

To get a less boring explanation on how to use this, see the tutorial (once it's there).

function init(cell_size, callback_start, callback_persist, callback_stop)^ top

Initializes the library. Call this in love.load(). All the parameters can be omitted.

Parameters:
number cell_size (100)
Cell size for internal search structure.
function callback_start (empty function)
Called when two shapes start colliding.
function callback_persist (empty function)
Called when two continue to collide, i.e. if the collision lasts more than one frame.
function callback_stop (empty function)
Called when two shapes stop colliding.
Returns:
Nothing
Example:
function love.load()
    hardoncollider.init(150)
end
function setCallbacks(start,persist,stop)^ top
function setCallbacks{start = start,persist = persist,stop = stop}

Sets the different callbacks. The second calling style let's you specify the callbacks by name, see the example.

If nil is passed as any callback, the callback will not be changed.

Each callback has the following prototype:

function callback(dt, shape_one, shape_two, mtv_x, mtv_y)
The two shape parameters are the colliding shapes. The last two parameters, mtv_x and mtv_y define the minimum translation vector, i.e. the direction and magnitude shape_one has to be moved so that the collision will be resolved.

Parameters:
function start
Called when two shapes start colliding.
function persist
Called when two continue to collide, i.e. if the collision lasts more than one frame.
function stop
Called when two shapes stop colliding.
Returns:
Nothing
Example:
function start(dt, shape_one, shape_two, mtv_x, mtv_y)
    print('started colliding:', shape_one, shape_two)
	print('mtv:', mtv_x, mtv_y)
end

function persist_one(dt, shape_one, shape_two, mtv_x, mtv_y)
    print('still colliding:', shape_one, shape_two)
    -- move both shape_one and shape_two to resolve the collision
    shape_one:move(mtv_x/2, mtv_y/2)
    shape_two:move(-mtv_x/2, -mtv_y/2)
end

function persist_two(dt, shape_one, shape_two, mtv_x, mtv_y)
    print('still colliding:', shape_one, shape_two)
    -- move only shape_one to resolve the collision
    shape_one:move(mtv_x, mtv_y)
end

function stop(dt, shape_one, shape_two) -- ignore the translation vector
    print('collision resolved')
end

function love.load()
    hardoncollider.init(100)
    -- set initial callbacks
    hardoncollider.setCallbacks(start, persist_one, stop)
    -- change persist callback
    hardoncollider.setCallbacks{persist = persist_two}
end
function addPolygon(x1,y1, ..., xn,yn)^ top

Add a polygon to the collision detection system. Any non-intersection polygon will work, even convex polygons.

Note that if three consecutive points lie on a line, the middle point will be discarded. This means you cannot construct polygon shapes out of lines.

Parameters:
numbers x1,y1, ..., xn,yn
The corners of the polygon. At least three corners (that do not lie on a line) are needed.
Returns:
Shape
The polygon shape added to the scene.
Example:
shape = hardoncollider.addPolygon(10,10, 40,50, 70,10, 40,30)
function addRectangle(x, y, w, h)^ top

Add a rectangle shape to the collision detection system.

Parameters:
numbers x, y
The upper left corner of the rectangle.
numbers w, h
The width and height of the rectangle.
Returns:
Shape
The rectangle added to the scene.
Example:
rect = hardoncollider.addRectangle(100,120, 200,40)
function addCircle(cx, cy, radius)^ top

Add a circle shape to the collision detection system.

Parameters:
numbers cx, cy
The circle center.
number radius
The circle radius.
Returns:
Shape
The circle added to the scene.
Example:
circle = hardoncollider.addCircle(400,300, 100)
function update(dt, max_delta)^ top

Checks for collisions and call callbacks. Use this in love.update(dt).

A maximum time delta can be specified. dt will be chopped up in slices not bigger than this maximum and the scene is updated for each time slice.

Note that the delta time has no effect on the collision detection itself, but will be passed to the callback functions.

Parameters:
number dt
The time since the last update.
number max_delta
Maximum time delta (see description).
Returns:
Nothing
Example:
function love.update(dt)
    hardoncollider.update(dt, .02)
end
function remove(shape)^ top

Remove a shape from the collision detection system. Note that if you remove a shape in the start or persist callback, other shapes might still have collided with it, so the shape will be argument to the other calls of start or persist. In any case, the stop callback will be called in the next call to update for each shape which the removed shape collided with.

Parameters:
Shape shape
The shape to be removed.
Returns:
Nothing
Example:
hardoncollider.remove(circle)
hardoncollider.shape^ top

Shape classes with collision detection methods.

This defines methods to move, rotate and draw shapes created with hardoncollider.add*.

If you don't want to use the full blown module, you can still use these classes to test for colliding shapes.

They might also be useful for doing GUI stuff, e.g. when testing if the mouse hovers a button.

Some functions (getAxes, projectOn, ...) are left undocumented, as they have little value outside the scope of collision detection.

class PolygonShape(x1,y1, ..., xn,yn)^ top
class PolygonShape(polygon)

Construct a shape using a non-intersecting ploygon.

You can either specify the coordinates as with hardoncollider.addPolygon() or use an instance of the Polygon class.

Parameters:
numbers x1,y1, ..., xn,yn
The corners of the polygon. At least three corners (that do not lie on a line) are needed.
Polygon polygon
Construct the shape from this convex polygon.
Returns:
Shape
The constructed shape.
Example:
shape = PolygonShape( Polygon(100,100, 200,200, 300,100) )
class CircleShape(cx,cy, radius)^ top

Construct a circular shape.

Parameters:
numbers cx, cy
The circle center.
number radius
The circle radius.
Returns:
Shape
The constructed circle shape.
Example:
shape = CircleShape(400,300, 100)
function shape:move(x, y)^ top

Move the shape.

Parameters:
numbers x, y
The direction to move the shape in.
Returns:
Nothing
Example:
circle:move(10,15) -- move the circle 10 units right and 15 units down
function shape:rotate(angle, cx,cy)^ top

Rotate the shape. A rotation center can be specified. If no center is given, the shape's center is used.

Parameters:
number angle
Amount to rotate the shape (in radians).
numbers cx, cy
Rotation center. Defaults to the shape's center if omitted.
Returns:
Nothing
Example:
rectangle:rotate(math.pi/4)
function shape:center()^ top

Get the center of the shape.

If the shape is a CircleShape, this is the circle center, else it's the polygon's centroid.

Parameters:
None
Returns:
numbers x, y
The center of the shape.
Example:
print("Circle at:", circle:center())
function shape:draw(mode)^ top

Draw the shape either filled or as outline.

Parameters:
DrawMode mode
How to draw the shape. Either 'line' or 'fill'.
Returns:
Nothing
Example:
circle:draw('fill')
function shape:collidesWith(other)^ top

Test if two shapes collide.

Parameters:
Shape other
Test for collision with this shape.
Returns:
boolean collide
true if the two shapes collide, false otherwise.
vector mtv
The minimum translation vector, or nil if the two shapes don't collide.
Example:
if circle:collidesWith(rectangle) then
    print("collision detected!")
end
hardoncollider.polygon^ top

Definition of a Polygon class and implementation of some handy algorithms.

On it's own, this class does not offer any collision detection. If you want that, use a PolygonShape instead.

class Polygon(x1,y1, ..., xn,yn)^ top

Construct a polygon.

At least three points that are not collinear (being on a straight line) are needed to construct the polygon. If there are collinear points, these points will be removed so that the overall shape of the polygon is not changed.

Parameters:
numbers x1,y1, ..., xn,yn
The corners of the polygon. At least three corners are needed.
Returns:
Polygon
The polygon object.
Example:
poly = Polygon(10,10, 40,50, 70,10, 40,30)
function Polygon:unpack()^ top

Get the polygon's vertices. Useful for drawing with love.graphics.polygon().

Parameters:
None
Returns:
numbers x1,y1, ..., xn,yn
The vertices of the polygon.
Example:
love.graphics.draw('line', poly:unpack())
function Polygon:clone()^ top

Get a copy of the polygon.

Since Lua uses references when simply assigning an existing polygon to a variable, unexpected things can happen when operating on the variable. Consider this code:

p1 = Polygon(10,10, 40,50, 70,10, 40,30)
p2 = p1
p3 = p1:clone()
p2:rotate(math.pi) -- p1 will be rotated, too!
p3:rotate(-math.pi) -- only p3 will be rotated
Parameters:
None
Returns:
Polygon polygon
A copy of the polygon.
Example:
copy = poly:clone()
copy:move(10,20)
function Polygon:getBBox()^ top

Get axis aligned bounding box.

Parameters:
None
Returns:
numbers x1, y1
Upper left corner of the bounding box.
numbers x2, y2
Lower right corner of the bounding box.
Example:
x1,y1,x2,y2 = poly:getBBox()
-- draw bounding box
love.graphics.rectangle('line', x1,y2, x2-x1, y2-y1)
function Polygon:isConvex()^ top

Test if a polygon is convex, i.e. a line line between any two points inside the polygon will lie in the interior of the polygon.

Parameters:
None
Returns:
boolean convex
true if the polygon is convex, false otherwise.
Example:
-- split into convex sub polygons
if not poly:isConvex() then
    list = poly:splitConvex()
else
    list = {poly:clone()}
end
function Polygon:move(x,y)^ top
function Polygon:move(direction)

Move a polygon in a direction. You can either use coordinates (x, y) or a hump vector.

Parameters:
numbers x, y
Coordinates of the direction to move.
vector direction
Direction to move.
Returns:
Nothing
Example:
poly:move(10,-5) -- move 10 units right and 5 units up
function Polygon:rotate(angle)^ top
function Polygon:rotate(angle, cx, cy)
function Polygon:rotate(angle, center)

Rotate the polygon. You can define a rotation center. If it is omitted, the polygon will be rotated around it's centroid.

For defining a rotation center, you can either use coordinate form (cx, cy) or a hump vector.

Parameters:
number angle
The angle to rotate in radians.
numbers cx, cy
The rotation center.
vector center
The rotation center.
Returns:
Nothing
Example:
p1:rotate(math.pi/2) -- rotate p1 by 90° around it's center
p2:rotate(math.pi/4, 100,100) -- rotate p2 by 45° around the point 100,100
function Polygon:triangulate()^ top

Split the polygon into triangles.

Parameters:
None
Returns:
Array of Polygon triangles
Triangles that the polygon is composed of.
Example:
triangles = poly:triangulate()
for i,triangle in ipairs(triangles) do
    triangles.move(math.random(5,10), math.random(5,10))
end	
function Polygon:mergedWith(other)^ top

Create a merged polygon of two polygons if, and only if the two polygons share one edge. If the polygons share more than one edge, the result may be erroneous.

This function does not change either polygon, but rather create a new one.

Parameters:
Polygon other
The polygon to merge with.
Returns:
Polygon merged
The merged polygon, or nil if the two polygons don't share an edge.
Example:
merged = p1:mergedWith(p2)
function Polygon:splitConvex()^ top

Split the polygon into convex sub polygons.

Parameters:
None
Returns:
Array of Polygon convex_polygons
Convex polygons that form the original polygon.
Example:
convex = concave_polygon:splitConvex()
function love.draw()
    for i,poly in ipairs(convex) do
        love.graphics.polygon('fill', poly:unpack())
    end
end
hardoncollider.spatialhash^ top

To be documented later.