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
|
self.centroid.y = self.centroid.y + (p.y+q.y) * det
|
||||||
end
|
end
|
||||||
self.centroid = self.centroid / (6 * self.area)
|
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}
|
end}
|
||||||
|
|
||||||
-- return vertices as x1,y1,x2,y2, ..., xn,yn
|
-- return vertices as x1,y1,x2,y2, ..., xn,yn
|
||||||
@ -321,6 +327,32 @@ function Polygon:splitConvex()
|
|||||||
return convex
|
return convex
|
||||||
end
|
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()
|
-- module() as shortcut to module.Polygon()
|
||||||
do
|
do
|
||||||
local m = getmetatable(_M)
|
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
|
return collide, sep
|
||||||
end
|
end
|
||||||
|
|
||||||
---------------
|
local function outcircles_intersect(shape_one, shape_two)
|
||||||
-- Base class
|
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)
|
local Shape = Class{name = 'Shape', function(self, t)
|
||||||
self._type = 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.COMPOUND = setmetatable({}, {__tostring = function() return 'COMPOUND' end})
|
||||||
Shape.CIRCLE = setmetatable({}, {__tostring = function() return 'CIRCLE' end})
|
Shape.CIRCLE = setmetatable({}, {__tostring = function() return 'CIRCLE' end})
|
||||||
|
|
||||||
-------------------
|
--
|
||||||
-- Polygon Shapes
|
-- class definitions
|
||||||
--
|
--
|
||||||
local ConvexPolygonShape = Class{name = 'ConvexPolygonShape', function(self, polygon)
|
local ConvexPolygonShape = Class{name = 'ConvexPolygonShape', function(self, polygon)
|
||||||
Shape.construct(self, Shape.POLYGON)
|
Shape.construct(self, Shape.POLYGON)
|
||||||
@ -92,7 +98,6 @@ local ConcavePolygonShape = Class{name = 'ConcavePolygonShape', function(self, p
|
|||||||
end}
|
end}
|
||||||
ConcavePolygonShape:inherit(Shape)
|
ConcavePolygonShape:inherit(Shape)
|
||||||
|
|
||||||
-- decides wether to use a convex or concave polygon
|
|
||||||
function PolygonShape(polygon, ...)
|
function PolygonShape(polygon, ...)
|
||||||
-- create from coordinates if needed
|
-- create from coordinates if needed
|
||||||
if type(polygon) == "number" then
|
if type(polygon) == "number" then
|
||||||
@ -107,6 +112,16 @@ function PolygonShape(polygon, ...)
|
|||||||
return ConcavePolygonShape(polygon)
|
return ConcavePolygonShape(polygon)
|
||||||
end
|
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()
|
function ConvexPolygonShape:getAxes()
|
||||||
local axes = {}
|
local axes = {}
|
||||||
local vert = self._polygon.vertices
|
local vert = self._polygon.vertices
|
||||||
@ -126,36 +141,29 @@ function ConvexPolygonShape:projectOn(axis)
|
|||||||
return math.min(unpack(projection)), math.max(unpack(projection))
|
return math.min(unpack(projection)), math.max(unpack(projection))
|
||||||
end
|
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)
|
function ConvexPolygonShape:collidesWith(other)
|
||||||
if other._type ~= Shape.POLYGON then
|
if other._type ~= Shape.POLYGON then
|
||||||
return other:collidesWith(self)
|
return other:collidesWith(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- else: type is POLYGON, use the SAT
|
-- 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())
|
return SAT(self, self:getAxes(), other, other:getAxes())
|
||||||
end
|
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)
|
function ConcavePolygonShape:collidesWith(other)
|
||||||
|
if not outcircles_intersect(self, other) then return false end
|
||||||
|
|
||||||
local sep, collide, collisions = vector(0,0), false, 0
|
local sep, collide, collisions = vector(0,0), false, 0
|
||||||
for _,s in ipairs(self._shapes) do
|
for _,s in ipairs(self._shapes) do
|
||||||
local status, separating_vector = s:collidesWith(other)
|
local status, separating_vector = s:collidesWith(other)
|
||||||
@ -167,6 +175,126 @@ function ConcavePolygonShape:collidesWith(other)
|
|||||||
return collide, sep / collisions
|
return collide, sep / collisions
|
||||||
end
|
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)
|
function ConcavePolygonShape:draw(mode)
|
||||||
local mode = mode or 'line'
|
local mode = mode or 'line'
|
||||||
if mode == 'line' then
|
if mode == 'line' then
|
||||||
@ -178,89 +306,10 @@ function ConcavePolygonShape:draw(mode)
|
|||||||
end
|
end
|
||||||
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)
|
function CircleShape:draw(mode, segments)
|
||||||
local segments = segments or math.max(3, math.floor(math.pi * math.log(self._radius)))
|
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)
|
love.graphics.circle(mode, self._center.x, self._center.y, self._radius, segments)
|
||||||
end
|
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