Skip to content

Instantly share code, notes, and snippets.

@troughton
Forked from dribnet/.block
Last active October 6, 2016 03:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save troughton/1033b61db7f652a9b58240c551b9e9fe to your computer and use it in GitHub Desktop.
Save troughton/1033b61db7f652a9b58240c551b9e9fe to your computer and use it in GitHub Desktop.
Dimensional Glyph
license: mit
// note: this file is poorly named - it can generally be ignored.
// helper functions below for supporting blocks/purview
function saveBlocksImages(doZoom) {
if(doZoom == null) {
doZoom = false;
}
// generate 960x500 preview.jpg of entire canvas
// TODO: should this be recycled?
var offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = 960;
offscreenCanvas.height = 500;
var context = offscreenCanvas.getContext('2d');
// background is flat white
context.fillStyle="#FFFFFF";
context.fillRect(0, 0, 960, 500);
context.drawImage(this.canvas, 0, 0, 960, 500);
// save to browser
var downloadMime = 'image/octet-stream';
var imageData = offscreenCanvas.toDataURL('image/jpeg');
imageData = imageData.replace('image/jpeg', downloadMime);
p5.prototype.downloadFile(imageData, 'preview.jpg', 'jpg');
// generate 230x120 thumbnail.png centered on mouse
offscreenCanvas.width = 230;
offscreenCanvas.height = 120;
// background is flat white
context = offscreenCanvas.getContext('2d');
context.fillStyle="#FFFFFF";
context.fillRect(0, 0, 230, 120);
if(doZoom) {
// pixelDensity does the right thing on retina displays
var pd = this._pixelDensity;
var sx = pd * mouseX - pd * 230/2;
var sy = pd * mouseY - pd * 120/2;
var sw = pd * 230;
var sh = pd * 120;
// bounds checking - just displace if necessary
if (sx < 0) {
sx = 0;
}
if (sx > this.canvas.width - sw) {
sx = this.canvas.width - sw;
}
if (sy < 0) {
sy = 0;
}
if (sy > this.canvas.height - sh) {
sy = this.canvas.height - sh;
}
// save to browser
context.drawImage(this.canvas, sx, sy, sw, sh, 0, 0, 230, 120);
}
else {
// now scaledown
var full_width = this.canvas.width;
var full_height = this.canvas.height;
context.drawImage(this.canvas, 0, 0, full_width, full_height, 0, 0, 230, 120);
}
imageData = offscreenCanvas.toDataURL('image/png');
imageData = imageData.replace('image/png', downloadMime);
p5.prototype.downloadFile(imageData, 'thumbnail.png', 'png');
}

PS4 MDDN 342 2016

For this problem set, I designed two glyphs based around a bold, geometric aesthetic. The original inspiration (and colour palette for the second glyph) came from the album cover for The Bones of What You Believe; I found the symbol on the cover striking and saw a lot of potential for it as a glyph system. It also seems to draw from the early 20th century style of graphic design focusing on bold, simplistic, and geometric symbols, which I think are quite effective.

I sketched out a few different ideas, but ultimately, I settled on something reasonably close to the inspiration; three triangles, with hollow centres, overlaid onto a set of concentric circles.

The general visual aesthetic is fairly bold, but the variability in the parameters plays with how bold each individual aspect of the glyph is, or how much impact that aspect has on the overall appearance. Switch the sketch into 'drive' mode and have a play around with how each parameter changes each glyph.

For the second glyph, I took my first glyph as a starting point and expanded out the circles to be squares – this immediately changed the character of the glyph dramatically. Incorporating the colours also gave it more visual impact; I considered playing with the hue of the triangles, but found that having the parameters drive the saturation of both the background and triangle fill to be more effective.

I've called the first glyph 'Centric', since everything seems to be leading towards the centre, and the second 'Angular Fragments', since everything seems sharp and scattered when the glyph is viewed in a random grid.

