How to give circles(ellipses) certain distance, apart from each other? giving random position every time I press key

Hi.
what I want to do is,
Every time I press key, I want to reposition my object inside screen, but each of my object should be apart from each other by certain distance. for example at least the distance should be larger than 50.

this is some code that I tried, but I think I have problems with while loop.

I made class Attractor, attractors[] which need to be repositioned when keypressed.

I hope my question is understandable.

Mover movers[] = new Mover[1000];
Attractor attractors[]=new Attractor[5];

void setup() {
  size(300, 400, P3D);
  for (int i=0; i<attractors.length; i++) {
    attractors[i] = new Attractor(random(0.1, 2), random(0, width), random(0, height), random(-100, 100));
  }
}

void draw() {
  background(255);
  for (int i=0; i<attractors.length; i++) {
    attractors[i].display();
  }
  
}

void keyPressed() {
for (int i=0; i<attractors.length; i++) {
    for (int j=0; j<attractors.length; j++) {
     PVector f = PVector.sub(attractors[i].loc,attractors[j].loc);
    float distance = f.mag();
      if (i!=j) {
        while (true) {
          attractors[j].loc.x = random(0, width);
          attractors[j].loc.y = random(0, height);
          attractors[j].loc.z = 0;
          if (distance>=50) {
            break;
          }    
        }

        println(distance);
      }
    }
  
  }
}
class Attractor {
  PVector loc;
  float mass;
  float G;
  Attractor(float m, float x, float y,float z) {
    loc = new PVector(x, y, z);
    mass = m;
    G=0.4;
  }
  PVector attract(Mover m){
  PVector force = PVector.sub(loc,m.loc);
  float distance = force.mag();
  distance = constrain(distance,5.0,25.0);
  force.normalize();
  float strength = (G*mass*m.mass)/(distance*distance);
  force.mult(strength);
  return force;
  }
void display(){
  pushMatrix();
  translate(loc.x,loc.y,loc.z);
  sphere(5);
  popMatrix();
  }
  
}

this is class Attractor

If you want to create a system with maximum number of circes in a space while being evenly spaced you should create an algorithm that creates circles like this
image
black is random, pick 1 pit of 2 dark red (then the other), draw red ones, then orange and finally yellow. Continue this infinetly and voila

Poisson-Disc Sampling is a go-to technique for this problem.

Poisson-disc sampling produces points that are tightly-packed, but no closer to each other than a specified minimum distance, resulting in a more natural pattern.

import java.util.Vector;
import java.util.LinkedList;

PoissonDistribution points = new PoissonDistribution();

int minimumDistance = 50;

void setup() {
  size(800, 800);
  fill(200, 40, 80);
  points.generate(0, 0, width, height, minimumDistance, 15);
}

void draw() {
  background(255);
  for (PVector coord : points.getPoints()) {
    ellipse(coord.x, coord.y, minimumDistance - 10, minimumDistance - 10);
  }
}

void keyPressed() {
  points.generate(0, 0, width, height, minimumDistance, 15);
}

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) && testGrid(n, minDist)) {
          processList.add(n);
          _points.add(n);
          addToGrid(n);
        }
      }
    }

    return _points;
  }

  private boolean insideBoundaries(PVector p)
  {
    return (p.x >= _xmin && p.x < _xmax && p.y >= _ymin && p.y < _ymax);
  }

  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;
}

Is efficiency an issue here, just wandering, otherwise the last post has you covered.

oh I think i made my question unclear. I mean, the spacing doesn’t have to be even. it can be random but the spacing should be no less than for example 50.(I choose the minimum value). thank you for your reply though.

not really. I was just playing around with attractor and got stuck in this problem.

thank you for the solution. this technique is new to me. I got something new to study.
but does this technique give dynamic changes in position???

ezgif.com-gif-maker

this is something I have done. this is close to what i want, but the spacing needs to be at least [some value that i choose] for example 50. other than that spacing can be big or small. doesn’t have to be even. right now minimum value of spacing is not set.

Mover movers[] = new Mover[1000];
Attractor attractors[]=new Attractor[5];

void setup() {
  size(800, 400, P3D);
  for (int i=0; i<attractors.length; i++) {
    attractors[i] = new Attractor(random(0.1, 2), random(0, width), random(0, height), random(-100, 100));
  }
}

void draw() {
  background(255);
  for (int i=0; i<attractors.length; i++) {
    attractors[i].display();
    attractors[i].update();
   
  }
}

void keyPressed(){
  for(int i=0;i<attractors.length;i++){
attractors[i].targetloc = new PVector(random(0,width),random(0,height),random(-100,0));
 
  }
}
class Attractor {
  PVector prevloc;
  PVector targetloc;
  float mass;
  float G;
  Attractor(float m, float x, float y,float z) {
    prevloc = new PVector(x, y, z);
    targetloc = new PVector(width/2,height/2,0);
    mass = m;
    G=0.4;
  }
  PVector attract(Mover m){
  PVector force = PVector.sub(prevloc,m.loc);
  float distance = force.mag();
  distance = constrain(distance,5.0,25.0);
  force.normalize();
  float strength = (G*mass*m.mass)/(distance*distance);
  force.mult(strength);
  return force;
  }
  
  void update(){
 prevloc.lerp(targetloc,0.1);
  }
void display(){
  pushMatrix();
  noStroke();
  fill(255,223,0);
  translate(prevloc.x,prevloc.y,prevloc.z);
  sphere(5);
  popMatrix();
  }

}

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;
}

it looks wonderful. when I was considering 3d i dont want overlap. But if it’s going to be 2d, I think it looks great. Thank you so much!! I love how it looks.