turtle.js

class Turtle {
  /** 
   * A Turtle that can be controlled in the style of the LOGO Language
   * @param  {number} x - Initial X Position of Turtle
   * @param  {number} y - Initial Y Position of Turtle 
   * @param  {number} heading - Initial Heading of Turtle
   */
  constructor(x = 0, y = 0, heading = -HALF_PI) {
    this.x = x ;
    this.y = y ;
    this.penDown = true;
    this.penColor = 0;
    this.penWeight = 1;
    this.heading = heading;
    this.stack = [];
    this.cl = this.color;
    this.pu = this.up;
    this.pd = this.down;
    this.rt = this.right;
    this.lt = this.left;
    this.fd = this.forward;
    this.fw = this.forward;
    this.fwd = this.forward;
    this.bk = this.backward;
    this.back = this.backward;
    this.st = this.style;
  }

  /**
  * Draws an L-System based on default values.
  * The turtle knows how to draw certain types of LSystems
  * 
  * <br>F means forward with pen down
  * <br>f means fordward with pen up
  * <br>X or any other letter pass.
  * <br>[ push
  * <br>] pop
  * <br>+ turn right
  * <br>- turn left
  * 
  * @param {LSystem} ls - the LSystem to draw
  * @param {number} step - the size of the step
  * @param {number} angle - the angle of turns 
   */
  drawLSystem(ls, step, angle) {
    this.drawString(ls.endString, step, angle);
  }

  /** Draw a string (used by drawLSystem)
   * @param  {string} string
   * @param  {number} step
   * @param  {number} angle
   */
  drawString(string, step, angle) {
    const draw = {
      "f": e => { e.pu; e.forward(step) },
      "F": e => { e.pd; e.forward(step) },
      "[": e => e.push(),
      "]": e => e.pop(),
      "+": e => e.right(angle),
      "-": e => e.left(angle)
    };
    const commands = Object.keys(draw);
    [...string].forEach(letter => {
      if (commands.includes(letter)) draw[letter](this);
    });
  }

  /** Pushes the current state of turtle to stack. */
  push() {
    this.stack.push({
      x: this.x,
      y: this.y,
      penDown: this.penDown,
      penColor: this.penColor,
      penWeight: this.penWeight,
      heading: this.heading
    });
  }

  /** Reverts to previous saved state of the turtle removing it from the stack */
  pop() {
    const el = this.stack.pop();
    this.x = el.x;
    this.y = el.y;
    this.penDown = el.penDown;
    this.penColor = el.penColor;
    this.penWeight = el.penWeight;
    this.heading = el.heading;
  }

  /** Shows the Turtle */
  show() {
    this.push();
    stroke('pink');
    this.down();
    this.rt(10);
    this.fd(-10);
    this.fd(10);
    this.lt(20);
    this.fd(-10);
    this.pop();
  }
  
  /** Sets the color of the pen 
   * @param  {Color} c - a P5js valid color
   */
  color(c) {
    this.penColor = c;
  }

  /** Put the pen up so it doesn't write */
  up() {
    this.penDown = false;
  }

  /** Put the pen down so it writes */
  down() {
    this.penDown = true;
  }
  
  /** Rotate the turtle to its right by certain degrees
   * @param  {number} rot - angle to rotate in degrees
   */
  right(rot) {
    this.heading += radians(rot);
  }

  /** Rotate the turtlo to its left by certain degrees
   * @param  {number} rot - angle to rotate in degrees
   */
  left(rot) {
    this.heading -= radians(rot);
  }
  
  /** Set turtle position in coordinate system
   * @param  {number} x - the X position
   * @param  {number} y - the Y position
   */
  pos(x, y) {
    this.x = x;
    this.y = y;
  }

  /** Move the turtle forward 
   * @param  {number} steps - the number of steps to move forward
   */
  forward(steps) {
    const tx = this.x + steps * cos(this.heading);
    const ty = this.y + steps * sin(this.heading);
    if (this.penDown) {
      push();
      stroke(this.penColor);
      strokeWeight(this.penWeight);
      line(this.x, this.y, tx, ty);
      pop();
    }
    this.x = tx;
    this.y = ty;
  }

  /** Move the turtle backwards without painting
   * @param  {number} steps - the number of steps to move
   */
  backward(steps) {
    const ps = this.penDown;
    this.penDown = false;
    this.forward(-steps);
    this.penDown = ps;
  }

  
  /** Set the weight of the pen 
   * @param  {number} weight - the weight of the pen
   */
  style(weight) {
    this.penWeight = weight;
  }

  /** Set heading of turtle East=0, CW
   * @param {number} heading - the heading of the turtle
    */
  hd(heading) {
    this.heading = radians(heading);
  }
}