Silverlan
Silverlan

Reputation: 2911

Clamp angle to arbitrary range

I need a function to clamp an angle (in degrees) into an arbitrary range [min,max]. Here are some examples: Angle range examples

The colored areas represent the valid angle range.

This is what I have so far:

static float clamp_angle(float ang,float min,float max)
{
    ang = normalize_angle(ang); // normalize_angle transforms angle into [-180,180) range
    min = normalize_angle(min);
    max = normalize_angle(max);
    if(angle_in_range(ang,min,max) == false)
    {
        if(abs(get_angle_difference(ang,min)) < abs(get_angle_difference(ang,max))
            ang = min; // Clamp to min if we're closer to min than max
        else
            ang = max;
    }
    return ang;
}

What I'm missing is the function angle_in_range (true if the angle is within the range, otherwise false).
What would be the simplest way of determining whether the angle is within the range or not?

Upvotes: 5

Views: 2869

Answers (3)

Facundo
Facundo

Reputation: 160

If you want to clamp the angle so that it stays within the range but it wraps around when it reaches max or min (instead of clipping to the limits) then you could use this fairly simple function I worte using python:

def clamp_to_range(value, min, max):
     return (value % (max - min)) + min

It behaves like this:

>>> for i in range(0, 30): print("|{:<30}|{:<30}|".format(" "*(clamp_to_range(i, 4, 15)) + "♥", " "*i + "♥"))
...
|    ♥                         |♥                             |
|     ♥                        | ♥                            |
|      ♥                       |  ♥                           |
|       ♥                      |   ♥                          |
|        ♥                     |    ♥                         |
|         ♥                    |     ♥                        |
|          ♥                   |      ♥                       |
|           ♥                  |       ♥                      |
|            ♥                 |        ♥                     |
|             ♥                |         ♥                    |
|              ♥               |          ♥                   |
|    ♥                         |           ♥                  |
|     ♥                        |            ♥                 |
|      ♥                       |             ♥                |
|       ♥                      |              ♥               |
|        ♥                     |               ♥              |
|         ♥                    |                ♥             |
|          ♥                   |                 ♥            |
|           ♥                  |                  ♥           |
|            ♥                 |                   ♥          |
|             ♥                |                    ♥         |
|              ♥               |                     ♥        |
|    ♥                         |                      ♥       |
|     ♥                        |                       ♥      |
|      ♥                       |                        ♥     |
|       ♥                      |                         ♥    |
|        ♥                     |                          ♥   |
|         ♥                    |                           ♥  |
|          ♥                   |                            ♥ |
|           ♥                  |                             ♥|

as I mentioned, it wraps around. If you instead want to clip the value to the specified limits, you can use this function:

def clip_to_range(value, min_, max_):  # underscores added to avoid name collisions
     return min(max_, max(value, min_))

In this case this will happen:

>>> for i in range(0, 30): print("|{:<30}|{:<30}|".format(" "*clip_to_range(i, 4, 15) + "♥", " "*i + "♥"))
...
|    ♥                         |♥                             |
|    ♥                         | ♥                            |
|    ♥                         |  ♥                           |
|    ♥                         |   ♥                          |
|    ♥                         |    ♥                         |
|     ♥                        |     ♥                        |
|      ♥                       |      ♥                       |
|       ♥                      |       ♥                      |
|        ♥                     |        ♥                     |
|         ♥                    |         ♥                    |
|          ♥                   |          ♥                   |
|           ♥                  |           ♥                  |
|            ♥                 |            ♥                 |
|             ♥                |             ♥                |
|              ♥               |              ♥               |
|               ♥              |               ♥              |
|               ♥              |                ♥             |
|               ♥              |                 ♥            |
|               ♥              |                  ♥           |
|               ♥              |                   ♥          |
|               ♥              |                    ♥         |
|               ♥              |                     ♥        |
|               ♥              |                      ♥       |
|               ♥              |                       ♥      |
|               ♥              |                        ♥     |
|               ♥              |                         ♥    |
|               ♥              |                          ♥   |
|               ♥              |                           ♥  |
|               ♥              |                            ♥ |
|               ♥              |                             ♥|

A C analog to the clamp_to_range function would look like this:

#include <math.h>

float clampInRange(float value, float min, float max)
{
    return fmod(value, max - min) + min;
}

Upvotes: 0

Serikov
Serikov

Reputation: 1209

You can normalize angles in such a way that ang become 0 and min and max is mapped to [-180; 180). Then you can check if angle is in provided range like this:

float clamp_angle(const float ang, const float min, const float max)
{
    float n_min = normalize180(min-ang);
    float n_max = normalize180(max-ang);

    if (n_min <= 0 && n_max >= 0)
    {
        return ang;
    }
    if (abs(n_min) < abs(n_max))
        return min;
    return max;
}

Live On Coliru

Upvotes: 4

Dmitry Lachinov
Dmitry Lachinov

Reputation: 129

Assume you use clockwise order. The distance between min and max in cw order is dist(min, max) = (max - min)mod N Assume point inside region. Then dist(min,A) + dist(A,max) = dist(min,max). Now you can measure distance between point A, min and max : dist(min, A)= (A - min)modN dist(A, max)=(max-A)modN. If A outside region, then sum of the distances should be N+dist(min, max), if inside it should be equal dist(min,max)

In your case N = 360, all values lies in [0,360)

Edit: In most languages behavior of (-x)modX is undefined, so you should manually convert -x to positive number e.g. (-x + X)modX where x lies in [0,X)

Upvotes: 0

Related Questions