Generative Typography

Classes

Students will leave the session with the ability to use Classes.

Class Agenda

Class was canceled this week and the material was covered the following week.

Further Resources

Code Lecture

Classes are a template for creating objects. Classes allow us to encapsulate logic, similar to functions, and the data that the logic operates on.

var name = "romello";

function greet() {
  console.log("hello " + name);
}

greet();

The above code can be turned into a class with all of the logic self contained. Several things to note is that the constructor is where you can define the initial values of your class. When you create an object from a class you are creating an instance. The functions you write on your class are called methods and they operate on the values assoicated with that specific instance. This is why the function uses the this keyword to access the class instance's name.

class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log("hello " + this.name);
  }
}

var person1 = new Person();

person1.greet();

Similar to functions using this logic now becomes easier with less copy and paste.

class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log("hello " + this.name);
  }
}

var person1 = new Person("romello");
var person2 = new Person("bob");
var person3 = new Person("barbera");

person1.greet();
person2.greet();
person3.greet();

Now lets apply it to a composition, using this example of a bouncing circle.

let xpos, ypos;
let size = 60;
let xspeed = 2.8;
let yspeed = 2.2;
let xdirection = 1; // Left or Right
let ydirection = 1; // Top to Bottom

function setup() {
  createCanvas(400, 400);
  noStroke();
  frameRate(30);
  ellipseMode(RADIUS);

  // Set the starting position of the shape
  xpos = width / 2;
  ypos = height / 2;
}

function draw() {
  background(220);

  // Update the position of the shape
  xpos = xpos + xspeed * xdirection;
  ypos = ypos + yspeed * ydirection;

  // Draw the shape
  ellipse(xpos, ypos, size, size);

  // If it does, reverse its direction by multiplying by -1
  if (xpos > width - size || xpos < size) {
    xdirection *= -1;
  }

  if (ypos > height - size || ypos < size) {
    ydirection *= -1;
  }
}

The following code can be turned into a class named Bounce with all of the logic encapsulated.

class Bounce {
  constructor() {
    this.xpos = width / 2;
    this.ypos = height / 2;
    this.size = 60;
    this.xspeed = 2.8;
    this.yspeed = 2.2;
    this.xdirection = 1; // Left or Right
    this.ydirection = 1; // Top to Bottom
  }

  show() {
    circle(this.xpos, this.ypos, this.size);
  }

  update() {
    // Update the position of the shape
    this.xpos = this.xpos + this.xspeed * this.xdirection;
    this.ypos = this.ypos + this.yspeed * this.ydirection;

    // If it does, reverse its direction by multiplying by -1
    if (this.xpos > width - this.size || this.xpos < this.size) {
      this.xdirection *= -1;
    }

    if (this.ypos > height - this.size || this.ypos < this.size) {
      this.ydirection *= -1;
    }
  }
}

var circle1;

function setup() {
  createCanvas(400, 400);
  noStroke();
  frameRate(30);
  ellipseMode(RADIUS);

  circle1 = new Bounce();
}

function draw() {
  background(220);

  circle1.show();
  circle1.update();
}

Similar to Person we can now create multiple Bounce instances and even encapsulate more information inside of them.

class Bounce {
  constructor() {
    this.xpos = random(width * 0.2, width * 0.8);
    this.ypos = random(height * 0.2, height * 0.8);
    this.size = 60;
    this.xspeed = random(2, 4);
    this.yspeed = random(2, 4);
    this.xdirection = random(0, 10) > 5 ? 1 : -1; // Left or Right
    this.ydirection = random(0, 10) > 5 ? 1 : -1; // Top to Bottom
  }

  show() {
    circle(this.xpos, this.ypos, this.size);
  }

  update() {
    // Update the position of the shape
    this.xpos = this.xpos + this.xspeed * this.xdirection;
    this.ypos = this.ypos + this.yspeed * this.ydirection;

    // If it does, reverse its direction by multiplying by -1
    if (this.xpos > width - this.size || this.xpos < this.size) {
      this.xdirection *= -1;
    }

    if (this.ypos > height - this.size || this.ypos < this.size) {
      this.ydirection *= -1;
    }
  }
}

var numberOfCircles = 10;
var circles = [];

function setup() {
  createCanvas(400, 400);
  // noStroke();
  frameRate(30);
  ellipseMode(RADIUS);

  for (var i = 0; i < numberOfCircles; i++) {
    circles[i] = new Bounce();
  }
}

function draw() {
  background(220);

  for (var i = 0; i < numberOfCircles; i++) {
    circles[i].show();
    circles[i].update();
  }
}

Lets apply this example to Letters and textToPoints.

var bebasFont;
var bounds;
var points = [];
var fontSize = 500;

function preload() {
  /**
   * Please note that this is a font hosted by another project of mine.
   * There is no guarantee that this url will function in the future.
   */
  bebasFont = loadFont("https://garnet.website/static/fonts/BebasNeue.ttf");
}

function setup() {
  createCanvas(500, 500);

  textFont(bebasFont);
  textSize(fontSize);
  textAlign(CENTER, CENTER);
  fill("white");
  noStroke();
  angleMode(DEGREES);

  // create the points and points
  points = bebasFont.textToPoints("A", 0, 0, fontSize);
  bounds = bebasFont.textBounds("A", 0, 0, fontSize);
}

function draw() {
  background(220);

  // translate the entire canvas
  translate(width / 2, height / 2);
  // align the bounds
  translate(-bounds.w / 2, bounds.h / 2);

  points.forEach(function (pt, index) {
    circle(pt.x, pt.y, 5);
  });
}

Similar to the above we can pass in the point information to a class.

class Point {
  constructor(pointData) {
    this.x = pointData.x;
    this.y = pointData.y;
  }

  show() {
    circle(this.x, this.y, 5);
  }
}

We can update our class to allow for more flexibility and interactiveness within our system.

var bebasFont;
var bounds;
var points = [];
var fontSize = 500;

function preload() {
  /**
   * Please note that this is a font hosted by another project of mine.
   * There is no guarantee that this url will function in the future.
   */
  bebasFont = loadFont("https://garnet.website/static/fonts/BebasNeue.ttf");
}

class Point {
  constructor(pointData) {
    this.x = pointData.x;
    this.y = pointData.y;
    this.size = 15;
    this.step = 0;
    this.speed = 0.1 * random(1, 5);
  }

  show() {
    circle(this.x, this.y, this.size);
  }

  update() {
    this.size = Math.sin(this.step) * 15;

    this.step += this.speed;
  }

  freeze() {
    if (this.speed === 0) {
      this.speed = 0.1 * random(1, 5);
    } else {
      this.speed = 0;
    }
  }
}

function setup() {
  createCanvas(500, 500);

  textFont(bebasFont);
  textSize(fontSize);
  textAlign(CENTER, CENTER);
  fill("white");
  noStroke();
  angleMode(DEGREES);

  // create the points and points
  bebasFont.textToPoints("A", 0, 0, fontSize).forEach(function (pointData) {
    var point = new Point(pointData);

    points.push(point);
  });

  bounds = bebasFont.textBounds("A", 0, 0, fontSize);
}

function draw() {
  background(220);

  // translate the entire canvas
  translate(width / 2, height / 2);
  // align the bounds
  translate(-bounds.w / 2, bounds.h / 2);

  points.forEach(function (pt, index) {
    pt.show();
    pt.update();
  });
}

function mousePressed() {
  points.forEach(function (pt, index) {
    pt.freeze();
  });
}