Monday, December 26, 2016

recreating processing / p5.js's map() function in php (and regular js)

Processing's map() function (here's an interactive p5 demo of it) is super useful and conceptually important, but I don't know if it natively exists in PHP, or I just don't know the term for it.

It takes 5 values, a basic value, the min and max value of the input range, and then the min and max value of the output range. So if you wanted to position something in Processing based on the pointer X position, but constrain it from, say, 1-100, you would call

map(mouseX, 0, width, 1,100);

Note that basic values outside the "input range" are valid, and will result in outputs outside the output range, and similarly you can flip either pair of range numbers and get things reversed, so to speak.

Anyway, I was starting a DIY wordcloud in PHP for tags on my website, and wanted to map the tag appearance count (ranging from 1-250 or so) to font pixel sizes (maybe from 10 to 100 or so).

Since the processing page provided a nice little tester, I wrote it in Java to make sure I wasn't screwing anything up, and could visually compare results to the existing map() function:

float mymap(float val, float inputMin,float inputMax,
    float outputMin, float outputMax){
     float inputRange = inputMax - inputMin;
     float outputRange = outputMax - outputMin;
     float scale = outputRange / inputRange;
     float trueVal = val - inputMin;
     return outputMin + (trueVal * scale);
}

and then ported it to PHP:

function mymap($val,$inputMin,$inputMax, $outputMin, $outputMax){
     $inputRange = $inputMax - $inputMin;
     $outputRange = $outputMax - $outputMin;
     $scale = $outputRange / $inputRange;
     $trueVal = $val - $inputMin;
     return $outputMin + ($trueVal * $scale);
}

Didn't do much error checking, if the min and max of the input are equal I guess that might be a problem.

2020 UPDATE: the PHP code above works verbatim in js! Or to be a little cooler about it, and remove the "$" prefix for variables (even though that's totally valid js)

export const map = (val, inputMin, inputMax, outputMin, outputMax) => {
    const inputRange = inputMax - inputMin;
    const outputRange = outputMax - outputMin;
    const scale = outputRange / inputRange;
    const trueVal = val - inputMin;
    return outputMin + trueVal * scale;
}

Here's the first (well, second, I fiddled with a consistent line-height) result:
Not great, not terrible. I might try something like jQCloud if I want it to look better (not clear to me yet if you can make each word clickable.)

No comments:

Post a Comment