block by dribnet 6ba0b1aa018a6db6a3c67d9560accf03

MDDN 342 Final Project

Full Screen

Face Generator - Andrew Moffat This is the refined version of my face generator. To begin with, the original version of the project featured a monochromatic sketch style, using Bezier curves. I wanted to revisit this project as I felt that I had a really solid base of code and graphic design to build upon, where I had left the project it was almost as if I had drawn the outline, but had forgotten to colour it in. I identified that one part of the project, to produce unique faces was being held back by the stylistic similarity, the lack of distinct visual elements.

I initially began by changing each face element into its own object, and created the grid using an array of objects. My intent was to utilise this for animation, but unfortunately this did not work out. I then enlarged the faces to allow for quicker comparisons, and increased the saturation on the faces to a more natural skin tone. I also refined the mouth down to enhance the visibility of emotion, and enlarged the eyes, as well as adding in variation with the eyebrows as the eye position. I also added in variation around the ‘Gauntness’ of the lower half of the face.

With the next version I decided to focus my efforts on replicating a ‘male’ face, as this would reduce the complexity of the code and allow me to focus on refinement. I chose to work on facial hair next, beginning by adding in a simple shape representation, but decided that this didn’t look quite right. I wanted a bit more realism and uniqueness, so I chose to add in another layer of thicker but more transparent edge lines that were coloured blonde or brown to break up the contrast of the black edge lines and the beard, and to soften up the lower half of the faces to create more diversity.

The next iteration was focused on adding in hair. I initially began by approaching it in the same way as the beard, by using a block shape to shade in the area, and soften up the edges using softer transparent lines. This block shape did not work at all, and just made the faces look blocky and uniform, so I removed this and just left it with the soft edge lines. I didn’t like how flat everything was looking, so I experimented with layering the base shape with different opacities and brightness to push it into 3d space, but this didn’t really produce an effect I liked. I decided to stick with the sketch style, and utilizing my line function create shading similar to what you would get with a graphics marker. I really loved the effect this produced, but wanted it to pop a bit more, so made the hair some nice bright colours.

With my final iteration, I decided I loved the forms that I had come to, but didn’t like the aesthetic that was produced by the colour combinations. I decided to look back to last project, and pull the colour palette from that and use it for this version. That colour scheme tied it all together, with a primary colour scheme perfectly tying in and complementing the sketch/graphic marker style

index.html

<head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.2/p5.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.2/addons/p5.dom.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.0/seedrandom.min.js"></script>
    <script src="https://d3js.org/d3-random.v1.min.js"></script>
    <script language="javascript" type="text/javascript" src="focusedRandom.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">
<div id="canvasContainer"></div>
</body>

focusedRandom.js

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;
  }
  sigma = (max - mean) / focus;
  val = d3.randomNormal(mean, sigma)();
  if (val > min && val < max) {
    return val;
  }
  return d3.randomUniform(min, max)();
}

purview.json

{
  "commits": [
    {
      "sha": "0000000000000000000000000000000000000012",
      "name": "final_version"
    },
    {
      "sha": "0000000000000000000000000000000000000011",
      "name": "interaction_2"
    },
    {
      "sha": "0000000000000000000000000000000000000010",
      "name": "interaction_1"
    },
    {
      "sha": "0000000000000000000000000000000000000009",
      "name": "distribution_2"
    },
    {
      "sha": "0000000000000000000000000000000000000008",
      "name": "distribution_1"
    },
    {
      "sha": "0000000000000000000000000000000000000007",
      "name": "randomizer_3"
    },
    {
      "sha": "0000000000000000000000000000000000000006",
      "name": "randomizer_2"
    },
    {
      "sha": "0000000000000000000000000000000000000005",
      "name": "randomizer_1"
    },
    {
      "sha": "0000000000000000000000000000000000000004",
      "name": "drawing_styles_3"
    },
    {
      "sha": "0000000000000000000000000000000000000003",
      "name": "drawing_styles_2"
    },
    {
      "sha": "0000000000000000000000000000000000000002",
      "name": "drawing_styles_1"
    },
    {
      "sha": "6e6db89be3cb99f99d07171faee645e84379dd06",
      "name": "self_portrait"
    }
  ]
}

readme.purview_helper.js

// note: this file is poorly named - it can generally be ignored.

// helper functions below for supporting blocks/purview

