My 2D physics aabb testing seems a little flooey

@UberGoober I think you’re missing the point of aabb. It’s not supposed to be used for precise or current collision results. It’s a quick and easy way (code wise) to check if 2 objects are colliding. If the 2 bounding areas don’t overlap, then the 2 objects in those areas aren’t colliding. If the bounding areas do overlap, then you can either say that’s close enough, or do a more intense (code wise) calculation to determine if they collide. It’s very simple code to determine if 2 rectangles overlap, so that would be very fast when checking a lot.

@dave1707 I think you’re missing the point of me missing the point.

:wink:

If I understand you correctly, you think I want aabb testing to be something it’s not, and that’s what I may have failed to make clear: I’m just trying to make an accurate demo.

I’m looking for a way to easily and accurately and visually demonstrate it to others, because the current PhysicsLab doesn’t.

Also I apologize @dave1707 for being a jerko earlier about circles being circles and stuff. You’d think I’d eaten enough humble pie in my life to not act that way anymore, and I try not to, but clearly I fail now and then. Sorry.

@UberGoober No need to apologize about anything. Most of the time I don’t understand what is being asked and I go off in the wrong direction.

Glad to hear it, @dave1707, thanks for letting me know.

For what it’s worth, in actual Box2D, you can query a body directly for its precise current aabb boundary, so wanting and using that information is at least common enough that they built it right into the API.

If you want to take a second to try to think of a way to clearly and visually demonstrate this unusual aspect of aabb querying without actually showing the boundaries themselves, I’d love to know what occurs to you.

@UberGoober I don’t think there’s an option in Codea to show it. If the aabb area is stationary, then it just the defined rectangle around it. If it’s a moving aabb, then it’s harder to pinpoint the area.

PS. Haven’t found a way to highlight the aabb area of a moving object.

@dave1707 maybe play around with this:


viewer.mode=OVERLAY

function boundsOf(body)
    local ll, ur = body.position, body.position
    if body.shapeType == CIRCLE then 
        ll = body.position - vec2(body.radius, body.radius)
        ur = body.position + vec2(body.radius, body.radius)
    else
        for _, thisPoint in ipairs(body.points) do
            local worldPoint = body:getWorldPoint(thisPoint)
            if worldPoint.x < ll.x then ll.x = worldPoint.x end
            if worldPoint.y < ll.y then ll.y = worldPoint.y end
            if worldPoint.x > ur.x then ur.x = worldPoint.x end
            if worldPoint.y > ur.y then ur.y = worldPoint.y end
        end 
    end  
    return ll, ur
end

function tableHas(targetTable, candidate)
    if not targetTable or not candidate then return end
    for key, element in pairs(targetTable) do
        if element == candidate then return key end
    end
end

function setup()
    
    rectMode(CENTER)
    noStroke()
    physics.gravity(vec2(0,0))
    
    s1=physics.body(CIRCLE,60)
    s1.x=WIDTH/2
    s1.y=HEIGHT/2+200
    s1.type=STATIC
    
    r1=physics.body(POLYGON,vec2(-40,40),vec2(-40,-40),vec2(40,-40),vec2(40,40))
    r1.x=WIDTH/2
    r1.y=300
    
    tx,ty=WIDTH/2-300,HEIGHT-300
    
    r2=physics.body(POLYGON,vec2(-40,40),vec2(-40,-40),vec2(40,-40),vec2(40,40))
    r2.x=tx
    r2.y=ty
        
end

function drawAABB()
    local all = physics.queryAABB(vec2(0,0),vec2(WIDTH, HEIGHT))
    for _, body in ipairs(all) do 
        local ll, ur = boundsOf(body)
        if ll and ur then
            local foundUpper, foundLower, foundRight, foundLeft, foundBounds
            while not foundBounds do
                local yUp, yDown, xLeft, xRight = 0, 0, 0, 0
                while not foundUpper do
                    local bodiesAbove = physics.queryAABB(vec2(ll.x, ur.y + yUp), vec2(ur.x, ur.y + yUp + 1))
                    local indexOfBody = tableHas(bodiesAbove, body)
                    if bodiesAbove[indexOfBody] then 
                        yUp = yUp + 1
                    else 
                        foundUpper = true
                    end
                end
                while not foundLower do
                    local bodiesBelow = physics.queryAABB(vec2(ll.x, ll.y - (yDown + 1)), vec2(ur.x, ll.y - yDown))
                    local indexOfBody = tableHas(bodiesBelow, body)
                    if bodiesBelow[indexOfBody] then 
                        yDown = yDown + 1
                    else 
                        foundLower = true
                    end
                end
                while not foundRight do
                    local bodiesRight = physics.queryAABB(vec2(ur.x + xRight, ll.y), vec2(ur.x + xRight + 1, ur.y))
                    local indexOfBody = tableHas(bodiesRight, body)
                    if bodiesRight[indexOfBody] then 
                        xRight = xRight + 1
                    else 
                        foundRight = true
                    end
                end
                while not foundLeft do
                    local bodiesLeft = physics.queryAABB(vec2(ll.x - (xLeft + 1), ll.y), vec2(ll.x - xLeft, ur.y))
                    local indexOfBody = tableHas(bodiesLeft, body)
                    if bodiesLeft[indexOfBody] then 
                        xLeft = xLeft + 1
                    else 
                        foundLeft = true
                    end
                end
                
                ur.x = ur.x + xRight
                ur.y = ur.y + yUp

                ll.x = ll.x - xLeft
                ll.y = ll.y - yDown
            
                foundBounds = true
            end
            pushStyle()
            rectMode(CORNER)
            fill(0)
            rect(ll.x, ll.y, ur.x - ll.x, ur.y - ll.y)
            popStyle()
        end
    end
