I am having some difficulty placing assets and objects into my sketch. Currently the two issues that I am having are that the plant needs to display in front of the plane, and that there is a slight issue without how I am clearing the canvas.
For the first issue, I have tried rearranging the order of the code a bit, but for some reason the plant is always behind the plane, when it should be in front of it.
I tried implementing createGraphics for the second issue in order to preserve the drawing created by the class after it had drawn one iteration of the plant. However, I am not sure how to implement the background or how to clear it so that the plant is always displayed. I don't want to implement noLoop(), because the plant will eventually grow.
Any help would be appreciated, thanks!
let gui;
let ui;
let plantGraphics;
const len = 4;
const ang = 25;
let drawRules;
let word = "X";
let rules = {
X: [
// Original rule
{ rule: "F[+X][-X]FX", prob: 0.5 },
// Fewer limbs
{ rule: "F[-X]FX", prob: 0.05 },
{ rule: "F[+X]FX", prob: 0.05 },
// Extra rotation
{ rule: "F[++X][-X]FX", prob: 0.1 },
{ rule: "F[+X][--X]FX", prob: 0.1 },
// Berries/fruits
{ rule: "F[+X][-X]FA", prob: 0.1 },
{ rule: "F[+X][-X]FB", prob: 0.1 },
],
F: [
// Original rule
{ rule: "FF", prob: 0.85 },
// Extra growth
{ rule: "FFF", prob: 0.05 },
// Stunted growth
{ rule: "F", prob: 0.1 },
],
};
const planeWidth = 200;
const planeHeight = 200;
let platformRotation = 0;
let sapling;
let saplingPlanted = false;
function setup() {
createCanvas(400, 400, WEBGL);
background(220);
ui = {
// regrow: function() {
// console.log('button clicked')
// growPlant();
// },
// Camera Settings
X: 0,
Y: -150,
Z: -400,
centerX: 0,
centerY: 0,
centerZ: 0,
upX: 0,
upY: 1,
upZ: 0,
};
gui = new dat.GUI();
// gui.add(ui, 'regrow').name('Click Me!');
let cameraFolder = gui.addFolder('Camera Settings');
cameraFolder.add(ui, 'X', -2000, 400);
cameraFolder.add(ui, 'Y', -2000, 400);
cameraFolder.add(ui, 'Z', -2000, 400);
cameraFolder.add(ui, 'centerX', -2000, 400);
cameraFolder.add(ui, 'centerY', -2000, 400);
cameraFolder.add(ui, 'centerZ', -2000, 400);
cameraFolder.add(ui, 'upX', -1, 1);
cameraFolder.add(ui, 'upY', -1, 1);
cameraFolder.add(ui, 'upZ', -1, 1);
drawRules = {
"A": () => {
// Draw circle at current location
// noStroke();
// fill("#E5CEDC");
stroke("#E5CEDC");
strokeWeight(5);
// circle(0, 0, len*2);
point(0, 0, len*2)
},
"B": () => {
// Draw circle at current location
// noStroke();
// fill("#FCA17D");
// circle(0, 0, len*2);
stroke("#FCA17D");
strokeWeight(2);
point(0, 0, len*2)
},
"F": () => {
// Draw line forward, then move to end of line
// const nextY = currentY - len;
// if (nextY > -maxY) {
stroke("#9ea93f");
line(0, 0, 0, -len);
// beginShape();
// vertex(0, 0, 0);
// vertex(0, 0, -len);
// endShape();
translate(0, -len);
// }
},
"+": () => {
// Rotate right
rotate(PI/180 * -ang);
},
"-": () => {
// Rotate right
rotate(PI/180 * ang);
},
// Save current location
"[": push,
// Restore last location
"]": pop,
};
plantGraphics = createGraphics(width, height, WEBGL);
plantGraphics.sapling = new Plant(0, 0, 0);
}
function draw() {
if (
mouseIsPressed &&
mouseX >= 0 &&
mouseX <= width &&
mouseY >= 0 &&
mouseY <= height) {
platformRotation += mouseX - pmouseX;
}
rotateX(PI / 2);
rotateZ(radians(platformRotation));
push();
noStroke();
fill(255)
plane(planeWidth, planeHeight);
pop();
// background(211, 217, 219);
// sapling.plantSapling();
plantGraphics.sapling.grow();
image(plantGraphics, -width/2, -height/2);
// sapling.translate(-width/2, -height/2, 0); // center graphics buffer
// image(sapling.pg, 0, 0);
// background(211, 217, 219);
let cameraZ = ui.Z;
let cameraX = ui.X;
let cameraY = ui.Y;
let centerX = ui.centerX;
let centerY = ui.centerY;
let centerZ = ui.centerZ;
let upX = ui.upX;
let upY = ui.upY;
let upZ = ui.upZ;
camera(cameraX, cameraY, cameraZ, centerX, centerY, centerZ, upX, upY, upZ);
}
class Plant {
constructor(x, y, z) {
this.pos = createVector(x, y, z);
this.vel = createVector();
this.acc = createVector();
this.grown = false;
this.len = 3;
this.angle = 25;
this.numGens = 6;
this.word = "X";
// this.next = ""
this.next;
this.drawRules = {
A: () => {
// Draw circle at current location
// noStroke();
// fill("#E5CEDC");
// circle(0, 0, this.len * 2);
stroke("#E5CEDC");
strokeWeight(this.len * 2);
point(this.pos.x, this.pos.y, this.pos.z);
},
B: () => {
// Draw circle at current location
// noStroke();
// fill("#FCA17D");
// circle(0, 0, this.len * 2);
stroke("#FCA17D");
strokeWeight(this.len * 2);
point(this.pos.x, this.pos.y, this.pos.z);
},
F: () => {
// Draw line forward, then move to end of line
stroke("#9ea93f");
// line(0, 0, 0, -this.len);
// translate(0, -this.len);
line(this.pos.x, this.pos.y, this.pos.z, this.pos.x, this.pos.y, -this.len);
translate(this.pos.x, this.pos.y, this.len);
},
"+": () => {
// Rotate right
rotate((PI / 180) * -this.angle);
},
"-": () => {
// Rotate right
rotate((PI / 180) * this.angle);
},
// Save current location
"[": push,
// Restore last location
"]": pop,
};
}
grow() {
if (this.grown) return;
let next = "";
let word = "X";
for (let i = 0; i < this.numGens; i++) {
word = this.generateWord(word);
}
for (let i = 0; i < word.length; i++) {
let c = word[i];
if (c in this.drawRules) {
this.drawRules[c]();
}
}
this.grown = true;
}
generateWord(word) {
let next = "";
for (let i = 0; i < word.length; i++) {
let c = word[i];
if (c in rules) {
let rule = rules[c];
if (Array.isArray(rule)) {
next += this.chooseOne(rule);
} else {
next += rules[c];
}
} else {
next += c;
}
}
return next;
}
chooseOne(ruleSet) {
let n = random(); // Random number between 0-1
let t = 0;
for(let i = 0; i < ruleSet.length; i++) {
t += ruleSet[i].prob; // Keep adding the probability of the options to total
if(t > n) { // If the total is more than the random value
return ruleSet[i].rule; // Choose that option
}
}
return "";
}
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/addons/p5.sound.min.js"></script>
<!-- import dat.gui -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.9/dat.gui.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<main>
</main>
<script src="sketch.js"></script>
</body>
</html>
There are a couple of issues with your code:
1. Attempt to use createGraphics
You are attempting to draw your sapling to a separate buffer, but all of your drawing instructions use the global drawing functions. Assigning your instance of the
Plantclass as a property of theGraphicsinstance does not have any effect. If you actually want to draw your sapling to theGraphicsyou need to use instance methods on theGraphicsobject returned fromcreateGraphics.2. Coupling L-System Rule Iteration and Drawing
Point 1. having been made, I don't think drawing your sapling to a separate buffer is a good solution anyway. The reason you are having to try this is because the logic that draws the sapling and the logic that iterates the L-System rules is mushed together in the
grow()function, which is only meant to be called once. If you move the drawing code into a separate function then you can callgrow()once, but call the drawing function for thePlantonce per frame.3. Using the
rotate()functionIn your draw rules function you draw lines in 3d where the only change is along the Z axis, and then you use the
rotate()function for rotation instructions. This is problematic because therotate()function rotates around the Z axis, so this has no effect on subsequently drawn lines.Working Example