var canvasWidth = 4000;
var canvasHeight = 3000;

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 = canvasWidth;
  offscreenCanvas.height = canvasHeight;
  var context = offscreenCanvas.getContext('2d');
  // background is flat white
  context.fillStyle="#FFFFFF";
  context.fillRect(0, 0, canvasWidth, canvasHeight);
  context.drawImage(this.canvas, 0, 0, canvasWidth, canvasHeight);
  // save to browser
  var downloadMime = 'image/octet-stream';
  var imageData = offscreenCanvas.toDataURL('image/png');
  imageData = imageData.replace('image/png', downloadMime);
  p5.prototype.downloadFile(imageData, 'canvas.png', 'png');

/*
  // 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');
  */
}

sketch.js

var main_canvas;
var colF=100;
var smilebool=[];
var smiletime=[];
var sec = 0;
var faces=[];
var faceGaunt;
var   midW;
var beardcheck;
var haircheck;

var canvasWidth = 4000;
var canvasHeight = 3000;

function setup () {
  // create the drawing canvas, save the canvas element
  main_canvas = createCanvas(canvasWidth, canvasHeight);
  background(227,225,216);
  beardcheck=round(random(0,1));
  haircheck=round(random(0,2));
  faceGaunt=random(.4,.8)




  for (var i=0; i<200; i++) {
        faces.push(new drawface());
  }
    smilebool[i]=false;
    smiletime[i]=round(random(10,40));
  
  angleMode(DEGREES);
  main_canvas.parent('canvasContainer');
}




function draw(){
  sec = second();
  var smilesize=7;
  var countface=0;

  background(227,225,216);

  for(i=3.0;i<=25;i+=1.1) {
   for(u=3.4;u<=15;u+=0.8) {
  // for(i=.5;i<=(14*2) + 0.5;i++){
  //  for(u=.5;u<=(8*3) + 0.5;u++){  
  // for(i=.5;i<=(1) + 0.5;i++){
  //   for(u=.5;u<=(1) + 0.5;u++){  
      beardcheck=round(random(0,1)); 
      haircheck=round(random(0,2));  
      faces[countface].x=1000/5*i
      faces[countface].y=500/3*u
      if(smiletime[countface]==sec){
        faces[countface].smilesize=16      
      }
      faces[countface].display()
      countface++  
      println(countface)
    }
  }
  noLoop();
}

function keyTyped() {
  if (key == '!') {
    saveBlocksImages();
  }
}




function drawface() {

 this.x
 this.y
 this.basegen=random(60,110);
 this.smilesize=4;

 
this.display = function() {
 basegen=this.basegen
 x=this.x
 y=this.y
 smilesize=this.smilesize
 top1 = basegen
 bottom = basegen*random(1.04,1.17)
 size=random(70,80)/100
 side = basegen*random(size-.04,size+.04)
  push()
  rotate(random(-10,10))
    translate(x, y); 
    push()
    rotate(random(-40,40))
      faceGaunt=random(.5,.8)
      head(top1,bottom,side,smilesize)
      eyes(top1,bottom,side)
      mouth2(top1,bottom,side,smilesize)
      hair(top1,bottom,side)
      headlines(top1,bottom,side)
      pop()
  pop()
}
}

function keyTyped() {
  if (key == '!') {
    saveBlocksImages();
  }
}

