HUMP - yet another set of helpers

Showcase your libraries, tools and other projects that help your fellow love users.
User avatar
Przemator
Party member
Posts: 107
Joined: Fri Sep 28, 2012 6:59 pm

Re: HUMP - yet another set of helpers

Post by Przemator »

Hi vrld! First of all, thanks for creating HUMP. I like your sense of humor (hump, hardon, etc.) :ultraglee:

When looking at the source code, I only see this:

Code: Select all

function vector.dist(a, b)
	assert(isvector(a) and isvector(b), "dist: wrong argument types (<vector> expected)")
	local dx = a.x - b.x
	local dy = a.y - b.y
	return sqrt(dx * dx + dy * dy)
end
How does lua know that vector(a):dist(b) refers to this function?
User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: HUMP - yet another set of helpers

Post by Roland_Yonaba »

Well, that's a little trick:

Calling a method on in table using colon passes the table itself as the first argument.

Code: Select all

t = {}
function t.foo(arg1,arg2) print(arg1,arg2) end
t:foo() -- table: 003AB5F8, nil
t:foo(2) -- table: 003AB5F8, 2
-- 003AB5F8 is the memory location address for table t
In Hump.vector, calling (a):dist(b) will pass vector(a) itself as the first argument to method dist.
Then (a):dist(b) will result in:

Code: Select all

a.dist(a,b)
User avatar
Przemator
Party member
Posts: 107
Joined: Fri Sep 28, 2012 6:59 pm

Re: HUMP - yet another set of helpers

Post by Przemator »

@Roland: yes, it did help. However, it is extremely hard to learn how it works ;).

only makes me wonder why was the vector.dist(a, b) implemented in the code, and not directly vector:dist(b). the vector.dist function cannot be used anyway.

if I wanted to make this work, would i have to write dist = dist in the return statement?


EDIT: BTW I have a very CPU-intensive function for calculating the distance between a point and a curve. It is run for every segment of the curve, which has about 1000 segments, in every love.update, so the segment function is run 6000 times per second. I made 2 versions of it:

Code: Select all

function segdist(p, a, b)
	local l2 = (b - a) * (b - a)
	if l2 == 0 then return (p - a):len() end
	local t = (p - a) * (b - a) / l2
	if t < 0 then return (p - a):len() end
	if t > 1 then return (p - b):len() end
	local proj = a + (p - a):projectOn(b - a)
	return (p - proj):len()
end

function segdist2(ax, ay, bx, by, px, py)
	local l2 = (bx - ax) * (bx - ax) + (by - ay) * (by - ay)
	if l2 == 0 then return math.sqrt((px - ax) * (px - ax) + (py - ay) * (py - ay)) end
	local t = ((px - ax) * (bx - ax) + (py - ay) * (by - ay)) / l2
	if t < 0 then return math.sqrt((px - ax) * (px - ax) + (py - ay) * (py - ay)) end
	if t > 1 then return math.sqrt((px - bx) * (px - bx) + (py - by) * (py - by)) end
	local tx, ty = ax + t * (bx - ax), ay + t * (by - ay)
	return math.sqrt((px - tx) * (px - tx) + (py - ty) * (py - ty))
end
Using the function based on hump.vector, it takes 0.01 second to calculate (almost the whole dt!). The segdist2 takes 0.0007 second, so it is 15 times faster! So I guess the vector class is REALLY heavy.
User avatar
vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: HUMP - yet another set of helpers

Post by vrld »

Przemator wrote:@Roland: yes, it did help. However, it is extremely hard to learn how it works ;).
Be sure to check out the documentation: Every function has at least one example how to use it.
Przemator wrote:only makes me wonder why was the vector.dist(a, b) implemented in the code, and not directly vector:dist(b). the vector.dist function cannot be used anyway.
It is really just a renaming of the variables. vector:dist(b) is the same as writing vector.dist(self, b).
As to why you can't use the vector.dist(a,b), vector.clone(v), etc notation, see how the vector-table is defined and how the module is returned.

If you really want to, you can use this to get access to the vector table:

Code: Select all

vector = require 'hump.vector'
vector = getmetatable(vector(0,0))
Drawback: you need to use vector.new() instead of the shortcut notation:

Code: Select all

a = vector.new(10, 20)
b = vector.new(30, 40)
dst = vector.dist(a, b)
Przemator wrote:if I wanted to make this work, would i have to write dist = dist in the return statement?
Could you rephrase that question?
Przemator wrote:EDIT: BTW I have a very CPU-intensive function for calculating the distance between a point and a curve. It is run for every segment of the curve, which has about 1000 segments, in every love.update, so the segment function is run 6000 times per second. I made 2 versions of it:
[snip]
Using the function based on hump.vector, it takes 0.01 second to calculate (almost the whole dt!). The segdist2 takes 0.0007 second, so it is 15 times faster! So I guess the vector class is REALLY heavy.
Well, (nearly) each vector operation creates a new table, and while that's ok in most cases, it adds up if you do it very (very) often per frame. That's why there is hump.vector-light. ;)

