Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

My 2D physics aabb testing seems a little flooey

in Bugs Posts: 1,379

Attached is a project I made on top of PhysicsLab to better understand raycasting and aabb queries.

It’s pretty simple and self-explanatory, but if you play with it for any length of time, you’ll probably notice the bug: the purple square seems to be detecting the blue line when they aren’t touching at all.

In the attached screenshot you can see this happening: the purple text reports that the query has detected the line, but they’re not touching—I’ve racked my brain on this one, can anybody help?

Comments

  • dave1707dave1707 Mod
    Posts: 9,584

    @UberGoober The problem with trying to figure out what’s wrong is first trying to figure out what code is actually being run. Then trying to jump around from tab to tab to see what to look for. You’re adding code to an existing project that in my opinion is hard to follow to begin with since there’s 7 different examples all reusing some different code here and there. Whenever I have a problem with something not working, I’ll create another project with as little code as possible and work with that until I figure out what’s wrong. Since you added code, take what you added and create another project with that and see how raycast and aabb works, or in your case, doesn’t.

  • Posts: 1,125

    +1 on small test code.

  • dave1707dave1707 Mod
    Posts: 9,584

    @UberGoober Slide your finger to move the left square to the circle or the other square. The moving square is the aabb area. Below are some things you’ll see.

    1.) rect to rect. They don’t have to touch to cause a hit.
    2.) rect to circle. They don’t have to touch and the hit area around the circle appears to be a square the size of the circle.

    viewer.mode=FULLSCREEN
    
    function setup()
        rectMode(CENTER)
        stroke(255)
        strokeWidth(2)
    
        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
        r1.type=STATIC
    
        tx,ty=WIDTH/2-300,HEIGHT-300
    end
    
    function draw()
        background()
    
        noFill()
        rect(r1.x,r1.y,80,80)
        ellipse(s1.x,s1.y,120)
    
        q=physics.queryAABB(vec2(tx-40,ty-40),vec2(tx+40,ty+40))
        if #q~=0 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
        end
    end
    
  • edited September 27 Posts: 1,379
    @dave1707 what a great little demo you whipped up—I was here deleting all the extraneous tabs in my existing project and arguing with myself about which lines of code should stay or not.

    So yeah it looks like you done zeroed in on a bug—the 2D physics engine’s aabb routines seem to be yielding the wrong results.

    @Simeon, did we find something new here?
  • dave1707dave1707 Mod
    Posts: 9,584

    @UberGoober I wouldn’t say they’re the wrong results, just not as exact as they could be. As far as I know, that might be the way it’s written, close enough to get the job done. If you’re running a game at full speed that’s using aabb to check something, you might not even notice the difference.

  • Posts: 1,379

    @dave1707 ennnnnnnnnnnh I think if a piece of software is going to tell me it made me a circle, that thing should act like a circle.

  • dave1707dave1707 Mod
    Posts: 9,584

    @UberGoober Its possible they were trying to keep the code for aabb small. Try writing some code that will indicate when any part of a rectangle of any size touches any part of a circle of any size.

  • edited September 28 Posts: 1,379
    @dave1707 I have had a lifetime’s worth of experience being wrong about things, so I’m sure this could be yet another one, but I find it hard to believe that a codebase that can accurately calculate a ball rolling down a slope doesn’t already have all the code it needs to accurately detect a circle overlapping a square.

    I’ll be flabbergasted if it’s not a bug.
  • dave1707dave1707 Mod
    edited September 28 Posts: 9,584

    @UberGoober You may be right, but I’m sure that codes been around for a long time. I’d be surprised if no one found it in all that time.

    PS. I checked up on aabb and it’s bounding volumes. It’s not meant to be exact, but a quick check to see if two objects might be overlapping. If they pass the aabb check then if a more exact check needs to be done, it can be. If they don’t pass the simple aabb check, then there’s no need to do the more intensive calculation.

    So it’s not a bug, but just a simple check. I didn’t know that, so thanks.

  • Posts: 1,379
    Huh, I guess that’s another log on the “things I was wrong about” fire!

    I am, as the kids on my playground used to say, moded.

    From my own brief googling it seems like there’s kind of a grid underlying everything in the physics 2D system—it’s not that simple, I gather, but for the sake of illustration, we could say that aabb querying isn’t telling you exactly where a shape is, it’s telling you if any part of the shape is in the same square of the grid.
  • Posts: 1,379
    @dave1707 I think it’s even more complex than that—according to this:

    https://stackoverflow.com/questions/18689451/what-is-aabb-explanation-required

    …aabb bounding boxes can also include **a body’s recent motions**!

    …man, I wonder if there’s some way to actually render all aabb boxes for any given moment on screen.
  • dave1707dave1707 Mod
    Posts: 9,584

    @UberGoober Here’s another example showing that the bounding area is a square around the triangle. Move the triangle around the square and make contact. I’ll have to try another example to see if the motion is also included.

    viewer.mode=STANDARD
    
    function setup()
        rectMode(CENTER)
        stroke(255)
        strokeWidth(2)    
        physics.gravity(0,0)
        r1=physics.body(POLYGON,vec2(0,0),vec2(100,0),vec2(100,100))
        r1.x=WIDTH/2
        r1.y=100
        r1.type=DYNAMIC    
    end
    
    function draw()
        background(0)
        noFill()
        line(r1.x,r1.y,r1.x+100,r1.y)
        line(r1.x+100,r1.y,r1.x+100,r1.y+100)
        line(r1.x,r1.y,r1.x+100,r1.y+100)
        q=physics.queryAABB(vec2(WIDTH//2-50,HEIGHT//2-50),vec2(WIDTH//2+50,HEIGHT//2+50))
        if #q~=0 then
            fill(0,255,0)        
        end
        rect(WIDTH/2,HEIGHT/2,100,100)    
    end
    
    function touched(t)
        if t.state==BEGAN or t.state==CHANGED then
            r1.x=t.x-50
            r1.y=t.y+150
        end
    end
    
  • dave1707dave1707 Mod
    Posts: 9,584

    @UberGoober Here’s an example showing the trailing bounding area. There’s a fix circle shown and another circle off screen that’s falling. You have to wait about 8 seconds for the falling circle to hit the fixed circle. After the collision, move the aabb square above the circles and move it down. The aabb area will turn green well above the top circle. I’m not sure how the size of the trailing area is calculated. It has something to do with the speed of the moving object. If you remove the *10 so it doesn’t fall as far, the trailing area isn’t as much.

    viewer.mode=FULLSCREEN
    
    function setup()
        rectMode(CENTER)
        stroke(255)
        strokeWidth(2)
    
        s1=physics.body(CIRCLE,60)
        s1.x=WIDTH/2
        s1.y=HEIGHT*10
        s1.type=DYNAMIC
    
        s2=physics.body(CIRCLE,60)
        s2.x=WIDTH/2
        s2.y=100
        s2.type=STATIC
    
        tx,ty=WIDTH/2-300,HEIGHT-300
    end
    
    function draw()
        background()    
        noFill()
        ellipse(s1.x,s1.y,120)
        ellipse(s2.x,s2.y,120)
        q=physics.queryAABB(vec2(tx-40,ty-40),vec2(tx+40,ty+40))
        if #q~=0 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
        end
    end
    
  • Posts: 1,379

    Nice demonstration!

    I've been trying to figure out if there's a way to detect a body's actual aabb value at any moment in time.

    In box2d (2.4.1 at least) bodies have a command "GetFixtureList", and whatever a 'fixture' is, it has a "GetAABB" command that returns the actual AABB being tracked by the physics simulation.

    It looks like 'fixtures' are a component of Box2D that's intentionally obscured by Codea--I think fixtures allow a body to contain sub-bodies, and I am assuming @John decided that was an unnecessary layer of complexity for Codea, and in my opinion I think that's the right call in general.

  • Posts: 1,379

    The fourth answer on this page : https://stackoverflow.com/questions/2620377/lua-reflection-get-list-of-functions-fields-on-an-object#comment2638663_2620398

    …contains code that claims to output a pretty comprehensive survey of functions available at the global level, I think.

    It’s written in lua, for a version of lua running on C#, though, and I can’t get it to do anything.

  • dave1707dave1707 Mod
    Posts: 9,584

    @UberGoober I don’t see any reason to know the aabb area. The whole purpose of aabb is just a quick check to see if two objects might be collideing. If aabb is true, then a more complex calculation can be don’t if the objects aren’t simple rectangles and you want to know exact collisions. In simple games, the aabb check might be enough, but in the physics code, you already have a collision function.

  • Posts: 1,379

    There are many reasons I’d like to know it — frankly, it’s honestly surprising to me that you don’t — but the simplest is that not knowing it created this whole post.

    Isn’t that enough?

  • dave1707dave1707 Mod
    edited September 28 Posts: 9,584

    @UberGoober I guess I know more about aabb now then I did before, so in that respect it’s good. But as for using it, that doesn’t change much other than writing a few examples that uses it.

  • Posts: 1,379

    Right now, or should I say over the last few weeks, I've been trying to make a better demo of aabb and raycasting than the one in the current PhysicsLab--from which, currently, none of this is obvious or even, really, possible to guess.

    I think being able to show people what's happening by visually representing the actual aabb boundaries would be fantastic.

    And I think it would save other people a huge amount of frustration if right from the start they knew not to use aabb testing if you want results that are precise or--for goodness' sakes--current.

  • dave1707dave1707 Mod
    Posts: 9,584

    @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.

  • Posts: 1,379

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

    ;)

    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.

  • Posts: 1,379

    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.

  • dave1707dave1707 Mod
    Posts: 9,584

    @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.

  • Posts: 1,379

    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.

  • dave1707dave1707 Mod
    edited September 29 Posts: 9,584

    @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.

  • Posts: 1,379

    @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.

  • Posts: 1,379

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

  • dave1707dave1707 Mod
    Posts: 9,584

    @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.

  • Posts: 1,379

    @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.

  • Posts: 1,379

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

  • Posts: 1,379

    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.

  • dave1707dave1707 Mod
    Posts: 9,584

    @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.

  • Posts: 1,379
    @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.
  • dave1707dave1707 Mod
    Posts: 9,584

    @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.

  • edited October 3 Posts: 1,379
    @dave1707 that makes sense

    Have any idea why the objects only sometimes collide??
  • Posts: 1,379

    @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.

  • Posts: 1,379

    @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.

  • dave1707dave1707 Mod
    Posts: 9,584

    @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.

  • dave1707dave1707 Mod
    edited October 3 Posts: 9,584

    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.

  • Posts: 1,379
    @dave1707 can you provide an example? When I try to make precise movements with linear velocity I always mess it up.
  • dave1707dave1707 Mod
    edited October 3 Posts: 9,584

    Here’s your code modified a little to use appleForce instead of linearVelocity. I changed the gravity so the square falls. I also made the lower square static so it doesn’t fall. To move the upper square, with your finger below mid screen, slide your finger near the left side to move it left and right side to move it right. To move it up, slide your finger anywhere above mid screen.

    It looks like all that’s happening is the black background is just the max size of the square as it rotates.

    viewer.mode=FULLSCREEN
    
    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,-5))
    
        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
        r1.type=STATIC
    
        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
            if t.y<HEIGHT/2 then
                if t.x>r2.position.x then
                    r2:applyForce(vec2(10,0))
                elseif t.x<r2.position.x then
                    r2:applyForce(vec2(-10,0))
                end
            else
                if t.y>r2.position.y then
                    r2:applyForce(vec2(0,10))
                end
            end
        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
    
  • Posts: 1,379
    This version has the AABB drawing routine applied to all the physics lab tests.

    Usually if you run the “sensors” test, and sometimes when you run the “filtering” test, once everything is still you can see an aabb region that isn’t a product of rotation and that exceeds the regular rectangular boundary of the shape (screenshot included).

    I’m still not clear if you’re doubting that aabb sometimes incorporates momentum.
  • dave1707dave1707 Mod
    Posts: 9,584

    @UberGoober When an object is falling, I can see the bounding area increase in size above and below the object. I also see as the object rotates that the bounding area increases or decreases to keep a rotating object enclosed in it. I agree with you that it increases depending on its speed and direction. It’s just that in your one example it was increasing in size by a lot on all 4 sides even though it was barely moving. So I don’t have a problem with it.

    If the aabb function is used just to determine collisions, I’ll just let the collide function determine the collision. It takes care of the collisions with any shaped object.

  • Posts: 1,379
    In the sensor example you can see the aabb area stretch out behind the box as it falls, and then once it hits the ground the aabb box (usually) stays extended beyond the boundaries of the physics body even though the physics body has stopped moving.

    Honestly I don’t know what aabb boundaries are for, but they do seem to be working the way they’re supposed to, and most importantly the way they work is a lot different from the way the documentation says they work.
  • dave1707dave1707 Mod
    Posts: 9,584

    @UberGoober They had me confused too. I didn’t know that the bounding area stretched in front and behind a moving object. I guess that’s the fun part of Codea, trying to figure out what’s really happening and creating examples to show it.

  • Posts: 1,379

    @dave1707 clearly we have vastly different ideas of fun ;)

    Kudos to Codea for being a good tool for both of us.

  • Posts: 1,379

    @dave1707 I want to see what happens to aabb boundaries around a circle that’s rapidly moving in an elliptical orbit.

    I am no good at all the sine and cosine stuff to make that kind of motion happen—but you’re a pro at that right? Would you be willing to share some basic code for describing a circular orbit?

  • dave1707dave1707 Mod
    edited October 3 Posts: 9,584
    viewer.mode=FULLSCREEN
    
    function setup()  
        fill(255)  
        physics.gravity(vec2(0,0))
    
        s1=physics.body(CIRCLE,50)
        s1.x=0
        s1.y=0
    
        ang=0
        speed=1
        radius=math.min(WIDTH/2,HEIGHT/2)-50
    end
    
    function draw()
        background(0)
        ang=ang+1    
        s1.x=math.cos(math.rad(ang))*radius+WIDTH/2
        s1.y=math.sin(math.rad(ang))*radius+HEIGHT/2
        ellipse(s1.x,s1.y,100)   
        ang=ang+speed   
    end
    
  • Posts: 1,379

    @dave1707 further somewhat-interesting results.

    I applied your code to a physics body, and at the same time I made a circle that uses a REVOLUTE joint and angular velocity to achieve elliptical motion—in other words, a body that achieves circular motion just using the physics engine.

    Of note:

    • the aabb rect of the manually-positioned circle actually lags behind the circle as it travels, meaning the circle is often outside its own aabb rect… which is odd…
    • the aabb rect of the manually-positioned circle never changes size—it’s always the size of the minimum rect needed to enclose the circle, no matter how fast it goes
    • the aabb rect of the circle moved by the physics engine never lags behind—even at the same or greater speeds than the manually-positioned one, it always fully encloses the circle
    • the aabb rect of the circle moved by the physics engine does change size as the circle moves, and the faster it moves the larger it gets.
Sign In or Register to comment.