|
function Face(position, dimensions, colour, featureOffset){ |
|
//constrain parameters + usefull variables |
|
featureOffset = constrain(featureOffset, 0, 2.0); |
|
|
|
//set positions and dimensions |
|
this.pos = position; |
|
var avgDims = (dimensions.min + dimensions.max) / 2; |
|
// this.dims = createVector(avgDims + (featureOffset * random(-dimensions.min/2, dimensions.max/2)), avgDims + (featureOffset * random(-dimensions.min/2, dimensions.max/2)));//dimensions; |
|
|
|
var xDim = avgDims + (featureOffset * focusedRandom(-dimensions.min/2, dimensions.max/2,1)); |
|
var yDim = avgDims + (featureOffset * focusedRandom(-dimensions.min/2, dimensions.max/2,1)); |
|
this.dims = createVector(xDim, yDim);//dimensions; |
|
|
|
//average of face size for setting feature sizes |
|
var faceSize = (this.dims.x + this.dims.y) / 2; |
|
|
|
//calculate mouth positions |
|
var mX = (featureOffset * random(-1,1) * (faceSize / 10)); |
|
var mY = (this.dims.y / 3) + (featureOffset * random(-1,1) * (faceSize / 10)); |
|
this.mouth = new Mouth(mX, mY, faceSize/5, random(0,1)); |
|
//left eye |
|
var lEX = - (this.dims.x / 3) + (featureOffset * random(1) * (this.dims.x / 10)); |
|
var lEY = - (this.dims.y / 7) + (featureOffset * random(-1,1) * (faceSize / 10)); |
|
this.lEyePos = createVector(lEX, lEY); |
|
this.lEyeDims = createVector(faceSize/8, random(faceSize/15,faceSize/8)); |
|
//right eye |
|
var rEX = (this.dims.x / 3) - (featureOffset * random(1) * (this.dims.x / 10)); |
|
var rEY = this.lEyePos.y + (featureOffset * random(-1,1) * (faceSize / 20)); |
|
this.rEyePos = createVector(rEX, rEY); |
|
this.rEyeDims = createVector(faceSize/8, random(faceSize/15,faceSize/8)); |
|
//nosef |
|
var nX = (featureOffset * random(-1,1) * (faceSize / 30)); |
|
var nY = (featureOffset * random(-1,1) * (faceSize / 30)); |
|
this.nosePos = createVector(nX, nY); |
|
var noseWid = faceSize/8 + random(-1,1) * (faceSize/30); |
|
var noseHei = faceSize/8 + random(-1,1) * (faceSize/30); |
|
this.noseDims = createVector(noseWid, noseHei); |
|
//left ear |
|
var lEarX = - (this.dims.x / 2); |
|
var lEarY = - (featureOffset * random(-1,1) * (faceSize / 15)); |
|
this.lEarPos = createVector(lEarX, lEarY); |
|
this.lEarDims = createVector(faceSize/10, faceSize/6); |
|
//right ear |
|
var rEarX = (this.dims.x / 2); |
|
var rEarY = - (featureOffset * random(-1,1) * (faceSize / 15)); |
|
this.rEarPos = createVector(rEarX, rEarY); |
|
this.rEarDims = createVector(faceSize/10, faceSize/6); |
|
//hair -> numhairs, dir, area |
|
var hairArea = { |
|
min: createVector(-random(faceSize*0.3),-this.dims.y/2.5 - random(faceSize*0.1)), |
|
max: createVector(random(faceSize*0.3), -this.dims.y/2.5 + random(faceSize*0.1)) |
|
}; |
|
this.hair = createHair(random(MIN_NUM_HAIRS, MAX_NUM_HAIRS), createVector(random(-6,6),random(4,-8)), hairArea, MAX_HAIR_FUNK); |
|
|
|
//funk |
|
this.funk = random(0.3,1.0); |
|
this.tilt = focusedRandom(-QUARTER_PI, QUARTER_PI,5); |
|
this.seed = (this.pos.x - this.pos.y) * random(200); |
|
this.strokes = int(random(MIN_STROKES, MAX_STROKES+1)); |
|
this.sketchyness = random(0,0.4);//focusedRandom(0, 0.4, 0, 0.2); |
|
this.colour = colour; |
|
this.popCalled = false; |
|
this.mouseOn = false; |
|
} |
|
Face.prototype.pop = function(){ |
|
this.start = millis(); |
|
this.popCalled = true; |
|
} |
|
/* A draw function for each face */ |
|
Face.prototype.draw = function(tilt){ |
|
funk = this.sketchyness; |
|
strokes = this.strokes; |
|
strokes = (strokes < 1) ? int(1) : int(strokes); |
|
|
|
//as numStrokes increase, reduce opacity |
|
stroke(this.colour); |
|
push(); |
|
translate(this.pos.x,this.pos.y); |
|
rotate(this.tilt * tilt); |
|
if(this.mouseOn){ |
|
scale(easeOutElastic(0,millis() - this.start, 1, .5, 2000)); |
|
} |
|
// head |
|
noFill(); |
|
sketchyEllipse( |
|
0, |
|
0, |
|
this.dims.x, |
|
this.dims.y, |
|
funk, |
|
strokes, |
|
this.seed |
|
); |
|
// mouth |
|
if(this.mouseOn){ |
|
noiseSeed(this.pos.x - this.pos.y); |
|
this.mouth.smile = noise(millis()/900)+0.5; |
|
} |
|
this.mouth.draw(); |
|
// left eye |
|
sketchyEllipse( |
|
this.lEyePos.x, |
|
this.lEyePos.y, |
|
this.lEyeDims.x, |
|
this.lEyeDims.y * (1 - this.mouth.smile), |
|
funk, |
|
strokes > 1? 2 : 1, |
|
this.seed + 50 |
|
); |
|
fill(this.colour); |
|
var mousePos = createVector(mouseX, mouseY); |
|
var lPupil = createVector(this.lEyePos.x, this.lEyePos.y); |
|
var rPupil = createVector(this.rEyePos.x, this.rEyePos.y); |
|
ellipse(lPupil.x,lPupil.y, 2, 2); |
|
ellipse(this.rEyePos.x, this.rEyePos.y, 2, 2); |
|
noFill(); |
|
// right eye |
|
sketchyEllipse( |
|
this.rEyePos.x, |
|
this.rEyePos.y, |
|
this.rEyeDims.x, |
|
this.rEyeDims.y * (1 - this.mouth.smile), |
|
funk, |
|
strokes > 1? 2 : 1, |
|
this.seed + 80 |
|
); |
|
//nose |
|
sketchyEllipse( |
|
this.nosePos.x, |
|
this.nosePos.y, |
|
this.noseDims.x, |
|
this.noseDims.y, |
|
funk, |
|
strokes > 1? 2 : 1, |
|
this.seed + 90 |
|
); |
|
//l ear |
|
sketchyEllipse( |
|
this.lEarPos.x, |
|
this.lEarPos.y, |
|
this.lEarDims.x, |
|
this.lEarDims.y, |
|
funk, |
|
strokes > 1? 2 : 1, |
|
this.pos.x - this.pos.y |
|
); |
|
//r ear |
|
sketchyEllipse( |
|
this.rEarPos.x, |
|
this.rEarPos.y, |
|
this.rEarDims.x, |
|
this.rEarDims.y, |
|
funk, |
|
strokes > 1? 2 : 1, |
|
this.pos.x - this.pos.y |
|
); |
|
this.hair(); |
|
pop(); |
|
} |
|
Face.prototype.on = function (x,y) { |
|
var minX = this.pos.x - this.dims.x/2; |
|
var maxX = this.pos.x + this.dims.x/2; |
|
var minY = this.pos.y - this.dims.y/2; |
|
var maxY = this.pos.y + this.dims.y/2; |
|
if(x < minX || x > maxX || y < minY || y > maxY)return false; |
|
return true; |
|
}; |
|
|
|
function Mouth(x, y, width, smile){ |
|
this.smile = smile || 1; |
|
var height = width / 3; |
|
this.draw = function(sketchy){ |
|
noiseSeed(x - y); |
|
beginShape(); |
|
//smile vert l |
|
curveVertex(x - width/2, y - height * this.smile); |
|
curveVertex(x - width/5, y - height); |
|
curveVertex(x + width/5, y - height); |
|
//smile vert 2 |
|
curveVertex(x + width/2, y - height * this.smile); |
|
curveVertex(x + width/5, y); |
|
curveVertex(x - width/5, y); |
|
//smile vert 1 |
|
curveVertex(x - width/2, y - height * this.smile); |
|
curveVertex(x - width/5, y - height); |
|
curveVertex(x + width/5, y - height); |
|
endShape(); |
|
} |
|
} |
|
|
|
function createHair(numHairs, roughDir, area, bend){ |
|
var hairs = []; |
|
for (var i = 0; i < numHairs; i++){ |
|
var hairPoints = []; |
|
var pos = createVector(random(area.min.x, area.max.x), random(area.min.y, area.max.y)); |
|
hairs.push(pos); |
|
var dir = roughDir.copy(); |
|
var vel = createVector(random(-1,1),random(-1,1)); |
|
for(var j = 0; j < 5; j++){ |
|
vel = createVector(random(-bend/2,bend/2), random(-bend/2,bend/2)); |
|
dir.add(vel); |
|
pos.add(dir); |
|
hairPoints.push(pos.copy()); |
|
} |
|
hairs.push(hairPoints); |
|
} |
|
return function draw(){ |
|
hairs.forEach(function(h){ |
|
var lastP = h[0]; |
|
for(var i = 1; i < h.length; i++){ |
|
var nextP = h[i]; |
|
line(lastP.x, lastP.y, nextP.x, nextP.y); |
|
lastP = nextP; |
|
} |
|
}) |
|
} |
|
} |
|
|
|
function resetFocusedRandom() { |
|
return Math.seedrandom(arguments); |
|
} |
|
|
|
function focusedRandom(min, max, focus, mean) { |
|
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; |
|
} |
|
sigma = (max - mean) / focus; |
|
val = d3.randomNormal(mean, sigma)(); |
|
if (val > min && val < max) { |
|
return val; |
|
} |
|
return d3.randomUniform(min, max)(); |
|
} |
|
|
|
// Courtesy of http://www.joshondesign.com/2013/03/01/improvedEasingEquations |
|
function easeOutElastic(x,t,b,c,d){ |
|
var s=1.70158; |
|
var p=0; |
|
var a=c; |
|
if(t==0)return b; |
|
if((t/=d)==1)return b+c;if(!p)p=d*.3; |
|
if(a<Math.abs(c)){ a=c; var s=p/4;} |
|
else var s=p/(2*Math.PI)*Math.asin(c/a); |
|
return a*Math.pow(2,-10*t)*Math.sin((t*d-s)*(2*Math.PI)/p)+c+b; |
|
} |