Урок 2.2. Элементарное моделирование в Processing

Базовый курс "Программирование микроконтроллеров"
Модуль 2. Создание интерактивных интерфейсов в среде Processing

Processing позволяет решать большое количество задач: от простого рисования до анализа изображений. Довольно часто им пользуются художники и учёные для визуализации различных процессов.

Нашей целью на этом занятии будет визуализировать движение группы молекул в замкнутом сосуде. Для простоты пока что не будем учитывать столкновения молекул друг с другом, но будем учитывать столкновения молекул со стенками сосуда. Начнём с создания одной молекулы, круга, который будет “чувствовать” стенки окна и отскакивать от них.
Для этого рассмотрим случаи пересечения круга с границами окна.

Пересечение с правой границей окна

При приближении круга к правому краю, он коснется его как только выполнится условие X_{c}+R \geq width, а отскок от края окна мы получим простым умножением горизонтальной скорости V_{x} – 1.

 

 

 

Напишем простой код для отскока круга от правого края:

void loop() {
  int duration;
  float cm;
  // генерируем на выводе Trig положительный импульс длиной 10 мкс
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  // считываем длительность импульса, т.е. время между испусканием УЗ волны и её возвратом
  duration = pulseIn(echoPin, HIGH);
  // вычисляем расстояние
  cm = (float)duration / 58;
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Dist = " + String(cm) + " cm");
  delay(200);
}

Пересечение с левой границей окна

Когда круг приблизиться к левому краю, он коснется его как только выполнится условие X_{c}-R \leq 0.

 

 

 

 

Напишем простой код, для отскока круга от правого края и левого:

// координаты центра окружности
float x_c;
float y_c;
// радиус окружности в пикселях
float R=20;
// скорость нашей молекулы
float Vx=2;
float Vy=0;

void setup() {
  size(800, 800);
  colorMode(HSB, 360, 100, 100);
  background(0);
  // установка начальной позиции окружности по центру экрана
  x_c = width/2;
  y_c = height/2;
}

void draw() {
  background(0);
  fill(200, 100, 100);
  circle(x_c, y_c, 2*R);
  // сдвигаем молекулу
  x_c=x_c+Vx;
  y_c=y_c+Vy;
  // есkи край молекулы коснулся края окна то изменяем скорость на противоположную
  if (x_c + R >= width)
    Vx=-Vx;
}

Пересечение с нижней границей окна

Когда круг приблизиться к нижнему краю, он коснется его как только выполнится условие Y_{c}+R \geq height.


Напишем простой код, для отскока круга от правого края, левого и нижнего:

// координаты центра окружности
float x_c;
float y_c;
// радиус окружности в пикселях
float R=20;
// скорость нашей молекулы
float Vx=0;
float Vy=2;

void setup() {
  size(800, 800);
  colorMode(HSB, 360, 100, 100);
  background(0);
  // установка начальной позиции окружности по центру экрана
  x_c = width/2;
  y_c = height/2;
}

void draw() {
  background(0);
  fill(200, 100, 100);
  circle(x_c, y_c, 2*R);
  // сдвигаем молекулу
  x_c=x_c+Vx;
  y_c=y_c+Vy;
  // если край молекулы коснулся края окна то изменяем скорость на противоположную
  if (x_c + R >= width || x_c - R <= 0)
    Vx=-Vx;
  if (y_c + R >= height)
    Vy=-Vy;
}

Пересечение с верхней границей окна

Когда круг приблизиться к верхнему краю, он коснется его как только выполнится условие Y_{c}-R \leq 0.

 

 

 

Закончим наш код для отскока круга от всех границ окна:

// координаты центра окружности
float x_c;
float y_c;
// радиус окружности в пикселях
float R=20;
// скорость нашей молекулы
float Vx=3;
float Vy=2;

void setup() {
  size(800, 800);
  colorMode(HSB, 360, 100, 100);
  background(0);
  // установка начальной позиции окружности по центру экрана
  x_c = width/2;
  y_c = height/2;
}

void draw() {
  background(0);
  fill(200, 100, 100);
  circle(x_c, y_c, 2*R);
  // сдвигаем молекулу
  x_c=x_c+Vx;
  y_c=y_c+Vy;
  // если край молекулы коснулся края окна то изменяем скорость на противоположную
  if (x_c + R >= width || x_c - R <= 0)
    Vx=-Vx;
  if (y_c + R >= height || y_c - R <= 0)
    Vy=-Vy;
}

Увеличение количества молекул и изменение их параметров

Теперь увеличим количество молекул, для этого создадим массивы, в которых будут хранится характеристики наших молекул, и для разнообразия при каждом запуске программы их параметры будут генерироваться случайно:

// количество молекул
int N=10;
// массив координат центра окружностей
float[] x_c= new float[N];
float[] y_c= new float[N];
// массив радиуса окружностей в пикселях
float[] R= new float[N];
// массив скоростей наших молекул
float[] Vx= new float[N];
float[] Vy= new float[N];
// массив цветов молекул
color[] c= new color[N];

void setup() {
  size(800, 800);
  colorMode(HSB, 360, 100, 100);
  background(0);
  // установка начальных параметров молекул
  for (int i=0; i<N; i++)
  {
    R[i]=random(20, 60);
    //генерация координаты от размеров молекулы до ширины - размеры молекулы, что бы молекулы не генерировались за пределами окна
    x_c[i]=random(R[i], width - R[i]);
    y_c[i]=random(R[i], height - R[i]);
    // ((int)random(0,2) == 0 ? 1 : -1); генерация случайного значения 0 или 1, далее идёт короткая запись переключателя if
    // если сгенерируется 0 то скорость умножим на 1, если сгенерируется 0 то скорость умножим на -1
    Vx[i]=random(1, 5)*(int(random(0, 2)) == 0 ? 1 : -1);
    Vy[i]=random(1, 5)*(int(random(0, 2)) == 0 ? 1 : -1);
    c[i]=color(random(360), 100, 100);
  }
}

