Draw lines between nodes placed on the circumference.

An example pieces of creative coding work with the 'Node Garden' technique.

The 'Node Garden' is the one of simple techniques of creative coding. It creates a beautiful pattern with a simple code.

I made example pieces of work with the 'Node Garden' technique in this article. I also wrote example codes. You can make your own works with it and have fun.

I used the 'p5.js' to describe the mechanism and put the complete codes of the 'p5.js' and the 'Processing' at the end of this article.

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

 

The example piece of creative coding work

The example piece of work in this article is 'Draw lines between nodes that are placed on the circumference'.

Drawing lines between nodes that are placed on the circumference.

I got this idea when I watched the YouTube movie 「Daily Coding Live Sessions #15 [ARTSCLOUD]」 by creative coder @takawo.

This YouTube series is the live performance of @takawo's creative coding. I watch these often and I'm interested in his way of creating his works and his thought during the coding.

The @takawo's code places the circles randomly, but I tried to place the circles regularly.

 

The basic code to create the 'Node Garden'

If you want to know what's the 'Node Garden' and how to create it, you can read my articles that explain it simply.


The basic code to create the 'Node Garden' is here.


/* 
 * The basic code to create the 'Node Garden'
 * p5.js
 * @author @deconbatch
 * @version 0.1
 * created 2022.05.04
 * license CC0 https://creativecommons.org/publicdomain/zero/1.0/
 */

function setup() {

  createCanvas(640, 640);
  
  const baseDist = min(width, height);
  const nodes = getNodes();
  drawNodeGarden(nodes, baseDist * 0.1, baseDist * 0.2);

  for (let n of nodes) {
    circle(n.x, n.y, 10);
  }

}

/* 
 * getNodes : returns the nodes array
 */
function getNodes() {
  const nodes = new Array();
  for (let i = 0; i < 50; i++) {
    nodes.push(
      createVector(
        random(width),
        random(height)
      )
    );
  }
  return nodes;
}

/* 
 * drawNodeGarden : draw the lines between nodes
 */
function drawNodeGarden(_nodes, _minDist, _maxDist) {
  for (let n of _nodes) {
    for (let m of _nodes) {
      let d = dist(n.x, n.y, m.x, m.y);
      if (d > _minDist && d < _maxDist) {
        line(n.x, n.y, m.x, m.y);
      }
    }
  }
}

I wrote functions for placing nodes (getNodes) and drawing (drawNodeGarden). So you can modify this code easily.

The 'getNodes()' function place the nodes randomly at this time. You can make your original 'getNodes()' function that places the nodes as you like.

The 'drawNodeGarden()' gets three parameters as below and draws line between nodes when the distance is within the minimum and the maximum value.

  1. nodes
  2. minimum distance
  3. maximum distance
I wrote simple but inefficient code. It draws line double.

The code below shows the place of nodes. You can check if the nodes place wrong. Of course, you can delete it if you don't need to check.


  for (let n of nodes) {
    circle(n.x, n.y, 10);
  }

 

Create a piece of work by changing the 'getNodes()' function

Let's place the 36 nodes on the circumference.


function getNodes() {
  const nodeNum = 36; // node number on the circumference
  const ringR = min(width, height) * 0.4; // radius

  const nodes = new Array();
  for (let nodeCnt = 0; nodeCnt < nodeNum; nodeCnt++) {
    let t = TWO_PI * nodeCnt / nodeNum;
    let x = ringR * cos(t);
    let y = ringR * sin(t);
    nodes.push(createVector(x, y));
  }
  return nodes;
}

Placing nodes on the circumference.

The placing of nodes looks good. Then make three different radius rings and place concentric.

The node number increases toward the outside.


function getNodes() {
  const ringNum = 3;     // ring number
  const nodeNumMin = 18; // node number on most inner ring
  const ringMaxR = min(width, height) * 0.4;

  const nodes = new Array();
  for (let ring = 0; ring < ringNum; ring++) {
    
    let ringR = ringMaxR * (ring + 1) / ringNum;
    let nodeNum = (ring + 1) * nodeNumMin;

    for (let nodeCnt = 0; nodeCnt < nodeNum; nodeCnt++) {
      let t = TWO_PI * nodeCnt / nodeNum;
      let x = ringR * cos(t);
      let y = ringR * sin(t);
      nodes.push(createVector(x, y));
    }
  }
  return nodes;
}

