【问题标题】:Oscillate or "ping pong" between two values?在两个值之间振荡或“乒乓”?
【发布时间】:2012-07-18 14:28:13
【问题描述】:

我有一条在时间 't' 评估的路径,并根据路径类型返回方向和位置。

时间的值受路径类型的影响:

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;
}

缺少的链接在摆动。

我的问题是什么是在两个值之间摆动的好方法?

例如 (2, 7)。如果时间达到 7,它会反转并减少到 2,一旦达到 2,它就会反转并增加到 7。

算法应该知道是否根据原始值增加/减少值,所以如果值为 9,它知道答案是 7 - (Abs(7 - 9)。如果值为 14,则值已经环绕所以会导致增加1。

较高的值也会增加或减少该值,具体取决于它环绕原始范围的次数。

我希望这是有道理的,因为我很难解释。

编辑:

不随浮点值振荡:

        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;
    }

【问题讨论】:

标签: c# algorithm math


【解决方案1】:

这是我想出的:

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

我假设input 将是一个从 0 开始的计数器。

【讨论】:

  • 这对整数很有效。我在方法的开头添加了以下行以将值带入范围(即相对于最小值)value -= min;
  • 虽然这是整数的最佳变体,但对于某些应用程序来说通常更有用(int input, int length)。也稍微简化了数学。
【解决方案2】:

理想情况下,您应该将此功能抽象到某种类中,而不必担心在使用它时实现的实际工作方式。这是 C++ 中的初步看法(我的 C# 有点生疏)。我认为您可以毫不费力地将其转换为 C#。

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;
    }
};

【讨论】:

  • 按预定义值递增/递减。我所追求的是任何被包装到最小/最大范围并在两个值之间振荡的值输入。
  • 是的,这就是我的想法。我已将其转换为 C# 并适合一个静态类。谢谢你。 :)
【解决方案3】:

这将使您的数字在 2 和 7 之间振荡,在本例中,时间是一个整数:

bool isIncreasing = time <= 7;
for (int i = 0; i < 20; i++) //some random loop
{
    time = time + (isIncreasing ? 1 : -1);
    if (time >= 7 || time <= 2) isIncreasing = !isIncreasing;
}

【讨论】:

  • 我猜他想要一个不使用外部变量的函数。
  • 据我测试,上述算法在任何给定时间都不会在 2 到 7 之间波动。
  • 该函数必须知道它最后移动的方向,这可用吗?等等,我明白了……
【解决方案4】:

考虑浮点值的新答案:

    // Note: Increase FACTOR depending on how many decimal places of accuracy you need.
    private const float FACTOR = 10;

    public void Test()
    {
        for (float i = 0; i < 1000; i += 0.1F)
        {
            Console.WriteLine("{0} {1}", i, Oscillate(2.5F, 7.5F, i));
        }
    }

    private float Oscillate(float min, float max, float time)
    {
        return (float)(Oscillate_Aux(Upscale(min), Upscale(max), Upscale(time))) / FACTOR;
    }

    private int Upscale(float value)
    {
        return (int)(value * FACTOR);
    }

    private int Oscillate_Aux(int min, int max, int time)
    {
        int range = max - min;

        int multiple = time / range;

        bool ascending = multiple % 2 == 0;
        int modulus = time % range;

        return ascending ? modulus + min : max - modulus;
    }

【讨论】:

  • 我无法让它与浮点值一起使用。请参阅原始帖子编辑。
【解决方案5】:

您所描述的内容听起来很像两个值之间的周期性线性插值。考虑使用 XNA 的MathHelper.Lerp 函数作为振荡的基础。

注意它使用百分比来控制振荡作为它的第三个参数。您必须弄清楚如何将您的时间 t 值转换为百分位数,但您可以从 ex 开始。 sin(t) 看看事情是如何运作的。

如果您不愿意将 XNA 导入您的项目,核心方程式很简单:

value1 + (value2 - value1) * amount

编辑:如果您的问题的核心是“什么是在两个值之间振荡的好、有效的方法?”,那么 Math.Sin(t)(或 Cos)可以为您提供 0 到 1 之间的规律振荡。


【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多