I'm trying to convert this Daniel Shiffman tutorial from p5js to html5 canvas without a library:
var angle;
var axiom = "F";
var sentence = axiom;
var len = 100;
var rules = [];
rules[0] = {
a: "F",
b: "FF+[+F-F-F]-[-F+F+F]"
}
function generate() {
len *= 0.5;
var nextSentence = "";
for (var i = 0; i < sentence.length; i++) {
var current = sentence.charAt(i);
var found = false;
for (var j = 0; j < rules.length; j++) {
if (current == rules[j].a) {
found = true;
nextSentence += rules[j].b;
break;
}
}
if (!found) {
nextSentence += current;
}
}
sentence = nextSentence;
createP(sentence);
turtle();
}
function turtle() {
background(51);
resetMatrix();
translate(width / 2, height);
stroke(255, 100);
for (var i = 0; i < sentence.length; i++) {
var current = sentence.charAt(i);
if (current == "F") {
line(0, 0, 0, -len);
translate(0, -len);
} else if (current == "+") {
rotate(angle);
} else if (current == "-") {
rotate(-angle)
} else if (current == "[") {
push();
} else if (current == "]") {
pop();
}
}
}
function setup() {
createCanvas(400, 400);
angle = radians(25);
background(51);
createP(axiom);
turtle();
var button = createButton("generate");
button.mousePressed(generate);
}
This is my code and there is a problem with it that I am not able to debug... When I run the following code, I get the start of this tree with an error message prompting me to change rotate(-this.angle) to call the context. But when I fix the issue, the tree disappears & the rectangle in the "buildFlower()" function appears... why is this happening? :/ :
My code:
const init = () => {
const html = document.getElementsByTagName("html").item(0),
canvas = document.getElementsByTagName("canvas").item(0),
c = canvas.getContext("2d");
let flower;
const gui = new dat.GUI();
function line(x1, y1, x2, y2, color) {
c.strokeStyle = color;
c.beginPath();
c.moveTo(x1, y1);
c.lineTo(x2, y2);
c.stroke();
}
class Flower {
constructor(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.angle = (Math.PI / 180) * 25;
this.axiom = "F";
this.sentence = this.axiom;
this.len = 100;
this.rules = [];
this.rules[0] = {
a: "F",
b: "FF+[+F-F-F]-[-F+F+F]",
};
}
generateLSystem() {
this.len *= 0.5;
let nextSentence = "";
for (let i = 0; i < this.sentence.length; i++) {
let current = this.sentence.charAt(i);
let found = false;
for (let j = 0; j < this.rules.length; j++) {
if (current == this.rules[j].a) {
found = true;
nextSentence += this.rules[j].b;
break;
}
}
if (!found) {
nextSentence += current;
}
}
this.sentence = nextSentence;
this.turtle();
}
turtle() {
c.resetTransform();
c.translate(100, getResolution().h);
for (let i = 0; i < this.sentence.length; i++) {
let current = [this.sentence.charAt(i)];
if (current == "F") {
line(0, 0, 0, -this.len);
c.translate(0, -this.len);
} else if (current == "+") {
c.rotate(this.angle);
} else if (current == "-") {
// WHEN I CHANGE THE LINE BELOW TO (`c.rotate`) THE ENTIRE THING DISAPPEARS.
rotate(-this.angle);
} else if (current == "[") {
current.push();
} else if (current == "]") {
current.pop();
}
}
}
buildFlower() {
c.beginPath();
c.rect(
getResolution().w / 2 - this.size / 2,
getResolution().h / 2 - this.size / 2,
this.size,
this.size
);
c.stroke();
}
}
const resize = () => {
canvas.width = w = window.innerWidth;
canvas.height = h = window.innerHeight;
console.log(`screen resolution: ${w}px × ${h}px`);
};
const getResolution = () => {
return { w: canvas.width, h: canvas.height };
};
const setup = () => {
c.clearRect(0, 0, getResolution().w, getResolution().h);
flower = new Flower(getResolution().w, getResolution().h, 200);
flower.generateLSystem();
gui.add(flower, "size", 0, 200);
gui.add(flower, "axiom");
};
setup();
const draw = (t) => {
c.fillStyle = "rgba(255, 255, 255, .5)";
c.fillRect(0, 0, w, h);
window.requestAnimationFrame(draw);
};
let w,
h,
last,
i = 0,
start = 0;
window.removeEventListener("load", init);
window.addEventListener("resize", resize);
resize();
window.requestAnimationFrame(draw);
};
window.addEventListener("load", init);
I've been trying for hrs and can't seem to debug :/
General suggestion: work in small sprints and run the code frequently to verify that it behaves as expected. The code here seems like it was built up in just a few long sprints without much validation for each component along the way. This apparently led to a buildup of complexity, causing confusion and subtle bugs.
Try to avoid premature abstractions like excessive functions and classes until the code is working correctly. At that point, the correct abstractions and cut points will be apparent.
Also, minimize the problem space by only introducing "bells and whistles"-type peripheral functionality once the core logic is working. When the basic drawing is failing, functionality like a GUI, resizing, a RAF loop and so forth just get in the way of progress. Add those after the basic drawing works.
The main reason for the disappearing drawing on the canvas is that
resize()
is called aftersetup()
. When you resize the canvas, it wipes it clean. Callsetup()
after your initialresize()
call:You see a partial drawing showing up because your crash prevents the
resize()
from executing. This should be a big debug hint: some code after the drawing must be wiping it out.The translation from p5's
push()
andpop()
to HTML5 canvas doesn't appear correct:current
is an array, but we don't want to mess with it. We're supposed to be pushing and popping the canvas context, not an array. These calls arecontext.save()
andcontext.restore()
in HTML5:A strange design choice is:
Here, you've created an array of one element, then used coercion to compare it to a string. Always use
===
and don't create a one-element array unnecessarily:I could analyze the design further and do a full rewrite/code review, but I'll leave it at this just to get you moving again and not belabor the point: