Учитывая значение начала, конца и приращения, я хочу алгоритм, который считает вверх и вниз - PullRequest
2 голосов
/ 16 сентября 2009

Хорошо, у меня есть элемент управления, который хочет постепенно исчезать (постоянно). Чтобы уменьшить его, я настраиваю значение прозрачности элемента управления в процедуре рисования. Поэтому я установил таймер, который запускается, и настраиваю член m_transparency. Таймер работает, и он должен перемещаться назад и вперед между двумя определенными значениями m_start, m_end. Это может быть 0-255.

например. начало 30, конец 55, значение приращения = 5. Это будет выглядеть так:

30, 35, 40, 45, 55, 50, 45, 40, 35, 30, 35, 40 .......

также должен обрабатывать начало 55, конец 30.

В любом случае, я написал ужасающую функцию, которая в основном проверяет каждое состояние (двигаюсь ли я вверх или вниз и достиг ли я начала, конца, точки поворота и т. Д.) Это уродливо, и я смущен (тройное вложенное ifs), но я не могу придумать более чистый способ сделать это, а затем проверить все. Есть ли более простой способ?

Ответы [ 8 ]

5 голосов
/ 16 сентября 2009

Следующий класс делает это довольно просто. Вы захотите подумать о том, как вы хотите, чтобы он вел себя в граничных случаях (например, это будет 55 для RangeScanner (30, 54, 5), но это легко изменить на желаемое поведение).

class RangeScanner
{
public:
    RangeScanner(int start, int end, unsigned int inc) :
        value(start),
        lower(std::min(start, end)),
        upper(std::max(start, end)),
        increment(inc),
        incrementing(start < end)
    {
    }

    int nextValue()
    {
        int ret = value;

        value += incrementing ? increment : -increment;

        if (value >= upper || value <= lower)
        {
            incrementing = !incrementing;
        }

        return ret;
    }

private:
    int value;
    const int lower;
    const int upper;
    const unsigned int increment;
    bool incrementing;
};
4 голосов
/ 16 сентября 2009

Вот основной подход для линейного нарастания - ndash; замедление как функция time.

int triangle(int time) {
    int d = m_end - m_start;
    return m_start + abs(((m_increment * time + d) % 2*d) - d);
}
4 голосов
/ 16 сентября 2009

Вот как я бы подошел, решение на основе C (которое, конечно, будет работать на C ++, но не имеет никаких функций C ++):

int nextVal (int curr, int min, int max, int *pStep) {
    // Handle situations where you want to turn around.

    if ((curr == min) || (curr == max)) {
        *pStep = -(*pStep);
        return curr + *pStep;
    }

    // Handle situation where you would exceed your bounds (just
    //   go to the bound).

    if (curr + *pStep < min) return min;
    if (curr + *pStep > max) return max;

    // Otherwise, just move within the bounds.

    return curr + *pStep;
}

int main(void) {
    int i;

    // Set up your variables here.

    int min = 30;
    int max = 55;
    int step = 5;

    // This is the running current value and we need to negate the step
    //   to force initial turn-around.

    int curr = min;
    step = -step;

    // Simple loop to demonstrate.

    printf ("%d\n", curr);
    for (i = 19; i > 0; i--) {
        curr = nextVal (curr, min,max,&step);
        printf ("%d\n", curr);
    }

    return 0;
}

Это выводит:

30, 35, 40, 45, 50, 55, 50, 45, 40, 35, 30, 35, 40, 45, 50, 55, 50, 45, 40, 35

для вашего теста. Он также интеллектуально обрабатывает последовательности, где диапазон не является точным кратным приращению (например, [7,40,10]):

7, 17, 27, 37, 40, 30, 20, 10, 7, 17, 27, 37, 40, 30, 20, 10, 7, 17, 27, 37
2 голосов
/ 16 сентября 2009

Как насчет использования функции синуса, чтобы получить более естественный вид ...

#include <math.h>

class SineFader
{
public:
  SineFader(int min, int max)
    : base((double)min + ((double)(max - min) / 2))
    , range((double)(max - min) / 2)
    , theta(4.71)
    , speed(0.1)
  { }

