Page 9 of 15

Re: HUMP - yet another set of helpers

Posted: Fri Nov 02, 2012 12:10 pm
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?

Re: HUMP - yet another set of helpers

Posted: Fri Nov 02, 2012 12:44 pm
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)

Re: HUMP - yet another set of helpers

Posted: Fri Nov 02, 2012 12:54 pm
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.

Re: HUMP - yet another set of helpers

Posted: Fri Nov 02, 2012 3:24 pm
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 ;)

Re: HUMP - yet another set of helpers

Posted: Fri Nov 02, 2012 3:51 pm
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).

Re: HUMP - yet another set of helpers

Posted: Tue Nov 06, 2012 4:04 pm
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.

Re: HUMP - yet another set of helpers

Posted: Wed Feb 20, 2013 4:27 pm
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.

Re: HUMP - yet another set of helpers

Posted: Sat Feb 23, 2013 11:13 am
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}

Re: HUMP - yet another set of helpers

Posted: Mon Feb 25, 2013 3:12 pm
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)

Re: HUMP - yet another set of helpers

Posted: Mon Feb 25, 2013 5:57 pm
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?