qr code

BIT-101

Bill Gates touched my MacBook Pro

FOTD: Clamp


[ fotd ]

Clamp

We’ll continue the discussion of functions that work with values within ranges. The clamp function is a pretty simple one. It just makes any value that’s higher than the range equal to the max value of the range, and any value that’s lower than the range equal to the min value.

Code

function clamp(value, minimum, maximum) {
	if (value < minimum) {
		value = minimum
	}
	if (value > maximum) {
		value = maximum
	}
	return value
}

If value is either greater than maximum or less than minimum, it “clamps” it to be within that range.

This is the brute force method. If your language has min and max functions, this can be simplified a bit. These are often part of a math library, i.e. Math.min, Math.max or something similar. In pseudocode, I’ll just call them min and max:

function clamp(value, minimum, maximum) {
    return max(minimum, min(value, maximum))
}

Working inside out, it takes the smallest value, comparing value and maximum. This will result it maximum at the most. It feeds that into the outer call to take the largest value between that and minimum. It has the same effect of clamping it into that range.

There’s one upgrade that can make the function even more valuable. Allow for the minimum and maximum values to be reversed. If you’re calling the function directly, it’s easy enough to make sure the smaller of the two goes first. But if it’s part of a larger algorithm, it might happen that minimum gets assigned a value that’s larger than maximum. If so, you can just swap them and have it work fine. Here’s an easy take on this using a temp variable:

function clamp(value, minimum, maximum) {
    if (minimum > maximum) {
        temp = minimum
        minimum = maximum
        maximum = temp
    }
    return max(minimum, min(value, maximum))
}

If you’re lucky enough to be using a language that allows the following syntax, it makes it much easier…

function clamp(value, minimum, maximum) {
    if (minimum > maximum) {
        minimum, maximum = maximum, minimum
    }
    return max(minimum, min(value, maximum))
}

Example(s)

One real life example where I had to use clamp recently was in calculating color values. Normally rgb color channels can either go from 0.0 to 1.0 as floats, or from 0 to 255 as integers. In Cairographics, which I usually work with, it’s 0-1, and if you assign a value outside of that range, it doesn’t matter. I guess internally it clamps it to be within that range.

But somewhat recently… OK, a couple of years ago, I was creating a bitmap encoder. This stored an array of pixel channel values, which eventually got written to a file with the correct headers. Here’s a very simplified pseudocode version of the original function I came up with for setting the value of a single pixel:

function setPixel(x, y, r, g, b) {
	index = (y * imageWidth + x) * 3
	pixels[index] = b
	pixels[index + 1] = g
	pixels[index + 2] = r
}

Because this was a one dimensional array, I had to calculate the index of the array to store the red, green and blue values. They went in sequential elements in reverse order - blue, green, red. Each element was a float from 0.0 to 1.0.

For the most part, this worked great. But now and then, I’d get a group of pixels that would just glitch out and render random, unpredictable colors - generally in very bright areas of the image. After a bit of debugging I realized that sometimes the calculated value of one or more channels would be greater than 1.0. It was either wrapping around or overflowing or something. At any rate, it looked like hell. Once I realized what the problem as, all I had to do was clamp the values to a range of 0 to 1.

function setPixel(x, y, r, g, b) {
	index = (y * imageWidth + x) * 3
	pixels[index] = clamp(b, 0, 1)
	pixels[index + 1] = clamp(g, 0, 1)
	pixels[index + 2] = clamp(r, 0, 1)
}

Problem solved! If a channel value was too bright, it would just max out at 1.0. I don’t think I had an issue of values going negative, but the code would have guarded against that as well.

« Previous Post
Next Post »

Comments? Best way to shout at me is on Mastodon

Or share this post directly on Mastodon