end

function draw()
    background(233, 218, 80)
    
    drawAABB()
    
    fill(233, 218, 80)
    rect(r1.x,r1.y,80,80)
    ellipse(s1.x,s1.y,120)
    rect(r2.x,r2.y,80,80)
    
    q=physics.queryAABB(vec2(tx-40,ty-40),vec2(tx+40,ty+40))
    if #q > 1 then
        fill(0,255,0)
    end
    
    rect(tx,ty,80,80)
    
end

function touched(t)
    if t.state==BEGAN or t.state==CHANGED then
        tx=t.x
        ty=t.y+150
        r2.position = vec2(tx, ty)
    end
end

…I think it draws the actual aabb, though it’s all brute-force. There might be a better way to do it.

The bodies are drawn in yellow and the aabbs are drawn as black boxes behind them.

I’m not sure if any single body’s aabb can be a shape other than a rectangle, if so this is imprecise because it only draws rectangles.

…and my original intersection lab project with accurate aabbs drawn (attached)

@UberGoober I don’t think this is working. There’s no reason the aabb area should increase in size just because I touch one of the other objects with the other.

@dave1707 it might not be working, but according to what I’ve read, there’s totally a reason the aabb would increase in size because of a touch.

Again, my code could be wrong, but as I understand it, aabb shows where an object could be, because it’s intended to return extremely best-guess results, leaving it up to other querying techniques to get more precise if needed.

So my code may not accurately reflect the aabb areas involved, but the concept that a given contact between two bodies results in a range of possibilities, therefore creating a larger black square, makes sense to me.

Also, if you run the intersection lab, the same code seems to give very accurate aabb results…

Also you’ll note that the faster you move the square into the circle’s space the larger the black box becomes.

That seems consistent.

@UberGoober I don’t think so. I’m barely moving the square into the circle and the black area increases in size as I move the square. Also, it only sometimes does it after I run the square into the other square and then into the circle. It should do it consistently if that’s how it works. I can agree that the faster you move the object, the more the aabb area will be behind the object. I would say that’s caused by the lag in calculating the aabb area and the position of the object. The aabb area is calculated where the object used to be. The only problem with trying to verify that, is the object is moving too fast to do anything to check it. The aabb area is calculated with the lower left and upper right x,y values. Those remain the same relative to the moving object, so I can’t see how the bounding area is going to change for the object.

@dave1707 I’m not sure if you’re getting, or if you’re getting and rejecting, the concept that aabb incorporates movement.

It’s not lag, it’s intentional, that’s how it’s supposed to work, at least as far as that SO article I linked to.

@UberGoober From what I’ve found so far, aabb will alter it size ( recalculate it’s area in the direction it’s moving ) to account for fast moving objects. It will also alter its position after a collision to reposition the object so fast moving objects aren’t overlapping each other at collision. So the increased bounding area will be in front or behind the moving object depending on what’s happening. I haven’t found any info about the bounding area increasing in size to each side. In your example, I was barely moving the square into the circle and the black area was increasing in size all around the square. That’s what I don’t think should be happening.

@dave1707 that makes sense

Have any idea why the objects only sometimes collide??

@dave1707 i just realized, one thing that could account for a larger box from even a small touch is the potential for rotation. If aabb queries take into account potential rotation as well as potential motion then even a small contact could create a larger box.

@dave1707 I think that hunch turned out to be at least partially correct.

I added a proper physics body drawer from PhysicsLab, so we can see what’s actually going on, and while it doesn’t fully explain the size of the boxes, it seems clear that box size expansion is linked to body rotation.


viewer.mode=OVERLAY

function boundsOf(body)
    local ll, ur = body.position, body.position
    if body.shapeType == CIRCLE then 
        ll = body.position - vec2(body.radius, body.radius)
        ur = body.position + vec2(body.radius, body.radius)
    else
        for _, thisPoint in ipairs(body.points) do
            local worldPoint = body:getWorldPoint(thisPoint)
            if worldPoint.x <= ll.x then ll.x = worldPoint.x end
            if worldPoint.y <= ll.y then ll.y = worldPoint.y end
            if worldPoint.x >= ur.x then ur.x = worldPoint.x end
            if worldPoint.y >= ur.y then ur.y = worldPoint.y end
        end 
    end  
    return ll, ur
end

function tableHas(targetTable, candidate)
    if not targetTable or not candidate then return end
    for key, element in pairs(targetTable) do
        if element == candidate then return key end
    end
