Populate area with circles based on image no overlap

I’m sure I saw someone with the same question on here not too long ago, but I can’t find it for the life of me.

I guess what Im trying to achieve is circle packing but with a more efficient way of filling the space rather than randomly waiting for a free space to be “picked” like in Daniel Shiffman’s coding challenge.

I’d also want to keep the circles all the same size.

thanks for any help

1 Like
1 Like

Hi @TimoL,

If I understand correctly, you would like to have:

  • circles of same size
  • packed (no overlapping)
  • within a specific area
  • almost instantly

One solution would be to display circles at each corner of a grid spreading all over the canvas. The unit size of that grid would be the desired diameter of your circles. You could then create/load a specific shape with the Geomerative library and only draw the circles whose location is in that shape.

Annotated example sketch (Python mode)

add_library('geomerative')

W, H = 1000, 600 #Dimensions of canvas
D = 8 #Desired diameter (circle)
w, h = W/D, H/D  #Scaled dimensions


def setup():
    size(W, H, P2D)
    background(255)
    smooth(8)
    
    RG.init(this) #Intitialize the library
     
    rpoly = RPolygon() #Create an empty polygon
    
    #Adding vertices (heart formula)
    for i in xrange(360):
        x = 16 * pow(sin(radians(i)), 3) 
        y = 13 * cos(radians(i)) - 5 * cos(2 * radians(i)) - 2 * cos(3 * radians(i)) - cos(4 * radians(i))
        rpoly.addPoint(RPoint(width/2 + x*10, height/2 - y*10))
   
    #Create a RShape object from polygon
    heart = RShape(rpoly.toShape())
    
    #Display circles all over the canvas
    for i in xrange(w * h):
        x = (i%w) * D
        y = (i/w) * D
        
        #Only draws those within the heart shape
        if heart.contains(RPoint(x, y)):
            ellipse(x, y, D, D)

1 Like

Thanks @solub
Yea those are the requirements.
The grid idea will work I suppose but I’m not sure if that’s optional packing.
Also I think I’d prefer a more irregular arrangement.

I wonder if I add a bit of random to the grid arrangement perhaps?

According to Wikipedia:

the highest-density lattice arrangement of circles is the hexagonal packing arrangement,[2] in which the centres of the circles are arranged in a hexagonal lattice (staggered rows, like a honeycomb), and each circle is surrounded by 6 other circles.

So you could use the same sketch and just add an offset:

add_library('geomerative')

W, H = 1000, 600 #Dimensions of canvas
D = 8 #Desired diameter (circle)
w, h = W/D, H/D  #Scaled dimensions


def setup():
    size(W, H, P2D)
    background(255)
    smooth(8)
    
    RG.init(this) #Intitialize the library
     
    rpoly = RPolygon() #Create an empty polygon
    
    #Adding vertices (heart formula)
    for i in xrange(360):
        x = 16 * pow(sin(radians(i)), 3) 
        y = 13 * cos(radians(i)) - 5 * cos(2 * radians(i)) - 2 * cos(3 * radians(i)) - cos(4 * radians(i))
        rpoly.addPoint(RPoint(width/2 + x*10, height/2 - y*10))
   
    #Create a RShape object from polygon
    heart = RShape(rpoly.toShape())
    
    #Display circles all over the canvas
    for i in xrange(w * h):
        x = (i%w) * D
        y = (i/w) * D
        
        if (i/w)%2 == 0:
            offset = D/2
        else:
            offset = 0
        
        #Only draws those within the heart shape
        if heart.contains(RPoint(x + offset, y)):
            ellipse(x + offset, y, D, D)

If you prefer an irregular look, Poisson-Disk Sampling could also be an option, but the packing won’t be optimal.

EDIT: Java version without dependencies

int W = 1000, H = 600; //Dimensions of canvas
int D = 8; //Desired diameter (circle)
int w = W/D, h = H/D;  //Scaled dimensions

PShape heart;
float offset;

void setup(){
  size(1000, 600, P2D);
  background(255);
  
  //Create heart shape (heart formula)
  heart = createShape();
  heart.beginShape();
  for (int i = 0; i < 360; i++){
    float x = 16 * pow(sin(radians(i)), 3);
    float y = 13 * cos(radians(i)) - 5 * cos(2 * radians(i)) - 2 * cos(3 * radians(i)) - cos(4 * radians(i));
    heart.vertex(width/2 + x*10, height/2 - y*10);
  }
  heart.endShape();
  
  //Grid coordinates
  for (int i = 0; i < w*h; i++){
    float x = (i%w);
    float y = (i/w);
    
    //Offset
    offset = (y%2 == 0) ? D/2 : 0;
    
    //Only draw circles within the heart shape
    if (contains(heart, x*D + offset, y*D)){
      ellipse(x*D + offset, y*D, D, D);
    }
    
  }
  
  noLoop();
  
  
}



  

//Function to determine whether a point is inside a shape/polygon or not
boolean contains(PShape shp, float x, float y){
    int N = shp.getVertexCount();
    boolean c = false;
    for (int i = 0; i < N-1; i++){
        PVector v1 = shp.getVertex(i+1);
        PVector v2 = shp.getVertex(i);
        if ((v2.y > y) != (v1.y > y)){
            if (x < (v1.x - v2.x) * (y - v2.y) / (v1.y - v2.y) + v2.x){
                c = !c;
             }
         }
    }
    return c;
}
1 Like

This is exactly what I was looking for :slight_smile:
Thanks so much!