Processing:Примеры/Столкновение эллипсов

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Описание[1]

Этот скетч-пример основан на примере из книги «Основы анимации с помощью AutoScript: Двигай это!» Кита Питерса.

Пример

Ball[] balls =  { 
  new Ball(100, 400, 20), 
  new Ball(700, 400, 80) 
};

void setup() {
  size(640, 360);
}

void draw() {
  background(51);

  for (Ball b : balls) {
    b.update();
    b.display();
    b.checkBoundaryCollision();
  }
  
  balls[0].checkCollision(balls[1]);
}

class Ball {
  PVector position;
  PVector velocity;

  float radius, m;

  Ball(float x, float y, float r_) {
    position = new PVector(x, y);
    velocity = PVector.random2D();
    velocity.mult(3);
    radius = r_;
    m = radius*.1;
  }

  void update() {
    position.add(velocity);
  }

  void checkBoundaryCollision() {
    if (position.x > width-radius) {
      position.x = width-radius;
      velocity.x *= -1;
    } else if (position.x < radius) {
      position.x = radius;
      velocity.x *= -1;
    } else if (position.y > height-radius) {
      position.y = height-radius;
      velocity.y *= -1;
    } else if (position.y < radius) {
      position.y = radius;
      velocity.y *= -1;
    }
  }

  void checkCollision(Ball other) {

    // считываем расстояние между эллипсами:
    PVector distanceVect = PVector.sub(other.position, position);

    // рассчитываем длину вектора, разделяющего эллипсы:
    float distanceVectMag = distanceVect.mag();

    // минимальное расстояние до столкновения эллипсов:
    float minDistance = radius + other.radius;

    if (distanceVectMag < minDistance) {
      float distanceCorrection = (minDistance-distanceVectMag)/2.0;
      PVector d = distanceVect.copy();
      PVector correctionVector = d.normalize().mult(distanceCorrection);
      other.position.add(correctionVector);
      position.sub(correctionVector);

      // считываем угол «distanceVect»:
      float theta  = distanceVect.heading();
      // заранее рассчитываем тригонометрические значения: 
      float sine = sin(theta);
      float cosine = cos(theta);

      /* Массив «bTemp» будет хранить позиции вращающихся эллипсов.
       Вас должна волновать лишь позиция bTemp[1] */
      PVector[] bTemp = {
        new PVector(), new PVector()
      };

      /* Это позиция второго эллипса относительно позиции первого,
         поэтому вы можете использовать вектор между ними (bVect)
         в качестве ориентира для уравнений,
         с помощью которых осуществляется расчет вращения.
         «bTemp[0].position.x» и «bTemp[0].position.y»
         будут автоматически инициализированы со значением «0.0»,
         и это то как раз, что нужно, потому что эллипс b[1]
         будет вращаться вокруг эллипса b[0]. */
      bTemp[1].x  = cosine * distanceVect.x + sine * distanceVect.y;
      bTemp[1].y  = cosine * distanceVect.y - sine * distanceVect.x;

      // меняем предварительные углы направления векторов:
      PVector[] vTemp = {
        new PVector(), new PVector()
      };

      vTemp[0].x  = cosine * velocity.x + sine * velocity.y;
      vTemp[0].y  = cosine * velocity.y - sine * velocity.x;
      vTemp[1].x  = cosine * other.velocity.x + sine * other.velocity.y;
      vTemp[1].y  = cosine * other.velocity.y - sine * other.velocity.x;

      /* Теперь, когда углы направления векторов изменены,
       можно воспользоваться 1D-преобразованием уравнений импульса
       для расчета финальных углов направления векторов по оси X. */
      PVector[] vFinal = {  
        new PVector(), new PVector()
      };

      // финальный угол направления вектора для эллипса b[0]:
      vFinal[0].x = ((m - other.m) * vTemp[0].x + 2 * other.m * vTemp[1].x) / (m + other.m);
      vFinal[0].y = vTemp[0].y;

      // финальный угол направления вектора для эллипса b[1]:
      vFinal[1].x = ((other.m - m) * vTemp[1].x + 2 * m * vTemp[0].x) / (m + other.m);
      vFinal[1].y = vTemp[1].y;

      // небольшой трюк, чтобы избежать комкования:
      bTemp[0].x += vFinal[0].x;
      bTemp[1].x += vFinal[1].x;

      /* Инвертируем позиции эллипсов и углы направления векторов
       при помощи тригонометрических уравнений. */
      // вращаем эллипсы:
      PVector[] bFinal = { 
        new PVector(), new PVector()
      };

      bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y;
      bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x;
      bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y;
      bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x;

      // обновляем позиции эллипсов на экране:
      other.position.x = position.x + bFinal[1].x;
      other.position.y = position.y + bFinal[1].y;

      position.add(bFinal[0]);

      // обновляем углы направления векторов:
      velocity.x = cosine * vFinal[0].x - sine * vFinal[0].y;
      velocity.y = cosine * vFinal[0].y + sine * vFinal[0].x;
      other.velocity.x = cosine * vFinal[1].x - sine * vFinal[1].y;
      other.velocity.y = cosine * vFinal[1].y + sine * vFinal[1].x;
    }
  }

  void display() {
    noStroke();
    fill(204);
    ellipse(position.x, position.y, radius*2, radius*2);
  }
}

См.также

Внешние ссылки