Перейдем к моделированию движения нити, то есть цепочки частиц, связанных пружинами (см. 40-й урок NeHe). Для этого создадим класс Rope:

const unsigned numberOfParticles = 20;


class Rope : public Application
{
    Particle particle[numberOfParticles];
    ForceRegistry registry;

    const double stiffness;
    const double normalLength;

    Vector3 anchor;
    AnchoredSpring anchoredSpring;
    Spring spring[2*(numberOfParticles-1)];

    void render();

public:

    Rope();
    virtual const char* getTitle() { return "Rope"; }
    virtual void update();
    virtual void display();
};

Частицы хранятся в статическом массиве particle, пружины — в массиве spring. Первая из частиц, составляющих трос, связана с точкой крепления при помощи объекта класса AnchoredSpring. Кроме того, задаются координаты точки крепления anchor, жесткость пружин stiffness и длина нерастянутой нити (пружины) normalLength.

Силы, действующие на частицы, регистрируется в реестре registry.

Рассмотрим конструктор класса Rope:

Rope::Rope()
: stiffness(20.0),
  normalLength(10.0/numberOfParticles),
  anchor(0.0, 20.0, 20.0),
  anchoredSpring(&anchor, stiffness, normalLength)
{
    for (unsigned i = 0; i < numberOfParticles; i++) {
        particle[i].setPosition(0.0, 20.0, 20+(i+1)*normalLength);
        particle[i].setVelocity(0.0, 0.0, 0.0);
        particle[i].setMass(2.0/numberOfParticles);
        particle[i].setDamping(0.9);
        particle[i].setAcceleration(0.0, -10.0, 0.0);
        particle[i].clearAccumulator();
    }

    for (unsigned i = 0, j = 0; i < numberOfParticles-1; i++, j+=2) {
        spring[j] = Spring(&particle[i+1], stiffness, normalLength);
        spring[j+1] = Spring(&particle[i], stiffness, normalLength);
    }

    registry.add(&particle[0], &anchoredSpring);
    for (unsigned i = 0, j = 0; i < numberOfParticles-1; i++, j+=2) {
        registry.add(&particle[i], &spring[j]);
        registry.add(&particle[i+1], &spring[j+1]);
    }
}

Заметим, что сила тяжести представлена не с помощью класса наследника от Force, а через ускорение частицы, как в нашем первом примере. Поскольку в рассматриваемом случае тяжести постоянна и действует на протяжении всего периода моделирования, такой подход ведет к меньшим вычислительным затратам. Действительно, в этом случае в классе частиц используется дополнительное поле, тогда как иначе пришлось бы создавать для каждой частицы новый объект — силу Gravity, — и затем добавлять ее в реестр.

Рисование нити выполняется функцией render(). Нить представлена ломаной линией, вершины которой соответствуют частицам. Толщина линии определяется функцией glLineWidth().

void Rope::render()
{
    glLineWidth(2);
    glColor3f(0.0f, 0.0f, 0.0f);
    glBegin(GL_LINES);
        glVertex3f(anchor.x,
                   anchor.y,
                   anchor.z);
        glVertex3f(particle[0].getPosition().x,
                   particle[0].getPosition().y,
                   particle[0].getPosition().z);
    glEnd();
    for (unsigned i = 0; i < numberOfParticles-1; i++) {
        glBegin(GL_LINES);
            glVertex3f(particle[i].getPosition().x,
                       particle[i].getPosition().y,
                       particle[i].getPosition().z);
            glVertex3f(particle[i+1].getPosition().x,
                       particle[i+1].getPosition().y,
                       particle[i+1].getPosition().z);
        glEnd();
    }
}

Однако моделирование движения нити, выполненное с помощью рассмотренного выше кода, приведет к физически бессмысленным результатам. Вместо того, чтобы стабилизироваться возле положения устойчивого равновесия (прямой, проходящей через точку крепления параллельно оси y), нить будет раскачиваться, все больше удаляясь от равновесия.

Движение нити с некорректно выбранным шагом численного интегрирования

Причиной подобного поведения нити является некорректный выбор шага численного интегрирования.



Комментарии

comments powered by Disqus