Задача 1.
Движение двух шаров на плоскости. (по шарикам можно "ударить" мышью придав
начальную скорость; изменить их размер и массу и "трение"; выводятся координаты и скорости
шаров)
Исполнение онлайн:
https://openprocessing.org/sketch/2649091 - ВНИМАНИЕ
! Слайдеры не работают, к сожалению...
Вид экрана:

Программа (нужно скопировать и вставить в окно редактора программы
Processing):
import controlP5.*;
ControlP5 cp5;
// Параметры шаров (одинаковые, управляются слайдерами)
float friction = 1; // "трение" - вернее какая часть
скорости остается у шариков при переходе на следующий цикл расчета и отрисовки
экрана. 0 - шары стоят. 1- "движутся вечно".
float ballRadius = 0.01; // Радиус в метрах
float ballMass = 0.1; // Масса в кг
// Шар 1
float ball1X, ball1Y;
float ball1SpeedX = 0, ball1SpeedY = 0;
// Шар 2
float ball2X, ball2Y;
float ball2SpeedX = 0, ball2SpeedY = 0;
// Константы
int gameAreaHeight = 350; // размер области движения шара в пикселях
final float PIXELS_TO_METERS = 0.0001f; // 1 пиксель = 0.1 мм = 0.0001 м
color[] ballColors = {color(255, 100, 100), color(100, 100, 255)}; // Красный
и синий - цвета шаров
void setup() {
size(600, 500); // Инициализация позиций шаров
ball1X = width/3 * PIXELS_TO_METERS;
ball1Y = gameAreaHeight/2 * PIXELS_TO_METERS;
ball2X = 2*width/3 * PIXELS_TO_METERS;
ball2Y = gameAreaHeight/2 * PIXELS_TO_METERS;
smooth();
textSize(12);
// Слайдеры для ввода данных
cp5 = new ControlP5(this);
cp5.addSlider("friction")
.setPosition(120, gameAreaHeight + 15)
.setSize(200, 20)
.setRange(0, 1)
.setValue(0.98)
.setLabel("Трение")
.setColorCaptionLabel(0);
cp5.addSlider("ballMass")
.setPosition(120, gameAreaHeight + 45)
.setSize(200, 20)
.setRange(9.11e-31, 1.0)
.setValue(0.1)
.setLabel("Масса (кг)")
.setColorCaptionLabel(0);
cp5.addSlider("ballRadius")
.setPosition(120, gameAreaHeight + 75)
.setSize(200, 20)
.setRange(1e-10, 0.05)
.setValue(0.01)
.setLabel("Радиус (м)")
.setColorCaptionLabel(0);
}
void draw() {
background(35, 100, 30);
// Игровая зона движения шаров с желтой рамкой
noFill();
stroke(255, 255, 0);
strokeWeight(1);
rect(0, 0, width-1, gameAreaHeight-1);
// Обработка физики для обоих шаров
updateBall(1); // Шар 1
updateBall(2); // Шар 2
// Проверка столкновений между шарами
checkBallCollision();
// Вывод данных
displayStats();
}
void updateBall(int ballNum) {
// Выбираем параметры нужного шара
float x, y, speedX, speedY;
if (ballNum == 1) {
x = ball1X;
y = ball1Y;
speedX = ball1SpeedX;
speedY = ball1SpeedY;
} else {
x = ball2X;
y = ball2Y;
speedX = ball2SpeedX;
speedY = ball2SpeedY;
}
// Обновляем позицию
x += speedX * (1.0/60.0);
y += speedY * (1.0/60.0);
// Применяем "трение"
speedX *= friction;
speedY *= friction;
// Отскок от стен
if (x > width * PIXELS_TO_METERS - ballRadius || x < ballRadius) {
speedX *= -1;
}
if (y > gameAreaHeight * PIXELS_TO_METERS - ballRadius || y < ballRadius) {
speedY *= -1;
}
// Корректировка позиции
x = constrain(x, ballRadius, width * PIXELS_TO_METERS - ballRadius);
y = constrain(y, ballRadius, gameAreaHeight * PIXELS_TO_METERS - ballRadius);
// Сохраняем изменения
if (ballNum == 1) {
ball1X = x;
ball1Y = y;
ball1SpeedX = speedX;
ball1SpeedY = speedY;
} else {
ball2X = x;
ball2Y = y;
ball2SpeedX = speedX;
ball2SpeedY = speedY;
}
// Отрисовка шара
float displayRadius = ballRadius / PIXELS_TO_METERS;
fill(ballColors[ballNum-1]);
noStroke();
ellipse(
x / PIXELS_TO_METERS,
y / PIXELS_TO_METERS,
displayRadius * 2,
displayRadius * 2
);
// Отрисовка кия при клике
if (mousePressed && mouseY < gameAreaHeight) {
float distToBall = dist(mouseX, mouseY,
x / PIXELS_TO_METERS,
y / PIXELS_TO_METERS);
if (distToBall < displayRadius * 2) {
stroke(200, 200, 0);
strokeWeight(3);
line(
x / PIXELS_TO_METERS,
y / PIXELS_TO_METERS,
mouseX,
mouseY
);
}
}
}
void checkBallCollision() {
// Расстояние между центрами шаров
float dx = ball1X - ball2X;
float dy = ball1Y - ball2Y;
float distance = sqrt(dx*dx + dy*dy);
// Если шары пересекаются
if (distance < 2 * ballRadius) {
// Нормализованный вектор столкновения
float nx = dx / distance;
float ny = dy / distance;
// Относительная скорость
float vx = ball1SpeedX - ball2SpeedX;
float vy = ball1SpeedY - ball2SpeedY;
// Импульс столкновения
float impulse = 2 * (vx * nx + vy * ny) / (1/ballMass + 1/ballMass);
// Обмен скоростями (упругое столкновение)
ball1SpeedX -= impulse * nx / ballMass;
ball1SpeedY -= impulse * ny / ballMass;
ball2SpeedX += impulse * nx / ballMass;
ball2SpeedY += impulse * ny / ballMass;
// Разделяем шары, чтобы они не застревали
float overlap = 2 * ballRadius - distance;
ball1X += overlap * nx * 0.5;
ball1Y += overlap * ny * 0.5;
ball2X -= overlap * nx * 0.5;
ball2Y -= overlap * ny * 0.5;
}
}
void displayStats() {
// Расчёт энергий
float speed1 = sqrt(ball1SpeedX*ball1SpeedX + ball1SpeedY*ball1SpeedY);
float speed2 = sqrt(ball2SpeedX*ball2SpeedX + ball2SpeedY*ball2SpeedY);
float energy1 = 0.5 * ballMass * speed1 * speed1;
float energy2 = 0.5 * ballMass * speed2 * speed2;
// Вывод данных
fill(255);
textAlign(LEFT);
String stats = String.format(
"Шар 1: X=%5.3f м Y=%5.3f м VX=%5.3f м/с VY=%5.3f м/с\n" +
"Шар 2: X=%5.3f м Y=%5.3f м VX=%5.3f м/с VY=%5.3f м/с\n" +
"Масса: %.3e кг Радиус: %.3e м\n" +
"Энергия 1: %.3e Дж Энергия 2: %.3e Дж",
ball1X, ball1Y, ball1SpeedX, ball1SpeedY,
ball2X, ball2Y, ball2SpeedX, ball2SpeedY,
ballMass, ballRadius, energy1, energy2
);
text(stats, 20, gameAreaHeight + 110);
// Подписи слайдеров
fill(255);
text("Трение:", 20, gameAreaHeight + 25);
text("Масса:", 20, gameAreaHeight + 55);
text("Радиус:", 20, gameAreaHeight + 85);
}
void mouseReleased() {
// Удар по шарам
for (int i = 1; i <= 2; i++) {
float ballX = (i == 1) ? ball1X : ball2X;
float ballY = (i == 1) ? ball1Y : ball2Y;
float displayRadius = ballRadius / PIXELS_TO_METERS;
if (dist(mouseX, mouseY, ballX / PIXELS_TO_METERS, ballY / PIXELS_TO_METERS) <
displayRadius * 2) {
if (i == 1) {
ball1SpeedX = (ball1X - mouseX * PIXELS_TO_METERS) * 60 * 0.1;
ball1SpeedY = (ball1Y - mouseY * PIXELS_TO_METERS) * 60 * 0.1;
} else {
ball2SpeedX = (ball2X - mouseX * PIXELS_TO_METERS) * 60 * 0.1;
ball2SpeedY = (ball2Y - mouseY * PIXELS_TO_METERS) * 60 * 0.1;
}
}
}
}
