Skip to content

Instantly share code, notes, and snippets.

@uwcc
Last active July 19, 2021 07:21
Show Gist options
  • Save uwcc/51cfcd10e533bb4afe4fac64543dd4c3 to your computer and use it in GitHub Desktop.
Save uwcc/51cfcd10e533bb4afe4fac64543dd4c3 to your computer and use it in GitHub Desktop.
Chen Qiu: Randomised Collections
license: mit

Due to my unskilled use of the Bessel curve, it is not possible to recreate the effect in Sketch. By extracting the overall concept and visual effects of these voodoo masks, I reduced them to their present form.

Sliders: Face position/size Face Roundness Eye position Iris position/size Nose length Teeth position

/*
* This program draws your arrangement of faces on the canvas.
*/
const canvasWidth = 960;
const canvasHeight = 500;
let curRandomSeed = 0;
let lastSwapTime = 0;
const millisPerSwap = 3000;
function setup () {
// create the drawing canvas, save the canvas element
let main_canvas = createCanvas(canvasWidth, canvasHeight);
main_canvas.parent('canvasContainer');
curRandomSeed = int(focusedRandom(0, 1000));
// rotation in degrees
angleMode(DEGREES);
}
function changeRandomSeed() {
curRandomSeed = curRandomSeed + 1;
lastSwapTime = millis();
}
// global variables for colors
// slight green
const bg_color1 = [255, 218, 185];
function mouseClicked() {
changeRandomSeed();
}
function draw () {
if(millis() > lastSwapTime + millisPerSwap) {
changeRandomSeed();
}
// reset the random number generator each time draw is called
resetFocusedRandom(curRandomSeed);
// clear screen
background(bg_color1);
noStroke();
// draw a 7x4 grid of faces
let w = canvasWidth / 6;
let h = canvasHeight / 3;
for(let i=0; i<3; i++) {
for(let j=0; j<6; j++) {
let y = h/2 + h*i;
let x = w/2 + w*j;
let picker = random(1, 7);
// center face
if (picker > 0 && picker<=3) {
// smoother
noseLength = focusedRandom(2, 5, 5);
teethPos1 = int(focusedRandom(-1, 2));
teethPos2 = int(focusedRandom(-1, 2));
teethPos3 = int(focusedRandom(-1, 2));
teethPos4 = int(focusedRandom(-1, 2));
teethPos5 = int(focusedRandom(-1, 2));
irisPos1 = int(focusedRandom(-3, -1));
irisPos2 = int(focusedRandom(1, 3));
push();
translate(x, y);
scale(w/25, h/25);
face1(noseLength, teethPos1, teethPos2, teethPos3, teethPos4, teethPos5, irisPos1, irisPos2);
pop();
} else if (picker >3 && picker <=6){
push();
translate(x, y-10);
scale(w/30, h/30);
teethPos1 = int(focusedRandom(1, 3));
teethPos2 = int(focusedRandom(1, 3));
teethPos3 = int(focusedRandom(1, 3));
eyeSize = focusedRandom(3, 4);
iris = focusedRandom(1, 2);
rectSize = int(focusedRandom(0, 3));
noseLength = focusedRandom(2, 4, 5);
face2(teethPos1, teethPos2, teethPos3, eyeSize, iris, rectSize, noseLength);
pop();
} else if(picker >6 && picker <=7){
pop();
}
// else if (i > 0) {
// // all other faces
// push();
// translate(x, y);
// scale(w/25, h/25);
// if((i+j)%2 == 0) {
// face1();
// }
// else {
// thinness_value = focusedRandom(0, 100, 3);
//drawFace2(thinness_value);
// }
pop();
}
}
}
//}
function keyTyped() {
if (key == '!') {
saveBlocksImages();
}
else if (key == '@') {
saveBlocksImages(true);
}
}
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.3/seedrandom.min.js"></script>
<script src="https://d3js.org/d3-random.v1.min.js"></script>
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script>
<script language="javascript" type="text/javascript" src="z_focused_random.js"></script>
<script language="javascript" type="text/javascript" src="face_code.js"></script>
<script language="javascript" type="text/javascript" src="editor.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.0;
-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>
<a href="index.html">arrangement</a>
(<a href="arrangement.js">arrangement code</a>,
<a href="face_code.js">face code</a>)<br>
<a href="editor.html">editor</a>
(<a href="editor.js">editor code</a>,
<a href="face_code.js">face code</a>)<br>
<a href="sketch.html">sketch</a>
</div>
<div class="inner" id="controls" height="500px">
<table>
<tr>
<td>Setting 1</td>
<td id="slider1Container"></td>
</tr>
<tr>
<td>Setting 2</td>
<td id="slider2Container"></td>
</tr>
<tr>
<td>Setting 3</td>
<td id="slider3Container"></td>
</tr>
<tr>
<td>Setting 4</td>
<td id="slider4Container"></td>
</tr>
<tr>
<td>Setting 5</td>
<td id="slider5Container"></td>
</tr>
<tr>
<td>Setting 6</td>
<td id="slider6Container"></td>
</tr>
<tr>
<td>Setting 7</td>
<td id="slider7Container"></td>
</tr>
<tr>
<td>Setting 8</td>
<td id="slider8Container"></td>
</tr>
<tr>
<td>Setting 9</td>
<td id="slider9Container"></td>
</tr>
<tr>
<td>Setting 10</td>
<td id="slider10Container"></td>
</tr>
<tr>
<td>Show Target</td>
<td id="checkbox1Container"></td>
</tr>
<tr>
<td>Face</td>
<td id="selector1Container"></td>
</tr>
</table>
</div>
</div>
</table>
</body>
/*
* This editor shows the possible faces that can be created
*/
const canvasWidth = 960;
const canvasHeight = 500;
let slider1, slider2, slider3, slider4, slider5;
let slider6, slider7, slider8, slider9, slider10;
let faceSelector;
let faceGuideCheckbox;
function setup () {
// create the drawing canvas, save the canvas element
let main_canvas = createCanvas(canvasWidth, canvasHeight);
main_canvas.parent('canvasContainer');
// create sliders
slider1 = createSlider(0, 100, 50);
slider2 = createSlider(0, 100, 50);
slider3 = createSlider(0, 100, 50);
slider4 = createSlider(0, 100, 50);
slider5 = createSlider(0, 100, 50);
slider6 = createSlider(0, 100, 50);
slider7 = createSlider(0, 100, 50);
slider8 = createSlider(0, 100, 50);
slider9 = createSlider(0, 100, 50);
slider10 = createSlider(0, 100, 50);
slider1.parent('slider1Container');
slider2.parent('slider2Container');
slider3.parent('slider3Container');
slider4.parent('slider4Container');
slider5.parent('slider5Container');
slider6.parent('slider6Container');
slider7.parent('slider7Container');
slider8.parent('slider8Container');
slider9.parent('slider9Container');
slider10.parent('slider10Container');
faceGuideCheckbox = createCheckbox('', false);
faceGuideCheckbox.parent('checkbox1Container');
faceSelector = createSelect();
faceSelector.option('1');
faceSelector.option('2');
faceSelector.option('3');
faceSelector.value('1');
faceSelector.parent('selector1Container');
}
const bg_color = [225, 206, 187];
function draw () {
strokeWeight(0.2);
let mode = faceSelector.value();
background(bg_color);
let s1 = slider1.value();
let s2 = slider2.value();
let s3 = slider3.value();
let s4 = slider4.value();
let s5 = slider5.value();
let s6 = slider6.value();
let s7 = slider7.value();
let s8 = slider8.value();
let s9 = slider9.value();
let s10 = slider10.value();
let show_face_guide = faceGuideCheckbox.checked();
// use same size / y_pos for all faces
let face_size = canvasWidth / 5;
let face_scale = face_size / 10;
let face_y = height / 2;
let face_x = width / 2;
push();
translate(face_x, face_y);
scale(face_scale);
push();
if (mode == '1') {
// draw 3rd face using values mapped from 8 sliders
// length of the nose from 2 to 4
let noseLength = map(s1, 0, 100, 2, 4);
// positions of each row of teeth from -1 to 2
let teethPos1 = map(s2, 0, 100, -1, 2);
let teethPos2 = map(s3, 0, 100, -1, 2);
let teethPos3 = map(s4, 0, 100, -1, 2);
let teethPos4 = map(s5, 0, 100, -1, 2);
let teethPos5 = map(s6, 0, 100, -1, 2);
// positions of iris from left or right
let irisPos1 = map(s7, 0, 100, -3, -1);
let irisPos2 = map(s8, 0, 100, 1, 3);
face1(noseLength, teethPos1, teethPos2, teethPos3, teethPos4, teethPos5, irisPos1, irisPos2);
}
if (mode == '2') {
// draw 3rd face using values mapped from 7 sliders
// positions of teeth
let teethPos1 = map(s1, 0, 100, 1, 3);
let teethPos2 = map(s2, 0, 100, 1, 3);
let teethPos3 = map(s3, 0, 100, 1, 3);
// size of eyes
let eyeSize = map(s4, 0, 100, 3, 4);
let iris = map(s5, 0, 100, 1, 2);
// roundness of the face
let rectSize = map(s6, 0, 100, 0, 3);
// length of the nose
let noseLength = map(s7, 0, 100, 2, 4);
face2(teethPos1, teethPos2, teethPos3, eyeSize, iris, rectSize, noseLength);
}
if (mode == '3') {
// draw 3rd face using values mapped from 7 sliders
// size of eyes
let eyeSize = map(s1, 0, 100, 3, 4);
// size of iris
let iris = map(s2, 0, 100, 1, 2);
// length of nose
let noseLength = map(s3, 0, 100, -1, -8);
// positions of teeth
let teethPos1 = map(s4, 0, 100, 1, 3);
let teethPos2 = map(s5, 0, 100, 1, 3);
let teethPos3 = map(s6, 0, 100, 1, 3);
//let teethPos4 = map(s7, 0, 100, 1, 3);
face3(eyeSize, iris, noseLength, teethPos1, teethPos2, teethPos3, teethPos4);
}
pop();
if(show_face_guide) {
strokeWeight(0.1);
rectMode(CORNER);
noFill()
stroke(0, 0, 255);
// ellipse(0, 0, 20, 20);
rect(-10, -10, 20, 20);
line( 0, -11, 0, -10);
line( 0, 10, 0, 11);
line(-11, 0,-10, 0);
line( 11, 0, 10, 0);
}
pop();
}
function keyTyped() {
if (key == '!') {
saveBlocksImages();
}
else if (key == '@') {
saveBlocksImages(true);
}
}
/*
* This file should contain code that draws your faces.
*
* Each function takes parameters and draws a face that is within
* the bounding box (-10, -10) to (10, 10).
*
* These functions are used by your final arrangement of faces as well as the face editor.
*/
function face1(noseLength, teethPos1, teethPos2, teethPos3, teethPos4, teethPos5, irisPos1, irisPos2) {
//ears
fill(60);
rect(-6, -7, 12, 8, 0.5);
//face
fill(60);
rect(-5, -10, 10, 15, 0.5);
fill(255);
rect(-5, -6.5, 10, teethPos3+4);
//teeth
for(let i=0; i<4; i++) {
//stroke(60);
//strokeWeight(0.1);
for(let j=0; j<10; j++) {
fill(0, 191, 255, 120);
rect(-5+j, teethPos1+i, 1, 1, 0.01);
fill(0, 250, 154, 120);
rect(-5+j, teethPos2+i, 1, 1, 0.01);
fill(245, 222, 176, 120);
rect(-5+j, teethPos3+i, 1, 1, 0.01);
}
}
//print
fill(235, 64, 64);
rect(-5, -2, 5, 0.5);
rect(0, -2, 5, 0.5);
rect(-5, -3, 5, 0.5);
rect(0, -3, 5, 0.5);
stroke(255);
strokeWeight(0.4);
//line(-4, -10, 0, -5);
//line(4, -10, 0, -5);
line(-5, -8, 5, -8);
//nose
stroke(60);
strokeWeight(0.1);
fill(235, 64, 64);
rect(-1, -5, 2, noseLength*1.5, 0.5);
//eyes
fill(60);
ellipse(-2.5, -5, 4, 2);
ellipse(2.5, -5, 4, 2);
fill(255);
ellipse(irisPos1-0.5, -5, 1.5);
ellipse(irisPos2+0.5, -5, 1.5);
fill(60);
ellipse(irisPos1-0.5, -5, 1);
ellipse(irisPos2+0.5, -5, 1);
fill(255);
ellipse(irisPos1-0.5, -5, 0.75);
ellipse(irisPos2+0.5, -5, 0.75);
}
function face2(teethPos1, teethPos2, teethPos3, eyes, iris, circle, noseLength) {
//ears
fill(60);
rect(-6, -2.5, 12, 8, 0.5);
//face
rectMode(RADIUS);
fill(60);
ellipse(0, -2, 10, 18);
fill(255);
rect(0, 0, 5, 3, 0.5);
//nose
rectMode(CORNER);
fill(235, 64, 64);
rect(-1, -5, 2, noseLength*2, circle);
//print
rectMode(RADIUS);
fill(235, 64, 64);
rect(0, -5, 2, 2, circle);
fill(255);
rect(0, -5, 1.5, 1.5, circle);
fill(235, 64, 64);
rect(0, -5, 1, 1, circle);
//eyes
stroke(60);
strokeWeight(0.3);
fill(230);
ellipse(3, -1, eyes);
ellipse(-3, -1, eyes);
noStroke();
fill(60);
rect(3, -1, iris*0.7, iris*0.7, circle);
rect(-3, -1, iris*0.7, iris*0.7, circle);
fill(255);
ellipse(3, -1, eyes*1/4);
ellipse(-3, -1, eyes*1/4);
//jew
rectMode(CORNER);
fill(60);
rect(-4, 3, 8, 5, 0.5);
//teeth
for(let i=0; i<3; i++) {
for(let j=0; j<10; j++) {
rectMode(CORNER);
fill(0, 191, 255, 180);
rect(-5+j, teethPos1+i+0.5, 1, 1);
fill(0, 250, 154, 180);
rect(-5+j, teethPos2+i+0.5, 1, 1);
fill(245, 222, 176, 180);
rect(-5+j, teethPos3+i+0.5, 1, 1);
}
}
}
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.3/seedrandom.min.js"></script>
<script src="https://d3js.org/d3-random.v1.min.js"></script>
<script language="javascript" type="text/javascript" src="z_purview_helper.js"></script>
<script language="javascript" type="text/javascript" src="z_focused_random.js"></script>
<script language="javascript" type="text/javascript" src="face_code.js"></script>
<script language="javascript" type="text/javascript" src="arrangement.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.0;
-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>
<a href="index.html">arrangement</a>
(<a href="arrangement.js">arrangement code</a>,
<a href="face_code.js">face code</a>)<br>
<a href="editor.html">editor</a>
(<a href="editor.js">editor code</a>,
<a href="face_code.js">face code</a>)<br>
<a href="sketch.html">sketch</a>
</div>
<div class="inner" id="controls" height="500px">
<table>
<tr>
<td></td>
<td id="slider1Container"></td>
</tr>
<tr>
<td></td>
<td id="slider2Container"></td>
</tr>
<tr>
<td></td>
<td id="slider3Container"></td>
</tr>
<tr>
<td></td>
<td id="slider4Container"></td>
</tr>
<tr>
<td></td>
<td id="slider5Container"></td>
</tr>
<tr>
<td></td>
<td id="selector1Container"></td>
</tr>
</table>
</div>
</div>
</table>
</body>
{
"commits": [
{
"sha": "deda2f11d05803901f634cce38ee89beae4c18fb",
"name": "final_version"
},
{
"sha": "f2f2c949e60b82d7c2916b4a438023913947fdf4",
"name": "refined_distribution"
},
{
"sha": "5db9d31bcb6b044ab5e5e59030ee86befdf709e6",
"name": "weighted_selection"
},
{
"sha": "7268bc76303753c2972fdee5978e9cce75cf0616",
"name": "extra_experiment1"
}
]
}
<head>
<style> body {padding: 0; margin: 0;} </style>
</head>
<body style="background-color:white">
<img src="sketch.jpg" width="960" height="480"/>
<p>
<a href="index.html">arrangement</a>
(<a href="arrangement.js">arrangement code</a>,
<a href="face_code.js">face code</a>)<br>
<a href="editor.html">editor</a>
(<a href="editor.js">editor code</a>,
<a href="face_code.js">face code</a>)<br>
<a href="sketch.html">sketch</a>
</body>
function resetFocusedRandom() {
return Math.seedrandom(arguments);
}
function focusedRandom(min, max, focus, mean) {
// console.log("hello")
if(max === undefined) {
max = min;
min = 0;
}
if(focus === undefined) {
focus = 1.0;
}
if(mean === undefined) {
mean = (min + max) / 2.0;
}
if(focus == 0) {
return d3.randomUniform(min, max)();
}
else if(focus < 0) {
focus = -1 / focus;
}
let sigma = (max - min) / (2 * focus);
let val = d3.randomNormal(mean, sigma)();
if (val >= min && val < max) {
return val;
}
return d3.randomUniform(min, max)();
}
// 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);
// call this function after 1 second
setTimeout(function(){
p5.prototype.downloadFile(imageData, 'thumbnail.png', 'png');
}, 1000);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment