L-system trial code made with p5.js
I was encouraged to try drawing some shapes with the L-system by the article by Gin (@gin_graphic) san.
【p5.js】L-systemで木を描く - ギンの備忘録
And I was more interested in the geometric shape than in imitating the natural trees.
Drawing with the L-system can vary depending on the combination of parameters. And it is hard for me to predict the result by the parameters.
I've created the L-system trial code in p5.js to find the attractive drawing result out of such an infinite number of combinations.
Totally different results just by changing the angle
I was surprised by the results when I changed the angle value in the code of the article by Gin-san as I mentioned before.
let distance = 20;
let angle = radians(90);
let distance = 20;
let angle = radians(120);
When I changed the production rule, it drew a different geometrical shape. These geometrical shapes attract me. The combination of the production rule, the number of recursions, and the angle to draw the branch create various result shapes.
Well, it would allow me to play endlessly.
L-system trial code
I've written 'L-system trial code' in p5.js. It'll help me to find the combination of the production rule, the number of recursions, and the angle to draw the branch that create attractive shape.
You can see the drawing result and the combination of the parameters on the screen. The Up-Down arrow key changes the number of recursions. The Left-Right arrow key changes the angle to draw the branch.
The production rule is important, and I don't want to lose it. So I decided to write it on the code.
/**
* L-system tester.
* ref. https://gin-graphic.hatenablog.com/entry/2022/12/20/000000
*
* @author @deconbatch
* @version 0.1
* @license GPL3
* p5.js 1.5.0
* created 2022.12.31
*/
const initProduction = '+F[+F]F';
const maxRepeat = 10;
const maxAngle = 180;
const w = 800;
const h = w;
let repeatNum = 2;
let rotateAngle = 15;
/**
* keyPressed : change parameters then re-draw.
*/
function keyPressed() {
if (keyCode == UP_ARROW) {
repeatNum++;
} else if (keyCode == DOWN_ARROW) {
repeatNum--;
} else if (keyCode == RIGHT_ARROW) {
rotateAngle += 15;
} else if (keyCode == LEFT_ARROW) {
rotateAngle -= 15;
}
repeatNum = constrain(repeatNum, 1, maxRepeat);
rotateAngle = constrain(rotateAngle, -maxAngle, maxAngle);
redraw();
}
/**
* setup : world famous setup function.
*/
function setup() {
createCanvas(w, h)
angleMode(DEGREES);
noLoop();
noFill();
stroke(0);
}
/**
* draw : generate the production then draw it.
*/
function draw() {
// generate the production
const production = genProduction(initProduction, repeatNum);
// fit size and position with canvas
const fit = fitting(production, rotateAngle);
// draw
background(240);
push();
translate(fit.x, fit.y);
strokeWeight(3 / repeatNum);
drawShape(production, rotateAngle, fit.z);
pop();
drawParams(initProduction, repeatNum, rotateAngle, fit.z);
}
/**
* genProduction : generate the production
*/
function genProduction(_baseP, _num) {
let word = 'F';
for (let i = 0; i < _num; i++) {
let replaced = word.replace(/F/g, _baseP);
word = replaced;
}
return word;
}
/**
* fitting : calculate the size and position to fit to the canvas
*/
function fitting(_prd, _angle) {
let len = 10; // size
let cX = 0; // center x
let cY = 0; // center y
let minX = width;
let minY = height;
let maxX = 0;
let maxY = 0;
let x = 0;
let y = 0;
let a = 0;
const saveX = [];
const saveY = [];
const saveA = [];
for (let c of _prd) {
switch (c) {
case "F":
x += len * sin(a);
y += len * cos(a);
break;
case "+":
a += _angle;
break;
case "-":
a -= _angle;
break;
case "[":
saveX.push(x);
saveY.push(y);
saveA.push(a);
break;
case "]":
x = saveX.pop();
y = saveY.pop();
a = saveA.pop();
break;
default:
break;
}
minX = min(minX, x);
minY = min(minY, y);
maxX = max(maxX, x);
maxY = max(maxY, y);
}
const rate = max((maxX - minX) / width, (maxY - minY) / height) * 1.5;
if (rate != 0) {
len /= rate;
const rminX = minX / rate;
const rminY = minY / rate;
const rmaxX = maxX / rate;
const rmaxY = maxY / rate;
cX = width * 0.5 - (rminX + (rmaxX - rminX) * 0.5);
cY = height * 0.5 - (rminY + (rmaxY - rminY) * 0.5);
}
return createVector(cX, cY, len);
}
/**
* drawShape : draw the shape
*/
function drawShape(_prd, _angle, _len) {
for (let c of _prd) {
switch (c) {
case "F":
line(0, 0, 0, _len);
translate(0, _len);
break;
case "+":
rotate(-_angle);
break;
case "-":
rotate(+_angle);
break;
case "[":
push();
break;
case "]":
pop();
break;
default:
break;
}
}
}
/**
* drawParams : draw parameters
*/
function drawParams(_f, _n, _a, _l) {
const siz = 12;
push();
noStroke();
fill(0);
textSize(siz);
textAlign(LEFT, CENTER);
textFont('monospace');
text('PRODUCTION:' + _f, 10, siz);
text(' REPEAT:' + _n, 10, siz * 2);
text(' ANGLE:' + _a, 10, siz * 3);
text(' LENGTH:' + _l, 10, siz * 4);
pop();
}
/*
Copyright (C) 2022- 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/>
*/
The combinations that I loved
Let me show you some combinations and the results.
Ghostie
I used 'blendMode(SCREEN)'.
- rule : -F[+F+F]-F
- reccursion : 8
- angle : 150 degree
Harvest
I combined the two results. I drew it with ellipse() and line().
- rule : F[[[+FF]-F[-F]]F]-F
- reccursion : 4
- angle : 13 degree, -9 degree
With line() without ellipse()
Abstract painting
Angle magic.
- rule : F[+F]-F[+F[-F]+F]
- reccursion : 5
- angle : 60.1 degree, 120.1 degree
Product development.
You can waste your time with L-system
You can't stop if you start with the L-system once because the little differences can lead to unexpected results.
You can waste your infinite time with my L-system trial code. It's so much fun!