Discord
Login
Community
DARK THEME

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 :-)

Post a reply

Progress

Status

Preview
Cancel
Post
Validate your e-mail address to participate in the community