function trianglePoints(radius, centreAngle, angleSize) {
var halfRadius = radius * 0.5;
var points = [];
var innerPoint = [-halfRadius * cos(centreAngle), -halfRadius * sin(centreAngle)];
points.push(innerPoint);
var innerAngleSize = angleSize * 0.5;
var pointB = [innerPoint[0] + radius * cos(centreAngle - innerAngleSize), innerPoint[1] + radius * sin(centreAngle - innerAngleSize)];
points.push(pointB);
var pointC = [innerPoint[0] + radius * cos(centreAngle + innerAngleSize), innerPoint[1] + radius * sin(centreAngle + innerAngleSize)];
points.push(pointC);
return points;
}
/*
* val4 is an array of 4 numbers that range from [0,100]
* size is the number of pixels for width and height
* use p5.js to draw a round grawscale glpyh within the bounding box
*/
function glyph4(values, size) {
push();
scale(size, size);
strokeWeight(1.0/size, 1.0/size);
var fillColour = color(100 - values[0]);
var backgroundColour = color(155 + values[0]);
fill(backgroundColour);
ellipse(0.5, 0.5, 1.0);
noFill();
stroke(fillColour);
var ringWeight = map(values[2], 0, 100, 0.1, 1.25);
strokeWeight(0.5/8.0 * ringWeight);
for (var i = 0; i < 4; i += 1) {
var diameter = 0.2 + 0.25 * i;
ellipse(0.5, 0.5, diameter);
}
var triangleStroke = map(values[3], 0, 100, 0.005, 0.065);
strokeWeight(triangleStroke);
var triangleSpacing = map(values[3], 0, 100, 0.02, 0.15);
var triangleAngle = map(values[1], 0, 100, radians(45), radians(100));
for (var i = 0; i < 3; i++) {
var centreAngle = 0.3 + radians(120) * i;
var startAngle = centreAngle - triangleAngle * 0.5;
var endAngle = centreAngle + triangleAngle * 0.5;
{
push();
stroke(fillColour);
var triangleCentreX = 0.5 + 0.22 * cos(centreAngle);
var triangleCentreY = 0.5 + 0.22 * sin(centreAngle);
translate(triangleCentreX, triangleCentreY);
var pointsOuter = trianglePoints(0.44, centreAngle, triangleAngle);
triangle(pointsOuter[0][0], pointsOuter[0][1], pointsOuter[1][0], pointsOuter[1][1], pointsOuter[2][0], pointsOuter[2][1]);
var pointsInner = trianglePoints(0.44 - 2 * triangleSpacing, centreAngle, triangleAngle);
triangle(pointsInner[0][0], pointsInner[0][1], pointsInner[1][0], pointsInner[1][1], pointsInner[2][0], pointsInner[2][1]);
strokeWeight(triangleStroke * 0.8);
stroke(backgroundColour);
var pointsMiddle = trianglePoints(0.44 - triangleSpacing, centreAngle, triangleAngle);
triangle(pointsMiddle[0][0], pointsMiddle[0][1], pointsMiddle[1][0], pointsMiddle[1][1], pointsMiddle[2][0], pointsMiddle[2][1]);
pop();
}
}
pop();
}
/*
* val8 is an array of 8 numbers that range from [0,100]
* size is the number of pixels for width and height
* use p5.js to draw a square color glpyh within the bounding box
*/
function glyph8(values, size) {
//values[0] is rotation of triangles.
//values[1] is the triangle spread
//values[2] is square stroke weight
//values[3] is the triangle weight
//values[4] is the saturation of the background.
//values[5] is the saturation of the triangles
//values[7] is the triangle rotation
push();
colorMode(HSL);
rectMode(CENTER);
scale(size, size);
strokeWeight(1.0/size, 1.0/size);
var backgroundColour = color(359, values[4], 43);
var ringColour = color(0, 0, 0);
var triangleColour = color(215, 0.88 * values[5], 55);
fill(backgroundColour);
rect(0.5, 0.5, 1.0, 1.0);
noFill();
stroke(ringColour);
var ringWeight = map(values[2], 0, 100, 0.1, 1.25);
strokeWeight(0.5/8.0 * ringWeight);
var goldenRatio = 1.61803398875;
var exponentialStopStep = 1.0 / 17.94427191000132; //to make the last stop be at 1.
var exponentialStop = exponentialStopStep;
for (var i = 0; i < 4; i += 1) {
var linearStop = 0.2 + 0.2666666667 * i; //to make the last stop be at 1.
var diameter = lerp(linearStop, exponentialStop, values[7] * 0.01);
rect(0.5, 0.5, diameter, diameter);
exponentialStop += exponentialStop * goldenRatio;
}
var triangleStroke = map(values[3], 0, 100, 0.005, 0.065);
strokeWeight(triangleStroke);
var triangleSpacing = map(values[3], 0, 100, 0.02, 0.15);
var triangleAngle = map(values[1], 0, 100, radians(45), radians(100));
var scaleXY = map(values[0], 0, 100, 0.6, 1.0);
var translation = (1 - scaleXY) * 0.5;
translate(translation, translation);
scale(scaleXY, scaleXY);
for (var i = 0; i < 4; i++) {
var centreAngle = radians(45) + radians(90) * i;
var startAngle = centreAngle - triangleAngle * 0.5;
var endAngle = centreAngle + triangleAngle * 0.5;
var triangleRotationOffset = values[6] * PI * 0.005;
{
push();
stroke(ringColour);
var triangleCentreX = 0.5 + 0.25 * cos(centreAngle);
var triangleCentreY = 0.5 + 0.25 * sin(centreAngle);
translate(triangleCentreX, triangleCentreY);
var adjustedCentreAngle = centreAngle + triangleRotationOffset;
var pointsOuter = trianglePoints(0.44, adjustedCentreAngle, triangleAngle);
triangle(pointsOuter[0][0], pointsOuter[0][1], pointsOuter[1][0], pointsOuter[1][1], pointsOuter[2][0], pointsOuter[2][1]);
var pointsInner = trianglePoints(0.44 - 2 * triangleSpacing, adjustedCentreAngle, triangleAngle);
triangle(pointsInner[0][0], pointsInner[0][1], pointsInner[1][0], pointsInner[1][1], pointsInner[2][0], pointsInner[2][1]);
strokeWeight(triangleStroke * 0.8);
stroke(triangleColour);
var pointsMiddle = trianglePoints(0.44 - triangleSpacing, adjustedCentreAngle, triangleAngle);
triangle(pointsMiddle[0][0], pointsMiddle[0][1], pointsMiddle[1][0], pointsMiddle[1][1], pointsMiddle[2][0], pointsMiddle[2][1]);
pop();
}
}
pop();
}
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.3/p5.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.3/addons/p5.dom.js"></script>
<script language="javascript" type="text/javascript" src=".purview_helper.js"></script>
<script language="javascript" type="text/javascript" src="glyph.js"></script>
<script language="javascript" type="text/javascript" src="sketch.js"></script>
<style>
body { padding: 0; margin: 0; }
.inner { position: absolute; }
#controls {
font: 300 12px "Helvetica Neue";
padding: 5;
margin: 5;
background: #f0f0f0;
opacity: 0.1;
-webkit-transition: opacity 0.2s ease;
-moz-transition: opacity 0.2s ease;
-o-transition: opacity 0.2s ease;
-ms-transition: opacity 0.2s ease;
}
#controls:hover { opacity: 0.9; }
</style>
</head>
<body style="background-color:white">
<div class="outer">
<div class="inner">
<div id="canvasContainer"></div>
</div>
<div class="inner" id="controls" height="500px">
<table>
<tr>
<td>1</td>
<td id="slider1Container"></td>
</tr>
<tr>
<td>2</td>
<td id="slider2Container"></td>
</tr>
<tr>
<td>3</td>
<td id="slider3Container"></td>
</tr>
<tr>
<td>4</td>
<td id="slider4Container"></td>
</tr>
<tr>
<td>5</td>
<td id="slider5Container"></td>
</tr>
<tr>
<td>6</td>
<td id="slider6Container"></td>
</tr>
<tr>
<td>7</td>
<td id="slider7Container"></td>
</tr>
<tr>
<td>8</td>
<td id="slider8Container"></td>
</tr>
<tr>
<td>
<hr>
</td>
</tr>
<tr>
<td>Glyph</td>
<td id="selector1Container"></td>
</tr>
<tr>
<td>Mode</td>
<td id="selector2Container"></td>
</tr>
<tr>
<td>Size</td>
<td id="selector3Container"></td>
</tr>
<tr>
<td></td>
<td id="buttonContainer"></td>
</tr>
</div>
</div>
</table>
</body>
var canvasWidth = 960;
var canvasHeight = 500;
var glyphSelector;
var modeSelector;
var sizeSelector;
var val_sliders = [];
function setup () {
// create the drawing canvas, save the canvas element
var main_canvas = createCanvas(canvasWidth, canvasHeight);
main_canvas.parent('canvasContainer');
// create two sliders
for (i=0; i<8; i++) {
var slider = createSlider(0, 100, 50);
slider.parent("slider" + (i+1) + "Container")
slider.changed(sliderUpdated);
slider.mouseMoved(sliderUpdated);
slider.touchMoved(sliderUpdated);
val_sliders.push(slider);
}
modeSelector = createSelect();
modeSelector.option('gradient');
modeSelector.option('analogy');
modeSelector.option('drive');
modeSelector.option('random_grid');
modeSelector.changed(modeChangedEvent);
modeSelector.value('gradient');
modeSelector.parent('selector1Container');
glyphSelector = createSelect();
glyphSelector.option('Centric');
glyphSelector.option('Angular Fragments');
glyphSelector.changed(modeChangedEvent);
glyphSelector.parent('selector2Container');
sizeSelector = createSelect();
sizeSelector.option('64');
sizeSelector.option('128');
sizeSelector.option('256');
sizeSelector.parent('selector3Container');
sizeSelector.value('128');
sizeSelector.changed(sizeChangedEvent);
button = createButton('redo');
button.mousePressed(buttonPressedEvent);
button.parent('buttonContainer');
noLoop();
refreshGridData();
modeChangedEvent();
}
function sliderUpdated() {
redraw();
}
function mouseClicked() {
analogyCycleStep = (analogyCycleStep + 1) % 3;
if(analogyCycleStep == 0) {
refreshAnalogyData();
}
redraw();
}
function dataInterpolate(data1, data2, val) {
var d = new Array(8);
for(var i=0; i<8; i++) {
d[i] = lerp(data1[i], data2[i], val);
}
return d;
}
var numGridRows;
var numGridCols;
var gridValues; // row, col order
var gridOffsetX, gridOffsetY;
var gridSpacingX, gridSpacingY;
// Generate data for putting glyphs in a grid
var numAnalogyChoices = 5;
var analogyValues = new Array(4);
var analogyChoices = new Array(numAnalogyChoices);
var analogyAnswer;
var analogyCycleStep;
function clamp(num, min, max) {
return Math.min(Math.max(num, min), max);
}
function refreshAnalogyData() {
for (var i=0; i<4; i++) {
analogyValues[i] = new Array(8);
}
for (var i=0; i<3; i++) {
for (var j=0; j<8; j++) {
analogyValues[i][j] = random(100);
}
}
for (var j=0; j<8; j++) {
analogyValues[3][j] = clamp(analogyValues[1][j] - analogyValues[0][j] + analogyValues[2][j], 0, 100);
// handle overflow
analogyValues[1][j] = clamp(analogyValues[3][j] - analogyValues[2][j] + analogyValues[0][j], 0, 100);
}
analogyAnswer = Math.floor(random(numAnalogyChoices))
for (var i=0; i<numAnalogyChoices; i++) {
analogyChoices[i] = new Array(8);
for (var j=0; j<8; j++) {
if (i == analogyAnswer) {
analogyChoices[i][j] = analogyValues[3][j];
}
else {
analogyChoices[i][j] = random(100);
}
}
}
analogyCycleStep = 0;
}
function refreshGridData() {
var glyphSize = parseInt(sizeSelector.value(), 10);
if(glyphSize == 128) {
numGridCols = 7;
numGridRows = 3;
gridOffsetX = 10;
gridSpacingX = 136;
gridOffsetY = 20;
gridSpacingY = 166;
}
else if(glyphSize == 256) {
numGridCols = 3;
numGridRows = 1;
gridOffsetX = 20;
gridSpacingX = 320;
gridOffsetY = 100;
gridSpacingY = 500;
}
else if(glyphSize == 64) {
numGridCols = 14;
numGridRows = 7;
gridOffsetX = 3;
gridSpacingX = 68;
gridOffsetY = 6;
gridSpacingY = 71;
}
gridValues = new Array(numGridRows);
for (var i=0; i<numGridRows; i++) {
gridValues[i] = new Array(numGridCols);
for (var j=0; j<numGridCols; j++) {
gridValues[i][j] = new Array(8);
}
}
var mode = modeSelector.value();
if (mode == "gradient") {
var top_left = Array(8);
var top_right = Array(8);
var bottom_left = Array(8);
var bottom_right = Array(8);
for (var k=0; k<8; k++) {
top_left[k] = random(100);
top_right[k] = random(100);
bottom_left[k] = random(100);
bottom_right[k] = random(100);
}
for (var i=0; i<numGridRows; i++) {
if(numGridRows == 0) {
var frac_down = 0;
}
else {
var frac_down = i / (numGridRows - 1.0);
}
d_left = dataInterpolate(top_left, bottom_left, frac_down);
d_right = dataInterpolate(top_right, bottom_right, frac_down);
for (var j=0; j<numGridCols; j++) {
if(numGridCols == 0) {
var frac_over = 0;
}
else {
var frac_over = j / (numGridCols - 1.0);
}
gridValues[i][j] = dataInterpolate(d_left, d_right, frac_over);
}
}
}
else {
for (var i=0; i<numGridRows; i++) {
for (var j=0; j<numGridCols; j++) {
for (var k=0; k<8; k++) {
gridValues[i][j][k] = random(100);
}
}
}
}
refreshAnalogyData();
}
function sizeChangedEvent() {
var mode = modeSelector.value();
if (mode != "drive") {
refreshGridData();
}
redraw();
}
function modeChangedEvent() {
var mode = modeSelector.value();
var glyph = glyphSelector.value();
// enable/disable sliders
if (mode === "drive") {
// disable the button
button.attribute('disabled','');
// enable the size selector
sizeSelector.removeAttribute('disabled');
// enable the first four sliders
for(i=0; i<4; i++) {
val_sliders[i].removeAttribute('disabled');
}
if(glyph === "Centric") {
for(i=4; i<8; i++) {
val_sliders[i].attribute('disabled','');
}
}
else {
for(i=4; i<8; i++) {
val_sliders[i].removeAttribute('disabled');
}
}
}
else {
// enable the button
button.removeAttribute('disabled');
// disable the sliders
for(i=0; i<8; i++) {
val_sliders[i].attribute('disabled','');
}
if (mode == "analogy") {
// enable the size selector
sizeSelector.attribute('disabled','');
}
else {
// enable the size selector
sizeSelector.removeAttribute('disabled');
}
// refresh data
refreshGridData();
}
redraw();
}
function buttonPressedEvent() {
analogyCycleStep = 0;
refreshGridData();
redraw();
}
var colorBack = [128, 128, 128];
var colorFront = [200, 200, 200];
function drawDriveMode() {
var glyph_is_glyph4 = true;
if(glyphSelector.value() === "Angular Fragments")
glyph_is_glyph4 = false;
var glyphSize = parseInt(sizeSelector.value(), 10);
var halfSize = glyphSize / 2;
background(colorBack);
var halfSize = glyphSize / 2;
var middle_x = canvasWidth / 2;
var middle_y = canvasHeight / 2;
resetMatrix();
translate(middle_x - halfSize, middle_y - halfSize);
var val = [0,0,0,0,0,0,0,0];
for(i=0; i<8; i++) {
val[i] = val_sliders[i].value();
}
stroke(128, 128, 192);
noFill();
if(glyph_is_glyph4) {
ellipse(halfSize, halfSize, glyphSize+2);
glyph4(val, glyphSize)
}
else {
rect(-1, -1, glyphSize+2, glyphSize+2);
glyph8(val, glyphSize)
}
}
function drawGridMode() {
var glyph_fn = glyph4;
if(glyphSelector.value() === "Angular Fragments")
glyph_fn = glyph8;
var glyphSize = parseInt(sizeSelector.value(), 10);
background(colorBack);
for (var i=0; i<numGridRows; i++) {
for (var j=0; j<numGridCols; j++) {
resetMatrix();
translate(gridOffsetX + j * gridSpacingX, gridOffsetY + i * gridSpacingY);
glyph_fn(gridValues[i][j], glyphSize);
}
}
}
var analogyOffsetX = 350;
var analogyOffsetY = 40;
var analogySpacingX = 160;
var analogySpacingY = 160;
var analogyChoiceOffsetX = 260;
var analogyChoiceOffsetY = 380;
var analogyChoiceSpacingX = 100;
function drawAnalogy() {
background(colorBack);
var glyph_fn = glyph4;
if(glyphSelector.value() === "Angular Fragments")
glyph_fn = glyph8;
resetMatrix();
translate(analogyOffsetX + 0 * analogySpacingX, analogyOffsetY + 0 * analogySpacingY);
glyph_fn(analogyValues[0], 128);
resetMatrix();
translate(analogyOffsetX + 1 * analogySpacingX, analogyOffsetY + 0 * analogySpacingY);
glyph_fn(analogyValues[1], 128);
resetMatrix();
translate(analogyOffsetX + 0 * analogySpacingX, analogyOffsetY + 1 * analogySpacingY);
glyph_fn(analogyValues[2], 128);
resetMatrix();
translate(analogyOffsetX + 1 * analogySpacingX, analogyOffsetY + 1 * analogySpacingY);
if(analogyCycleStep == 2) {
glyph_fn(analogyValues[3], 128);
}
else {
stroke(64, 64, 192);
noFill();
if(glyph_fn === glyph4) {
ellipse(64, 64, 128+2);
}
else {
rect(-1, -1, 128+2, 128+2);
}
}
if(analogyCycleStep != 0) {
for(var i=0; i<numAnalogyChoices; i++) {
resetMatrix();
translate(analogyChoiceOffsetX + i * analogyChoiceSpacingX, analogyChoiceOffsetY);
if(analogyCycleStep == 2 && analogyAnswer == i) {
stroke(64, 64, 192);
fill(64, 64, 192);
rect(-6, -6, 64+12, 64+12);
}
glyph_fn(analogyChoices[i], 64);
}
}
}
function draw () {
var mode = modeSelector.value();
if (mode == "drive") {
drawDriveMode();
}
else if (mode == "analogy") {
drawAnalogy();
}
else {
drawGridMode();
}
}
function keyTyped() {
if (key == '!') {
saveBlocksImages();
}
else if (key == '@') {
saveBlocksImages(true);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment