You can get dynamic changes in position by lerping between two sets of points.
The start and end states meet the distance-apart condition, but as the circles are moving there will be overlap between them – it sounds like you don’t want this.
Circles applying a repulsion force to each other as they travel from their start to end destination may be a solution. In this case, the toxiclibs
library may be worth a look.
import java.util.Vector;
import java.util.LinkedList;
PoissonDistribution points = new PoissonDistribution();
Vector<PVector> pointsA;
Vector<PVector> pointsB;
int minimumDistance = 100;
PVector[] animate;
void setup() {
size(800, 800);
colorMode(HSB, 1);
noStroke();
pointsA = points.generate(0, 0, width, height, minimumDistance, 15);
pointsB = points.generate(0, 0, width, height, minimumDistance, 15);
animate = new PVector[(min(pointsA.size(), pointsB.size()))];
}
void draw() {
background(0, 0, 1);
float lerp = 1 - abs(millis()*0.0004 % (2) - 1); // 0...1 triangle wave
float smoothLerp = easeInOutQuint(lerp);
for (int i = 0; i < animate.length; i++) {
animate[i] = PVector.lerp(pointsA.get(i), pointsB.get(i), smoothLerp);
fill(i/(float) animate.length, 0.8, 1, 0.75);
ellipse(animate[i].x, animate[i].y, minimumDistance - 10, minimumDistance - 10);
}
}
void keyPressed() {
pointsA = points.generate(0, 0, width, height, minimumDistance, 15);
pointsB = points.generate(0, 0, width, height, minimumDistance, 15);
animate = new PVector[(min(pointsA.size(), pointsB.size()))];
}
static float easeInOutQuint(float x) {
return x < 0.5 ? 16 * x * x * x * x * x : 1 - pow(-2 * x + 2, 5) / 2;
}
class PoissonDistribution
{
/** From "Fast Poisson Disk Sampling in Arbitrary Dimensions"
* by Robert Bridson
* http://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf
**/
PoissonDistribution()
{
_points = new Vector<PVector>();
}
Vector<PVector> getPoints() {
return _points;
}
Vector<PVector> generate(float xmin, float ymin, float xmax, float ymax, float minDist, int rejectionLimit)
{
_xmin = xmin;
_xmax = xmax;
_ymin = ymin;
_ymax = ymax;
_cellSize = minDist / sqrt(2);
_gridWidth = ceil((xmax-xmin) / _cellSize);
_gridHeight = ceil((ymax-ymin) / _cellSize);
int s = _gridWidth * _gridHeight;
_grid = new ArrayList<Vector<PVector>>();
for (int i=0; i<s; i++)
_grid.add(new Vector<PVector>());
_points.clear();
LinkedList<PVector> processList = new LinkedList<PVector>();
PVector p = new PVector(random(_xmin, _xmax), random(_ymin, _ymax));
processList.add(p);
_points.add(p);
addToGrid(p);
while (processList.size() > 0)
{
int i = floor(random(processList.size()));
p = processList.get(i);
processList.remove(i);
for (i=0; i<rejectionLimit; i++)
{
PVector n = createRandomPointAround(p, minDist, minDist*2);
if (insideBoundaries(n, minDist/2) && testGrid(n, minDist)) {
processList.add(n);
_points.add(n);
addToGrid(n);
}
}
}
return new Vector<PVector>(_points);
}
private boolean insideBoundaries(PVector p, float border)
{
// return (p.x >= _xmin && p.x < _xmax && p.y >= _ymin && p.y < _ymax); // keep center points in bounds of sketch
return (p.x >= _xmin+border && p.x < _xmax-border && p.y >= _ymin+border && p.y < _ymax-border); // keep cicles in bounds of sketch
}
private PVector createRandomPointAround(PVector p, float minDist, float maxDist)
{
float a = random(2*PI);
float r = random(minDist, maxDist);
return new PVector(p.x + r * cos(a), p.y + r * sin(a));
}
// return true if there are no points inside the circle of minDist radius around p
private boolean testGrid(PVector p, float minDist)
{
int minX = floor(max(0, (p.x - minDist - _xmin) / _cellSize));
int maxX = ceil(min(_gridWidth - 1, (p.x + minDist - _xmin) / _cellSize));
int minY = floor(max(0, (p.y - minDist - _ymin) / _cellSize));
int maxY = ceil(min(_gridHeight - 1, (p.y + minDist - _ymin) / _cellSize));
for (int y=minY; y<=maxY; y++) {
for (int x=minX; x<=maxX; x++) {
Vector<PVector> cell = _grid.get(y * _gridWidth + x);
for (PVector t : cell)
if (dist(p.x, p.y, t.x, t.y) <= minDist)
return false;
}
}
return true;
}
private void addToGrid(PVector p)
{
_grid.get(index(p.x, p.y)).add(p);
}
protected int index(float x, float y)
{
int gx = floor((x - _xmin) / _cellSize);
int gy = floor((y - _ymin) / _cellSize);
return gy * _gridWidth + gx;
}
private ArrayList<Vector<PVector>> _grid;
private float _cellSize;
private int _gridWidth, _gridHeight;
private float _xmin, _xmax, _ymin, _ymax;
private Vector<PVector> _points;
}