end

function setup()
    
    rectMode(CENTER)
    stroke(74)
    strokeWidth(2)
    physics.gravity(vec2(0,0))
    
    s1=physics.body(CIRCLE,60)
    s1.x=WIDTH/2
    s1.y=HEIGHT/2+200
    s1.type=STATIC
    
    r1=physics.body(POLYGON,vec2(-40,40),vec2(-40,-40),vec2(40,-40),vec2(40,40))
    r1.x=WIDTH/2
    r1.y=300
    
    tx,ty=WIDTH/2-300,HEIGHT-300
    
    r2=physics.body(POLYGON,vec2(-40,40),vec2(-40,-40),vec2(40,-40),vec2(40,40))
    r2.x=tx
    r2.y=ty
        
end

function drawAABB()
    local all = physics.queryAABB(vec2(0,0),vec2(WIDTH, HEIGHT))
    for _, body in ipairs(all) do 
        local ll, ur = boundsOf(body)
        if ll and ur then
            local foundUpper, foundLower, foundRight, foundLeft, foundBounds
            while not foundBounds do
                local yUp, yDown, xLeft, xRight = 0, 0, 0, 0
                --find left edge
                while not foundLeft do
                    local bodiesToLeft = physics.queryAABB(vec2(0,0), vec2(ll.x - xLeft, HEIGHT))
                    if tableHas(bodiesToLeft, body) then
                        xLeft = xLeft + 1
                    else
                        xLeft = xLeft - 1
                        foundLeft = true
                    end
                end
                --find right edge
                while not foundRight do
                    local bodiesToRight = physics.queryAABB(vec2(ur.x + xRight, 0), vec2(WIDTH, HEIGHT))
                    if tableHas(bodiesToRight, body) then
                        xRight = xRight + 1
                    else
                        xRight = xRight - 1
                        foundRight = true
                    end
                end
                --find top
                while not foundUpper do
                    local bodiesAbove = physics.queryAABB(vec2(0, ur.y + yUp), vec2(WIDTH, HEIGHT))
                    if tableHas(bodiesAbove, body) then
                        yUp = yUp + 1
                    else
                        yUp = yUp - 1
                        foundUpper = true
                    end
                end
                --find bottom
                while not foundLower do
                    local bodiesBelow = physics.queryAABB(vec2(0, 0), vec2(WIDTH, ll.y - yDown))
                    if tableHas(bodiesBelow, body) then
                        yDown = yDown + 1
                    else
                        yDown = yDown - 1
                        foundLower = true
                    end
                end
                
                ur.x = ur.x + xRight
                ur.y = ur.y + yUp

                ll.x = ll.x - xLeft
                ll.y = ll.y - yDown
            
                foundBounds = true
            end
            pushStyle()
            rectMode(CORNER)
            fill(0)
            rect(ll.x, ll.y, ur.x - ll.x, ur.y - ll.y)
            popStyle()
        end
    end
end

function draw()
    background(233, 218, 80)
    
    drawAABB()
    
    q=physics.queryAABB(vec2(tx-40,ty-40),vec2(tx+40,ty+40))
    if #q > 1 then
        fill(0,255,0)
    end
    
    bodiesDraw({r1, r2, s1})
end

function touched(t)
    if t.state==BEGAN or t.state==CHANGED then
        tx=t.x
        ty=t.y+150
        r2.position = vec2(tx, ty)
    end
end

function bodiesDraw(bodies)
for i,body in ipairs(bodies) do
    pushMatrix()
    translate(body.x, body.y)
    rotate(body.angle)
    
    if body.type == STATIC then
        stroke(255,255,255,255)
    elseif body.type == DYNAMIC then
        stroke(150,255,150,255)
    elseif body.type == KINEMATIC then
        stroke(150,150,255,255)
    end
    
    if body.shapeType == POLYGON then
        strokeWidth(3.0)
        local points = body.points
        for j = 1,#points do
            a = points[j]
            b = points[(j % #points)+1]
            line(a.x, a.y, b.x, b.y)
        end
    elseif body.shapeType == CHAIN or body.shapeType == EDGE then
        strokeWidth(3.0)
        local points = body.points
        for j = 1,#points-1 do
            a = points[j]
            b = points[j+1]
            line(a.x, a.y, b.x, b.y)
        end      
    elseif body.shapeType == CIRCLE then
        strokeWidth(3.0)
        line(0,0,body.radius-3,0)            
        ellipse(0,0,body.radius*2)
    end
    
    popMatrix()
    end 
    end

I think my aabb drawing technique seems to be working—also please check out how it looks on all the default PhysicsLab projects included with IntersectionLab—and that any results that look odd are not the code being wrong, they’re us not understanding the reason for the results.

@UberGoober That looks nice, but I haven’t looked at why the collision isn’t happening all the time. Maybe while I’m watch football this afternoon, I’ll look thru your code.

I think the problem with the collisions is because you’re overriding the physics engine by moving the square with the touch function. Need to move it using applyForce so the physics code is doing everything.