xxxxxxxxxx
135
// drawing ttf fonts with opentype.js and p5js commands.
// note that this ONLY works with ttf fonts, which provide
// the shapes of counters (i.e., the hole in 'O') in
// counter-clockwise order. I use this in the code below
// with the beginContour()/endContour() functions in p5js
// to draw the shapes accordingly.
let font;
let fontData;
// groups a list of opentype.js vector commands by contour
function groupByContour(cmds) {
contours = [];
current = [];
for (let cmd of cmds) {
current.push(cmd);
if (cmd.type == 'Z') {
contours.push(current);
current = [];
}
}
return contours;
}
// determines if a list of commands specify a path in clockwise
// or counter-clockwise order
function clockwise(cmds) {
let sum = 0;
for (let i = 0; i < cmds.length - 1; i++) {
let a = cmds[i];
let b = cmds[i+1];
if (!(a.hasOwnProperty('x') && b.hasOwnProperty('x'))) {
continue;
}
sum += (b.x - a.x) * (b.y + a.y);
}
return sum < 0;
}
// draws contours grouped by groupByContour(). uses clockwise()
// to determine if this contour should be a p5js shape or a p5js
// contour (i.e., cutout of a shape)
function drawContours(contours) {
let inShape = false;
for (let i = 0; i < contours.length; i++) {
if (clockwise(contours[i])) {
if (inShape) {
endShape(CLOSE);
}
beginShape();
inShape = true;
drawContour(contours[i]);
}
else {
beginContour();
drawContour(contours[i]);
endContour();
}
}
if (inShape) {
endShape(CLOSE);
}
}
// draws an individual contour
function drawContour(cmds) {
for (let i = 0; i < cmds.length; i++) {
cmd = cmds[i];
switch (cmd.type) {
case 'M':
case 'Z':
break;
case 'L':
vertex(cmd.x, cmd.y);
break;
case 'C':
bezierVertex(
cmd.x1, cmd.y1, cmd.x2, cmd.y2, cmd.x, cmd.y);
break;
case 'Q':
quadraticVertex(cmd.x1, cmd.y1, cmd.x, cmd.y);
break;
}
}
}
function preload() {
fontData = loadBytes('ReplicaProTT-Bold.ttf');
}
let path;
function setup() {
createCanvas(400, 400);
font = opentype.parse(fontData.bytes.buffer);
path = font.getPath("Design", 0, 0, 72);
}
// applies a transformation to the x/y coordinates of each opentypejs
// command you pass to it, according to the callback, which will be
// given the x/y coordinates as parameters and should return an array
// in the form of [x, y]
function commandTransform(cmds, callback) {
let transformed = [];
for (let cmd of cmds) {
let newCmd = {type: cmd.type}
for (let pair of [['x', 'y'], ['x1', 'y1'], ['x2', 'y2']]) {
if (cmd.hasOwnProperty(pair[0]) && cmd.hasOwnProperty(pair[1])) {
let result = callback(cmd[pair[0]], cmd[pair[1]]);
newCmd[pair[0]] = result[0];
newCmd[pair[1]] = result[1];
}
}
transformed.push(newCmd);
}
return transformed;
}
function draw() {
background(255);
fill(40);
noStroke;
push();
translate(25, 150);
drawContours(
groupByContour(
commandTransform(path.commands, function(x, y) {
let newX = x + sin((x*0.15) + (frameCount*0.11));
let newY = y + cos((y*0.35) + (frameCount*0.17));
return [newX, newY];
})
)
);
pop();
}