Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Oscillate or "ping pong" between two values?

Tags:

c#

algorithm

math

I have a path that is evaluate at time 't' and returns an orientation and position based on the path type.

The value for time is affected by the path type:

switch (type)
{
    case PathType.Closed:
        time = ToolBox.Wrap(time, StartTime, EndTime);
        break; // Wrap time around the path time to loop

    case PathType.Open:
        time = ToolBox.Min(time, EndTime);
        break; // Clamp the time value to the max path time range

    case PathType.Oscillating:
        break;
}

The missing link is oscillating.

My question is what is a good, efficient way for oscillating between two values?

For example (2, 7). If time reaches 7 it reverses and decrements towards to 2 and once it reaches 2 it reverses and increases towards 7.

The algorithm should know whether to increase/decrease the value based on the original value so if the value is 9 it knows the answer is 7 - (Abs(7 - 9). If the value is 14 the value has wrapped around so it will result in an increase of 1.

Higher values will also increase or decrease the value depending on the number of times it wraps around the original range.

I hope that makes sense as I'm finding it difficult to explain.

EDIT:

Doesn't oscillate with floating point values:

        for (float i = 0; i < 100; i += 0.1f)
        {
            Console.WriteLine("{0} {1}", i, Oscillate(2.5f, 7.5f, i));
        }

    private float Oscillate(float min, float max, float value)
    {
        float range = max - min;

        float multiple = value / range;

        bool ascending = multiple % 2 == 0;
        float modulus = value % range;

        return ascending ? modulus + min : max - modulus;
    }
like image 204
user1423893 Avatar asked Nov 30 '25 05:11

user1423893


2 Answers

Here is what I came up with:

public static int Oscillate(int input, int min, int max)
{
    int range = max - min ;
    return min + Math.Abs(((input + range) % (range * 2)) - range);
}

I'm assuming input will be a counter starting at 0.

like image 157
Servy Avatar answered Dec 02 '25 17:12

Servy


Ideally, you should be abstracting this functionality into some kind of a class and not be concerned about how the implementation actually works when you're using it. Here's an initial take on what that would look like in C++ (my C# is a little rusty). I think you can work it into C# with only little difficulty.

class oscillator
{
  private:
    float min;
    float max;

    static float mod(float num, float div)
    {
        float ratio = num / div;
        return div * (ratio - std::floor(ratio));
    }

  public:
    oscillator(float a, float b)
      : min(a < b ? a : b), max(a > b ? a : b) {}

    float range() ( return max-min; }

    float cycle_length() { return 2*range(); } 

    float normalize(float val)
    {
        float state = mod(val-min, cycle_length());

        if (state > range())
            state = cycle_length()-state;

        return state + min;
    }
};
like image 29
andand Avatar answered Dec 02 '25 18:12

andand