• UtilX -- Extending GLua -- Need community input
    371 replies, posted
This is only really useful for functions that recurse deeply otherwise calculating the function is always faster (and less memory-intensive) than looking it up in a table not the 80s anymore :)
[QUOTE=TomyLobo;17270978]This is only really useful for functions that recurse deeply otherwise calculating the function is always faster (and less memory-intensive) than looking it up in a table not the 80s anymore :)[/QUOTE] Actually, it depends on what you're doing. [lua] local a, b, i, t t = { } a = os.clock( ) for i = 0, 500000 do if t[ i ] then t[ i ] = t[ i ] else t[ i ] = math.sin( i ) end end a = os.clock( ) - a collectgarbage( "collect", 2 ^ 32 ) b = os.clock( ) for i = 0, 500000 do math.sin( i ) end b = os.clock( ) - b if a < b then print( "B is " .. math.floor( ( b - a ) / a * 100 ) .. "% slower than A ( ~" .. math.floor( ( b - a ) * 1000 ) .. "ms )" ) else print( "A is " .. math.floor( ( a - b ) / b * 100 ) .. "% slower than B ( ~" .. math.floor( ( a - b ) * 1000 ) .. "ms )" ) end [/lua] [code] A is 32% slower than B ( ~141ms ) [/code] That's about average for me. I probably screwed up the percentage, but the time still stands. It should be worth noting, however, that if I duplicate the line b to read like so: [lua] for i = 0, 500000 do math.sin( i ) math.sin( i ) end [/lua] It's a whole different story. [code]B is 32% slower than A ( ~184ms )[/code] That's around what it usually spits out. And I'm not doing anything with it! I'm just running the math function twice! Of course, this is an extreme example - nobody should be calling math.sin 500,000 times without a really good reason. But if you call a function like that at least twice, it becomes a great deal less expensive to cache it than to call it multiple times. Three times: B is 89% slower than A ( ~520ms ) Four times: B is 146% slower than A ( ~844ms ) Five times: B is 203% slower than A ( ~1174ms ) And those are all from a cold start. If you cache the value, the overall performance gain ( however negligible to the situation ) will be far greater than if you were to solve for it each time you need to access the function. Deco's right if you plan to call the function more than once ever.
that is because you look up math.sin every time local sin = math.sin and according code changes should fix that.
[QUOTE=TomyLobo;17294170]that is because you look up math.sin every time local sin = math.sin and according code changes should fix that.[/QUOTE] You are still wrong. Resulting output: [code] 1 sin: A is 34% slower than B ( ~142ms ) 2 sin: B is 31% slower than A ( ~173ms ) 3 sin: B is 87% slower than A ( ~479ms ) 4 sin: B is 144% slower than A ( ~786ms ) 5 sin: B is 200% slower than A ( ~1095ms ) [/code] However, if I take the cache feature and make a function call out of it ( eg, sin would become a lookup function ), it becomes stupidly expensive and worthless and you become correct. If I use a lookup table, though, and just manually index it myself, after one call it becomes faster, and for each call from there on out it becomes faster and faster just to index the table, as you can see in my code. It does become somewhat expensive to store all that data, though - my example should be using close to 500mb of memory ( given that an integer key and float value are 4 bytes each, making a grand total of 1kb per pair ), but, then again, this isn't the eighties and this is a rather extreme example
So....ignoring any optimizations and such, do we see a window for release of this?
[lua]local meta = FindMetaTable( "Player" ) if ( !meta ) then return end function meta:IsBot() return table.HasValue( player.GetBots(), self ) end[/lua] Always annoyed me there wasn't a function for this.
[QUOTE=Dlaor;17318119][lua]local meta = FindMetaTable( "Player" ) if ( !meta ) then return end function meta:IsBot() return table.HasValue( player.GetBots(), self ) end[/lua] Always annoyed me there wasn't a function for this.[/QUOTE] [url]http://wiki.garrysmod.com/?title=Player.IsBot[/url]
[QUOTE=The-Stone;17318169][url]http://wiki.garrysmod.com/?title=Player.IsBot[/url][/QUOTE] Fuck.
[lua] function table.WeightedRandom(tbl,weights) local selectTbl = {} for k,v in pairs(tbl) do for i=1,weights[v] do table.insert(selectTbl,v) end end return table.Random(selectTbl) end [/lua] Just made it for my GCC entry.
dunno if this is useful to anyone at all but i made just because i felt like it. [lua] function CircleArea(r) local a = 3.14159265358979323846264338327950288419716939937510 * r //50 decimal places return a*a end [/lua] I'm sure there was a function for pi but i cant seem to find it.
[QUOTE=Carnag3;17385597]dunno if this is useful to anyone at all but i made just because i felt like it. [lua] function CircleRadius(r) local a = 3.14159265358979323846264338327950288419716939937510 * r //50 decimal places return a*a end [/lua] I'm sure there was a function for pi but i cant seem to find it.[/QUOTE] You got it completely wrong. 1. You're trying to calculate the area. Obviously there's no need to calculate the radius if you're already specifying it. 2. Your formula is wrong. [lua]function utilx.CircleArea( r ) return math.pi * r^2; end[/lua]
[QUOTE=Carnag3;17385597] I'm sure there was a function for pi but i cant seem to find it.[/QUOTE] [url]http://www.lua.org/manual/5.1/manual.html#pdf-math.pi[/url]
[QUOTE=Jawalt;17085624] [lua]function meta:CanSee( ent ) toscr = ent:EyePos():ToScreen() if toscr.visible then local trace = { } trace.start = self:EyePos( ); trace.endpos = ent:EyePos( ); trace.filter = player.GetAll() local tr = util.TraceLine( trace ); if !tr.Hit then return true end return false; end return false end [/lua] Line of sight code, this will only do a trace only if the player is in the other players field of view. You'd probably want an OPAQUE mask on this.[/QUOTE] That would have more uses if it included distance as an output ;)
[QUOTE=MadDog986;17431259]That would have more uses if it included distance as an output ;)[/QUOTE] [lua] Vector1 = Vector(0,0,0) Vector2 = Vector(50,10,70) Distance = Vector1:Distance(Vector2) [/lua] :eng101:
[QUOTE=MadDog986;17431259]That would have more uses if it included distance as an output ;)[/QUOTE] That's pretty easy to add yourself...
[QUOTE=Jawalt;17431605]That's pretty easy to add yourself...[/QUOTE] Yes i know, just pointing out it can be more useful if it outputs distance. Edit: There is also a "Entity:Visible" command that pretty much replaces this. Thats why i pointed out that it would be more useful if it included distance. [url]http://wiki.garrysmod.com/?title=Entity.Visible[/url] Edit Again: (not tested) [lua]local meta = FindMetaTable("Entity") if (meta) then function meta:CanSee( ent ) if (self:Visible(ent)) then return self:GetPos():Distance( ent:GetPos() ) else return false end end end[/lua]
(blob202 = TylerP9P, can't be arsed to make a new Facepunch at this time) Here are some math functions, mostly interpolation, etc. from my game engine's math library. I did a quick and dirty conversion to Lua, so they may not be exact, and I am indeed aware some of them already exist in the Garry's Mod library; I am posting them all for continuity. [lua] -- Interpolation -- Performs a linear interpolation between start and end with the factor amount function utilx.Lerp(start, endval, amount) return ((1.0 - amount) * start) + (amount * endval) end -- Performs a Hermite interpolation (linear with eased inner and outer limits) between start and end with the factor amount function utilx.Hermite(start, endval, amount) return utilx.Lerp(start, endval, amount * amount * (3.0 - 2.0 * amount)) end -- Performs a sinusoidal interpolation, while easing around the end (when the return value approaches 1) function utilx.Sinerp(start, endval, amount) return utilx.Lerp(start, endval, math.sin(amount * math.pi * 0.5)) end -- Performs a cosinusoidal interpolation, while easing around the start (when the return value approaches 0) function utilx.Coserp(start, endval, amount) return utilx.Lerp(start, endval, 1.0 - math.cos(amount * math.pi * 0.5)) end -- Performs a boing-like interpolation, where the end value is initially overshot, and the return value bounces back and fourth the end value before coming to rest function utilx.Berp(start, endval, amount) amount = utilx.Clamp(amount, 0.0, 1.0) amount = (math.sin(amount * math.pi * (0.2 + 2.5 * (amount ^ 3))) * math.pow(1.0 - amount, 2.2) + amount) * (1.0 + (1.2 * (1.0 - amount))) return start + (endval - start) * amount end -- Performs a linear interpolation, but eases the values function utilx.SmoothStep(x, min, max) x = Clamp(x, min, max) v1 = (x-min)/(max-min) v2 = (x-min)/(max-min) return -2*v1 * v1 *v1 + 3*v2 * v2 end -- Performs an approach from the old value to the new value function utilx.Curve(newvalue, oldvalue, increments) if (increments > 1) then oldvalue = oldvalue - (oldvalue - newvalue) / increments end if (increments <= 1) then oldvalue = newvalue end return oldvalue end -- Returns a value between 0 and 1 as if a ball was bouncing and moving X units function utilx.Bounce(x) return math.abs(math.sin(6.28*(x+1.0)*(x+1.0)) * (1.0-x)) end -- Performs a linear interpolation but with respect to circular limits (0 - 360 degrees) -- NOTE: start and end are expected in units of Degrees (0 - 360) function utilx.Clerp(start, endval, amount) min = 0.0; max = 360.0; half = math.abs((max - min)/2.0) retval = 0.0; diff = 0.0; if((endval - start) < -half) then diff = ((max - start)+endval)*amount retval = start+diff else if((endval - start) > half) then diff = -((max - endval)+start)*amount retval = start+diff else retval = start+(endval-start)*amount end return retval end -- Performs a positive cumulative approach from current to target using the absolute value of the indicated increment function utilx.Approach(current, target, increment) increment = math.abs( increment ) if (current < target) then return utilx.Clamp( current + increment, current, target ) else if (current > target) then return utilx.Clamp( current - increment, target, current ) end return target end -- Prevention / Precision -- Clamps the value between the minimum and maximum limits function utilx.Clamp(value, min, max) if (value < min) then value = min else if (value > max) then value = max end return value end -- Calculation function utilx.Average(values) total = 0 for i,v in pairs(values) do total = total + values[i] end return (total / #values) end function utilx.StandardDeviation(values) avg = 0 totaldev = 0 avg = utilx.Average(values); for i,v in ipairs(values) do totaldev = totaldev + math.pow(values[i] - avg, 2); end return math.sqrt(totaldev / #values); end -- Comparison -- Determines whether the difference between the value and target is less than the permitted error function utilx.Approximate(value, target, errorval) return ( ( math.abs(value - target) < errorval) ) end function utilx.CompareFloat(a, b, tolerance) if ( ( a + tolerance ) < b ) return -1 if ( ( b + tolerance ) < a ) return 1 return 0 end [/lua]
This is a function to do a LineOfSight test between a player and an entity, other player, etc. Yes, I am aware that there are simple visibility test commands available, but the beauty of this function is that it lets you simulate a view range and view angle. I believe the best application for such a function would be Lua-based AI, for a bot or such. Hopefully someone(s) can find it useful. [lua] local meta = FindMetaTable("Player") if (meta) then function meta:LineOfSight( ent, viewrange, viewangle ) local plypos = self:GetPos() -- Our position local targetpos = ent:GetPos() -- Target Entity's Position local dist = self:GetPos():Distance( ent:GetPos() ) -- Distance between us if (dist > viewrange) then return false end -- If outside our view range, return false -- Pick Vector local dx = (plypos.X - targetpos.X) / dist local dy = (plypos.Y - targetpos.Y) / dist local dz = (plypos.Z - targetpos.Z) / dist local eyevec = self:EyeAngles():Forward():GetNormalized() local dot = (eyevec.X*dx) + (eyevec.Y*dy) + (eyevec.Z*dz) -- Check if target is outside our view angle if (dot < math.cos(viewangle/2.0)) then return false end local trace = self:GetEyeTrace() if (!trace.Hit) then return false end -- Out of eye view range (engine specified) if (trace.HitWorld) then return false end -- The world is between us and our target if (trace.HitNonWorld and IsValid(trace.Entity) and trace.Entity == ent) then return true end -- We hit our entity! return false -- Not visible, sorry end end [/lua]
[QUOTE=blob202;17507523](blob202 = TylerP9P, can't be arsed to make a new Facepunch at this time) Here are some math functions, mostly interpolation, etc. from my game engine's math library. I did a quick and dirty conversion to Lua, so they may not be exact, and I am indeed aware some of them already exist in the Garry's Mod library; I am posting them all for continuity. [lua] -- Interpolation -- Performs a linear interpolation between start and end with the factor amount function utilx.Lerp(start, endval, amount) return ((1.0 - amount) * start) + (amount * endval) end -- Performs a Hermite interpolation (linear with eased inner and outer limits) between start and end with the factor amount function utilx.Hermite(start, endval, amount) return utilx.Lerp(start, endval, amount * amount * (3.0 - 2.0 * amount)) end -- Performs a sinusoidal interpolation, while easing around the end (when the return value approaches 1) function utilx.Sinerp(start, endval, amount) return utilx.Lerp(start, endval, math.sin(amount * math.pi * 0.5)) end -- Performs a cosinusoidal interpolation, while easing around the start (when the return value approaches 0) function utilx.Coserp(start, endval, amount) return utilx.Lerp(start, endval, 1.0 - math.cos(amount * math.pi * 0.5)) end -- Performs a boing-like interpolation, where the end value is initially overshot, and the return value bounces back and fourth the end value before coming to rest function utilx.Berp(start, endval, amount) amount = utilx.Clamp(amount, 0.0, 1.0) amount = (math.sin(amount * math.pi * (0.2 + 2.5 * (amount ^ 3))) * math.pow(1.0 - amount, 2.2) + amount) * (1.0 + (1.2 * (1.0 - amount))) return start + (endval - start) * amount end -- Performs a linear interpolation, but eases the values function utilx.SmoothStep(x, min, max) x = Clamp(x, min, max) v1 = (x-min)/(max-min) v2 = (x-min)/(max-min) return -2*v1 * v1 *v1 + 3*v2 * v2 end -- Performs an approach from the old value to the new value function utilx.Curve(newvalue, oldvalue, increments) if (increments > 1) then oldvalue = oldvalue - (oldvalue - newvalue) / increments end if (increments <= 1) then oldvalue = newvalue end return oldvalue end -- Returns a value between 0 and 1 as if a ball was bouncing and moving X units function utilx.Bounce(x) return math.abs(math.sin(6.28*(x+1.0)*(x+1.0)) * (1.0-x)) end -- Performs a linear interpolation but with respect to circular limits (0 - 360 degrees) -- NOTE: start and end are expected in units of Degrees (0 - 360) function utilx.Clerp(start, endval, amount) min = 0.0; max = 360.0; half = math.abs((max - min)/2.0) retval = 0.0; diff = 0.0; if((endval - start) < -half) then diff = ((max - start)+endval)*amount retval = start+diff else if((endval - start) > half) then diff = -((max - endval)+start)*amount retval = start+diff else retval = start+(endval-start)*amount end return retval end -- Performs a positive cumulative approach from current to target using the absolute value of the indicated increment function utilx.Approach(current, target, increment) increment = math.abs( increment ) if (current < target) then return utilx.Clamp( current + increment, current, target ) else if (current > target) then return utilx.Clamp( current - increment, target, current ) end return target end -- Prevention / Precision -- Clamps the value between the minimum and maximum limits function utilx.Clamp(value, min, max) if (value < min) then value = min else if (value > max) then value = max end return value end -- Calculation function utilx.Average(values) total = 0 for i,v in pairs(values) do total = total + values[i] end return (total / #values) end function utilx.StandardDeviation(values) avg = 0 totaldev = 0 avg = utilx.Average(values); for i,v in ipairs(values) do totaldev = totaldev + math.pow(values[i] - avg, 2); end return math.sqrt(totaldev / #values); end -- Comparison -- Determines whether the difference between the value and target is less than the permitted error function utilx.Approximate(value, target, errorval) return ( ( math.abs(value - target) < errorval) ) end function utilx.CompareFloat(a, b, tolerance) if ( ( a + tolerance ) < b ) return -1 if ( ( b + tolerance ) < a ) return 1 return 0 end [/lua][/QUOTE] [url=http://wiki.garrysmod.com/?title=Math.Approach]Approach[/url] and [url=http://wiki.garrysmod.com/?title=G.Lerp]Lerp[/url] already exist in gmod lua. [editline]12:49PM[/editline] And so does [url=http://wiki.garrysmod.com/?title=Math.Clamp]Clamp[/url]
"I am indeed aware some of them already exist in the Garry's Mod library; I am posting them all for continuity." *cough* I knew that already. I just like to keep things consistent.
Agree'd. It makes for easier understanding of the code
[QUOTE=blob202;17513438]This is a function to do a LineOfSight test between a player and an entity, other player, etc. Yes, I am aware that there are simple visibility test commands available, but the beauty of this function is that it lets you simulate a view range and view angle. I believe the best application for such a function would be Lua-based AI, for a bot or such. Hopefully someone(s) can find it useful. [lua] local meta = FindMetaTable("Player") if (meta) then function meta:LineOfSight( ent, viewrange, viewangle ) local plypos = self:GetPos() -- Our position local targetpos = ent:GetPos() -- Target Entity's Position local dist = self:GetPos():Distance( ent:GetPos() ) -- Distance between us if (dist > viewrange) then return false end -- If outside our view range, return false -- Pick Vector local dx = (plypos.X - targetpos.X) / dist local dy = (plypos.X - targetpos.X) / dist local dz = (plypos.X - targetpos.X) / dist local eyevec = self:EyeAngles():GetNormalized() local dot = (eyevec.X*dx) + (eyevec.Y*dy) + (eyevec.Z*dz) -- Check if target is outside our view angle if (dot < math.cos(viewangle/2.0) then return false end local trace = self:GetEyeTrace() if (!trace.Hit) then return false end -- Out of eye view range (engine specified) if (trace.HitWorld) then return false end -- The world is between us and our target if (trace.HitNonWorld and IsValid(trace.Entity) and trace.Entity == ent) then return true end -- We hit our entity! return false -- Not visible, sorry end end [/lua][/QUOTE] I get and error about "attempt to call method 'GetNormalized' (a nil value)". And line 22 is missing a ')'. Also are dy, and dz supposed to use plypos.X and targetpos.X?
[lua]local Entity = FindMetaTable("Entity") function Entity:IsInCone(p1,p2,Radius) local dist = p1:Distance(p2) local v1 = p2-p1 v1:Normalize() local v2 = self:GetPos()-p1 v2:Normalize() return (v1:Dot(v2) >= (math.atan2(Radius,dist)*2)) end function ents.FindInConeFix(p1,p2,Radius) local tbl = {} local dist = p1:Distance(p2) local v1 = p2-p1 v1:Normalize() for k,v in ipairs(ents.GetAll()) do local v2 = v:GetPos()-p1 v2:Normalize() if v1:Dot(v2) >= (math.atan2(Radius,dist)*2) then table.insert(tbl,v) end end return tbl end function player.FindInCone(p1,p2,Radius) local tbl = {} local dist = p1:Distance(p2) local v1 = p2-p1 v1:Normalize() for k,v in ipairs(player.GetAll()) do local v2 = v:GetPos()-p1 v2:Normalize() if v1:Dot(v2) >= (math.atan2(Radius,dist)*2) then table.insert(tbl,v) end end return tbl end [/lua] Since ents.FindInCone doesn't seem to work.
Thank you Levy, those will be very useful.
Here is the fixed code for the LineofSight metafunction: [lua] local meta = FindMetaTable("Player") if (meta) then function meta:LineOfSight( ent, viewrange, viewangle ) local plypos = self:GetPos() -- Our position local targetpos = ent:GetPos() -- Target Entity's Position local dist = self:GetPos():Distance( ent:GetPos() ) -- Distance between us if (dist > viewrange) then return false end -- If outside our view range, return false -- Pick Vector local dx = (plypos.X - targetpos.X) / dist local dy = (plypos.Y - targetpos.Y) / dist local dz = (plypos.Z - targetpos.Z) / dist local eyevec = self:EyeAngles():Forward():GetNormalized() local dot = (eyevec.X*dx) + (eyevec.Y*dy) + (eyevec.Z*dz) -- Check if target is outside our view angle if (dot < math.cos(viewangle/2.0)) then return false end local trace = self:GetEyeTrace() if (!trace.Hit) then return false end -- Out of eye view range (engine specified) if (trace.HitWorld) then return false end -- The world is between us and our target if (trace.HitNonWorld and IsValid(trace.Entity) and trace.Entity == ent) then return true end -- We hit our entity! return false -- Not visible, sorry end end [/lua] My fault for not attempting a test of the function first.
That function won't work unless you're looking straight at the target.
[QUOTE=Jawalt;17524539]That function won't work unless you're looking straight at the target.[/QUOTE] If you're looking for a LOS check, use a combination of my FindInCone function and [url=http://wiki.garrysmod.com/?title=Entity.Visible]Visible[/url]
This is the (quite optimised) FindInCone function from expression2 (converted to fit lua): [lua]function utilx.filterList(list, criterion) local index = 1 while index <= #list do if not criterion(list[index]) then list[index] = list[#list] table.remove(list) else index = index + 1 end end return list end function ents.FindInConeE2(position, direction, length, angleRadians) direction:Normalize() local findlist = ents.FindInSphere(position, length) local cosAngleRadians = math.cos(cosAngleRadians) local Dot = direction.Dot -- apply the cone filter. utilx.filterList(findlist, function(ent) return Dot(direction, (ent:GetPos() - position):Normalize()) > cosAngleRadians end) return findlist end[/lua] argument order might be wrong, but that is trivial to fix. also included is filterList, also from E2. all written by me btw, so you can have it :)
My function has a view angle, so you have a cone of vision...
[QUOTE=blob202;17560251]My function has a view angle, so you have a cone of vision...[/QUOTE] That's the same the difference between our functions is: mine finds, yours checks if an ent is in that cone. (if I didn't misread that) mine was more a reply to Levybreak's unoptimised FindInCone function.
Sorry, you need to Log In to post a reply to this thread.