SHIP:Sail:towards

From Serious Documentation
Jump to: navigation, search
Function Returns Introduced Description
towards Float/Integer v5.0.207 Adjusts a number by a percentage in a range towards another number

See Also:

Prototypes

Float towards(Float percent, Float current, Float target[, Float snapWithin ]);

Integer towards(Float percent, Integer current, Integer target[, Integer snapWithin ]);

Parameters/Return Value

Parameter Data Type Description
percent Float Percent to move current towards target
current Float
Integer
Current value
target Float
Integer
Target ending value
snapWithin Float
Integer
(optional) When the result would be less than or equal to this distance from the target, the value is "snapped" to the target and the target value is returned. Defaults to 1 (or 1.0f) if not supplied.
Return Float
Integer
A new number "towards" the target

Detailed Description

The towards function moves a current numeric value towards a target value by a percentage.

The function is very useful in moving objects (e.g. gauge needles, sliders, boxes/images) in a fluid manner from their current positions to a target position. For example, when a slider needs to move from pixel position 55 to 85, rather than just assign the slider's position directly to 85 on a change, one can create a much more fluid movement using towards.

The structure of this would use a timer within the object (such as a slider box):

   variable Integer target
   box
       timer oneshot=true value=1 period=1 autoreload=true enabled=false
           listener listeningto=timer.alarm condition=timer.alarm
               script
                   box.ol        = towards(0.33f, box.ol, target);
                   timer.alarm   = false;
                   timer.enabled = (target != box.ol);
           listener listeningto=target
                   timer.enabled = (target != box.ol);

The snapWithin Parameter and the Float Variant of the Function

In the floating point version of this function (where any of the 3 parameters current, target, and/or snapWithin are supplied as floating point values), the returned value will asymptotically approach the target.

By specifying the snapWithin parameter, when the returned value would be less than or equal to a snapWithin distance from the target, the target value is returned.

For example, continuously moving 25% towards a target number using floating point values:

   Float f = 1.0f;
   ...
   (repeated) f = towards(0.25f, f, 5.0f, 0.1f);

Will, upon repetition of the formula, adjust f by 25% towards the target of 5.0 until is within 0.1 of 5.0, at which point 5.0 will be returned.

snapWithin, if not supplied, defaults to 1.0f in the floating point variant of the function.

The snapWithin Parameter and the Integer Variant of the Function

When using integer values, an infinite loop possibility exists where the current never (after repeated assigns) reaches the target.

For example, 33% of the distance between 0 and 1 is always 0 from an integer perspective, and so repeated calls with these parameters will never have the current reach the target.

In a related example, 25% of the distance between 0 and 2 is always 0 from an integer perspective, again causing a potential infinite loop where the current value never moves at all.

There are 2 mechanisms that prevent these behaviors.

First, the remaining distance to the target is internally calculated and truncated. Therefore any fractional amount will cause the current to move by at least 1. This mechanism handles both problematic scenarios above.

The snapWithin, if not supplied, defaults to 1 in the integer variant of the function, and is redundant with the remaining-distance truncation method. However, a designer may choose a greater value to "snap to" a target when within a larger distance.

Troubleshooting and Common Issues

The most common problem with towards occurs when inadvertently you are using the floating point variant of the function with an integer variable as the result. In this case, it is easy to have the function get "stuck" and never reach the target. For example, repeated calls to

current = towards(0.33f, current, target);

if current and target are both integers (Integer, Short, and/or Byte) will converge as expected to the final condition when current is equal to target. This is because the distance remaining between the two values at each iteration is truncated down, and so the value of current always moves at least 1 count every iteration.

However, if inadvertently target is specified as a floating point number, for example if current is 82 and target is 85.0f, the function is promoted to the floating point variant, and the result of the function is now 82.0+(1-0.33)*(85.0-82.0) = 82.99 which, on assignment to current is truncated to 82. Hence the function never converges to the target as expected.

The simple solution to this issue, in this example, is to force the rounding or truncation of the target value as desired into an integer form, making the function use the integer variant:

current = towards(0.33f, current, round(target));

Be careful with attempting this form of solution for this issue:

current = round(towards(0.1f, 82, 85.0f));

as you will find cases (as in this exact example) where the round is ineffectual -- 82.0+(1-0.1)*(85.0-82.0) = 82.3 which still rounds down to 82... hence still non-converging.

Examples

Example Result Notes
towards(0.25f,0,4); 1 25% from 0 to 4 is 1.
towards(0.25f,4,0); 3 25% from 4 to 0 is 3.
towards(0.25f,-4,4); -2 25% from -4 to +4 is 2.
towards(0.25f,3,4); 4 Integer form of function, remaining distance (0.75) is truncated so the return value is 4.
towards(0.25f,3,4.0f,0.25f); 3.25f Floating point form of function
towards(0.75f,3,4.0f,0.25f); 4.00f Floating point form of function; result is 3.75 but is within the 0.25f snapWithin range so result is target.

References