Color Physics

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.

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

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.

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

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()