function head(top1,bottom,side,smilesize){  
  midW=random(.8,1.2)
  top1 = top1
  bottom = bottom
  side2 = midW*side
  colorMode(HSB,360)
  colF=random(225,360)
  fill(37,90,colF)
  noStroke()
  colorMode(RGB,255)
var linecolors = [  [222,86,74],  [89,198,213],  [238,236,166]]
  fill(linecolors[(round(random(2)))])

  colorMode(HSB,360)
  beginShape();
    vertex(0, bottom);//chin
    vertex(faceGaunt*side,.85*bottom);//bottomRight
    vertex(side2,0);//midRight
    vertex(.9*side2,-.66*top1);//topRight
    vertex(0,-top1);//top of head
    vertex(-.9*side2,-.66*top1);//topLeft
    vertex(-side2, 0);//midleft
    vertex(-faceGaunt*side2,.85*bottom);//bottomleft
  endShape();
  shadeW=random(40,60)
  facelines2(0, bottom,0,-top1,360,5,shadeW)
  facelines2(faceGaunt*side,.85*bottom,.9*side2,-.66*top1,360,20,shadeW)
  facelines2(side2,0,side2,0,360,30,shadeW) 
  facelines2(-faceGaunt*side,.85*bottom,-.9*side2,-.66*top1,0,10,shadeW)
  facelines2(-side2,-15,-side2,15,0,10,shadeW)     



    colorMode(RGB,255)


  linescol=color(0,150)

  stroke(linescol)
  facelines(0,bottom,faceGaunt*side2,.85*bottom,linescol)
  facelines(faceGaunt*side2,.85*bottom,side2,0,linescol)
  facelines(side2,0,.9*side2,-.66*top1,linescol)
  facelines(.9*side2,-.66*top1,0,-top1,linescol)
  facelines(0,bottom,-faceGaunt*side2,.85*bottom,linescol)
  facelines(-faceGaunt*side2,.85*bottom,-side2,0,linescol)
  facelines(-side2,0,-.9*side2,-.66*top1,linescol)
  facelines(-.9*side2,-.66*top1,0,-top1,linescol)  


}

function headlines(top1,bottom,side){  
  
  top1 = top1
  bottom = bottom
  side2 = midW*side
  colorMode(HSB,360)
  colF=random(225,360)
  fill(37,90,colF)
  noStroke()
    colorMode(RGB,255)
  linescol=color(0,150)
  stroke(linescol)
  facelines(0,bottom,faceGaunt*side2,.85*bottom,linescol)
  facelines(faceGaunt*side2,.85*bottom,side2,0,linescol)
  facelines(side2,0,.9*side2,-.66*top1,linescol)
  facelines(.9*side2,-.66*top1,0,-top1,linescol)
  facelines(0,bottom,-faceGaunt*side2,.85*bottom,linescol)
  facelines(-faceGaunt*side2,.85*bottom,-side2,0,linescol)
  facelines(-side2,0,-.9*side2,-.66*top1,linescol)
  facelines(-.9*side2,-.66*top1,0,-top1,linescol)  


}


function facelines (ax1,ay1,ax2,ay2,colorfacelines){
  noFill()
  //fill(29,130,colF)
   //colorMode(HSB,360)

  //stroke(0,200)
  strokeWeight(random(2))
  r1=-20
  r2=20
  i=0
  for(var i=0;i<=5;i++){
    controlx1=round(ax1+random(r1,r2)*cos(random(0,360)))
    controly1=round(ay1+random(r1,r2)*sin(random(0,360)))
    controlx2=round(ax2+random(r1,r2)*cos(random(0,360)))
    controly2=round(ay2+random(r1,r2)*sin(random(0,360)))
    strokeWeight(random(3))
    bezier(ax1,ay1,controlx2,controly2,controlx1,controly1,ax2,ay2) 
    ellipse(ax1,ay1,5,5)
    ellipse(ax2,ay2,5,5)
  }
     colorMode(HSB,360)
}

function facelines2 (ax1,ay1,ax2,ay2,colorfacelines,colorfacelines2,shade){
  noFill()

  r1=-20
  r2=20
  i=0
  for(var i=0;i<=5;i++){
    controlx1=round(ax1+random(r1,r2)*cos(random(0,360)))
    controly1=round(ay1+random(r1,r2)*sin(random(0,360)))
    controlx2=round(ax2+random(r1,r2)*cos(random(0,360)))
    controly2=round(ay2+random(r1,r2)*sin(random(0,360)))
    stroke(colorfacelines,colorfacelines2)
    strokeWeight(shade)
    bezier(ax1,ay1,controlx2,controly2,controlx1,controly1,ax2,ay2) 
  }
     colorMode(HSB,360)
}

function beardlines (ax1,ay1,ax2,ay2,colorfacelines,neat){
  noFill()
  //fill(29,130,colF)
   //colorMode(HSB,360)
  //stroke(0,colorfacelines)


  strokeWeight(random(2))
  r1=-20
  r2=20
  i=0
  for(var i=0;i<=5;i++){
    controlx1=round(ax1+random(r1,r2)*cos(random(0,360)))
    controly1=round(ay1+random(r1,r2)*sin(random(0,360)))
    controlx2=round(ax2+random(r1,r2)*cos(random(0,360)))
    controly2=round(ay2+random(r1,r2)*sin(random(0,360)))
    strokeWeight(neat)
    bezier(ax1,ay1,controlx2,controly2,controlx1,controly1,ax2,ay2) 
    //ellipse(ax1,ay1,5,5)
    //ellipse(ax2,ay2,5,5)
  }
}

