mirror of
https://github.com/vrld/HC.git
synced 2024-11-18 12:54:23 +00:00
Add point location and broad phase
This commit is contained in:
parent
4554742579
commit
56737e7aa0
32
polygon.lua
32
polygon.lua
@ -144,6 +144,12 @@ Polygon = Class{name = "Polygon", function(self, ...)
|
||||
self.centroid.y = self.centroid.y + (p.y+q.y) * det
|
||||
end
|
||||
self.centroid = self.centroid / (6 * self.area)
|
||||
|
||||
-- get outcircle
|
||||
self._radius = 0
|
||||
for i = 1,#vertices do
|
||||
self._radius = math.max(vertices[i]:dist(self.centroid), self._radius)
|
||||
end
|
||||
end}
|
||||
|
||||
-- return vertices as x1,y1,x2,y2, ..., xn,yn
|
||||
@ -321,6 +327,32 @@ function Polygon:splitConvex()
|
||||
return convex
|
||||
end
|
||||
|
||||
function Polygon:contains(x,y)
|
||||
-- test if an edge cuts the ray
|
||||
local function cut_ray(p,q)
|
||||
return ((p.y > y and q.y < y) or (p.y < y and q.y > y)) -- possible cut
|
||||
and (x - p.x < (y - p.y) * (q.x - p.x) / (q.y - p.y)) -- x < cut.x
|
||||
end
|
||||
|
||||
-- test if the ray crosses boundary from interior to exterior.
|
||||
-- this is needed due to edge cases, when the ray passes through
|
||||
-- polygon corners
|
||||
local function cross_boundary(p,q)
|
||||
return (p.y == y and p.x > x and q.y < y)
|
||||
or (q.y == y and q.x > x and p.y < y)
|
||||
end
|
||||
|
||||
local v = self.vertices
|
||||
local in_polygon = false
|
||||
for i = 1, #v - 1 do
|
||||
if cut_ray(v[i], v[i+1]) or cross_boundary(v[i], v[i+1]) then
|
||||
in_polygon = not in_polygon
|
||||
end
|
||||
end
|
||||
return in_polygon
|
||||
end
|
||||
|
||||
|
||||
-- module() as shortcut to module.Polygon()
|
||||
do
|
||||
local m = getmetatable(_M)
|
||||
|
257
shapes.lua
257
shapes.lua
@ -54,8 +54,14 @@ local function SAT(shape_one, axes_one, shape_two, axes_two)
|
||||
return collide, sep
|
||||
end
|
||||
|
||||
---------------
|
||||
-- Base class
|
||||
local function outcircles_intersect(shape_one, shape_two)
|
||||
local x1,y1,r1 = shape_one:outcircle()
|
||||
local x2,y2,r2 = shape_two:outcircle()
|
||||
return (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) <= (r1+r2)*(r1+r2)
|
||||
end
|
||||
|
||||
--
|
||||
-- base class
|
||||
--
|
||||
local Shape = Class{name = 'Shape', function(self, t)
|
||||
self._type = t
|
||||
@ -72,8 +78,8 @@ Shape.POLYGON = setmetatable({}, {__tostring = function() return 'POLYGON' end
|
||||
Shape.COMPOUND = setmetatable({}, {__tostring = function() return 'COMPOUND' end})
|
||||
Shape.CIRCLE = setmetatable({}, {__tostring = function() return 'CIRCLE' end})
|
||||
|
||||
-------------------
|
||||
-- Polygon Shapes
|
||||
--
|
||||
-- class definitions
|
||||
--
|
||||
local ConvexPolygonShape = Class{name = 'ConvexPolygonShape', function(self, polygon)
|
||||
Shape.construct(self, Shape.POLYGON)
|
||||
@ -92,7 +98,6 @@ local ConcavePolygonShape = Class{name = 'ConcavePolygonShape', function(self, p
|
||||
end}
|
||||
ConcavePolygonShape:inherit(Shape)
|
||||
|
||||
-- decides wether to use a convex or concave polygon
|
||||
function PolygonShape(polygon, ...)
|
||||
-- create from coordinates if needed
|
||||
if type(polygon) == "number" then
|
||||
@ -107,6 +112,16 @@ function PolygonShape(polygon, ...)
|
||||
return ConcavePolygonShape(polygon)
|
||||
end
|
||||
|
||||
CircleShape = Class{name = 'CircleShape', function(self, cx,cy, radius)
|
||||
Shape.construct(self, Shape.CIRCLE)
|
||||
self._center = vector(cx,cy)
|
||||
self._radius = radius
|
||||
end}
|
||||
CircleShape:inherit(Shape)
|
||||
|
||||
--
|
||||
-- collision functions
|
||||
--
|
||||
function ConvexPolygonShape:getAxes()
|
||||
local axes = {}
|
||||
local vert = self._polygon.vertices
|
||||
@ -126,36 +141,29 @@ function ConvexPolygonShape:projectOn(axis)
|
||||
return math.min(unpack(projection)), math.max(unpack(projection))
|
||||
end
|
||||
|
||||
function CircleShape:projectOn(axis)
|
||||
-- v:projectOn(a) * a = v * a (see ConvexPolygonShape)
|
||||
-- therefore: (c +- a*r) * a = c*a +- |a|^2 * r
|
||||
local center = self._center * axis
|
||||
local shift = self._radius * axis:len2()
|
||||
return center - shift, center + shift
|
||||
end
|
||||
|
||||
-- collision dispatching:
|
||||
-- let circle shape or compund shape handle the collision
|
||||
function ConvexPolygonShape:collidesWith(other)
|
||||
if other._type ~= Shape.POLYGON then
|
||||
return other:collidesWith(self)
|
||||
end
|
||||
|
||||
-- else: type is POLYGON, use the SAT
|
||||
if not outcircles_intersect(self, other) then return false end
|
||||
return SAT(self, self:getAxes(), other, other:getAxes())
|
||||
end
|
||||
|
||||
function ConvexPolygonShape:draw(mode)
|
||||
local mode = mode or 'line'
|
||||
love.graphics.polygon(mode, self._polygon:unpack())
|
||||
end
|
||||
|
||||
function ConvexPolygonShape:center()
|
||||
return self._polygon.centroid:unpack()
|
||||
end
|
||||
|
||||
function ConvexPolygonShape:move(x,y)
|
||||
-- y not given => x is a vector
|
||||
if y then x = vector(x,y) end
|
||||
self._polygon:move(x)
|
||||
end
|
||||
|
||||
function ConvexPolygonShape:rotate(angle, cx,cy)
|
||||
if cx and cy then cx = vector(cx,cy) end
|
||||
self._polygon:rotate(angle, cx)
|
||||
end
|
||||
|
||||
function ConcavePolygonShape:collidesWith(other)
|
||||
if not outcircles_intersect(self, other) then return false end
|
||||
|
||||
local sep, collide, collisions = vector(0,0), false, 0
|
||||
for _,s in ipairs(self._shapes) do
|
||||
local status, separating_vector = s:collidesWith(other)
|
||||
@ -167,6 +175,126 @@ function ConcavePolygonShape:collidesWith(other)
|
||||
return collide, sep / collisions
|
||||
end
|
||||
|
||||
function CircleShape:collidesWith(other)
|
||||
if other._type == Shape.CIRCLE then
|
||||
local d = self._center:dist(other._center)
|
||||
if d < self._radius + other._radius then
|
||||
return true, d * (self._center - other.center)
|
||||
end
|
||||
return false
|
||||
elseif other._type == Shape.COMPOUND then
|
||||
return other:collidesWith(self)
|
||||
end
|
||||
|
||||
-- else: other._type == POLYGON
|
||||
if not outcircles_intersect(self, other) then return false end
|
||||
-- retrieve closest edge to center
|
||||
local points = other._polygon.vertices
|
||||
local closest, dist = points[1], (self._center - points[1]):len2()
|
||||
for i = 2,#points do
|
||||
local d = (self._center - points[i]):len2()
|
||||
if d < dist then
|
||||
closest, dist = points[i], d
|
||||
end
|
||||
end
|
||||
return SAT(self, {(closest-self._center):normalize_inplace()}, other, other:getAxes())
|
||||
end
|
||||
|
||||
--
|
||||
-- point location/ray intersection
|
||||
--
|
||||
function ConvexPolygonShape:contains(x,y)
|
||||
return self._polygon:contains(x,y)
|
||||
end
|
||||
|
||||
function ConcavePolygonShape:contains(x,y)
|
||||
return self._polygon:contains(x,y)
|
||||
end
|
||||
|
||||
function CircleShape:contains(x,y)
|
||||
if y then x = vector(x,y) end
|
||||
return (x - self._center):len2() < self._radius * self._radius
|
||||
end
|
||||
|
||||
--
|
||||
-- auxiliary
|
||||
--
|
||||
function ConvexPolygonShape:center()
|
||||
return self._polygon.centroid:unpack()
|
||||
end
|
||||
|
||||
function ConcavePolygonShape:center()
|
||||
return self._polygon.centroid:unpack()
|
||||
end
|
||||
|
||||
function CircleShape:center()
|
||||
return self._center:unpack()
|
||||
end
|
||||
|
||||
|
||||
function ConvexPolygonShape:outcircle()
|
||||
local cx,cy = self:center()
|
||||
return cx,cy, self._polygon._radius
|
||||
end
|
||||
|
||||
function ConcavePolygonShape:outcircle()
|
||||
local cx,cy = self:center()
|
||||
return cx,cy, self._polygon._radius
|
||||
end
|
||||
|
||||
function CircleShape:outcircle()
|
||||
local cx,cy = self:center()
|
||||
return cx,cy, self._radius
|
||||
end
|
||||
|
||||
|
||||
function ConvexPolygonShape:move(x,y)
|
||||
-- y not given => x is a vector
|
||||
if y then x = vector(x,y) end
|
||||
self._polygon:move(x)
|
||||
end
|
||||
|
||||
function ConcavePolygonShape:move(x,y)
|
||||
-- y not give => x is a vector
|
||||
if y then x = vector(x,y) end
|
||||
self._polygon:move(x)
|
||||
for _,p in ipairs(self._shapes) do
|
||||
p:move(x)
|
||||
end
|
||||
end
|
||||
|
||||
function CircleShape:move(x,y)
|
||||
-- y not given => x is a vector
|
||||
if y then x = vector(x,y) end
|
||||
self._center = self._center + x
|
||||
end
|
||||
|
||||
|
||||
function ConcavePolygonShape:rotate(angle,cx,cy)
|
||||
if cx and cy then cx = vector(cx,cy) end
|
||||
self._polygon:rotate(angle,cx)
|
||||
for _,p in ipairs(self._shapes) do
|
||||
p:rotate(angle, cx or self._polygon.centroid)
|
||||
end
|
||||
end
|
||||
|
||||
function ConvexPolygonShape:rotate(angle, cx,cy)
|
||||
if cx and cy then cx = vector(cx,cy) end
|
||||
self._polygon:rotate(angle, cx)
|
||||
end
|
||||
|
||||
function CircleShape:rotate(angle, cx,cy)
|
||||
if not cx then return end
|
||||
if cx and cy then cx = vector(cx,cy) end
|
||||
self._center = (self._center - cx):rotate_inplace(angle) + cx
|
||||
end
|
||||
|
||||
|
||||
function ConvexPolygonShape:draw(mode)
|
||||
local mode = mode or 'line'
|
||||
love.graphics.polygon(mode, self._polygon:unpack())
|
||||
end
|
||||
|
||||
function ConcavePolygonShape:draw(mode)
|
||||
local mode = mode or 'line'
|
||||
if mode == 'line' then
|
||||
@ -178,89 +306,10 @@ function ConcavePolygonShape:draw(mode)
|
||||
end
|
||||
end
|
||||
|
||||
function ConcavePolygonShape:center()
|
||||
return self._polygon.centroid:unpack()
|
||||
end
|
||||
|
||||
function ConcavePolygonShape:move(x,y)
|
||||
-- y not give => x is a vector
|
||||
if y then x = vector(x,y) end
|
||||
self._polygon:move(x)
|
||||
for _,p in ipairs(self._shapes) do
|
||||
p:move(x)
|
||||
end
|
||||
end
|
||||
|
||||
function ConcavePolygonShape:rotate(angle,cx,cy)
|
||||
if cx and cy then cx = vector(cx,cy) end
|
||||
self._polygon:rotate(angle,cx)
|
||||
for _,p in ipairs(self._shapes) do
|
||||
p:rotate(angle, cx or self._polygon.centroid)
|
||||
end
|
||||
end
|
||||
|
||||
-------------------
|
||||
-- Perfect circle
|
||||
--
|
||||
CircleShape = Class{name = 'CircleShape', function(self, cx,cy, radius)
|
||||
Shape.construct(self, Shape.CIRCLE)
|
||||
self._center = vector(cx,cy)
|
||||
self._radius = radius
|
||||
end}
|
||||
CircleShape:inherit(Shape)
|
||||
|
||||
function CircleShape:collidesWith(other)
|
||||
if other._type == Shape.CIRCLE then
|
||||
local d = self._center:dist(other._center)
|
||||
if d < self._radius + other._radius then
|
||||
return true, d * (self._center - other.center)
|
||||
end
|
||||
return false
|
||||
elseif other._type == Shape.COMPOUND then
|
||||
return other:collidesWith(self)
|
||||
end
|
||||
-- else: other._type == POLYGON
|
||||
-- retrieve closest edge to center
|
||||
local points = other._polygon.vertices
|
||||
local closest, dist = points[1], (self._center - points[1]):len2()
|
||||
for i = 2,#points do
|
||||
local d = (self._center - points[i]):len2()
|
||||
if d < dist then
|
||||
closest, dist = points[i], d
|
||||
end
|
||||
end
|
||||
return SAT(self, {(closest-self._center):normalize_inplace()}, other, other:getAxes())
|
||||
end
|
||||
|
||||
function CircleShape:draw(mode, segments)
|
||||
local segments = segments or math.max(3, math.floor(math.pi * math.log(self._radius)))
|
||||
love.graphics.circle(mode, self._center.x, self._center.y, self._radius, segments)
|
||||
end
|
||||
|
||||
function CircleShape:center()
|
||||
return self._center:unpack()
|
||||
end
|
||||
|
||||
function CircleShape:move(x,y)
|
||||
-- y not given => x is a vector
|
||||
if y then x = vector(x,y) end
|
||||
self._center = self._center + x
|
||||
end
|
||||
|
||||
function CircleShape:rotate(angle, cx,cy)
|
||||
if not cx then return end
|
||||
if cx and cy then cx = vector(cx,cy) end
|
||||
self._center = (self._center - cx):rotate_inplace(angle) + cx
|
||||
end
|
||||
|
||||
function CircleShape:projectOn(axis)
|
||||
-- v:projectOn(a) * a = v * a (see ConvexPolygonShape)
|
||||
-- therefore: (c +- a*r) * a = c*a +- |a|^2 * r
|
||||
local center = self._center * axis
|
||||
local shift = self._radius * axis:len2()
|
||||
return center - shift, center + shift
|
||||
end
|
||||
|
||||
function CircleShape:center()
|
||||
return self._center:unpack()
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user