But your comparison is a little bit unfair, as the second function is a bit more optimized than the first one. Also, the first variant creates about 9 vectors in the worst case, so thats about 9000 new tables per frame.
Here are three alternatives, one using vector and one using vector-light and one using neither (all untested):

Code: Select all

-- using hump.vector
function segdist(p, a, b) -- creates 3 vectors
    local pa, ba = p - a, b - a
    local l2 = ba:len2()
    if l2 == 0 then return pa:len() end -- or p:dist(a)

    local t = pa * ba / l2  -- basically already pa:projectOn(ba)
    if t < 0 then return pa:len() end
    if t > 1 then return p:dist(b) end

    local proj = a + ba * t / l2
    return p:dist(proj)
end

-- using hump.vector-light
function segdist(px, py, ax, ay, bx, by)
    local pax, pay = px - ax, py - ay
    local bax, bay = bx - ax, by - ay

    local l2 = vector.len2(bax, bay)
    if l2 == 0 then return vector.len(pax, pay) end -- or vector.dist(ax, ay, px, py)

    local t = vector.dot(pax, pay, bax, bay) / l2
    if t < 0 then return vector.len(pax, pay) end -- or vector.dist(ax, ay, px, py)
    if t > 1 then return vector.dist(bx, by, px, py) end

    local qx, qy = vector.add(ax,ay, vector.mul(bax, bay, t / l2))
    return vector.dist(px, py, qx, py)
end

-- using neither
function segdist(px, py, ax, ay, bx, by)
    local pax, pay = px - ax, py - ay
    local bax, bay = bx - ax, by - ay

    local l2 = bax*bax + bay*bay
    if l2 == 0 then return math.sqrt(pax*pax, pay*pay) end

    local t = (pax * bax + pay * bay) / l2
    if t < 0 then return math.sqrt(pax*pax + pay*pay) end
    if t > 1 then return math.sqrt((px-bx)^2 + (py-by)^2) end

    local qx, qy = ax + (bax * t / l2), ay + bay * t / l2
    return math.sqrt((px - qx)^2 + (py - qy)^2)
end
I am not sure whether the vector-light or no-library version is faster, but I think the former is more readable than the latter - which is also important ;)
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
User avatar
Przemator
Party member
Posts: 107
Joined: Fri Sep 28, 2012 6:59 pm

Re: HUMP - yet another set of helpers

Post by Przemator »

Wow that is a BIG reply, thanks ;).
vrld wrote:Could you rephrase that question?
I was referring to this line of vector.lua:

Code: Select all

return setmetatable({new = new, isvector = isvector, dist = dist},
But it doesn't matter now, I think I got it after you wrote that "vector:dist(b) is the same as writing vector.dist(self, b)".

BTW, that eye in your avatar reminds me of Charlie Sheen ;)

EDIT: I've got a question. Is it possible to somehow combine hump.class and hump.gamestate? I am using both and sometimes I would like one state to inherit methods from another. Like when I have a map editor, and there are different modes to add, edit, delete objects on the map, but all of them draw the same stuff (have the same draw method).
User avatar
vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: HUMP - yet another set of helpers

Post by vrld »

Sorry for the late answer, but better late than never :roll:
Przemator wrote:I've got a question. Is it possible to somehow combine hump.class and hump.gamestate? I am using both and sometimes I would like one state to inherit methods from another. Like when I have a map editor, and there are different modes to add, edit, delete objects on the map, but all of them draw the same stuff (have the same draw method).
Yes, it's possible, although a bit hackish (notice the dot instead of a colon):

Code: Select all

local class = require 'hump.class'
-- states.mapedit_common defines common drawing, etc
class.inherit(states.mapedit_add, states.mapedit_common)
class.inherit(states.mapedit_edit, states.mapedit_common)
class.inherit(states.mapedit_delete, states.mapedit_common)
That works because in class.inherit(child, base) simply copies everything from base to child, except the portions that are already present in child.
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
User avatar
vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: HUMP - yet another set of helpers

Post by vrld »

tl;dr: Leaner gamestate.lua. No more ringbuffer.lua. Rewrite of class.lua (now 1.598 times more useful!).

ntl;wr version:

I got rid of some stuff in gamestate.lua. Most notably, there is no more need to use Gamestate.new() - just use regular Lua tables instead. In fact, Gamestate.new() just returns an empty table.

ringbuffer.lua is not a part of hump any longer, because I almost never used it in any of my games. Worse, most of the time ringbuffer.lua would make stuff more complicated than just using a plain table. You can still get the module here if you need it.