void draw() {
  background(0);
  for (int i=0; i<N; i++)
  {
    fill(c[i]);
    circle(x_c[i], y_c[i], 2*R[i]);
    x_c[i]=x_c[i]+Vx[i];
    y_c[i]=y_c[i]+Vy[i];
    if (x_c[i] + R[i] >= width || x_c[i] - R[i] <= 0)
      Vx[i]=-Vx[i];
    if (y_c[i] + R[i] >= height || y_c[i] - R[i] <= 0)
      Vy[i]=-Vy[i];
  }
}

Далее разнообразим нашу анимацию, пусть при соударении молекулы с границей окна, она будет “отдавать” свою энергию и уменьшаться в размерах, когда она уменьшится до нуля то “возродится”.

// количество молекул
int N=10;
// массив координат центра окружностей
float[] x_c= new float[N];
float[] y_c= new float[N];
// массив радиуса окружностей в пикселях
float[] R= new float[N];
// массив скоростей наших молекул
float[] Vx= new float[N];
float[] Vy= new float[N];
// массив цветов молекул
color[] c= new color[N];

void setup() {
  size(800, 800);
  colorMode(HSB, 360, 100, 100);
  background(0);
  // установка начальных параметров молекул
  for (int i=0; i<N; i++)
  {
    R[i]=random(20, 60);
    //генерация координаты от размеров молекулы до ширины - размеры молекулы, 
//чтобы молекулы не генерировались за пределами окна
x_c[i]=random(R[i], width - R[i]); y_c[i]=random(R[i], height - R[i]); // ((int)random(0,2) == 0 ? 1 : -1); генерация рандомного значения 0 или 1, далее идёт короткая запись переключателя if // если сгенерируется 0 то скорость умножим на 1, если сгенерируется 0 то скорость умножим на -1 Vx[i]=random(1, 5)*(int(random(0, 2)) == 0 ? 1 : -1); Vy[i]=random(1, 5)*(int(random(0, 2)) == 0 ? 1 : -1); c[i]=color(random(360), 100, 100); } } void draw() { background(0); for (int i=0; i<N; i++) { fill(c[i]); circle(x_c[i], y_c[i], 2*R[i]); x_c[i]=x_c[i]+Vx[i]; y_c[i]=y_c[i]+Vy[i]; if (x_c[i] + R[i] >= width || x_c[i] - R[i] <= 0) { Vx[i]=-Vx[i]; // если молекула ударилась о стенку окна уменьшаем её диаметр на 10 пикселей R[i] = R[i] - 5; } if (y_c[i] + R[i] >= height || y_c[i] - R[i] <= 0) { Vy[i]=-Vy[i]; // если молекула ударилась о стенку окна уменьшаем её диаметр на 10 пикселей R[i] = R[i] - 5; } if (R[i]<=0) { R[i]=random(20, 60); //генерация координаты от размеров молекулы до ширины - размеры молекулы,
//чтобы молекулы не генерировались за пределами окна
x_c[i]=random(R[i], width - R[i]); y_c[i]=random(R[i], height - R[i]); } } }

Ну и напоследок, визуализируем связи между нашими молекулами, нарисовав линии между ними:

// количество молекул
int N=10;
// массив координат центра окружностей
float[] x_c= new float[N];
float[] y_c= new float[N];
// массив радиуса окружностей в пикселях
float[] R= new float[N];
// массив скоростей наших молекул
float[] Vx= new float[N];
float[] Vy= new float[N];
// массив цветов молекул
color[] c= new color[N];

void setup() {
  size(800, 800);
  colorMode(HSB, 360, 100, 100);
  background(0);
  // установка начальных параметров молекул
  for (int i=0; i<N; i++)
  {
    R[i]=random(20, 60);
    //генерация координаты от размеров молекулы до ширины - размеры молекулы, 
//чтобы молекулы не генерировались за пределами окна
x_c[i]=random(R[i], width - R[i]); y_c[i]=random(R[i], height - R[i]); // ((int)random(0,2) == 0 ? 1 : -1); генерация рандомного значения 0 или 1, далее идёт короткая запись переключателя if // если сгенерируется 0 то скорость умножим на 1, если сгенерируется 0 то скорость умножим на -1 Vx[i]=random(1, 5)*(int(random(0, 2)) == 0 ? 1 : -1); Vy[i]=random(1, 5)*(int(random(0, 2)) == 0 ? 1 : -1); c[i]=color(random(360), 100, 100); } } void draw() { background(0); for (int i=0; i<N; i++) { fill(c[i]); circle(x_c[i], y_c[i], 2*R[i]); x_c[i]=x_c[i]+Vx[i]; y_c[i]=y_c[i]+Vy[i]; if (x_c[i] + R[i] >= width || x_c[i] - R[i] <= 0) { Vx[i]=-Vx[i]; // если молекула ударилась о стенку окна уменьшаем её диаметр на 10 пикселей R[i] = R[i] - 5; } if (y_c[i] + R[i] >= height || y_c[i] - R[i] <= 0) { Vy[i]=-Vy[i]; // если молекула ударилась о стенку окна уменьшаем её диаметр на 10 пикселей R[i] = R[i] - 5; } if (R[i]<=0) { R[i]=random(20, 60); //генерация координаты от размеров молекулы до ширины - размеры молекулы,
//чтобы молекулы не генерировались за пределами окна
x_c[i]=random(R[i], width - R[i]); y_c[i]=random(R[i], height - R[i]); } } }