Page 1 of 2

Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Sun Dec 16, 2018 12:03 pm
by TheHUG
Hi everyone!
I made a little library in the vein of windfield, but simpler to use and modify.
https://github.com/HDictus/breezefield
Basically, it makes working with the love.physics library a lot easier, faster, and more concise.
calling bf.Collider.new(world, shape, args*) creates body, fixture, and shape for you, and all their methods are accesible from the returned Collider object
all physics objects can automatically be drawn with World:draw(alpha)
circle/rectangle areas can

Please give me any advice, criticism, questions or requests you have, either here or on github. Even if it's just a matter of coding style.

see future.org for my ideas on what I could add in the future.

On a related note, how do you add your library to the list of libraries on the wiki? I can't find the option to.

Re: Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Fri Dec 21, 2018 4:01 pm
by yetneverdone
Try also adding this to awesome-love2d list on github

Re: Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Sat Dec 22, 2018 5:58 pm
by TheHUG
Hey thanks! I'll do that right away!

Re: Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Mon Jan 07, 2019 1:40 pm
by 4vZEROv
I've rewritten your and the windfield library to match my style more (didn't implement queries yet).

Code: Select all

local lp, lg = love.physics, love.graphics

local function _set_funcs(obj1, obj2)  
    for k, v in pairs(obj2.__index) do
        if k~="__gc" and k~="__eq" and k~="__index" and k~="__tostring" and k~="isDestroyed" and k~="testPoint" and k~="getType" and k~="setUserData" and k~="rayCast" and k~="destroy" and k~="getUserData" and k~="release" and k~="type" and k~="typeOf" then obj1[k] = function(obj1, ...) return v(obj2, ...) end end
    end
end

local function _uuid()
    local fn = function(x) local r = math.random(16) - 1; r=(x=="x")and(r+1)or(r%4)+9; return ("0123456789ABCDEF"):sub(r, r) end
    return (("xxxxxxxx"):gsub("[x]", fn))
end

-------------------------------
--<<(^__^)  (0___U)  (*__*)>>--
-------------------------------

local Collider = {}

function Collider:new(world, collider_type, ...)
    local _w, _t, _a, _b, _s = world, collider_type, {...}
    if     _t == "circ"  then _b, _s = lp.newBody(_w, _a[1], _a[2], _a[4] and _a[4].type or "dynamic"), lp.newCircleShape(_a[3])
    elseif _t == "rect"  then _b, _s = lp.newBody(_w, _a[1], _a[2], _a[5] and _a[5].type or "dynamic"), lp.newRectangleShape(0, 0, _a[3], _a[4], _a[5] and _a[5].angle or 0)
    elseif _t == "poly"  then _b, _s = lp.newBody(_w,     0,     0, _a[2] and _a[2].type or "dynamic"), lp.newPolygonShape(unpack(_a[1]))
    elseif _t == "line"  then _b, _s = lp.newBody(_w,     0,     0, _a[5] and _a[5].type or "static" ), lp.newEdgeShape(_a[1], _a[2], _a[3], _a[4])
    elseif _t == "chain" then _b, _s = lp.newBody(_w,     0,     0, _a[3] and _a[3].type or "static" ), lp.newChainShape(_a[1], unpack(_a[2])) end

    local obj = {}
        obj.world    = _w
        obj.body     = _b
        obj.shape    = _s
        obj.fixture  = lp.newFixture(_b, _s)
        obj.shapes   = {default = obj.shape}
        obj.fixtures = {default = obj.fixture}
        _set_funcs(obj, obj.body)
        _set_funcs(obj, obj.shape)
        _set_funcs(obj, obj.fixture)
    return setmetatable(obj, {__index = Collider})
end

function Collider:destroy()
    for k, v in pairs(self.fixtures) do v:setUserData(nil); v:destroy(); k[v] = nil end 
    for k, v in pairs(self.shapes)   do v:destroy(); k[v] = nil end 
    self.body:setUserData(nil); self.body:destroy(); self.body = nil
end

function Collider:add_shape(type, name, ...)
    local _n, _a, _s = name or _uuid(), {...}
    if     type == "circle"    then _s = lp.newCircleShape(_a[1], _a[2], _a[3])
    elseif type == "rectangle" then _s = lp.newRectangleShape(_a[1], _a[2], _a[3], _a[4], _a[5])
    elseif type == "polygon"   then _s = lp.newPolygonShape(unpack(_a[1]))
    elseif type == "line"      then _s = lp.newEdgeShape(_a[1], _a[2], _a[3], _a[4])
    elseif type == "chain"     then _s = lp.newChainShape(_a[1], unpack(_a[2])) end
    self.fixtures[_n], self.shapes[_n] = lp.newFixture(self.body, _s), _s
end

-------------------------------
--<<(^__^)  (0___N)  (*__*)>>--
-------------------------------

local World = {}

function World:__call(xg,yg,sleep) 
    local obj = {}
        obj.box2d_world = lp.newWorld(xg,yg,sleep)
        obj.colliders   = {}
        obj.joints      = {}
        _set_funcs(obj, obj.box2d_world)
    return setmetatable(obj, {__index = World})
end