Looks like a wheel with too many spokes.

It looks like a wheel with too many spokes. The looks change when you change the number of nodes and the number of rings.


  const ringNum = 3;
  const nodeNumMin = 6;

An example of changing the number of nodes and the number of rings.


  const ringNum = 6;
  const nodeNumMin = 6;

An example of changing the number of nodes and the number of rings.

 

Make it sexy in the creative coding way

Let's tweak the code to make it sexy.

At first, let's move the center of the rings. I moved randomly this time. It may be fun if you shift these swirly.

'phaseT' means the direction of moving. 'phaseD' means the distance of moving.


  let phaseT = random(TWO_PI);
  let phaseD = ringMaxR * 0.25 / ringNum;

  for (let nodeCnt = 0; nodeCnt < nodeNum; nodeCnt++) {
    let t = TWO_PI * nodeCnt / nodeNum;
    let x = ringR * cos(t) + cos(phaseT) * phaseD;
    let y = ringR * sin(t) + sin(phaseT) * phaseD;
    nodes.push(createVector(x, y));
  }

An example of moving the center of the rings.

And next, you can change the look by changing the condition of the drawing line.


  drawNodeGarden(nodes, baseDist * 0.51, baseDist * 0.52);

An example of changing the condition of the drawing line.


  drawNodeGarden(nodes, baseDist * 0.14, baseDist * 0.15);

An example of changing the condition of the drawing line.

And let's color the lines. At first, make the color mode 'HSB'. And then change the hue value with the angle of the line.


