Skip to content

Instantly share code, notes, and snippets.

@brianfay
Created March 17, 2022 04:23
Show Gist options
  • Save brianfay/d909964aa48ba5728d0a31b286764a69 to your computer and use it in GitHub Desktop.
Save brianfay/d909964aa48ba5728d0a31b286764a69 to your computer and use it in GitHub Desktop.
p5.js implementation of a DOL system as described in "The Algorithmic Beauty of Plants"
function derive(axiom, rules) {
let out = '';
for (c of axiom) {
out += rules[c] || c;
}
return out
}
function deriveTimes(axiom, rules, n) {
let out = axiom;
while (n--) {
out = derive(out, rules);
}
return out;
}
function turtle(x, y, deg) {
return { x, y, deg };
}
let axiom = 'F-F-F-F';
let degrees = 90;
let rules = {
'F': 'F-F+F+FF-F-F+F',
}
function radians(deg) {
return 2.0 * Math.PI * deg;
}
const baseDistance = 500;
function setScale(system) {
const turt = turtle(0,0,0);
distance = baseDistance;
let minX = turt.x;
let minY = turt.y
let maxX = turt.x
let maxY = turt.y;
for (c of system) {
const {x, y} = turt;
switch (c) {
case 'F':
turt.x = x + (distance * Math.cos(radians(turt.deg)));
turt.y = y + (distance * Math.sin(radians(turt.deg)));
break;
case 'f':
turt.x = x + (distance * Math.cos(radians(turt.deg)));
turt.y = y + (distance * Math.sin(radians(turt.deg)));
break;
case '+':
turt.deg += degrees;
break;
case '-':
turt.deg -= degrees;
break;
default:
console.error('unrecognized case')
}
if (turt.x < minX) {
minX = turt.x;
}
if (turt.x > maxX) {
maxX = turt.x;
}
if (turt.y < minY) {
minY = turt.y;
}
if (turt.y > maxY) {
maxY = turt.y;
}
}
const distanceX = Math.abs(maxX - minX);
const distanceY = Math.abs(maxY - minY);
//scale distance:
const scaleX = distance / distanceX;
const scaleY = distance / distanceY;
distance *= scaleX; //doesn't guarantee that image stays in vertical bounds
//translate screen so that turtle will be centered:
translate((width / 2), (height / 2));
//circle(0,0,100,100);
const translateX = ((maxX + minX) / 2.0);
const translateY = ((minY + maxY) / 2.0);
translate(-scaleX * translateX, -scaleY*translateY);
}
function drawTurtle(system) {
const turt = turtle(0,0,0);
for (c of system) {
const {x, y} = turt;
switch (c) {
case 'F':
turt.x = x + (distance * Math.cos(radians(turt.deg)));
turt.y = y + (distance * Math.sin(radians(turt.deg)));
line(x, y, turt.x, turt.y );
break;
case 'f':
turt.x = x + (distance * Math.cos(radians(turt.deg)));
turt.y = y + (distance * Math.sin(radians(turt.deg)));
break;
case '+':
turt.deg += degrees;
break;
case '-':
turt.deg -= degrees;
break;
default:
console.error('unrecognized case')
}
}
}
let numIterationsSel;
function drawSystem() {
let system = deriveTimes(axiom, rules, numIterationsSel.value());
background(160);
setScale(system);
drawTurtle(system);
}
function changeNumIterations() {
drawSystem();
}
function changeAxiom() {
axiom = this.value();
drawSystem();
}
function changeRulesF() {
rules.F = this.value();
drawSystem();
}
function changeRulesf() {
rules.f = this.value();
drawSystem();
}
function changeDegrees() {
console.log(`changing degrees: ${this.value()}`)
degrees = parseFloat(this.value());
drawSystem();
}
function setup() {
createCanvas(800, 800);
let axiomLabel = createP('axiom');
axiomLabel.style('font-size', '16px');
axiomLabel.position(10,0);
let axiomInput = createInput('F-F-F-F');
axiomInput.position(10,40);
axiomInput.size(140);
axiomInput.input(changeAxiom);
let degreesLabel = createP('degrees');
degreesLabel.style('font-size', '16px');
degreesLabel.position(10,55);
let degreesInput = createInput('90');
degreesInput.position(10,95);
degreesInput.size(140);
degreesInput.input(changeDegrees);
let rulesLabelF = createP('rules (F)');
rulesLabelF.style('font-size', '16px');
rulesLabelF.position(170,0);
let rulesInputF = createInput('F-F+F+FF-F-F+F');
rulesInputF.position(170,40);
rulesInputF.size(140);
rulesInputF.input(changeRulesF);
let rulesLabelf = createP('rules (f)');
rulesLabelf.style('font-size', '16px');
rulesLabelf.position(170,55);
let rulesInputf = createInput('');
rulesInputf.position(170,95);
rulesInputf.size(140);
rulesInputf.input(changeRulesf);
let numIterationsLabel = createP('num iterations');
numIterationsLabel.style('font-size', '16px');
numIterationsLabel.position(350,0);
numIterationsSel = createSelect();
numIterationsSel.position(350, 40);
numIterationsSel.option(0);
numIterationsSel.option(1);
numIterationsSel.option(2);
numIterationsSel.option(3);
numIterationsSel.option(4);
numIterationsSel.option(5);
numIterationsSel.option(6);
numIterationsSel.option(7);
numIterationsSel.option(8);
numIterationsSel.option(9);
numIterationsSel.option(10);
numIterationsSel.changed(changeNumIterations);
//drawSystem();
}
function draw() {
drawSystem()
if (degrees <= 90) {
degrees += 0.2;
}
}
@brianfay
Copy link
Author

When degrees is set to anything less than 90, it animates up to 90. I don't know what's going on but it looks like some crazy biology.

l system 2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment