Draw lines between nodes placed on the circumference.
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'.
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.
- nodes
- minimum distance
- 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;
}
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;
}
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;
const ringNum = 6;
const nodeNumMin = 6;
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));
}
And next, you can change the look by changing the condition of the drawing line.
drawNodeGarden(nodes, baseDist * 0.51, baseDist * 0.52);
drawNodeGarden(nodes, baseDist * 0.14, baseDist * 0.15);
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);
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;
The 'p5.js/Processing' example codes
I wrote an application code in 'p5.js/Processing'. The license is 'CC0'. You can use these freely.
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);
}
}
}
}