Push rectangle out of line

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
darkfrei
Party member
Posts: 1169
Joined: Sat Feb 08, 2020 11:09 pm

Re: Push rectangle out of line

Post by darkfrei »

RNavega wrote: Tue Feb 28, 2023 5:19 pm But OP has abandoned us. Come back OP ❤️
I read all answers, but it's too difficult for me from polygon to line collision. I've asked just about the AABB to the tilted segment push vector that I can understand.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
RNavega
Party member
Posts: 239
Joined: Sun Aug 16, 2020 1:28 pm

Re: Push rectangle out of line

Post by RNavega »

darkfrei wrote: Tue Feb 28, 2023 5:25 pm I read all answers, but it's too difficult for me from polygon to line collision. I've asked just about the AABB to the tilted segment push vector that I can understand.
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,
}
(Edited to make the size adjustable.)

In this case, does the detection work in the way that you're after?
User avatar
darkfrei
Party member
Posts: 1169
Joined: Sat Feb 08, 2020 11:09 pm

Re: Push rectangle out of line

Post by darkfrei »

Here is the solution:
It looks like an easy collision solution with custom shape on the tilted line, but the end of line is not as smooth as I want.

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
Screenshot 2023-03-25 010335.png
Screenshot 2023-03-25 010335.png (10.94 KiB) Viewed 850 times
Attachments
push-rectangle-01.love
cc0, no license
(1.92 KiB) Downloaded 16 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
RNavega
Party member
Posts: 239
Joined: Sun Aug 16, 2020 1:28 pm

Re: Push rectangle out of line

Post by RNavega »

Hi Darkfrei,
On my phone right now. I don't know if I understand correct what's going on, but I want to suggest clamping 'vectorScale' to the [0, 1] range, so that if the polygon is beyond the line end or before its beginning, the collision point remains in that extremity and doesn't fly out into the air.

So below this line here...

Code: Select all

local vectorScale = dot / (dx * dx + dy * dy)
... you'd add this new line to clamp it:

Code: Select all

vectorScale = vectorScale > 1.0 and 1.0 or (vectorScale < 0.0 and 0.0 or vectorScale)
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 59 guests