This article is related to two patches to cairo, #12717 and
#12722. Many times in C, you want values in a certain range, or
some that are not above or below a certain value. The process which
ensures that values are within a certain range is called clamping.
Let us say that you have function that sets the position of something.
void
set_pos (Obj *obj, int x, int y)
{
obj->x = x;
obj->y = y;
}
Further assume that the position must be contained within obj:s
clip rectangle. That could be accomplished using this code:
void
set_pos (Obj *obj, int x, int y)
{
// First clip the values
if (x < obj->rect.x)
x = obj->rect.x;
if (x > obj->rect.x + obj->rect.width)
x = obj->rect.x + obj->rect.width;
if (y < obj->rect.y)
y = obj->rect.y;
if (y > obj->rect.y + obj->rect.height)
y = obj->rect.y + obj->rect.height;
// Then set them
obj->x = x;
obj->y = y;
}
A mathematical way to write the clipping would be something like:
obj->x = x, obj->rect.x <= x <= obj->rect.x + obj->rect.width
obj->y = y, obj->rect.y <= y <= obj->rect.y + obj->rect.height
Time to introduce the MIN and MAX functions and see how they can help
us. In C, they can be implemented as macros.
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
It should not be any surprise that
x = MAX (x, obj->rect.x)
and
if (x < obj->rect.x)
x = obj->rect.x;
is exactly equivalent. We can therefore rewrite the boundary checking
code in set_pos to take advantage of these macros:
void
set_pos (Obj *obj, int x, int y)
{
x = MAX (x, obj->rect.x);
x = MIN (x, obj->rect.x + obj->rect.width);
y = MAX (y, obj->rect.y);
y = MIN (y, obj->rect.y + obj->rect.height);
obj->x = x;
obj->y = y;
}
But wait, there is more! The code can be further rewritten if we
introduce a CLAMP macro.
#define CLAMP(x, l, h) (((x) > (h)) ? (h) : (((x) < (l)) ? (l) : (x)))
Now we can make the set_pos function even more readable:
void
set_pos (Obj *obj, int x, int y)
{
obj->x = CLAMP (x, obj->rect.x, obj->rect.x + obj->rect.width);
obj->y = CLAMP (y, obj->rect.y, obj->rect.y + obj->rect.height);
}
Note how similar this code is to the imagined mathematical definition.
It is also worth noting that the CLAMP macro doesn't work if the value
of the upper bound is lower than the lower bound. That is, if
obj->rect.width or obj->rect.height is negative, then the
macro will give erroneous results.