function World:draw() 
    local _r, _g, _b, _a = lg.getColor()
    -- Colliders --
    love.graphics.setColor(1, 1, 1)
    for k1,v1 in pairs(self:getBodies()) do for k2, v2 in pairs(v1:getFixtures()) do 
        if     v2:getShape():getType() == "circle"  then lg.circle("line", v1:getX(), v1:getY(), v2:getShape():getRadius())
        elseif v2:getShape():getType() == "polygon" then lg.polygon("line", v1:getWorldPoints(v2:getShape():getPoints()))
        else   local _p = {v1:getWorldPoints(v2:getShape():getPoints())}; for i=1, #_p, 2 do if i < #_p-2 then lg.line(_p[i], _p[i+1], _p[i+2], _p[i+3]) end end end
    end end
    -- Joints --
    love.graphics.setColor(1, 0.5, 0.25)
    for _, joint in ipairs(self.box2d_world:getJoints()) do
        local x1, y1, x2, y2 = joint:getAnchors()
        if x1 and y1 then love.graphics.circle('line', x1, y1, 6) end
        if x2 and y2 then love.graphics.circle('line', x2, y2, 6) end
    end
    love.graphics.setColor(_r, _g, _b, _a)
end

function World:add_circle(x, y, r, args)       local _c = Collider:new(self.box2d_world, "circ", x, y, r, args)        ; table.insert(self.colliders, _c); return _c end
function World:add_rectangle(x, y, w, h, args) local _c = Collider:new(self.box2d_world, "rect", x, y, w, h, args)     ; table.insert(self.colliders, _c); return _c end
function World:add_polygon(vertices, args)     local _c = Collider:new(self.box2d_world, "poly", vertices, args)       ; table.insert(self.colliders, _c); return _c end
function World:add_line(x1, y1, x2, y2, args)  local _c = Collider:new(self.box2d_world, "line", x1, y1, x2, y2, args) ; table.insert(self.colliders, _c); return _c end
function World:add_chain(vertices, loop, args) local _c = Collider:new(self.box2d_world, "chain", vertices, loop, args); table.insert(self.colliders, _c); return _c end
function World:add_joint(type, col1, col2, ...)
    if     type == "distance"  then local _j = lp.newDistanceJoint(col1.body, col2.body, ...) ; table.insert(self.joints, _j); return _j
    elseif type == "friction"  then local _j = lp.newFrictionJoint(col1.body, col2.body, ...) ; table.insert(self.joints, _j); return _j
    elseif type == "gear"      then local _j = lp.newGearJoint(col1.body, col2.body, ...)     ; table.insert(self.joints, _j); return _j
    elseif type == "motor"     then local _j = lp.newMotorJoint(col1, col2, ...)              ; table.insert(self.joints, _j); return _j
    elseif type == "mouse"     then local _j = lp.newMouseJoint(col1.body, col2.body, ...)    ; table.insert(self.joints, _j); return _j
    elseif type == "prismatic" then local _j = lp.newPrismaticJoint(col1.body, col2.body, ...); table.insert(self.joints, _j); return _j
    elseif type == "pulley"    then local _j = lp.newPulleyJoint(col1.body, col2.body, ...)   ; table.insert(self.joints, _j); return _j
    elseif type == "revolute"  then local _j = lp.newRevoluteJoint(col1.body, col2.body, ...) ; table.insert(self.joints, _j); return _j
    elseif type == "rope"      then local _j = lp.newRopeJoint(col1.body, col2.body, ...)     ; table.insert(self.joints, _j); return _j
    elseif type == "weld"      then local _j = lp.newWeldJoint(col1.body, col2.body, ...)     ; table.insert(self.joints, _j); return _j
    elseif type == "wheel"     then local _j = lp.newWheelJoint(col1.body, col2.body, ...)    ; table.insert(self.joints, _j); return _j end
end

return setmetatable({}, World)

Re: Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Tue Jan 08, 2019 1:24 pm
by TheHUG
Nicely done and thanks for sharing! Your implementation has some nice touches, I hope you don't mind if I poach a few of them? It also gave me some new ideas, so thanks again!

If you don't mind, could you state a couple ways in which the way breezefield is structured clashes with what is comfortable for you? I'd like it to be as intelligible and accessible as possible, so it really helps to have different perspectives.

Re: Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Tue Jan 08, 2019 6:22 pm
by 4vZEROv
do as you want with the code, i have a repo for it that i'll update to implement queries etc : https://github.com/4v0v/LOVE_PHYSICS

What i don't like is more or less only code structure:
-too much useless comments
-too much loc
-separate files
-separate mlib library for only 2 functions

Also fixture and body have each one their proper :getUserData() / :setUserData() methods that you overwrite when you call your set_funcs().
Anyway thanks for your library it gave me a good idea how to struture my code :)

Re: Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Wed Jan 09, 2019 9:56 am
by zorg
To be fair, those useless comments only bother you as a human, and not any application that runs the code;
besides, it may be useful for the person who coded the lib... like, i comment way more than what's in breezefield. :3

Re: Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Wed Jan 09, 2019 10:46 am
by 4vZEROv
I only stated what wasn't confortable for me :)

Re: Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Wed Jan 09, 2019 2:05 pm
by TheHUG
Thanks 4vZerov,
I'll definitely have a look at trimming the comments - I do tend to leave them lying around when they're no longer relevant. I'm keeping (and maybe adding more) docstrings though, if that's what you mean.

and yeah since I'm using so few of mlib's functions it is kind of overkill to install the whole thing, though I might need more of its functions once I implement more types of Colliders. I do plan to replace it with my own implementation eventually.

and good point about Collider:get/setUserData, I should make it explicit in the documentation that that is fixture:get/setUserData and then leave shape and body untouched in that respect.

Thanks for the feedback, i'll try and get round to incorporating it soon.

Re: Breezefield: a lightweight windfield alternative (love.physics wrapper)

Posted: Sat Jan 19, 2019 9:44 pm
by TheHUG
Hi,
I've pushed a couple changes, there are more collider types available now, I corrected get/setUserData and made some minor stylistic alterations (removing redundant comments, abbreviating core love libraries á la 4zerov).
Cheers, All