  int getValue()
  {
    theta += speed;
    return (int)(base + (range * sin(theta)));
  }

private:
  double base, theta, range, speed;
};

Вот как вы можете использовать это в своем коде:

SineFader myfade(0, 55);

void onTimer()
{
   setTransparency(myfade.getValue());
}
1 голос
/ 16 сентября 2009

Моя версия повторяет предельные значения: ... 50, 55, 55 , 50, ..., 35, 30, 30 , 35, ...

struct UpDown {
    int lo_val, hi_val, curr_val;
    int step;
};

void fade_in_out(struct UpDown *x) {
    x->curr_val += x->step;
    if ((x->curr_val > x->hi_val) || (x->curr_val < x->lo_val)) {
        x->step *= -1;
        x->curr_val += x->step;
    }
}

int main(void) {
    struct UpDown myControl = {30, 55, 30, 5};
    for (;;) {
        fade_in_out(&myControl);
    }
    return 0;
}
0 голосов
/ 16 сентября 2009

Вот еще один подход, подпрыгивающий между start и start + span с шагом приращения, с помощью вспомогательной функции:

// Goes from start to start + span, and repeats.
int ramp(start, increment, span) {
  static int lastval = start;
  lastval += increment;
  lastval = lastval % (span + 1);
  return lastval;
}

// Goes from start up to start+span and back down, and repeats.
int bounce(start, increment, span) {
  val = ramp(0, increment, 2*span - 1);
  if (val > span) val = span - val;
  return val + start;
}

По сути, это создает непрерывно увеличивающуюся линию, затем используя функцию MOD, чтобы нарезать ее в пилообразную форму, а затем отразить верхнюю половину пилообразной линии вниз, чтобы сделать зигзаг.

Обратите внимание, что, в отличие от всех других представленных реализаций (если я не пропустил одну), эта на самом деле проводит вас на том же расстоянии вдоль зигзага, даже если приращение не делится равномерно - например, для начала 0 , промежуток 10 и шаг 3, он будет увеличиваться с 3 до 3, с 3 до 6, с 3 до 9, с 1 на 1 и далее с 2 до 8, с 3 на 5, с 3 на 2, с 3 на 2, вниз на 2 и выше на 1 к 1 и т. д. - 3, 6, 9, 8, 5, 2, 1, 4, 7, 10, 7, 4, 1, .... И это также гарантирует, что числа всегда попадают в правильный диапазон и делают правильные вещи, даже если приращение намного больше, чем span или отрицательно.

(Вам, вероятно, следует переписать это, чтобы поместить такие элементы, как start, increment и span, где-нибудь в статическое хранилище, а не в аргументы функции, потому что это может запутаться, если они изменятся, но мне лень об этом. И, на самом деле, он все равно попадет в нужный диапазон и сделает несколько разумных вещей, если они изменятся - вы можете изменить приращение в любой точке, и он будет ускоряться или замедляться, не прыгая.)

0 голосов
/ 16 сентября 2009

Вот как я это сделал. Кажется довольно простым.

int computeTransparency(int start, int end, int& increment, int oldVal)
{
    if (start > end)
    {
        swap(start, end);
    }

    int newVal = oldVal + increment;
    bool goingUp = increment > 0;

    if (goingUp)
    {
        if (newVal >= end)
        {
            newVal = end;
            increment = -increment;
        }
    }
    else
    {
        if (newVal <= start)
        {
            newVal = start;
            increment = -increment;
        }
    }

    return newVal;
}
0 голосов
/ 16 сентября 2009

Это гибкий, вы можете начать с любого места в диапазоне, а также указать направление, в котором хотите начать движение, сделав dir либо 1, либо -1.

class Fader
{
public:
 Fader(int min, int max, int start, int dir = 1)
  : _min(min)
  , _max(max)
  , _val(start)
  , _dir(dir)
 { }

 int getValue()
 {
   if(_val <= min || _val >= _max)
     _dir = -_dir;

   _val += _dir;
   return _val;
  }

private:
  int _min, _max, _dir, _val;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...