Published
Edited
Dec 19, 2019
1 fork
1 star
Insert cell
md`# paper.js: Stochastic Fern`
Insert cell
paper = require("paper");
Insert cell
{
let size = 20
let height = width / 1.5;
let canvas = this || DOM.canvas(500, 750);
let ps = canvas.ps || (() => {
let ps = new paper.PaperScope();
ps.setup(canvas);
return ps;
})();
canvas.ps = ps;
let axiom = ["X"];
let grammar = {
"X": new Space([0.50, 0.55], ["+F[+XF-X]-F[-XF-X]", "+X[-FXF+]"]),
"F": new Space([0.60, 0.35, 0.5], ["FF", "FFF", ""]),
"+": new Rule("+"),
"-": new Rule("-"),
"[": new Rule("["),
"]": new Rule("]"),
}
let n = 6;
let code = axiom;
for (let i = 0; i < n; i++) {
code = code.map(l => grammar[l].rule).join("").split("")
}
draw(grammar, ps, code, 12, 2);

return canvas;
}
Insert cell
class Space {
constructor(probs, events) {
this.probs = probs;
this.events = events;
}
get rule() {
let r = Math.random();
let total = 0;
for (let i in this.probs) {
total += this.probs[i];
console.log(r, total, this.probs)

if (r <= total) {
return this.events[i];
}
}
}
}
Insert cell
class Rule {
constructor(e) {
this.e = e;
}
get rule() {
return this.e;
}
}
Insert cell
function draw(grammar, ps, code, rotate=90, magnitude=1) {
let angle = Math.PI / 2;
let start = new ps.Point(250, 700);
let circle = new ps.Path.Circle(start, 2);
circle.fillColor = "red";
let stack = []
let curr = start;
let next = start;
var state;
for (let char of code) {
switch(char) {
case "F":
next = new ps.Point(get_next(curr, angle, magnitude));
let seg = ps.Path.Line(curr, next);
seg.strokeColor = "black";
curr = next;
break;
case "-":
angle = turn_right(angle, rotate);
break;
case "+":
angle = turn_left(angle, rotate);
break;
case "[":
stack.push(new State(curr, angle));
// angle = turn_right(angle, rotate);
break;
case "]":
state = stack.pop();
curr = state.point;
angle = state.angle;
// angle = turn_left(state.angle, rotate);
break;
// fallthrough on X.
}
}
}

Insert cell
function turn_left(angle, rotate) {
const left = rotate / 180 * Math.PI;
return (angle + left) % (2 * Math.PI);
}
Insert cell
function turn_right(angle, rotate) {
const right = rotate / 180 * Math.PI;
return (angle - right) % (2 * Math.PI);
}
Insert cell
function get_next(curr, angle, magnitude) {
let x = magnitude * Math.cos(angle);
let y = magnitude * Math.sin(angle);
return new paper.Point(curr.x + x, curr.y - y);
}
Insert cell
class State {
constructor(point, angle) {
this.point = point;
this.angle = angle;
}
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more