Re: Push rectangle out of line
Posted: Tue Feb 28, 2023 5:25 pm
Ok. What i'd try is, in that code, replacing the polygon points with those of a rectangle, like this:
Code: Select all
local rectangleX = 200
local rectangleY = 200
local width = 100
-- X, Y pairs for the 4 clockwise points of a square.
local polygonPoints = {
rectangleX, rectangleY,
rectangleX+width, rectangleY,
rectangleX+width, rectangleY+width,
rectangleX, rectangleY+width,
}
Code: Select all
-- push-rectangle.lua
-- cc0, darkfrei 2023
local pr = {}
function pr.newPolygon (polygon)
if #polygon % 2 ~= 0 then
error('Polygon coordinates are missing a value')
end
local minX, minY = polygon[1], polygon[2]
local maxX, maxY = polygon[1], polygon[2]
for i = 3, #polygon-1, 2 do
minX = math.min (minX, polygon[i])
minY = math.min (minY, polygon[i+1])
maxX = math.max (maxX, polygon[i])
maxY = math.max (maxY, polygon[i+1])
end
for i = 1, #polygon-1, 2 do
polygon[i] = polygon[i] - minX
polygon[i+1] = polygon[i+1] - minY
end
polygon.x = minX
polygon.y = minY
polygon.w = maxX-minX
polygon.h = maxY-minY
return polygon
end
function pr.newline (line)
if #line % 2 ~= 0 then
error('line coordinates are missing a value')
end
local minX, minY = line[1], line[2]
local maxX, maxY = line[1], line[2]
for i = 3, #line-1, 2 do
minX = math.min (minX, line[i])
maxX = math.max (maxX, line[i])
minY = math.min (minY, line[i+1])
maxY = math.max (maxY, line[i+1])
end
line.x, line.y = minX, minY
line.w, line.h = maxX-minX, maxY-minY
return line
end
local function getDot (poly, line)
local px, py = poly.x, poly.y
local x1, y1 = line[1], line[2]
local x2, y2 = line[3], line[4]
local dx = x2 - x1
local dy = y2 - y1
local positiveBiggestDot = 0.0
local negativeBiggestDot = 0.0
local indexPositiveBiggest = nil
local indexNegativeBiggest = nil
for index = 1, #poly-1, 2 do
local vertexVectorX = px+poly[index] - x1
local vertexVectorY = py+poly[index+1] - y1
local dot = -dy*vertexVectorX + dx*vertexVectorY
if dot > 0.0 then
if dot > positiveBiggestDot then
positiveBiggestDot = dot
indexPositiveBiggest = index
end
else
if dot < negativeBiggestDot then
negativeBiggestDot = dot
indexNegativeBiggest = index
end
end
end
if positiveBiggestDot > -negativeBiggestDot then
return negativeBiggestDot, indexPositiveBiggest and indexNegativeBiggest
else
return positiveBiggestDot, indexNegativeBiggest and indexPositiveBiggest
end
end
function pr.collision(poly, line)
local bigdot, index = getDot (poly, line)
if not index then
-- All polygon points are on the same side of the line line, so
-- the line can't possibly cross the polygon.
poly.force = nil
return
end
poly.index = index
local px, py = poly.x+poly[index], poly.y+poly[index+1]
local x1, y1 = line[1], line[2]
local x2, y2 = line[3], line[4]
local dx = x2 - x1
local dy = y2 - y1
local vertexVectorX = px-x1
local vertexVectorY = py - y1
local dot = dx*vertexVectorX + dy*vertexVectorY
local vectorScale = dot / (dx * dx + dy * dy)
local collisionX = x1+dx*vectorScale
local collisionY = y1+dy*vectorScale
poly.force = {
px-poly.x, py-poly.y,
collisionX-poly.x, collisionY-poly.y}
return collisionX-(px), collisionY-(py)
end
function pr.push (poly, line, tx, ty)
if poly.x > line.x+line.w or line.x > poly.x+poly.w
or poly.y > line.y+line.h or line.y > poly.y+poly.h then
-- no collision
poly.collision = false
poly.force = false
return tx, ty
end
local pushX, pushY = pr.collision(poly, line)
if pushX then
tx = poly.x+pushX
ty = poly.y+pushY
end
poly.collision = true
return tx, ty
end
return pr
Code: Select all
local vectorScale = dot / (dx * dx + dy * dy)
Code: Select all
vectorScale = vectorScale > 1.0 and 1.0 or (vectorScale < 0.0 and 0.0 or vectorScale)