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]
[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.