Improving the Limit Function
As a continuation of a previous post “How to Limit a Number Between Two Values” I thought I’d make my “limiting” code a little more versatile.
The previous post used the following code to ‘limit’ or ‘constrain’ a number between a low and high value:
// Limit a number between two values.
function limit (num, lo, hi){
if (num < lo) return lo;
if (num > hi) return hi;
return num;
}
//
But what if you don’t want to, or can’t, control the order of the “lo” and “hi” arguments? Currently, if you try something like “limit(10, 60, 50)” the above limit function incorrectly returns “60”. Wouldn’t it be easier if the function properly limited the first argument regardless of the order of the second and third arguments? In other words, “limit(10, 50, 60)” and “limit(10, 60, 50)” would both produce “50”.
Implementing this improvement is a simple matter of adding a conditional statement to the beginning of the “limit” function to determine whether the second or third argument is larger and then proceeding accordingly. The new version looks like this:
// Limit a number between two values.
function limit (num, limA, limB){
if (limA <= limB){
if (num < limA) return limA;
if (num > limB) return limB;
return num;
}else {
if (num > limA) return limA;
if (num < limB) return limB;
return num;
}
}
//
//
And when we add this new version to the previous code sample we get this:
// Limit a number between two values.
function limit (num, limA, limB){
if (limA <= limB){
if (num < limA) return limA;
if (num > limB) return limB;
return num;
}else {
if (num > limA) return limA;
if (num < limB) return limB;
return num;
}
}
//
//
//
// Build Description textfields.
createTextField("LoDescTF", 10, 50, 10, 100, 20);
LoDescTF.text = "Limit A";
//
createTextField("LimitedDescTF", 20, 200, 10, 100, 20);
LimitedDescTF.text = "Limited Value";
//
createTextField("HiDescTF", 30, 350, 10, 100, 20);
HiDescTF.text = "Limit B";
//
//
//
// Build User-input textfields.
createTextField("ALimitTF", 11, 50, 30, 100, 20);
ALimitTF.type = "input";
ALimitTF.border = true;
ALimitTF.restrict = "0-9";
ALimitTF.text = "0";
//
createTextField("LimitedTF", 21, 200, 30, 100, 20);
LimitedTF.type = "input";
LimitedTF.border = true;
LimitedTF.restrict = "0-9";
LimitedTF.text = "50";
//
createTextField("BLimitTF", 31, 350, 30, 100, 20);
BLimitTF.type = "input";
BLimitTF.border = true;
BLimitTF.restrict = "0-9";
BLimitTF.text = "100";
//
//
//
// Build Instructions textfield.
createTextField("Instructions", 40, 10, 60, 480, 40);
Instructions.wordWrap = true;
Instructions.multiline = true;
Instructions.text = "Type a number in the middle box and press the enter key to constrain the number between the the two limiting values";
//
//
//
// Limit the value in the middle box when the Enter key is pressed.
LimitedTF.onKeyDown = function(){
if(Key.isDown(Key.ENTER)){
var aa = ALimitTF.text - 0;
var bb = BLimitTF.text - 0;
var lim = this.text - 0;
this.text = limit(lim, aa, bb);
}
}
Key.addListener(LimitedTF);
//
//
And the code above produces the following SWF:
As you can see, the middle number will now be limited (restricted or constrained) between the two outer numbers regardless of their order.
I hope you find it useful.
Hi Joga, although using an array provides an easy method to sort the limiters, I think the array notation (i.e. limits[0]) decreases performance. I ran a quick performance test and your code seemed about 85% slower than the code I provided.
Flash has to look-up a variable twice with array notation, once to find “limits”, and then another time to find index “0” within “limits”. In contrast, a local variable like “num” only needs to be looked-up once. In AS1 and AS2 looking-up variables can be a fairly slow process, I’m unsure about AS3.
what about simple array sorting. maybe iām wrong, iām still new to flash.
limits.sort();
return (num < limits[0]) ? limits[0] : (num > limits[1]) ? limits[1] : num;
}
function limitReadable(num:Number, limits:Array){
limits.sort();
if(num < limits[0]) {
return limits[0];
}
if(num > limits[1]) {
return limits[1];
}
return num;
}
Ah! good to know š In some languages it is more costly to have complicated “if” forking than simple variable declaration – has something to do with the processor being able to predict likely paths through the code – but that’s obviously not the case with actionScript. Also, your “early out” returns probably help in a lot of cases š
Hi Shaun,
I originally went the same way as you did with your code, but I decided to use the approach listed above for performance reasons since I frequently use the code in loops which iterate through a large number of objects.
The main thing that slows down the code you provided is the creation of the “tmp” variable, which in my tests was only a tiny bit slower, most notably when “hi” was less than “lo”.
Thanks for your suggestion, interest and perseverance. š
How about switching hi and lo if they are in the wrong order? Like such:
if (hi < lo) {
var tmp = hi;
hi = lo;
lo = tmp;
}
if (num < lo) return lo;
if (num > hi) return hi;
return num;
}