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