Mix() and Clamp() functions
So, I was working on my raymarcher-in-progress and I remembered some interesting functions that I thought would be good to add into microstudio! These would work similarly to the min() or max() functions.
1: Mix() function
This function would take 3 arguments, and would basically mix the first two values according to the third value. The code would look like this:
mix=function(x,y,a)
output=x*(1-a)+y*a // 'A' would be a value from 0-1. Also, please note that similarly to the min() function it would not actually
give a variable output
end
2: Clamp() function
This function would also take 3 arguments, and would basically clamp the first value between the second value. The code would look like this:
clamp=function(x,bottom,top) // Clamps x value between bottom and top value
output=max(x,bottom)
output=min(output,top)
if bottom>top then
// This would be an error
end
end
And then you could use it like this:
variable=clamp(variable,0,1)-mix(a,b,0.2)
I mean clamp already exists in mrLman's games-prog library.
As Crafter says. Quite easy to have a collection of helper functions tailored to your needs.
What would be useful would be if we could keep those snippets somewhere easily accessible.
Like a library tab, or something like that, where they are available project wide.
In FlowLab every user has his own 'Bundle' folder for that purpose, soooooo handy.
Another one I miss a lot is fmod
; I often reimplement it in many projects I make.
fmod = function(x,y)
x-floor(x/y)*y
end
I think clamp
and mix
would be nice additions as well. I wouldn't bother having an error case in clamp though, I would probably stick on the GLSL implementation: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/clamp.xhtml
P.S. your mix() looks like what is commonly known as lerp() function.
https://processing.org/reference/lerp_.html
@gilles ... trying to wrap my head around fmod() ...
What is the difference to the modulus operator? (x%y)
@TinkerSmith well first I called it fmod
which is misleading, I should call it simply mod
for clarity.
I just dug the subject a bit and found out the % operator in microScript, just like in JavaScript, operates like a remainder operator rather than a modulo operator. You will only see a difference when using the operators with negative values. Let's call the modulo operator mod
and the remainder operator rem
:
13 mod 10 = 3
13 rem 10 = 3 // same
but:
-13 mod 10 = 7
-13 rem 10 = -3
:embed https://microstudio.io/gilles/modulovsremainder/
Source: https://2ality.com/2019/08/remainder-vs-modulo.html
@tinkersmith
I think a library of these functions would be a great idea! Maybe in the documentation tab? We could also do a distance function and a dot product function.
Also, yes, something like the 'lerp' function would work perfectly.
I don't know about other people, but I find myself using a wrap
function quite a lot. Similar to clamp
but instead of keeping a value within limits, it wraps the value around.
wrap = function(value, min, max)
if value > max then return min end
if value < min then return max end
return value
end
Or in Lua:
function wrap(value, min, max)
return value > max and min or value < min and max or value
end
Although I could never decide if the first condition should be value > max
or value >= max
.
EDIT: a few days ago I noticed this wrap function doesn't do it properly. For example, given value v=29
, calling wrap(v+5, 10, 30)
returns 10
, but it should actually return 14
. That function will only work for increments of 1.
Some I found that I often need:
dist2d=function(x1,y1,x2,y2) sqrt((x2-x1)^2+(y2-y1)^2) end
dist3d=function(x1,y1,x2,y2,z1,z2) sqrt((x2-x1)^2+(y2-y1)^2+(z2-z1)^2) end // fixed (thanks Gilles)
angle2d=function(x1,y1,x2,y2) 180-atan2d(x2-x1,y2-y1) end
rand=function(lo,hi) (hi-lo)*random.next()+lo end
lerp=function(v1,v2,amt) (1-amt)*v1+amt*v2 end
clamp=function(v,lo,hi) max(lo,min(v,hi)) end
sgn=function(v) (v>0)-(v<0) end
wrap=function(v,lo,hi)
local r=hi-lo
return (lo+((((v-lo)%r)+r)%r))
end
remap=function(v,s1,e1,s2,e2) (v-s1)*(e2-s2)/(e1-s1)+s2 end
rectsOverlap=function(x1,y1,w1,h1,x2,y2,w2,h2)
not (x2>x1+w1 or x2+w2<x1 or y2>(y1+h1) or (y2+h2)<y1)
end
circlesOverlap=function(x1,y1,r1,x2,y2,r2) sqrt((x2-x1)^2+(y2-y1)^2)<abs(r2+r1) end
See also my post here if you would like to contribute other handy functions:
https://microstudio.dev/community/tips/useful-functions---please-help-to-build-a-list/207/
@JimB007 I just noticed a typo in the dist3d:
dist3d=function(x1,y1,x2,y2,z1,z2) sqrt((x2-x1)^2+(y2-y1)^2+(z2-z1)) end
should be:
dist3d=function(x1,y1,x2,y2,z1,z2) sqrt((x2-x1)^2+(y2-y1)^2+(z2-z1)^2) end
If you include a dist2d and dist3d, you risk distracting newbies from an easy efficiency lesson:
When calculating collisions between circle (or sphere) colliders, it's tempting to do something like this (pseudocode)
if distance_between(centre_of_a, centre_of_b) < (radius_of_a + radius_of_b)
then collision
else no collision
... but that involves a square root. Better then to do something like this:
if square_of_distance_between(centre_of_a, centre_of_b) < ((radius_of_a + radius_of_b) * (radius_of_a + radius_of_b))
then collision
else no collision
IIRC Unity has a square distance method for precisely this reason, and it's just the same as the distance function, but without the final square root operation.
If you're adding in a distance function, also adding the square distance function would actively push awareness of this sort of optimisation to new coders.
I note also that the "circlesOverlap" function can be optimised the same way:
circlesOverlap=function(x1,y1,r1,x2,y2,r2) ((x2-x1)^2+(y2-y1)^2)<(r2+r1)*(r2+r1) end
No need to calculate a square root this way, and it can save a surprising number of cycles when you've got a lot of collisions to check.
Wouldn't lerp be a better name than mix to avoid confusion? From my experience, it's a more common name for that kind of function than mix. Also maybe the circlesoverlap should be replaced with an ellipsesoverlap that has width and height instead of radius, it would be far more versatile.
@JmeJuniper, collisions between circles are different than between ellipses, though. You'd be making circle collision checking heavier than it needs to be. Plus, anyone only interested in circle collisions would have to provide more parameters to that function than necessary.
In terms of implementation, circles and ellipses are quite different, so it's probably a better idea to keep them separate.
@JmeJuniper agreed, lerp is better...
@JimBOO7 proposed addition to your handy functions
angletovector = function(ang)
return [sind(ang), cosd(ang)] // obviously returning a Vec2 object would be best here
end
Maybe documentation of vector classes would be a good idea as well?
@MyNameIsNotSir Good point! By the way, you can do enclosed text like this and above
using triple backquotes - just to make the code more readable :-)