Howdy, Stranger!

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

Color Physics

edited September 2012 in General Posts: 266

Hey Guys,
I'm using the Physics Test example as a model for a building block game I'm building. I just have a basic question that I can't figure out how to do. How do I color the physics box that is in test 1? Sorry if this is a noob question but I could only manage to color the circle.
Please help.

Tagged:

Comments

  • Everything is drawn in PhysicsDebugDraw, look out for body.shapetype == POLYGON.

  • If you do not want to interfere with the 'overlay' provided by ‘PhysicsDebugDraw‘, then how about something like:

    function Test1:setup()
        createGround()
        self.box = createBox(WIDTH/2, 100, 30, 30)
        createCircle(WIDTH/2 + 50, 110, 30)
        createRandPoly(WIDTH/2 + 150, 120)
    end
    
    function Test1:draw()
        pushMatrix()
        resetMatrix()
        pushStyle()
        fill(255, 255, 0)
        noStroke()
        rectMode(CENTER)
        translate(self.box.x, self.box.y)
        rotate(self.box.angle)
        rect(0, 0, 30, 30)
        popStyle()
        popMatrix()
    end
    
  • Hey @mpilgrem i tried your code in the physics example and it works perfectly, just not in my game. i keep on getting errors. here's the basic code im using for the box. it was in one of @Reefwing's tutorials. Hopefully he can help http://codeatuts.blogspot.com/2012/07/tutorial-11-physics-101.html
    if anyone can tell me how to do it directly from PhysicsDebugDraw, it would be really helpful. I tried putting

    fill(255,0,255,255)
    

    where i managed to adjust the stroke but i got no luck

  • Come on, you're using fill to adjust the stroke, even the English language reveals your bug.

    Read the code in PhysicsDebugDraw carefully, what's the difference between a circle and a box in this code?

  • @Codeslinger oh darn it. That's my fault. I meant that's used fill to adjust the fill and got no luck. I don't know why I wrote that here. I think my problem has to do with the noStroke() before everything in PhysicsDebugDraw(). I'm still a little confused

  • .@veeeralp - have a look at the PhysicsDebugDraw draw() function in particular at the body.shapeType == POLYGON block. This is where squares get drawn (as they are a type of polygon). Note that the line() function (not rect) is used to draw the square, hence fill has no effect (except on the CIRCLE shapeType).

    So you need to either come up with a new body.shapeType (called RECTANGLE say) and use rect() to draw it or you could use a sprite, but that is probably not the best option in this case.

  • .@Reefwing ok thanks. I will try it this afternoon. When I make the body.shapeType == RECTANGLE, do I have to get rid of the createBox() function. Will I have to dod everything I did with the Box in the rectangle. ie. make 1 rectangle fall every second

  • Maybe I have a reputation as a grumpy teacher, but I wanted veeeralp to find out the difference between a circle and a box himself, so I gave him only a hint. He wants to be a programmer, right?

    veeeralp, you cannot create a new shape type named RECTANGLE, you have to live with what the physics API provides, so stick to a polygon.

    Solution idea 1

    When creating a box, you have to remember that this specific kind of polygon is in fact a rectangle. When drawing a polygon, check if it is a rectangle and draw a Codea rectangle which can be filled.

    Solution idea 2

    Instead of drawing the outlines of the polygon, triangulate it and then draw the triangles with a mesh. This is more difficult, but I think there are examples for this in the forum. This solution is also more universal.

  • .@Codeslinger is of course correct (even if he is grumpy :)). I forgot that POLYGON and the like are defined types in physics body.

  • @Reefwing @Codeslinger I think the easiest method for me right now is @mpilgrem's code. I got it working inside the Physics Test example, just can't get it working through the Physics 101 tutorial.

  • The code below is a coloured-in version of Test1 (polygons only):

    Test1 = class()
    
    function Test1:init()
        self.title = "basic bodies (tap to create)"
        self.polys = {}
    end
    
    function Test1:setup()
        createGround()
        self:createBox(WIDTH/2, 100, 30, 30, color(255, 255, 0))
        createCircle(WIDTH/2 + 50, 110, 30)
        self:createRandPoly(WIDTH/2 + 150, 120, color(0, 255, 255))
    end
    
    -- Coloured-in box
    function Test1:createBox(x, y, w, h, c)
        local poly = {}
        local m = mesh()
        poly.body = createBox(x, y, w, h)
        m:addRect(0, 0, w, h)
        m:setColors(c)
        poly.mesh = m
        table.insert(self.polys, poly)
    end
    
    -- Coloured-in random polygon
    function Test1:createRandPoly(x, y, c)
        local poly = {}
        local m = mesh()
        poly.body = createRandPoly(x, y)
        m.vertices = triangulate(self:reverse(poly.body.points))
        m:setColors(c)
        poly.mesh = m
        table.insert(self.polys, poly)
    end
    
    -- Helper function
    function Test1:reverse(p)
        local n = #p
        local rp = {}
        for i = 1, n do
            rp[i] = p[n - i + 1]
        end
        return rp
    end
    
    function Test1:draw()
        pushMatrix()
        for i, poly in ipairs(self.polys) do
            resetMatrix()
            translate(poly.body.x, poly.body.y)
            rotate(poly.body.angle)
            poly.mesh:draw()
        end
        popMatrix()
    end
    
    function Test1:touched(touch)
        if touch.state == BEGAN then
            local r = math.random(0, 255)
            local g = math.random(0, 255)
            local b = math.random(0, 255)
            local c = color(r, g, b)
            self:createRandPoly(touch.x, touch.y, c)
        end
    end
    
  • That is an elegant solution @mpilgrem.

    Thinking about this, it is not actually that straight forward to work out if a polygon is a rectangle. My initial thought was, "easy just check that the number of points in the physics body was equal to 4". But of course you also need to check whether all of the internal angles are 90 degrees. If the rectangle had it sides parallel to the axis then you could just check that the (x,y) co-ordinates for the vertices lined up, but what about if it was rotated?

    So what would be the easiest test for detecting whether a polygon is a rectangle? My thoughts would be to firstly check the number of vertices. If this is 4 then you can move onto the next step, because at this stage you don't even know if you have a quadrilateral as it depends on what order the lines are drawn. We know that the diagonals of a rectangle are equal in size and bisect each other and that the length of a diagonal is given by:

    d = SQRT(w^2 + h^2)

    Consequently we could calculate both diagonals and check that they are the same, if so the four vertices of our polygon are a rectangle. Or are they?

    I'm afraid not, they may be, but to be certain you need to test whether a polygon is simple or complex. A complex polygon has intersections with itself. If our four sided polygon has equal diagonals and is simple then it is a rectangle.

    Maybe it is just easier to check the internal angles? Or perhaps our resident mathematician @Andrew_Stacy may have another approach?

  • @Reefwing do you know how I would get it working using the coding from your physics 101 tutorial. I need to color the boxes dropping down. I always get an error

  • edited September 2012 Posts: 489

    How about:

    --
    -- isRect
    --
    
    function setup()
        poly1 = {vec2(0, 0), vec2(1, 1), vec2(0, 2), vec2(-1, 1)}
        poly2 = {vec2(0, 0), vec2(1, 1), vec2(0, 2.1), vec2(-1, 1)}
        print(isRect(unpack(poly1)))
        print(isRect(unpack(poly2)))
    end
    
    function isRect(...)
        if arg.n ~= 4 then return false end
        local v = {arg[2] - arg[1]}
        for i = 2, 4 do
            v[i] = arg[i % 4 + 1] - arg[i]
            if math.abs(v[i]:dot(v[i - 1])) > 0.0001 then return false end
        end
        return true
    end
    
    function draw()
        background(0)
    end
    

    (Update) Not all simple quadrilaterals with equal-length diagonals are rectangles.

  • edited September 2012 Posts: 563

    .@mpilgrem - Nice, so you are taking the dot product of two adjacent vectors which I think is equivalent to the length of the two vectors multiplied by the cosine of the internal angle and since cos 90 = 0, if the dot product is not zero, we don't have a rectangle? I'm assuming you test for > 0.0001 to take into account rounding errors?

    Can you give me an example of a simple quadrilateral with two equal length diagonals which isn't a rectangle. I'm sure you are right I just cant think of an example (in 2D).

    .@veeeralp - you are better off adapting @mpilgrem's code above to drop random boxes than trying to shoehorn it into my tute. Have a go and then post the code if you get stuck.

  • edited September 2012 Posts: 489

    For @Reefwing (updated: less code, same effect)

    --
    -- QuadErat
    --
    
    displayMode(STANDARD)
    supportedOrientations(LANDSCAPE_ANY)
    function setup()
        len = WIDTH/3
        speed = 1
        turn = 0.05
        p, a, v, w = {}, {}, {}, {}
        d = {WIDTH - len, HEIGHT - len}
        for i = 1, 2 do
            p[i] = vec2(math.random(len, d[i]), math.random(len, d[i]))
            a[i] = 2 * math.pi * math.random()
            v[i] = (vec2(math.random(), math.random()) - vec2(0.5, 0.5)) * speed
            w[i] = (math.random() - 0.5) * turn
        end
    end
    
    function draw()
        local x, y = {}, {}
        background(0)
        for i = 1, 2 do
            p[i] = p[i] + v[i]
            a[i] = (a[i] + w[i]) % (2 * math.pi)
            for j = 1, 2 do
                if p[i][j] < len or p[i][j] > d[j] then v[i][j] = -v[i][j] end
                p[i][j] = math.max(math.min(p[i][j], d[j]), len)
            end
            x[i], y[i] = p[i].x, p[i].y
            x[i + 2] = x[i] + len * math.cos(a[i])
            y[i + 2] = y[i] + len * math.sin(a[i])
        end
        stroke(255)
        strokeWidth(10)
        for i = 1, 4 do
            local ni = i % 4 + 1
            line(x[i], y[i], x[ni], y[ni])
        end
        strokeWidth(5)
        stroke(255, 255, 0)
        line(x[1], y[1], x[3], y[3])
        stroke(0, 255, 0)
        line(x[2], y[2], x[4], y[4])
    end
    
  • .@Reefwing I'm just having trouble with error. Even when I get rid of everything that has to with your tutorial... I still get when I put self:createBox()

  • .@mpilgrem - Quod erat faciendum. Hypnotic!

  • .@veeeralp it is a bit of a hack but you need to blend the above with the tute code. Try this:

    In Main for the Physics 101 tute add:

    polys = {}
    createFilledBox(WIDTH/2, HEIGHT, 30, 30, color(255,0,0))
    

    Then in the Physics tab add a new function based on @mpilgrem's code above

    function createFilledBox(x, y, w, h, c)
        local poly = {}
        local m = mesh()
        poly.body = createBox(x, y, w, h)
        m:addRect(0, 0, w, h)
        m:setColors(c)
        poly.mesh = m
        table.insert(polys, poly)
    end
    

    Finally, in the PhysicsDebugDraw:draw() function, at the very end add:

    pushMatrix()
        for i, poly in ipairs(polys) do
            resetMatrix()
            translate(poly.body.x, poly.body.y)
            rotate(poly.body.angle)
            poly.mesh:draw()
        end
        popMatrix()
    
  • .@Reefwing thank you!!! This worked with no problems except when I tried to clear all the box pes after they reacher a certain limit. I always get an error. Any other way to clear the rectangles now

  • .@veeeralp - c'mon dude you have to do something yourself. You know where the problem is and you know what you just added. The answer should be obvious. Post the answer here when you work it out.

  • Posts: 2,161

    Just to say that I'd've gone for the dot products too.

    A quadrilateral with equal length diagonals is a rectangle if and only if the diagonals intersect at their midpoints (note that they needn't intersect at all).

  • Suddenly, I start to like my self-proclaimed grumpy teacher attitude. (In response to Reefwing, not Andrew.)

  • .@Codeslinger - lol.

    .@Andrew_Stacey - I dont think I would ever have come up with that approach, I think I would have either converted the cartesian co-ordinates to polar and checked the angle or used one of the available fill polygon algorithms (in which case you don't care if it is a rectangle).

  • @Reefwing I tried implementing the PhysicsDebugDraw:clear() function when the box count reaches 10, but I get a bad argument on the

    translate(poly.body.x, poly.body.y)
    

    part. I also tried putting resetMatrix() and got another error. Next I tried putting table.remove(polys, poly) instead of table.insert(polys, poly)

  • .@veeeralp - you are on the right track. Try just polys = {} in that function.

  • edited September 2012 Posts: 266

    .@Reefwing thank you it worked. Now im done with the paid version of my game. Now on to the free version and then iphone

    edit: do you remember that little bug that you had with the mines falling down in your minesweeper game becuase of the repeated call to the touch handler? I have the same problem right now, but worse becuase I drew the ground. How did you fix that?

  • Posts: 2,161

    @Reefwing Dot products are "angles made safe". They contain all the same information but have much better properties. If you find yourself computing angles, chances are you could do it more easily with dot products.

  • .@Andrew_Stacey - I love learning a new trick. Dot products it is - although if you just want the angle I guess you need to change them into unit vectors.

  • edited September 2012 Posts: 489

    For completeness, it may be worth mentioning the (signed) angleBetween function built into the vec2 userdata type:

    --
    -- v1:angleBetween(v2) example
    --
    
    -- In Lua, the logic is:
    --
    -- local angle = math.atan2(v2.y, v2.x) - math.atan2(v1.y, v1.x)
    -- if angle > math.pi then
    --     angle = angle - 2 * math.pi
    -- elseif angle < -math.pi then
    --     angle = angle + 2 * math.pi
    -- end
    -- return angle
     
    function setup()
        v1 = vec2(3, 1)
        v2 = vec2(1, 3)
        a = v1:angleBetween(v2)
        s = "The smallest angle turning from "..tostring(v1).." to "..tostring(v2)..
            " is %+.1f radians or %+.1f degrees."
        s = string.format(s, a, math.deg(a))
        print(s)
        print("A counterclockwise turn is positive"..
            " and a clockwise turn is negative.")    
    end
    
    function draw()
        background(0)
    end
    
  • Posts: 2,161

    And also important to note that it returns an angle in radians but the rotate function takes degrees.

  • PTNPTN
    Posts: 39

    Ouch! Makes me think of that mars rover that got lost due to metric and non-metric engineering teams... :-S
    Looks like we're doing rocketscience after all.

Sign In or Register to comment.