750 lines
23 KiB
Lua
750 lines
23 KiB
Lua
local ____lualib = require("lualib_bundle")
|
|
local __TS__Class = ____lualib.__TS__Class
|
|
local __TS__New = ____lualib.__TS__New
|
|
local __TS__ArraySplice = ____lualib.__TS__ArraySplice
|
|
local ____exports = {}
|
|
local T_VECTORS, T_ARRAYS
|
|
--- Flattens the specified array of points onto a unit vector axis,
|
|
-- resulting in a one dimensional range of the minimum and
|
|
-- maximum value on that axis.
|
|
--
|
|
-- @param points The points to flatten.
|
|
-- @param normal The unit vector axis to flatten on.
|
|
-- @param result An array. After calling this function,
|
|
-- result[0] will be the minimum value,
|
|
-- result[1] will be the maximum value.
|
|
function ____exports.flattenPointsOn(points, normal, result)
|
|
local min = 2 ^ 1024
|
|
local max = -2 ^ 1024
|
|
local len = #points
|
|
do
|
|
local i = 0
|
|
while i < len do
|
|
local dot = points[i + 1]:dot(normal)
|
|
if dot < min then
|
|
min = dot
|
|
end
|
|
if dot > max then
|
|
max = dot
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
result[1] = min
|
|
result[2] = max
|
|
end
|
|
--- Check whether two convex polygons are separated by the specified
|
|
-- axis (must be a unit vector).
|
|
--
|
|
-- @param aPos The position of the first polygon.
|
|
-- @param bPos The position of the second polygon.
|
|
-- @param aPoints The points in the first polygon.
|
|
-- @param bPoints The points in the second polygon.
|
|
-- @param axis The axis (unit sized) to test against. The points of both polygons
|
|
-- will be projected onto this axis.
|
|
-- @param response A Response object (optional) which will be populated
|
|
-- if the axis is not a separating axis.
|
|
-- @returns true if it is a separating axis, false otherwise. If false,
|
|
-- and a response is passed in, information about how much overlap and
|
|
-- the direction of the overlap will be populated.
|
|
function ____exports.isSeparatingAxis(aPos, bPos, aPoints, bPoints, axis, response)
|
|
local rangeA = table.remove(T_ARRAYS)
|
|
local rangeB = table.remove(T_ARRAYS)
|
|
local offsetV = table.remove(T_VECTORS):copy(bPos):sub(aPos)
|
|
local projectedOffset = offsetV:dot(axis)
|
|
____exports.flattenPointsOn(aPoints, axis, rangeA)
|
|
____exports.flattenPointsOn(bPoints, axis, rangeB)
|
|
rangeB[1] = rangeB[1] + projectedOffset
|
|
rangeB[2] = rangeB[2] + projectedOffset
|
|
if rangeA[1] > rangeB[2] or rangeB[1] > rangeA[2] then
|
|
T_VECTORS[#T_VECTORS + 1] = offsetV
|
|
T_ARRAYS[#T_ARRAYS + 1] = rangeA
|
|
T_ARRAYS[#T_ARRAYS + 1] = rangeB
|
|
return true
|
|
end
|
|
if response then
|
|
local overlap = 0
|
|
if rangeA[1] < rangeB[1] then
|
|
response.aInB = false
|
|
if rangeA[2] < rangeB[2] then
|
|
overlap = rangeA[2] - rangeB[1]
|
|
response.bInA = false
|
|
else
|
|
local option1 = rangeA[2] - rangeB[1]
|
|
local option2 = rangeB[2] - rangeA[1]
|
|
overlap = option1 < option2 and option1 or -option2
|
|
end
|
|
else
|
|
response.bInA = false
|
|
if rangeA[2] > rangeB[2] then
|
|
overlap = rangeA[1] - rangeB[2]
|
|
response.aInB = false
|
|
else
|
|
local option1 = rangeA[2] - rangeB[1]
|
|
local option2 = rangeB[2] - rangeA[1]
|
|
overlap = option1 < option2 and option1 or -option2
|
|
end
|
|
end
|
|
local absOverlap = math.abs(overlap)
|
|
if absOverlap < response.overlap then
|
|
response.overlap = absOverlap
|
|
response.overlapN:copy(axis)
|
|
if overlap < 0 then
|
|
response.overlapN:reverse()
|
|
end
|
|
end
|
|
end
|
|
T_VECTORS[#T_VECTORS + 1] = offsetV
|
|
T_ARRAYS[#T_ARRAYS + 1] = rangeA
|
|
T_ARRAYS[#T_ARRAYS + 1] = rangeB
|
|
return false
|
|
end
|
|
--- Checks whether polygons collide.
|
|
--
|
|
-- @param a The first polygon.
|
|
-- @param b The second polygon.
|
|
-- @param response Response object (optional) that will be populated if they interset.
|
|
-- @returns true if they intersect, false if they don't.
|
|
function ____exports.testPolygonPolygon(a, b, response)
|
|
local aPoints = a.calcPoints
|
|
local aLen = #aPoints
|
|
local bPoints = b.calcPoints
|
|
local bLen = #bPoints
|
|
local i
|
|
do
|
|
i = 0
|
|
while i < aLen do
|
|
if ____exports.isSeparatingAxis(
|
|
a.pos,
|
|
b.pos,
|
|
aPoints,
|
|
bPoints,
|
|
a.normals[i + 1],
|
|
response
|
|
) then
|
|
return false
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
do
|
|
i = 0
|
|
while i < bLen do
|
|
if ____exports.isSeparatingAxis(
|
|
a.pos,
|
|
b.pos,
|
|
aPoints,
|
|
bPoints,
|
|
b.normals[i + 1],
|
|
response
|
|
) then
|
|
return false
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
if response then
|
|
response.a = a
|
|
response.b = b
|
|
response.overlapV:copy(response.overlapN):scale(response.overlap)
|
|
end
|
|
return true
|
|
end
|
|
--- Represents a vector in two dimensions with `x` and `y` properties.
|
|
____exports.Vector = __TS__Class()
|
|
local Vector = ____exports.Vector
|
|
Vector.name = "Vector"
|
|
function Vector.prototype.____constructor(self, x, y)
|
|
if x == nil then
|
|
x = 0
|
|
end
|
|
if y == nil then
|
|
y = 0
|
|
end
|
|
self.x = x
|
|
self.y = y
|
|
end
|
|
function Vector.prototype.copy(self, other)
|
|
self.x = other.x
|
|
self.y = other.y
|
|
return self
|
|
end
|
|
function Vector.prototype.clone(self)
|
|
return __TS__New(____exports.Vector, self.x, self.y)
|
|
end
|
|
function Vector.prototype.perp(self)
|
|
local x = self.x
|
|
self.x = self.y
|
|
self.y = -x
|
|
return self
|
|
end
|
|
function Vector.prototype.rotate(self, radians)
|
|
local x = self.x
|
|
local y = self.y
|
|
self.x = x * math.cos(radians) - y * math.sin(radians)
|
|
self.y = x * math.sin(radians) + y * math.cos(radians)
|
|
return self
|
|
end
|
|
function Vector.prototype.reverse(self)
|
|
self.x = -self.x
|
|
self.y = -self.y
|
|
return self
|
|
end
|
|
function Vector.prototype.normalize(self)
|
|
local d = self:len()
|
|
if d > 0 then
|
|
self.x = self.x / d
|
|
self.y = self.y / d
|
|
end
|
|
return self
|
|
end
|
|
function Vector.prototype.add(self, other)
|
|
self.x = self.x + other.x
|
|
self.y = self.y + other.y
|
|
return self
|
|
end
|
|
function Vector.prototype.sub(self, other)
|
|
self.x = self.x - other.x
|
|
self.y = self.y - other.y
|
|
return self
|
|
end
|
|
function Vector.prototype.scale(self, x, y)
|
|
if y == nil then
|
|
y = x
|
|
end
|
|
self.x = self.x * x
|
|
self.y = self.y * y
|
|
return self
|
|
end
|
|
function Vector.prototype.project(self, other)
|
|
local amt = self:dot(other) / other:len2()
|
|
self.x = amt * other.x
|
|
self.y = amt * other.y
|
|
return self
|
|
end
|
|
function Vector.prototype.projectN(self, other)
|
|
local amt = self:dot(other)
|
|
self.x = amt * other.x
|
|
self.y = amt * other.y
|
|
return self
|
|
end
|
|
function Vector.prototype.reflect(self, axis)
|
|
local x = self.x
|
|
local y = self.y
|
|
self:project(axis):scale(2)
|
|
self.x = self.x - x
|
|
self.y = self.y - y
|
|
return self
|
|
end
|
|
function Vector.prototype.reflectN(self, axis)
|
|
local x = self.x
|
|
local y = self.y
|
|
self:projectN(axis):scale(2)
|
|
self.x = self.x - x
|
|
self.y = self.y - y
|
|
return self
|
|
end
|
|
function Vector.prototype.dot(self, other)
|
|
return self.x * other.x + self.y * other.y
|
|
end
|
|
function Vector.prototype.len2(self)
|
|
return self:dot(self)
|
|
end
|
|
function Vector.prototype.len(self)
|
|
return math.sqrt(self:len2())
|
|
end
|
|
--- Represents an axis-aligned box, with a width and height.
|
|
____exports.Box = __TS__Class()
|
|
local Box = ____exports.Box
|
|
Box.name = "Box"
|
|
function Box.prototype.____constructor(self, pos, w, h)
|
|
if pos == nil then
|
|
pos = __TS__New(____exports.Vector)
|
|
end
|
|
if w == nil then
|
|
w = 0
|
|
end
|
|
if h == nil then
|
|
h = 0
|
|
end
|
|
self.pos = pos
|
|
self.w = w
|
|
self.h = h
|
|
end
|
|
function Box.prototype.toPolygon(self)
|
|
local pos = self.pos
|
|
local w = self.w
|
|
local h = self.h
|
|
return __TS__New(
|
|
____exports.Polygon,
|
|
__TS__New(____exports.Vector, pos.x, pos.y),
|
|
{
|
|
__TS__New(____exports.Vector, 0, 0),
|
|
__TS__New(____exports.Vector, w, 0),
|
|
__TS__New(____exports.Vector, w, h),
|
|
__TS__New(____exports.Vector, 0, h)
|
|
}
|
|
)
|
|
end
|
|
--- Represents a circle with a position and a radius.
|
|
____exports.Circle = __TS__Class()
|
|
local Circle = ____exports.Circle
|
|
Circle.name = "Circle"
|
|
function Circle.prototype.____constructor(self, pos, r)
|
|
if pos == nil then
|
|
pos = __TS__New(____exports.Vector)
|
|
end
|
|
if r == nil then
|
|
r = 0
|
|
end
|
|
self.pos = pos
|
|
self.r = r
|
|
self.offset = __TS__New(____exports.Vector)
|
|
end
|
|
function Circle.prototype.getAABBAsBox(self)
|
|
local r = self.r
|
|
local corner = self.pos:clone():add(self.offset):sub(__TS__New(____exports.Vector, r, r))
|
|
return __TS__New(____exports.Box, corner, r * 2, r * 2)
|
|
end
|
|
function Circle.prototype.getAABB(self)
|
|
return self:getAABBAsBox():toPolygon()
|
|
end
|
|
function Circle.prototype.setOffset(self, offset)
|
|
self.offset = offset
|
|
return self
|
|
end
|
|
--- Represents a *convex* polygon with any number of points (specified in counter-clockwise order)
|
|
-- Note: Do _not_ manually change the `points`, `angle`, or `offset` properties. Use the
|
|
-- provided setters. Otherwise the calculated properties will not be updated correctly.
|
|
-- `pos` can be changed directly.
|
|
____exports.Polygon = __TS__Class()
|
|
local Polygon = ____exports.Polygon
|
|
Polygon.name = "Polygon"
|
|
function Polygon.prototype.____constructor(self, pos, points)
|
|
if pos == nil then
|
|
pos = __TS__New(____exports.Vector)
|
|
end
|
|
if points == nil then
|
|
points = __TS__New(Array)
|
|
end
|
|
self.pos = pos
|
|
self.points = {}
|
|
self.angle = 0
|
|
self.offset = __TS__New(____exports.Vector)
|
|
self.calcPoints = {}
|
|
self.edges = {}
|
|
self.normals = {}
|
|
self:setPoints(points)
|
|
end
|
|
function Polygon.prototype.setPoints(self, points)
|
|
local lengthChanged = not self.points or #self.points ~= #points
|
|
if lengthChanged then
|
|
local i
|
|
local ____temp_0 = {}
|
|
self.calcPoints = ____temp_0
|
|
local calcPoints = ____temp_0
|
|
local ____temp_1 = {}
|
|
self.edges = ____temp_1
|
|
local edges = ____temp_1
|
|
local ____temp_2 = {}
|
|
self.normals = ____temp_2
|
|
local normals = ____temp_2
|
|
do
|
|
i = 0
|
|
while i < #points do
|
|
do
|
|
local p1 = points[i + 1]
|
|
local p2 = i < #points - 1 and points[i + 1 + 1] or points[1]
|
|
if p1 ~= p2 and p1.x == p2.x and p1.y == p2.y then
|
|
__TS__ArraySplice(points, i, 1)
|
|
i = i - 1
|
|
goto __continue29
|
|
end
|
|
calcPoints[#calcPoints + 1] = __TS__New(____exports.Vector)
|
|
edges[#edges + 1] = __TS__New(____exports.Vector)
|
|
normals[#normals + 1] = __TS__New(____exports.Vector)
|
|
end
|
|
::__continue29::
|
|
i = i + 1
|
|
end
|
|
end
|
|
end
|
|
self.points = points
|
|
self:_recalc()
|
|
return self
|
|
end
|
|
function Polygon.prototype.setAngle(self, radians)
|
|
self.angle = radians
|
|
self:_recalc()
|
|
return self
|
|
end
|
|
function Polygon.prototype.setOffset(self, offset)
|
|
self.offset = offset
|
|
self:_recalc()
|
|
return self
|
|
end
|
|
function Polygon.prototype.rotate(self, radians)
|
|
local points = self.points
|
|
local len = #points
|
|
do
|
|
local i = 0
|
|
while i < len do
|
|
points[i + 1]:rotate(radians)
|
|
i = i + 1
|
|
end
|
|
end
|
|
self:_recalc()
|
|
return self
|
|
end
|
|
function Polygon.prototype.translate(self, x, y)
|
|
local points = self.points
|
|
local len = #points
|
|
do
|
|
local i = 0
|
|
while i < len do
|
|
local ____points_index_3, ____x_4 = points[i + 1], "x"
|
|
____points_index_3[____x_4] = ____points_index_3[____x_4] + x
|
|
local ____points_index_5, ____y_6 = points[i + 1], "y"
|
|
____points_index_5[____y_6] = ____points_index_5[____y_6] + y
|
|
i = i + 1
|
|
end
|
|
end
|
|
self:_recalc()
|
|
return self
|
|
end
|
|
function Polygon.prototype._recalc(self)
|
|
local calcPoints = self.calcPoints
|
|
local edges = self.edges
|
|
local normals = self.normals
|
|
local points = self.points
|
|
local offset = self.offset
|
|
local angle = self.angle
|
|
local len = #points
|
|
local i
|
|
do
|
|
i = 0
|
|
while i < len do
|
|
local calcPoint = calcPoints[i + 1]:copy(points[i + 1])
|
|
calcPoint.x = calcPoint.x + offset.x
|
|
calcPoint.y = calcPoint.y + offset.y
|
|
if angle ~= 0 then
|
|
calcPoint:rotate(angle)
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
do
|
|
i = 0
|
|
while i < len do
|
|
local p1 = calcPoints[i + 1]
|
|
local p2 = i < len - 1 and calcPoints[i + 1 + 1] or calcPoints[1]
|
|
local e = edges[i + 1]:copy(p2):sub(p1)
|
|
normals[i + 1]:copy(e):perp():normalize()
|
|
i = i + 1
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
function Polygon.prototype.getAABBAsBox(self)
|
|
local points = self.calcPoints
|
|
local len = #points
|
|
local xMin = points[1].x
|
|
local yMin = points[1].y
|
|
local xMax = points[1].x
|
|
local yMax = points[1].y
|
|
do
|
|
local i = 1
|
|
while i < len do
|
|
local point = points[i + 1]
|
|
if point.x < xMin then
|
|
xMin = point.x
|
|
elseif point.x > xMax then
|
|
xMax = point.x
|
|
end
|
|
if point.y < yMin then
|
|
yMin = point.y
|
|
elseif point.y > yMax then
|
|
yMax = point.y
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
return __TS__New(
|
|
____exports.Box,
|
|
self.pos:clone():add(__TS__New(____exports.Vector, xMin, yMin)),
|
|
xMax - xMin,
|
|
yMax - yMin
|
|
)
|
|
end
|
|
function Polygon.prototype.getAABB(self)
|
|
return self:getAABBAsBox():toPolygon()
|
|
end
|
|
function Polygon.prototype.getCentroid(self)
|
|
local points = self.calcPoints
|
|
local len = #points
|
|
local cx = 0
|
|
local cy = 0
|
|
local ar = 0
|
|
do
|
|
local i = 0
|
|
while i < len do
|
|
local p1 = points[i + 1]
|
|
local p2 = i == len - 1 and points[1] or points[i + 1 + 1]
|
|
local a = p1.x * p2.y - p2.x * p1.y
|
|
cx = cx + (p1.x + p2.x) * a
|
|
cy = cy + (p1.y + p2.y) * a
|
|
ar = ar + a
|
|
i = i + 1
|
|
end
|
|
end
|
|
ar = ar * 3
|
|
cx = cx / ar
|
|
cy = cy / ar
|
|
return __TS__New(____exports.Vector, cx, cy)
|
|
end
|
|
--- An object representing the result of an intersection. Contains:
|
|
-- - The two objects participating in the intersection
|
|
-- - The vector representing the minimum change necessary to extract the first object
|
|
-- from the second one (as well as a unit vector in that direction and the magnitude
|
|
-- of the overlap)
|
|
-- - Whether the first object is entirely inside the second, and vice versa.
|
|
____exports.Response = __TS__Class()
|
|
local Response = ____exports.Response
|
|
Response.name = "Response"
|
|
function Response.prototype.____constructor(self)
|
|
self.overlapN = __TS__New(____exports.Vector)
|
|
self.overlapV = __TS__New(____exports.Vector)
|
|
self.overlap = 2 ^ 1024
|
|
self.aInB = true
|
|
self.bInA = true
|
|
self:clear()
|
|
end
|
|
function Response.prototype.clear(self)
|
|
self.aInB = true
|
|
self.bInA = true
|
|
self.overlap = 2 ^ 1024
|
|
return self
|
|
end
|
|
T_VECTORS = {}
|
|
do
|
|
local i = 0
|
|
while i < 10 do
|
|
T_VECTORS[#T_VECTORS + 1] = __TS__New(____exports.Vector)
|
|
i = i + 1
|
|
end
|
|
end
|
|
T_ARRAYS = {}
|
|
do
|
|
local i = 0
|
|
while i < 5 do
|
|
T_ARRAYS[#T_ARRAYS + 1] = {}
|
|
i = i + 1
|
|
end
|
|
end
|
|
--- Temporary response used for polygon hit detection.
|
|
local T_RESPONSE = __TS__New(____exports.Response)
|
|
--- Tiny "point" polygon used for polygon hit detection.
|
|
local TEST_POINT = __TS__New(
|
|
____exports.Box,
|
|
__TS__New(____exports.Vector),
|
|
0.000001,
|
|
0.000001
|
|
):toPolygon()
|
|
--- Calculates which Voronoi region a point is on a line segment.
|
|
-- It is assumed that both the line and the point are relative to `(0,0)`
|
|
--
|
|
-- | (0) |
|
|
-- (-1) [S]--------------[E] (1)
|
|
-- | (0) |
|
|
--
|
|
-- @param line The line segment.
|
|
-- @param point The point.
|
|
-- @returns LEFT_VORONOI_REGION (-1) if it is the left region,
|
|
-- MIDDLE_VORONOI_REGION (0) if it is the middle region,
|
|
-- RIGHT_VORONOI_REGION (1) if it is the right region.
|
|
function ____exports.voronoiRegion(line, point)
|
|
local len2 = line:len2()
|
|
local dp = point:dot(line)
|
|
if dp < 0 then
|
|
return -1
|
|
elseif dp > len2 then
|
|
return 1
|
|
else
|
|
return 0
|
|
end
|
|
end
|
|
--- Check if a point is inside a circle.
|
|
--
|
|
-- @param p The point to test.
|
|
-- @param c The circle to test.
|
|
-- @returns true if the point is inside the circle, false if it is not.
|
|
function ____exports.pointInCircle(p, c)
|
|
local differenceV = table.remove(T_VECTORS):copy(p):sub(c.pos):sub(c.offset)
|
|
local radiusSq = c.r * c.r
|
|
local distanceSq = differenceV:len2()
|
|
T_VECTORS[#T_VECTORS + 1] = differenceV
|
|
return distanceSq <= radiusSq
|
|
end
|
|
--- Check if a point is inside a convex polygon.
|
|
--
|
|
-- @param p The point to test.
|
|
-- @param poly The polygon to test.
|
|
-- @returns true if the point is inside the polygon, false if it is not.
|
|
function ____exports.pointInPolygon(p, poly)
|
|
TEST_POINT.pos:copy(p)
|
|
T_RESPONSE:clear()
|
|
local result = ____exports.testPolygonPolygon(TEST_POINT, poly, T_RESPONSE)
|
|
if result then
|
|
result = T_RESPONSE.aInB
|
|
end
|
|
return result
|
|
end
|
|
--- Check if two circles collide.
|
|
--
|
|
-- @param a The first circle.
|
|
-- @param b The second circle.
|
|
-- @param response Response object (optional) that will be populated if the circles intersect.
|
|
-- @returns true if the circles intersect, false if they don't.
|
|
function ____exports.testCircleCircle(a, b, response)
|
|
local differenceV = table.remove(T_VECTORS):copy(b.pos):add(b.offset):sub(a.pos):sub(a.offset)
|
|
local totalRadius = a.r + b.r
|
|
local totalRadiusSq = totalRadius * totalRadius
|
|
local distanceSq = differenceV:len2()
|
|
if distanceSq > totalRadiusSq then
|
|
T_VECTORS[#T_VECTORS + 1] = differenceV
|
|
return false
|
|
end
|
|
if response then
|
|
local dist = math.sqrt(distanceSq)
|
|
response.a = a
|
|
response.b = b
|
|
response.overlap = totalRadius - dist
|
|
response.overlapN:copy(differenceV:normalize())
|
|
response.overlapV:copy(differenceV):scale(response.overlap)
|
|
response.aInB = a.r <= b.r and dist <= b.r - a.r
|
|
response.bInA = b.r <= a.r and dist <= a.r - b.r
|
|
end
|
|
T_VECTORS[#T_VECTORS + 1] = differenceV
|
|
return true
|
|
end
|
|
--- Check if a polygon and a circle collide.
|
|
--
|
|
-- @param polygon The polygon.
|
|
-- @param circle The circle.
|
|
-- @param response Response object (optional) that will be populated if they interset.
|
|
-- @returns true if they intersect, false if they don't.
|
|
function ____exports.testPolygonCircle(polygon, circle, response)
|
|
local circlePos = table.remove(T_VECTORS):copy(circle.pos):add(circle.offset):sub(polygon.pos)
|
|
local radius = circle.r
|
|
local radius2 = radius * radius
|
|
local points = polygon.calcPoints
|
|
local len = #points
|
|
local edge = table.remove(T_VECTORS)
|
|
local point = table.remove(T_VECTORS)
|
|
do
|
|
local i = 0
|
|
while i < len do
|
|
local next = i == len - 1 and 0 or i + 1
|
|
local prev = i == 0 and len - 1 or i - 1
|
|
local overlap = 0
|
|
local overlapN = nil
|
|
edge:copy(polygon.edges[i + 1])
|
|
point:copy(circlePos):sub(points[i + 1])
|
|
if response and point:len2() > radius2 then
|
|
response.aInB = false
|
|
end
|
|
local region = ____exports.voronoiRegion(edge, point)
|
|
if region == -1 then
|
|
edge:copy(polygon.edges[prev + 1])
|
|
local point2 = table.remove(T_VECTORS):copy(circlePos):sub(points[prev + 1])
|
|
region = ____exports.voronoiRegion(edge, point2)
|
|
if region == 1 then
|
|
local dist = point:len()
|
|
if dist > radius then
|
|
T_VECTORS[#T_VECTORS + 1] = circlePos
|
|
T_VECTORS[#T_VECTORS + 1] = edge
|
|
T_VECTORS[#T_VECTORS + 1] = point
|
|
T_VECTORS[#T_VECTORS + 1] = point2
|
|
return false
|
|
elseif response then
|
|
response.bInA = false
|
|
overlapN = point:normalize()
|
|
overlap = radius - dist
|
|
end
|
|
end
|
|
T_VECTORS[#T_VECTORS + 1] = point2
|
|
elseif region == 1 then
|
|
edge:copy(polygon.edges[next + 1])
|
|
point:copy(circlePos):sub(points[next + 1])
|
|
region = ____exports.voronoiRegion(edge, point)
|
|
if region == -1 then
|
|
local dist = point:len()
|
|
if dist > radius then
|
|
T_VECTORS[#T_VECTORS + 1] = circlePos
|
|
T_VECTORS[#T_VECTORS + 1] = edge
|
|
T_VECTORS[#T_VECTORS + 1] = point
|
|
return false
|
|
elseif response then
|
|
response.bInA = false
|
|
overlapN = point:normalize()
|
|
overlap = radius - dist
|
|
end
|
|
end
|
|
else
|
|
local normal = edge:perp():normalize()
|
|
local dist = point:dot(normal)
|
|
local distAbs = math.abs(dist)
|
|
if dist > 0 and distAbs > radius then
|
|
T_VECTORS[#T_VECTORS + 1] = circlePos
|
|
T_VECTORS[#T_VECTORS + 1] = normal
|
|
T_VECTORS[#T_VECTORS + 1] = point
|
|
return false
|
|
elseif response then
|
|
overlapN = normal
|
|
overlap = radius - dist
|
|
if dist >= 0 or overlap < 2 * radius then
|
|
response.bInA = false
|
|
end
|
|
end
|
|
end
|
|
if overlapN and response and math.abs(overlap) < math.abs(response.overlap) then
|
|
response.overlap = overlap
|
|
response.overlapN:copy(overlapN)
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
if response then
|
|
response.a = polygon
|
|
response.b = circle
|
|
response.overlapV:copy(response.overlapN):scale(response.overlap)
|
|
end
|
|
T_VECTORS[#T_VECTORS + 1] = circlePos
|
|
T_VECTORS[#T_VECTORS + 1] = edge
|
|
T_VECTORS[#T_VECTORS + 1] = point
|
|
return true
|
|
end
|
|
--- Check if a circle and a polygon collide.
|
|
-- **NOTE:** This is slightly less efficient than polygonCircle as it just
|
|
-- runs polygonCircle and reverses everything at the end.
|
|
--
|
|
-- @param circle The circle.
|
|
-- @param polygon The polygon.
|
|
-- @param response Response object (optional) that will be populated if they interset.
|
|
-- @returns true if they intersect, false if they don't.
|
|
function ____exports.testCirclePolygon(circle, polygon, response)
|
|
local result = ____exports.testPolygonCircle(polygon, circle, response)
|
|
if result and response then
|
|
local a = response.a
|
|
local aInB = response.aInB
|
|
response.overlapN:reverse()
|
|
response.overlapV:reverse()
|
|
response.a = response.b
|
|
response.b = a
|
|
response.aInB = response.bInA
|
|
response.bInA = aInB
|
|
end
|
|
return result
|
|
end
|
|
return ____exports
|