
Начнём с создания алгоритма описывающего физические взаимодействия между объектами. Точки обладающие гравитацией располагаются в пространстве случайно, а так как они ещё и не перемещаются(двигаются только частицы в гравитационном поле точек), то я решил заранее создать список векторов расположенных в пространстве с заданным шагом, и гордо назвать это «векторной сеткой» :) Вектора считаются как сумма векторов сил притяжения создаваемых каждой «гравитационной точкой» в точке приложения вектора.


Шаг сетки 20.


Шаг сетки 10.
class GravGrid
{
int w = 0; /* Ширина сетки.*/
int h = 0; /* Высота сетки.*/
int step = 0; /* Размер стороны ячейки векторной сетки.
Ну или просто шаг. */
float G =0; /* Гравитационная постоянная. От неё
зависит как будет изменяться
сила притяжения частиц к точкам с
изменением расстояния между ними */
/* Тут у нас будут жить гравитационные точки */
public ArrayList points = new ArrayList();
ArrayList vectors; /* А тут вектора составляющие
векторную сетку */
/* Конструктор класса. С w, h, step и G все уже знакомы */
GravGrid(int w, int h, int step, float G)
{
this.w = w;
this.h = h;
this.step = step;
this.G = G;
}
public void CreateGrid() /* Метод для построение сетки */
{
vectors = new ArrayList(); /* На случай если CreateGrid()
вызвана повторно очищаем
список */
GravVector v; /* Вектор */
GravPoint p; /* Точка */
float xSum; /* Сумма проэкций сил на ось X */
float ySum; /* Сумма проэкций сил на ось Y */
float a = 0; /* Угол между точкой приложения вектора
и гравитационной точкой*/
for(int x = 0; x < w; x += step) /* Получаем точки
приложения векторов */
for(int y = 0; y < h; y += step)
{
xSum = 0;
ySum = 0;
v = new GravVector(x, y); /* Создаем новый экземпляр
класса GravVector*/
/* Проходимся по списку гравитационных точек */
for(int i = 0; i < points.size(); i++)
{
/* Получаем i - ую точку */
p = (GravPoint)points.get(i);
a = atan2((p.x - x), (p.y - y)); /* Вычисляем угол */
/* Вычисляем проекции на ось X и прибавляем к сумме.
dist(x1, y1, x2, y2) - вычисляет расстояние между
двумя точками */
xSum += p.Grav(dist(x, y, p.x, p.y), G) * sin(a);
/* Вычисляем проекции на ось Y и прибавляем к сумме.*/
ySum += p.Grav(dist(x, y, p.x, p.y), G) * cos(a);
}
v.Set(xSum, ySum); /* Устанавливаем
направление вектора */
vectors.add(v); /* и добавляем его в список */
}
}
public void DrawGrid() /* Метод для рисования сетки */
{
float arrowsize = 4; /* Размер стрелочки */
float len = 0; /* Длинна вектора */
GravVector v;
/* Проходимся по списку векторов */
for(int i = 0; i < vectors.size(); i++)
{
/* Получаем i - ый вектор */
v = (GravVector)vectors.get(i);
/* Тут дальше идёт хитрый метод рисования вектора,
который я подсмотрел из File -> Examples ->
Topics -> Simulate -> SimpleParticleSystem
могу сказать что pushMatrix() - это как бэ создание
дополнительной системы координат.
Если что, поправьте меня пожалуйста.*/
pushMatrix();
translate(v.x0, v.y0); /* Положение относительно
основной системы координат
v.x0, v.y0 - точки приложения
вектора */
stroke(255, 0, 0); /* Цвет линий */
/* Поворачиваем дополнительную систему координат
относительно основной v.Get() - возвращает PVector */
rotate(atan2(v.Get().y, v.Get().x));
len = v.Get().mag(); /* Получаем длину вектора */
/* Дальше занимаемся рисованием */
line(0,0,len,0);
line(len,0,len-arrowsize,+arrowsize/2);
line(len,0,len-arrowsize,-arrowsize/2);
popMatrix();
}
}
/* Возвращает вектор силы в заданной точке */
public PVector GetVector(float x, float y)
{
GravVector v;
for(int i = 0; i < vectors.size(); i++)
{
v = (GravVector)vectors.get(i);
if((v.x0 <= x)
&& (x < (v.x0 + step))
&& (v.y0 <= y)
&& (y < (v.y0 + step)))
return v.Get();
}
return new PVector(0, 0);
}
}
class GravPoint /* Гравитационная точка */
{
public int x = 0; /* Положение по X */
public int y = 0; /* по Y */
float mass = 0; /* Масса точка. Чем больше масса
тем сильнее к точке будут
притягиваться частицы */
GravPoint(int x, int y, float mass) /* Конструктор */
{
this.x = x;
this.y = y;
this.mass = mass;
}
/* Тут вычисляем силу притяжения.
Чем больше d(расстояние) тем меньше сила */
public float Grav(float d, float G) {return mass * G / d; }
}
class GravVector /* Вектор */
{
public int x0 = 0; /* Точка приложения по X */
public int y0 = 0; /* по Y */
public float x = 0; /* Точка приложения по X + проекция
вектора на ось X */
public float y = 0; /* тоже самое только Y */
GravVector(int x0, int y0) /* Конструктор */
{
this.x0 = x0;
this.y0 = y0;
}
/* Устанавливаем величину и направление вектора */
public void Set(float x, float y)
{
this.x = x0 + x;
this.y = y0 + y;
}
/* Возвращает PVector для экземпляра класса GravVector*/
public PVector Get() {return new PVector(x - x0, y - y0); }
}
* This source code was highlighted with Source Code Highlighter.
Использование:
int w;
int h;
GravGrid gg;
Particle[] s;
void setup()
{
size(800, 600, JAVA2D); /* рендер P2D, по утверждению разработчиков
работает быстрее чем JAVA2D,
но почему - то у меня всё наоборот */
background(255);
w = width;
h = height;
s = new Particle[50]; /* Массив с частицами */
gg = new GravGrid(w, h, 20, 40); /* Создаём сетку */
for(int i = 0; i < s.length; i++)
s[i] = new Particle();
gg.points = new ArrayList();
/* Добавляем точки */
for(int i = 0; i < 7; i++)
gg.points.add(new GravPoint(
(int)random(0, w), /* Положение точки по X */
(int)random(0, h), /* Положение точки по Y */
/* Из соображений зрелищности
у нас будут точки даже с отрицательной
гравитацией! :) */
(int)random(-120, 120)));
gg.CreateGrid(); /* Создаём сетку */
}
GravPoint p;
void draw()
{
/* Закрашиваем всё белым цветом с прозрачностью 20
для того что бы за частицами оставался крутой след :)
Чем меньше alpha, тем соответственно длиннее шлейф */
fill(255, 20);
noStroke();
rect(0, 0, w, h);
for(int i = 0; i < s.length; i++)
{
s[i].DrawParticle(gg); /* Рисуем i - ую частицу */
/* Если вылетела за границы окна, то создаём новую */
if(s[i].x > w
|| s[i].x < 0
|| s[i].y > h
|| s[i].y < 0)
{
s[i] = new Particle();
}
else
{
for(int t = 0; t < gg.points.size(); t++)
{
/* Получаем гравитационную точку */
p = (GravPoint)gg.points.get(t);
fill(255);
stroke(0);
/* рисуем её */
ellipse(p.x, p.y, abs(p.mass), abs(p.mass));
/* Если попала в точку, то создаём новую */
if((((p.x - (p.mass / 4)) <= s[i].x)
&& (s[i].x < (p.x + (p.mass / 4)))
&& ((p.y - (p.mass / 4)) <= s[i].y)
&& (s[i].y < (p.y + (p.mass / 4)))))
{
s[i] = new Particle();
}
}
}
}
if(keyPressed) if(key == '1') setup();
if(keyPressed) if(key == '2') gg.DrawGrid();
}
class Particle
{
float t = 0;
float m = random(3, 6);
float y = random(m, h);
float x = random(m, w);
float v0x = 0;
float v0y = 0;
public void DrawParticle(GravGrid gg)
{
t += 0.005;
x += v0x * t + (gg.GetVector(x, y).x / m) * pow(t, 2) / 2;
y += v0y * t + (gg.GetVector(x, y).y / m) * pow(t, 2) / 2;
fill(0, 0, 0, t * 300);
noStroke();
ellipse(x, y, m, m);
}
}
* This source code was highlighted with Source Code Highlighter.
Кстати если установить малый шаг сетки и поиграться с отображением векторов в DrawGrid(), то можно получить много клякс подобных этим:

