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

Showcase your libraries, tools and other projects that help your fellow love users.
TheHUG
Citizen
Posts: 62
Joined: Sun Apr 01, 2018 4:21 pm

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

Post 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.
User avatar
yetneverdone
Party member
Posts: 448
Joined: Sat Sep 24, 2016 11:20 am
Contact:

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

Post by yetneverdone »

Try also adding this to awesome-love2d list on github
TheHUG
Citizen
Posts: 62
Joined: Sun Apr 01, 2018 4:21 pm

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

Post by TheHUG »

Hey thanks! I'll do that right away!
User avatar
4vZEROv
Party member
Posts: 126
Joined: Wed Jan 02, 2019 8:44 pm

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

Post 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)
TheHUG
Citizen
Posts: 62
Joined: Sun Apr 01, 2018 4:21 pm

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

Post 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.
User avatar
4vZEROv
Party member
Posts: 126
Joined: Wed Jan 02, 2019 8:44 pm

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

Post 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 :)
User avatar
zorg
Party member
Posts: 3468
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

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

Post 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
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
4vZEROv
Party member
Posts: 126
Joined: Wed Jan 02, 2019 8:44 pm

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

Post by 4vZEROv »

I only stated what wasn't confortable for me :)
TheHUG
Citizen
Posts: 62
Joined: Sun Apr 01, 2018 4:21 pm

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

Post 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.
TheHUG
Citizen
Posts: 62
Joined: Sun Apr 01, 2018 4:21 pm

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

Post 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
Post Reply

Who is online

Users browsing this forum: No registered users and 5 guests