PS1 MDDN 342 2016
A clone of an early experiment of PS1.
Generate template faces then draw on top using the mouse.
Middle mouse -> to add a new face at the current mouse position. Left click -> draw lines SPACE -> clear current drawing
license: mit |
// note: this file is poorly named - it can generally be ignored. | |
// helper functions below for supporting blocks/purview | |
function saveBlocksImages() { | |
// 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); | |
// 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); | |
imageData = offscreenCanvas.toDataURL('image/png'); | |
imageData = imageData.replace('image/png', downloadMime); | |
p5.prototype.downloadFile(imageData, 'thumbnail.png', 'png'); | |
} |
<head> | |
<script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.1/p5.js"></script> | |
<script language="javascript" type="text/javascript" src="readme.purview_helper.js"></script> | |
<script language="javascript" type="text/javascript" src="sketch.js"></script> | |
<style> body {padding: 0; margin: 0;} </style> | |
</head> | |
<body style="background-color:white"> | |
</body> |
var WIDTH = 960, | |
HEIGHT = 500; | |
var colors; | |
var currentColor; | |
var faces = []; | |
//parts | |
//var myEye; | |
function setup () { | |
//initialise global variables | |
colors = { | |
green: color(78, 255, 156), | |
blue: color(65, 253, 255), | |
black: color(0) | |
}; | |
currentColor = colors.black; | |
//myEye = eye("left",1,2); | |
createCanvas(WIDTH, HEIGHT); | |
//cover the background | |
fill(0); | |
rect(0,0,WIDTH,HEIGHT); | |
stroke(255); | |
faces.push(createFace(createVector(WIDTH / 2, HEIGHT / 2), createVector(175, 250), color(255), random(0, 1))); | |
} | |
var SIZE = 80; | |
var time = 0; | |
var lastMouseX; | |
var lastMouseY; | |
var centerPressed = false; | |
function draw() { | |
var funkAmt = mouseX / WIDTH; | |
var pos = createVector(mouseX, mouseY); | |
//draw things on top | |
if(mouseIsPressed){ | |
if(mouseButton == LEFT){ | |
if(lastMouseX != 0 || lastMouseY != 0){ | |
line(lastMouseX,lastMouseY,mouseX,mouseY); | |
} | |
lastMouseX = mouseX; | |
lastMouseY = mouseY; | |
} | |
if(mouseButton == CENTER){ | |
if(!centerPressed){ | |
var f = createFace(createVector(mouseX, mouseY), createVector(175, 250), color(255), random(0, 1)); | |
faces.push(f); | |
centerPressed = true; | |
} | |
} | |
} else { | |
lastMouseX = lastMouseY = 0; | |
centerPressed = false; | |
} | |
//draw faces | |
faces.forEach(function(elem){ | |
if(!elem.isDrawn){ | |
elem.draw(); | |
elem.isDrawn = true; | |
} | |
}); | |
} | |
function keyTyped() { | |
if (key == '!') { | |
saveBlocksImages(); | |
} else if (key == '1'){ | |
currentColor = color.black; | |
} else if (key == '2'){ | |
currentColor = color.blue; | |
} else if (key == '3'){ | |
currentColor = color.green; | |
} else if (key == ' '){ | |
rect(0,0,WIDTH,HEIGHT); | |
faces = []; | |
} | |
} | |
function createFace(position, dimensions, colour, featureOffset){ | |
featureOffset = constrain(featureOffset,0,1.0); | |
var faceSize = (dimensions.x + dimensions.y) / 2; | |
//new face object | |
var f = { | |
pos: position, | |
dims: dimensions, | |
mouthPos: {}, | |
mouthDims: {}, | |
lEyePos: {}, | |
lEyeDims: {}, | |
rEyePos: {}, | |
rEyeDims: {}, | |
nosePos: {}, | |
noseDims: {}, | |
col: colour, | |
funk: random(), | |
draw : "drawfunction" | |
}; | |
//calculate mouth positions | |
var mX = position.x + (featureOffset * random(-1,1) * (faceSize / 10)); | |
var mY = position.y + (dimensions.y / 3) + (featureOffset * random(-1,1) * (faceSize / 10)); | |
f.mouthPos = createVector(mX, mY); | |
f.mouthDims = createVector(faceSize/5, faceSize/15); | |
//calculate eye positions | |
var lEX = position.x - (dimensions.x / 5) + (featureOffset * random(-1,1) * (faceSize / 10)); | |
var lEY = position.y - (dimensions.y / 7) + (featureOffset * random(-1,1) * (faceSize / 10)); | |
f.lEyePos = createVector(lEX, lEY); | |
f.lEyeDims = createVector(faceSize/8, faceSize/15); | |
//r eye | |
var rEX = position.x + (dimensions.x / 5) + (featureOffset * random(-1,1) * (faceSize / 10)); | |
var rEY = position.y - (dimensions.y / 7) + (featureOffset * random(-1,1) * (faceSize / 10)); | |
f.rEyePos = createVector(rEX, rEY); | |
f.rEyeDims = createVector(faceSize/8, faceSize/15); | |
//nose | |
var nX = position.x + (featureOffset * random(-1,1) * (faceSize / 10)); | |
var nY = position.y + (featureOffset * random(-1,1) * (faceSize / 10)); | |
f.nosePos = createVector(nX, nY); | |
f.noseDims = createVector(faceSize/8 + random(-1,1) * (faceSize/30), faceSize/8 + random(-1,1) * (faceSize/30)); | |
f.draw = function(){ | |
// head | |
sketchyEllipse( | |
this.pos.x, | |
this.pos.y, | |
this.dims.x, | |
this.dims.y, | |
this.funk | |
); | |
// mouth | |
sketchyEllipse( | |
this.mouthPos.x, | |
this.mouthPos.y, | |
this.mouthDims.x, | |
this.mouthDims.y, | |
this.funk | |
); | |
// left eye | |
sketchyEllipse( | |
this.lEyePos.x, | |
this.lEyePos.y, | |
this.lEyeDims.x, | |
this.lEyeDims.y, | |
this.funk | |
); | |
// right eye | |
sketchyEllipse( | |
this.rEyePos.x, | |
this.rEyePos.y, | |
this.rEyeDims.x, | |
this.rEyeDims.y, | |
this.funk | |
); | |
//nose | |
sketchyEllipse( | |
this.nosePos.x, | |
this.nosePos.y, | |
this.noseDims.x, | |
this.noseDims.y, | |
this.funk | |
); | |
}; | |
return f; | |
} | |
function sketchyEllipse(xOrig, yOrig, wid, hei, funkAmt){ | |
wid = wid * 0.5; | |
hei = hei * 0.5; | |
//noise seed based on position | |
noiseSeed(funkAmt*20); | |
//set the max funkyness of the ellipse based on size & funk amt | |
var avgSizeOffset = ((wid + hei) * (constrain(funkAmt, 0, 1.0) * 0.5)); | |
var numPoints = 50; | |
var x = xOrig + sin(0) * (wid + (noise(0) - 0.5) * avgSizeOffset); | |
var y = yOrig + cos(0) * (hei + (noise(0) - 0.5) * avgSizeOffset); | |
var originalX = x, | |
originalY = y; | |
for(var i = 0; i < numPoints; i++){ | |
var amt = (i/numPoints) * TWO_PI; | |
var noiseAmt = noise(amt) - 0.5; | |
var nextX = xOrig + sin(amt) * (wid + noiseAmt * avgSizeOffset); | |
var nextY = yOrig + cos(amt) * (hei + noiseAmt * avgSizeOffset); | |
line(x, y, nextX, nextY); | |
x = nextX; | |
y = nextY; | |
} | |
//join the start and end | |
line(x, y, (originalX + x) * 0.5, (originalY + y) * 0.5); | |
line((originalX + x) * 0.5,(originalY + y) * 0.5, originalX, originalY); | |
} | |
/*Maps values 0 -> 1 to 0 -> 1 -> 0 in a zigzag pattern*/ | |
function saw(val){ | |
val = abs(val) % 1.0; | |
val = val * 2; | |
if(val > 1.0){ | |
val = 2.0 - val; | |
} | |
console.log(val); | |
return val; | |
} |