👉 この記事は日本語でも読めます。


These two different look circle packing.

These are two similar artworks using the circle packing method. You'll get a different impression from these each, don't you?

 

What is the 'Circle Packing'?

The 'Circle Packing' is one of the 'Packing problems'.

Packing problems : Wikipedia

In creative coding, you'll often see the algorithm that packs the circles in moderation other than the algorithm that solves the packing problems mathematically in strict.

'Place and grow' is one of these algorithms that pack the circles in moderation.

  1. Place : Get the random location in the area and place the small circle if there is no circle yet.
  2. Do below to each circle in the area.
  3. Grow : Make little bit bigger the circle if it does not contact other circles.
  4. Return to 1.

Mr. Daniel Shiffman describes this algorithm in 'The Coding Train'.


And you'll see only 'Place' without 'Grow' will pack in its own way.


Extra.
Mr. Takawo Shunsuke(@takawo) amazingly packed any shape!


👉 The article of the original idea "Non Circular Packing" by Mr Jeff++(@ippsketch).


Here is the example p5.js code of the circle packing with only 'Place' without 'Grow'.


const w = 640;
const h = 640;
const num = 2000; // trial number

function setup() {
  createCanvas(w, h);

  // circle packing
  const circles = getRandomCircles(num, w * 0.4, h * 0.4);

  // drawing
  translate(w * 0.5, h * 0.5);
  background(100);
  circles.forEach((c) => circle(c.x, c.y, c.z));
}

function getRandomCircles(_num, _w, _h) {
  let circles = [];
  for (let i = 0; i < _num; i++) {
    let x = random(-1, 1) * _w;
    let y = random(-1, 1) * _h;
    let z = random(10, 50); // z axis as radius of the circle
    if (circles.every((c) => dist(x, y, c.x, c.y) > (z + c.z) * 0.5)) {
      circles.push(createVector(x, y, z));
    }
  }
  return circles;
}


'Only place without grow' method will create many open gaps if you set a small trial number.



 

Circle packing on the grid.

I located the circle completely randomly in the example code above.

let x = random(-1, 1) * _w;
let y = random(-1, 1) * _h;

I'll change to locate the circle on the random point on the grid.

const gridDiv = 15;
let x = floor(random(-gridDiv, gridDiv)) / gridDiv * _w;
let y = floor(random(-gridDiv, gridDiv)) / gridDiv * _h;


These two similar artworks are the example results of the 'Completely random' and 'Random on the grid'.



You'll get a different impression if you change circle size or grid gap.



 

Circle packing on the various placing.

You can make various placing with the same idea of 'Circle packing on the grid'.

let rnd = random(-1, 1);
let x = rnd * _w;
let y = rnd * _h;

let rnd = random(TWO_PI);
let x = cos(rnd) * _w;
let y = sin(rnd) * _h;

for (let i = 0; i < _num; i++) {
  let rnd = 3 * TWO_PI * i / _num;
  let x = cos(rnd) * _w * i / _num;
  let y = sin(rnd) * _h * i / _num;

 

The example artwork.

It's an example of the circle packing on the noise field (vector field, flow field).


It's a drawing of the noise field.


The combination looks nice also.


 

The example 'p5.js' code of this artwork.







Please feel free to use this example code under the terms of the GPL. To see other works based on my code is my pleasure. And my honor.


const w = 570;
const h = 570;

function setup() {
  createCanvas(w, h);
  translate(w * 0.5, h * 0.5);
  background(100);

  // draw noise field and circle pack
  noFill();
  stroke(250);
  strokeWeight(3);
  const circles = getNoiseField(100, w * 0.4, h * 0.4, min(w, h) * 0.15, 5);

  // draw the circles
  fill(250);
  stroke(0);
  strokeWeight(1);
  circles.forEach((c) => {
    circle(c.x, c.y, c.z);
    if (c.z > 10) {
      circle(c.x, c.y, c.z * 0.5);
    }
  });
}

function getNoiseField(_num, _w, _h, _iDiv, _step) {
  const circles = [];
  for (let iX = -_w; iX < _w; iX += _iDiv) {
    for (let iY = -_h; iY < _h; iY += _iDiv) {
      let x = iX;
      let y = iY;
      beginShape();
      for (let i = 0; i < _num; i++) {
        let nVal = noise(x * 0.001, y * 0.001) * 2;
        x += _step * cos(nVal * TWO_PI);
        y += _step * sin(nVal * TWO_PI);
        vertex(x, y);
        let z = random(_step, _step * 5);
        if (circles.every((c) => dist(x, y, c.x, c.y) > (z + c.z) * 0.5)) {
          circles.push(createVector(x, y, z));
        }
      }
      endShape();
    }
  }
  return circles;
}

/*
Copyright (C) 2021- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/


This code draws noise field lines and calculates the circle packing at the same time in the function 'getNoiseField'. I think it is lame and I know you'll fix it and make it a cool code. 😌