0

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>
Jean
  • 35
  • 5

1 Answers1

2

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 Plant class as a property of the Graphics instance does not have any effect. If you actually want to draw your sapling to the Graphics you need to use instance methods on the Graphics object returned from createGraphics.

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 call grow() once, but call the drawing function for the Plant once per frame.

3. Using the rotate() function

In 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 the rotate() function rotates around the Z axis, so this has no effect on subsequently drawn lines.

Working Example

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 sapling;

function setup() {
  createCanvas(400, 400, WEBGL);

  sapling = new Plant(0, 0, 0);
  sapling.grow();
  console.log(sapling.word);
}

function draw() {
  background(150);
  orbitControl();

  rotateX(PI / 2);
  noStroke();
  fill(255);
  plane(planeWidth, planeHeight);

  sapling.draw();
}

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.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
        rotateY((PI / 180) * -this.angle);
      },
      "-": () => {
        // Rotate right
        rotateY((PI / 180) * this.angle);
      },
      // Save current location
      "[": push,
      // Restore last location
      "]": pop,
    };
  }
  grow() {
    if (this.grown) return;

    let next = "";
    for (let i = 0; i < this.numGens; i++) {
      this.word = this.generateWord(this.word);
    }
    this.grown = true;
  }
  draw() {
    for (let i = 0; i < this.word.length; i++) {
      let c = this.word[i];
      if (c in this.drawRules) {
        this.drawRules[c]();
      }
    }
  }
  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 "";
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
Paul Wheeler
  • 18,988
  • 3
  • 28
  • 41