function hairlines (ax1,ay1,ax2,ay2,colorfacelines,neat){
  noFill()
  //fill(29,130,colF)

  strokeWeight(random(2))
  r1=-20
  r2=20
  i=0
  for(var i=0;i<=5;i++){
    controlx1=round(ax1+random(r1,r2)*cos(random(0,360)))
    controly1=round(ay1+random(r1,r2)*sin(random(0,360)))
    controlx2=round(ax2+random(r1,r2)*cos(random(0,360)))
    controly2=round(ay2+random(r1,r2)*sin(random(0,360)))
    strokeWeight(neat)
    bezier(ax1,ay1,controlx2,controly2,controlx1,controly1,ax2,ay2) 
    //ellipse(ax1,ay1,5,5)
    //ellipse(ax2,ay2,5,5)
  }
}


function eyes (top1,bottom,side){
  top1=top1
  bottom=bottom
  side=side
  xR1=random(.25,.55)
  xR2=random(.25,.55)
  yR1=random(0,.15)
  r1=.1*20;
  r2=random(6,14)
  pupilcol=random(150,200)
  pupilcolb=random(0,100)

  noFill()
  stroke(65,65,65,150)
  strokeWeight(.2)
  ellipse(xR1*side,yR1*bottom,20,20)
  for(var i=0;i<6;i++){
    stroke(20,random(20,100))
    strokeWeight(random(1,2))
    ellipse(xR1*side,yR1*bottom,random(10-r1,10+r1),random(10-r1,10+r1))
    //rect(.4*side-random(10-r1,10+r1),.05*bottom-random(10-r1,10+r1),random(20-r1,20+r1),random(20-r1,20+r1))
  }

  fill(360)
  pupil1x=round((xR1*side)+random(0,4-r2)*cos(random(0,360)))
  pupil1y=round((yR1*bottom)+random(0,4-r2)*sin(random(0,360)))
  ellipse(pupil1x,pupil1y,random((r2-1)*2,(r2+1)*2),random((r2-1)*2,(r2+1)*2))
  fill(pupilcol,0,pupilcolb)
  ellipse(pupil1x,pupil1y,(random((r2-1)*2,(r2+1)*2))/1.4,(random((r2-1)*2,(r2+1)*2))/1.4)

  noFill()
  stroke(65,65,65,150)
  strokeWeight(.2)
  ellipse(-xR2*side,yR1*bottom,20,20)
  for(var i=0;i<6;i++){
    stroke(20,random(20,100))
    strokeWeight(random(1,2))
  ellipse(-xR2*side,yR1*bottom,random(10-r1,10+r1),random(10-r1,10+r1))
  //rect(-.4*side-random(10-r1,10+r1),.05*bottom-random(10-r1,10+r1),random(20-r1,20+r1),random(20-r1,20+r1))
  }
  fill(360)
  pupil2x=round((-xR2*side)+random(0,4-r2)*cos(random(0,360)))
  pupil2y=round((yR1*bottom)+random(0,4-r2)*sin(random(0,360)))
  ellipse(pupil2x,pupil2y,random((r2-1)*2,(r2+1)*2),random((r2-1)*2,(r2+1)*2))
  fill(pupilcol,0,pupilcolb)
  ellipse(pupil2x,pupil2y,(random((r2-1)*2,(r2+1)*2))/1.4,(random((r2-1)*2,(r2+1)*2))/1.4)

  eyebrows(top1,bottom,side,xR1,xR2,yR1)

}



function eyebrows (top1,bottom,side,xr1,xr2,yr1){
top1=top1
bottom=bottom
side=side
xr3=random((xr1-.05),(xr1+.05))
xr4=random((xr2-.05),(xr2+.05))
yr2=random((yr1-.05),(yr1+.05))
lift1=random(10,22)
col=round(random(0,1))*255
fill(65,65,65)
noStroke()
strokeWeight(3)
stroke(col)
line(xr3*side-random(3,15),yr1*bottom-lift1,xr3*side+random(3,15),yr1*bottom-random((lift1-5),lift1))
line((-xr3*side)-random(3,15),yr1*bottom-random((lift1-5),lift1),(-xr3*side)+random(3,15),yr1*bottom-lift1)

}