class.lua got a complete makeover and is not compatible with the old implementation.
The main changes are:
  • Removed named classes (was a pointless and buggy feature anyway).
  • Removed `is_a()`.
  • Constructors must be named `init()`:

    Code: Select all

    foo = Class{function(self) ... end}
    becomes

    Code: Select all

    foo = Class{init = function(self)...end}
  • You can embed any functions or variables in the class definition:

    Code: Select all

    foo = Class{foo = function(self) ... end, bar = 10}
  • `inherits` becomes `__includes` and `class:inherit` becomes `class:include`:

    Code: Select all

    foo = Class{inherits = bar}
    foo = Class{}
    foo:inherit(bar)
    is now:

    Code: Select all

    foo = Class{__includes = bar}
    foo = Class{}
    foo:include(bar)
  • `class:inherit(other)` does a deep copy:

    Code: Select all

    foo = Class{a = {b = {1,2,3}}}
    bar = Class{__includes = foo}
    bar.a.b = 'c'
    print(bar.a.b, foo.a.b) -- prints: 'c    table: 0x......'
  • Added `class:clone()` to create a clone/deep copy of the class. Super useful for prototype based OO.
  • You can use `Class.include()` and `Class.clone()` on arbitrary tables, not just hump classes:

    Code: Select all

    a = {foo = {bar = 'baz'}}
    b = {one = 1}
    Class.include(b,a) -- include a in b
    print(b.foo.bar) -- prints 'baz'
    
    c = Class.clone(b)
    c.one = 10
    print(b.one, c.one) -- prints '1    10'
Of course hump.class still implements Class Commons.

You can read the updated documentation here. The code is hosted on github.
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
User avatar
Patalo
Prole
Posts: 41
Joined: Sun Apr 29, 2012 1:15 pm
Contact:

Re: HUMP - yet another set of helpers

Post by Patalo »

Great class update!

However, I think you missed an update in the doc. In the hump.class global description, you don't specify the "init" function.

Code: Select all

Critter = Class{function(self, pos, img)
    self.pos = pos
    self.img = img
end}
Current project : 3D fractal explorer DEFract ( viewtopic.php?t=9193)
Github : https://github.com/plule/ | Twitter : http://twitter.com/plule_
Frohman
Prole
Posts: 21
Joined: Sat Dec 08, 2012 12:32 pm

Re: HUMP - yet another set of helpers

Post by Frohman »

Hey vrld, I just wanted to point out a little chain of issues I was having at viewtopic.php?f=4&t=18882 since they ended up being caused by some kind of incompatibility between LUBE and the latest revision of hump.class

I'm not sure which library is at fault, all I know is those issues I was having and that they stopped happening when I swapped out to a slightly older hump.class, which is odd since this new hump.class is still class commons compatible :S

Anyway, I hope the issues I was having helps point out any bugs in either library!

Unrelated to that, (and using the latest class revision) I just wanted to know if I'm working through inheritance and such correctly:

Here's player.lua, which I require in main.lua and use to create a LocalPlayer object (Player is also used in a RemotePlayer class)

Code: Select all

local Player = Class{}
function Player:init(uid)
    self.uid = uid
    self.pos = Vector()
    self.vel = Vector()
    self.acc = Vector()
    self.dir = 0
    self.scale = 3
end

function Player:update(dt)
    self.vel = self.vel + self.acc*dt
    self.pos = self.pos + self.vel*dt
end

function Player:draw(img)
    love.graphics.draw(img.img, self.pos.x, self.pos.y, self.dir, self.scale, self.scale, img.w / 2, img.h / 2)
end

LocalPlayer = Class{}
LocalPlayer:include(Player)

function LocalPlayer:init(uid, pos)
    Player.init(self, uid)
    self.pos = pos
    self.fwd = Vector(1, 0)
end

function LocalPlayer:update(dt)
	local displace = Vector(love.mouse.getPosition()) - self.pos
	self.fwd = displace:normalize_inplace()

	self.dir = math.atan2(self.fwd.y, self.fwd.x)
	if love.mouse.isDown("r") then
		self.acc = self.fwd*150
	else
	 	self.acc = Vector()
	end
	Player:update(dt)
end
Now in main.lua I can create a player object and use it just fine (I can call its update function with controls and such, as well as use its parent's inherited draw function and so on,
But what I'd also like to find out is how I can add one of these objects to a table like so:
universe[uid] = LocalPlayer(uid, Vector(x, y))
(doing it this way, none of the fields seem to be initiilised)
User avatar
vrld
Party member
Posts: 917
Joined: Sun Apr 04, 2010 9:14 pm
Location: Germany
Contact:

Re: HUMP - yet another set of helpers

Post by vrld »

Patalo wrote:In the hump.class global description, you don't specify the "init" function.
Thanks, I missed that!
Frohman wrote:(issues...) which is odd since this new hump.class is still class commons compatible :S
Well, this is embarrassing: I tested class commons compatibility with the old version of hump.class. So it's definitely hump's/my fault, not LUBE's.
The update I just pushed should resolve the issues. Thanks for reporting!
Frohman wrote:Unrelated to that, (and using the latest class revision) I just wanted to know if I'm working through inheritance and such correctly:
(code)
Everything looks fine to me, but I am not sure I understand the issue: If you objects of `LocalPlayer` to a table, then the object won't be initialized?
In other words, after `universe[uid] = LocalPlayer(uid, vector(x,y))`, the field `universe[uid]` is an empty table?
I have come here to chew bubblegum and kick ass... and I'm all out of bubblegum.

hump | HC | SUIT | moonshine
Post Reply

Who is online

Users browsing this forum: No registered users and 189 guests