As usual, I was blown away by Robert Hodgin’s presentation at FiTC, and decided to get back into Processing again. I picked up the official Ben and Casey book at the conference book table and was reading it on the way back on the plane. Three methods described in the book blew me away with their simplicity: norm(), lerp(), and map().
norm(value, min, max) takes a value within a given range and converts it to a number between 0 and 1 (actually it can be outside that range if the original value is outside its range.
lerp(min, max, value) is linear interpolation. It takes a normalized value and a range and returns the actual value for the interpolated value in that range.
map(value, min1, max1, min2, max2) takes a value in a given range (min1, max1) and finds the corresonding value in the next range(min2, max2).
These are built-in functions in processing, but it didn’t take more than 3 minutes to implement these in ActionScript:
[as]package com.bit101.utils
{
public class NumberUtils
{
public static function normalize(value:Number, minimum:Number, maximum:Number):Number
{
return (value – minimum) / (maximum – minimum);
}
public static function interpolate(normValue:Number, minimum:Number, maximum:Number):Number
{
return minimum + (maximum – minimum) * normValue;
}
public static function map(value:Number, min1:Number, max1:Number, min2:Number, max2:Number):Number
{
return interpolate( normalize(value, min1, max1), min2, max2);
}
}
}[/as]
The immediate use I thought for these, especially mapping, is in making a slider or a scrollbar. You need to map the position of the slider button within its own min/max sliding area to the value that represents given the minimum and maximum values of the slider. And vice versa – if you assign it a value, you need to map that value to the position the handle should be at. I’ve done that lots of times, but it was always a bit messy. Just seeing the name of the function, “map” and its parameters, got me to see the problem in a new light. Now, getting the value of a slider becomes a one line operation:
[as]value = NumberUtils.map(handle.y, height – handle.height, 0, minimum, maximum);[/as]
very useful, indeed. I’ve added a “constraint” switch that in some situations is important; it means that if the value falls outside the range, it sticks to the range limit.
let me explain: in flash we have parenting of displayObjects. Some times it’s interesting to do not remove a listener when the mouse/subject goes outside the area into the parent, but to keep the tracking limited to the target itself; that’s where the constraint works, keeping the scale fixed when the mouse is out bounds.
Very nice Keith! Can’t remember how many times I’ve used them inline. I’d add yet another one here:
[code]
public static function limit(value:Number, min:Number, max:Number):Number
{
return Math.min(Math.max(min, value), max);
}
[/code]
Please, suggest it for astro. Thanks !
Thanks, I added the limit method too. lunetta, I know exactly what you mean, and I tried doing exactly the same thing, but I love the elegance of these single line methods too much. 🙂 The limit method makes it easy to do anyway. but if i find myself needing it a lot, I might put the constraint in anyway.
One i find myself using often is :
public static function findPreferredRatio( width:Number, height:Number, maxWidth:Number, maxHeight:Number ):Number
{
var dw:Number = maxWidth/width;
var dh:Number = maxHeight/height;
return dw < dh ? dw : dh;
}
It give you the correct ratio to fit content in a container using maximum area.
Awesome methods, thanks for commenters also contributing equally great methods. 🙂
Thanks these are great! I’ve needed them for a long time, but never actually stepped back to abstract it enough and realize it! Lesson learned. Thanks.
Thanks for posting these Keith. Can’t recall how many times I’ve needed the functionality these 3 functions provide, and this, by far is the most elegant solution yet.
I always liked how the processing random() function works. Give it low and high values and it gives a random value between the two. Here it is in AS using your map() function:
public static function random(low:Number,high:Number):Number{
return map(Math.random(),0,1,low,high);
}
hey thse are useful, definitely added to my library, some good stuff i miss in other langs