function setup() {
  createCanvas(640, 640);
  colorMode(HSB, 360, 100, 100, 100); // make the color mode 'HSB'



  // change the hue value with the angle
  let lHue = map(atan2(m.y - n.y, m.x - n.x), -PI, PI, 0, 360);
  stroke(lHue, 60, 80, 100);
  line(n.x, n.y, m.x, m.y);

Colored node garden.

Too gaudy! It looks calm if you use the hue value within 120, not 360.


  // ex. 220(cyan) to 340(violet)
  let lHue = (220 + map(atan2(m.y - n.y, m.x - n.x), -PI, PI, 0, 120)) % 360;

An example of nice colored node garden.






 

The 'p5.js/Processing' example codes

I wrote an application code in 'p5.js/Processing'. The license is 'CC0'. You can use these freely.

An example pieces of work with the 'Node Garden' technique.

The example code of 'p5.js'


/* 
 * An example creative coding work of drawing lines between nodes that are placed on the circumference.
 * 
 * p5.js
 * @author @deconbatch
 * @version 0.1
 * created 2022.05.04
 * license CC0 https://creativecommons.org/publicdomain/zero/1.0/
 */

function setup() {

  createCanvas(980, 980);
  colorMode(HSB, 360, 100, 100, 100);
  smooth();
  noLoop();

  const ringNum = 26;  // ring number
  const nodeMin = 4;   // node number on most inner ring
  const minDist = 1.2; // min distance to draw nodes
  const maxDist = 1.8; // max distance to draw nodes
  const baseSiz = min(width, height) * 0.45;
  const baseHue = random(360);

  background((baseHue + 240) % 360, 90, 30, 100);
  noFill();
  translate(width * 0.5, height * 0.5);
  rotate(random(PI));
  drawNodeGarden(
    getNodes(baseSiz, ringNum, nodeMin),
    baseSiz * minDist / ringNum,
    baseSiz * maxDist / ringNum,
    baseHue
  );

}

/* 
 * getNodes : place the nodes and return nodes array
 * 
 * _ringMaxR   : max radius of the ring
 * _ringNum    : ring number
 * _nodeNumMin : node number on most inner ring
 */
function getNodes(_ringMaxR, _ringNum, _nodeNumMin) {

  const nodes = new Array();
  for (let ring = 0; ring < _ringNum; ring++) {
    
    let ringR   = _ringMaxR * (ring + 1) / _ringNum;
    let nodeNum = (ring + 1) * _nodeNumMin;
    let phaseT  = random(TWO_PI);
    let phaseD  = _ringMaxR * 0.5 / _ringNum;

    for (let nodeCnt = 0; nodeCnt < nodeNum; nodeCnt++) {
      let t = TWO_PI * nodeCnt / nodeNum;
      let x = ringR * cos(t) + cos(phaseT) * phaseD;
      let y = ringR * sin(t) + sin(phaseT) * phaseD;
      nodes.push(createVector(x, y));
    }
  }
  return nodes;
  
}

/* 
 * drawNodeGarden : draw between nodes by the distance condition
 * 
 * _nodes     : nodes array
 * _min, _max : draw line when _min < distance between nodes < _max
 * _hue       : base hue value
 */
function drawNodeGarden(_nodes, _min, _max, _hue) {

  strokeWeight(3);
  for (let i = 0; i < _nodes.length - 1; i++) {
    let n = _nodes[i];
    for (let j = i + 1; j < _nodes.length; j++) {
      let m = _nodes[j];
      let d = dist(n.x, n.y, m.x, m.y);
      if (d > _min && d < _max) {
        let lHue = _hue + map(d, _min, _max, 0, 120);
        stroke(lHue % 360, 60, 80, 100);
        line(n.x, n.y, m.x, m.y);
      }
    }
  }
  
}

 

The example code of 'Processing'


/* 
 * An example creative coding work of drawing lines between nodes that are placed on the circumference.
 * 
 * Processing 3.5.3
 * @author @deconbatch
 * @version 0.1
 * created 2022.05.04
 * license CC0 https://creativecommons.org/publicdomain/zero/1.0/
 */

void setup() {

  size(980, 980);
  colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  smooth();
  noLoop();

  int   ringNum = 26;   // ring number
  int   nodeMin = 4;    // node number on most inner ring
  float minDist = 1.2;  // min distance to draw nodes
  float maxDist = 1.8;  // max distance to draw nodes
  float baseSiz = min(width, height) * 0.45;
  float baseHue = random(360);

  background((baseHue + 240.0) % 360.0, 90.0, 30.0, 100.0);
  noFill();
  translate(width * 0.5, height * 0.5);
  rotate(random(PI));
  drawNodeGarden(
                 getNodes(baseSiz, ringNum, nodeMin),
                 baseSiz * minDist / ringNum,
                 baseSiz * maxDist / ringNum,
                 baseHue
                 );

}

/* 
 * getNodes : place the nodes and return nodes array
 * 
 * _ringMaxR   : max radius of the ring
 * _ringNum    : ring number
 * _nodeNumMin : node number on most inner ring
 */
ArrayList<PVector> getNodes(float _ringMaxR, int _ringNum, int _nodeNumMin) {

  ArrayList<PVector> nodes = new ArrayList();
  for (int ring = 0; ring < _ringNum; ring++) {
	
    float ringR   = _ringMaxR * (ring + 1) / _ringNum;
    int   nodeNum = (ring + 1) * _nodeNumMin;
    float phaseT  = random(TWO_PI);
    float phaseD  = _ringMaxR * 0.5 / _ringNum;

    for (int nodeCnt = 0; nodeCnt < nodeNum; nodeCnt++) {
	    float t = TWO_PI * nodeCnt / nodeNum;
	    float x = ringR * cos(t) + cos(phaseT) * phaseD;
	    float y = ringR * sin(t) + sin(phaseT) * phaseD;
	    nodes.add(new PVector(x, y));
    }
  }
  return nodes;
    
}

/* 
 * drawNodeGarden : draw between nodes by the distance condition
 * 
 * _nodes     : nodes array
 * _min, _max : draw line when _min < distance between nodes < _max
 * _hue       : base hue value
 */
void drawNodeGarden(ArrayList<PVector> _nodes, float _min, float _max, float _hue) {

  strokeWeight(3.0);
  for (int i = 0; i < _nodes.size() - 1; i++) {
    PVector n = _nodes.get(i);
    for (int j = i + 1; j < _nodes.size(); j++) {
      PVector m = _nodes.get(j);
	    float d = dist(n.x, n.y, m.x, m.y);
	    if (d > _min && d < _max) {
        float lHue = _hue + map(d, _min, _max, 0.0, 120.0);
        stroke(lHue % 360.0, 60.0, 80.0, 100.0);
        line(n.x, n.y, m.x, m.y);
	    }
    }
  }
    
}

 

Next Post Previous Post
No Comment
Add Comment
comment url