Created
March 17, 2022 04:23
-
-
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"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.