function mouth(){
mth=1
x1=35
y1=98
y2=88
y3=85
y4=95
fill(255)
beginShape();
vertex(x1, y1);
bezierVertex(-x1-mth, y2-mth, -x1+mth, y2-mth, -x1, y2);
bezierVertex(-x1, y3+mth, -x1, y3, -x1, y3);
bezierVertex(-x1, y4+mth, -x1, y4, x1, y4);
endShape();

}

function mouth2 (top1,bottom,side){
  top1=top1
  bottom=bottom
  side=side
  ax1=.3*side
  ay1=.55*bottom
  ax2=-.3*side
  ay2=.55*bottom
  noFill()
  stroke(255,185)
  strokeWeight(focusedRandom(0,3,.5)) 
  
  beardL=random(0,.3)
  if(beardcheck>0){
    noStroke()
    beardOpac=random(100,260)
  fill(0,beardOpac)
  xdif=(side-(faceGaunt*side))*beardL
  beardOpac=beardOpac/1.5

  beginShape();
    vertex(0, bottom);//chin
    vertex(faceGaunt*side,.85*bottom);//bottomRight
    vertex(side-xdif,beardL*bottom);//midRight
    vertex(ax1,ay1);//topRight    
    vertex(ax2,ay2);//topLeft
    vertex(-side+xdif, beardL*bottom);//midleft
    vertex(-faceGaunt*side,.85*bottom);//bottomleft
  endShape(); 
  noFill()
  neat=random(1,15)
  colorMode(RGB,255)
  //var linecolors = [  [84,72,58,100],  [238,236,166,100],  [random(60,80),65,57,100]]
var linecolors = [  [222,86,74],  [89,198,213],  [238,236,166]]
  stroke(linecolors[(round(random(2)))])
  colorMode(HSB,360)
  beardlines(0, bottom,faceGaunt*side,.85*bottom,beardOpac,neat)  
  beardlines(faceGaunt*side,.85*bottom,side-xdif,beardL*bottom,beardOpac,neat)  
  //beardlines(side-xdif,beardL*bottom,ax1,ay1,beardOpac)  

  //beardlines(ax2,ay2,-side+xdif, beardL*bottom,beardOpac)
  beardlines(-side+xdif, beardL*bottom,-faceGaunt*side,.85*bottom,beardOpac,neat)
  beardlines(-faceGaunt*side,.85*bottom,0, bottom,beardOpac,neat)  
}



  stroke(0,random(0,20),360,200)
  rand1=random(90,180)
  rand2=random(r1,r2)
  for(var i=0;i<=4;i++){
    controlx11=round(ax1+rand2*cos(rand1))
    controly11=round(ay1+rand2*sin(rand1))
    controlx21=round(ax2+rand2*cos(rand1))
    controly21=round(ay2+rand2*sin(rand1))
    strokeWeight(random(2,3))
    bezier(ax1,ay1,controlx11,controly21,controlx21,controly21,ax2,ay2) }
}

function hair (top1,bottom,side){
  top1=top1
  bottom=bottom
  side=side
  ax1=.3*side
  ay1=.55*bottom
  ax2=-.3*side
  ay2=.55*bottom
  noFill()
  stroke(255,185)
  strokeWeight(focusedRandom(0,3,.5))
  beardL=random(0,.3)
  if(haircheck>0){
    noStroke()
    hairOpac=random(100,260)
  fill(0,hairOpac)
  xdif=(side-(faceGaunt*side))*beardL
  hairOpac=hairOpac/1.5

  noFill()
  neat=random(1,15)
    colorMode(RGB,255)
var linecolors = [  [222,86,74],  [89,198,213],  [238,236,166]]
  stroke(linecolors[(round(random(2)))])

  colorMode(HSB,360)
  //stroke(random(0,360),100,300,100)
  hairlines(side2,0,.9*side2,-.66*top1,hairOpac,neat)  
  hairlines(.9*side2,-.66*top1,0,-top1,hairOpac,neat)  
  hairlines(0,-top1,-.9*side2,-.66*top1,hairOpac,neat)  
  hairlines(-.9*side2,-.66*top1,-side2, 0,hairOpac,neat)  

}
}