block by dribnet b0a6a211702c53ad9b371f820cac377b

new face mapping assignment prototype

Full Screen

Assignment 3 MDDN 342 2020

Adaptation of code from Fran Armstrong’s submisison in 2019.

index.html

<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/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 src="z_clmtrackr.js"></script>
    <script src="z_model_pca_20_svm.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="z_face_system.js"></script>
    <script language="javascript" type="text/javascript" src="z_kdTree.js"></script>
    <script language="javascript" type="text/javascript" src="z_face-api.js"></script>
    <script language="javascript" type="text/javascript" src="z_training_images.js"></script>
    <script language="javascript" type="text/javascript" src="z_testing_images.js"></script>
    <script language="javascript" type="text/javascript" src="face.js"></script>
    <script language="javascript" type="text/javascript" src="system_runner.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" id="controls" height="500">
            <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>setting 11</td>
                    <td id="slider11Container"></td>
                </tr>
                <tr>
                    <td>setting 12</td>
                    <td id="slider12Container"></td>
                </tr>
                <tr>
                </tr>
                <tr>
                    <td>show target</td>
                    <td id="sliderTintContainer"></td>
                </tr>
                <tr>
                    <td>Draw function</td>
                    <td id="selector1Container"></td>
                </tr>
                <tr>
                    <td>Face Draw</td>
                    <td id="checkbox1Container"></td>
                </tr>
                <tr>
                    <td>Face Targets</td>
                    <td id="checkbox2Container"></td>
                </tr>
                <tr>
                    <td>Face Points</td>
                    <td id="checkbox3Container"></td>
                </tr>
                <tr>
                    <td></td>
                    <td id="button1Container"></td>
                </tr>
                <tr>
                    <td></td>
                    <td id="button2Container"></td>
                </tr>
                <tr>
                    <td></td>
                    <td id="button3Container"></td>
                </tr>
                <tr>
                    <td></td>
                    <td id="button4Container"></td>
                </tr>
            </table>
        </div>
        <div>
            <div id="canvasContainer"></div>
<a href="face.js">face code</a><br>
<a href="sketch.html">sketches</a>
        </div>
    </div>
    <pre>
        <p id="output">
        </p>
    </pre>
</body>

face.js

/*
 * FaceMap class - holds all informaiton about one mapped
 * face and is able to draw itself.
 */  

// other variables can be in here too
// these control the colors used

//COLOURS 
const white = [225, 225, 225];
const black = [0, 0, 0];
const lightBlack = [40, 40, 40];

//FACE COLOURS
const face1Main = "#ded1ca";
const face1Detail = "#c7b2a7";

const face2Main = "#c7ad9d";
const face2Detail = "#9c8373";

const face3Main = "#ab8a72";
const face3Detail = "#8a6d5b";

const face4Main = "#856a59";
const face4Detail = "#594336";

const face5Main = "#594844";
const face5Detail = "#4a342c";

//EAR COLOURS
const earGrey = "#abaaa9";
const earLightBlonde = "#eddfc7";
const earDarkBlonde = "#d1ab7d";
const earRed = "#963917";
const earLightBrown = "#9c785a";
const earDarkBrown = "#572e1a";
const earBlack = "#210c02";


//EYES
const eyeDBrown = "#38220a";
const eyeLBrown = "#b58c6e";
const eyeGreen = "#739c60";
const eyeBlue = "#6591b5";


function Face() {
  // these are state variables for a face
  // (your variables may be different)
  this.tilt_value = 0;   // range is -30 to 30
  this.face_width = 0;
  this.face_colour = 1; // range 1-5
  this.hair_style = 1; // hair length in middle of ear
  this.ear_colour = 1; // 1= grey, 2 = lightBlonde, 3 = darkBlonde, 4 = red, 5 = lightBrown, 6= darkBrown, 7 = black;
  this.eye_colour = 1; // 1= dark brown, 2 = light brown, 3 = green, 4 = blue
  this.blush_value = 1; // 1 = true, 2 = false;

  /*
   * Draw a face with position lists that include:
   *    chin, right_eye, left_eye, right_eyebrow, left_eyebrow
   *    bottom_lip, top_lip, nose_tip, nose_bridge, 
   */  


  this.draw = function(positions) {
    // SETUP
    rectMode(CENTER);
    ellipseMode(CENTER);
    noStroke();
    rotate(this.tilt_value);

    //******** POSITIONS ********/
    let left_eyebrow1 = positions.left_eyebrow[0]; 
    let right_eyebrow5 = positions.right_eyebrow[4];
    let left_eye = positions.left_eye[5];
    let right_eye = positions.right_eye[5]; 
    let bottom_lip = positions.bottom_lip[9];
    let top_lip1 = positions.top_lip[0];
    let top_lip2 = positions.top_lip[1];
    let top_lip3 = positions.top_lip[2];
    let top_lip4 = positions.top_lip[3];
    let top_lip5 = positions.top_lip[4];
    let top_lip6 = positions.top_lip[5];
    let top_lip7 = positions.top_lip[6];
    let top_lip8 = positions.top_lip[7];
    let top_lip9 = positions.top_lip[8];
    let top_lip10 = positions.top_lip[9];
    let top_lip11 = positions.top_lip[10];
    let top_lip12 = positions.top_lip[11];
    let nose_bridge4 = positions.nose_bridge[3];

    //***** HEAD ******/
    //Colours
    let faceColour = face1Main;
    let detailColour = face1Detail;
    let hairColour = earBlack;

    //Facial Colouring
    if(this.face_colour == 1){
      faceColour = face1Main;
      detailColour = face1Detail;
    }
    else if(this.face_colour == 2){
      faceColour = face2Main;
      detailColour = face2Detail;
    }
    else if(this.face_colour == 3){
      faceColour = face3Main;
      detailColour = face3Detail;
    }
    else if(this.face_colour == 4){
      faceColour = face4Main;
      detailColour = face4Detail;
    }
    else if(this.face_colour == 5){
      faceColour = face5Main;
      detailColour = face5Detail;
    }

    //Ear/Hair Colouring
    if(this.ear_colour == 1){
      hairColour = earGrey;
    }
    else if(this.ear_colour == 2){
      hairColour = earLightBlonde;
    }
    else if(this.ear_colour == 3){
      hairColour = earDarkBlonde;
    }
    else if(this.ear_colour == 4){
      hairColour = earRed;
    }
    else if(this.ear_colour == 5){
      hairColour = earLightBrown;
    }
    else if(this.ear_colour == 6){
      hairColour = earDarkBrown;
    }
    else if(this.ear_colour == 7){
      hairColour = earBlack;
    }

    //Ears
    this.drawEars(detailColour, hairColour, left_eyebrow1[0], right_eyebrow5[0], left_eyebrow1[1], right_eyebrow5[1]);    

    //Hair in the Middle
    this.drawHair(hairColour, this.hair_style);

    //Face
    fill(faceColour);
    rect(0,0,this.face_width,4, 20);


    //******** EYES - LEFT EYE, RIGHT EYE ******/

      if(this.eye_colour == 1){
        this.drawEyes(detailColour, eyeDBrown, left_eye[0], left_eye[1], right_eye[0], right_eye[1]);
      }
      else if(this.eye_colour == 2){
        this.drawEyes(detailColour, eyeLBrown, left_eye[0], left_eye[1], right_eye[0], right_eye[1]);
      }
      else if(this.eye_colour == 3){
        this.drawEyes(detailColour, eyeGreen, left_eye[0], left_eye[1], right_eye[0], right_eye[1]);
      }
      else if(this.eye_colour == 4){
        this.drawEyes(detailColour, eyeBlue, left_eye[0], left_eye[1], right_eye[0], right_eye[1]);
      }
     


    //******** MOUTH - BOTTOM LIP, TOP LIP & NOSE - NOSE TIP, NOSE BRIDGE*********/
    //OuterMouth
    fill(detailColour);
    ellipse(top_lip4[0], top_lip4[1]+0.2, 2.3, 2);

    //BottomLip
    fill("#ad8e8e");
    beginShape();
    curveVertex(top_lip2[0], top_lip2[1]);
    curveVertex(top_lip4[0], top_lip4[1]);
    curveVertex(top_lip6[0], top_lip6[1]);
    curveVertex(bottom_lip[0] + 0.4, bottom_lip[1]);
    curveVertex(bottom_lip[0], bottom_lip[1]);
    curveVertex(bottom_lip[0] - 0.4, bottom_lip[1]);
    endShape(CLOSE);

    stroke(black);
    
    strokeCap(SQUARE);
    strokeWeight(0.08);
    noFill();
    beginShape();
    curveVertex(top_lip2[0], top_lip2[1] + 0.05);
    curveVertex(top_lip2[0], top_lip2[1]+ 0.05);
    curveVertex(bottom_lip[0] - 0.4, bottom_lip[1]-0.05);
    curveVertex(bottom_lip[0], bottom_lip[1]);
    curveVertex(bottom_lip[0] + 0.4, bottom_lip[1]-0.05);
    curveVertex(top_lip6[0], top_lip6[1]+0.05);
    curveVertex(top_lip6[0], top_lip6[1]+0.05);
    endShape();
    noStroke();

    
   
   

    //TopLip
    fill(black);
    beginShape();
    curveVertex(top_lip1[0], top_lip1[1]);
    curveVertex(top_lip2[0], top_lip2[1]);
    curveVertex(top_lip3[0], top_lip3[1]);
    curveVertex(top_lip4[0], top_lip4[1]);
    curveVertex(top_lip5[0], top_lip5[1]);
    curveVertex(top_lip6[0], top_lip6[1]);
    curveVertex(top_lip7[0], top_lip7[1]);
    curveVertex(top_lip8[0], top_lip8[1]);
    curveVertex(top_lip9[0], top_lip9[1]);
    curveVertex(top_lip10[0], top_lip10[1]);
    curveVertex(top_lip11[0], top_lip11[1]);
    curveVertex(top_lip12[0], top_lip12[1]);
    curveVertex(top_lip1[0], top_lip1[1]);
    curveVertex(top_lip2[0], top_lip2[1]);
    endShape();

      

    //Blush
    if(this.blush_value == 1){
    fill(255, 48, 93, 75);
    ellipse(top_lip1[0]-0.25, top_lip1[1], 0.75);
    ellipse(top_lip7[0] + 0.25, top_lip7[1], 0.75);
    }
    
    //Nose
    stroke(black);
    strokeWeight(0.15);
    strokeCap(SQUARE);
    noFill();
    line(nose_bridge4[0], nose_bridge4[1], top_lip4[0], top_lip4[1]);
    noStroke();
    fill(black);
    ellipse(nose_bridge4[0], nose_bridge4[1], 0.6, 0.4);
    fill(30);
    ellipse(nose_bridge4[0], nose_bridge4[1], 0.3, 0.2);
    fill(175);
    ellipse(nose_bridge4[0]+0.125, nose_bridge4[1]-0.1, 0.075, 0.05);

  }


  //****** OBJECTS FUNCTIONS FOR FACIAL FEATURES ****/

  //Ears
  this.drawEars = function(detail, hair, lx, rx, ly, ry){
    fill(detail);
    ellipse(lx+0.25, ly, 2, 2);
    ellipse(rx-0.25, ry, 2, 2);

    //left_ear floofs
    beginShape();
    vertex(lx, ly);
    vertex(lx, ly-0.7);
    vertex(lx-1, ly-0.6);
    endShape(CLOSE);
    beginShape();
    vertex(lx, ry-0.3);
    vertex(lx , ry-0.9);
    vertex(lx-1, ry-0.9);
    endShape(CLOSE);
   
    //right_ear floofs
    beginShape();
    vertex(rx, ry);
    vertex(rx, ry-0.7);
    vertex(rx+1, ry-0.6);
    endShape(CLOSE)
    beginShape();
    vertex(rx, ry-0.3);
    vertex(rx , ry-0.95);
    vertex(rx+0.75, ry-0.9);
    endShape(CLOSE);

    fill(hair);
    ellipse(lx+0.25, ly, 1.5, 1.5);
    ellipse(rx-0.25, ry, 1.5, 1.5);
  }

  //Eyes
  this.drawEyes = function(detail, eye, lx, ly, rx, ry){

     //EYE PATCHES
      //LeftEyePatch
      fill(detail);
      beginShape();
      curveVertex(lx + 0.65, ly); 
      curveVertex(lx + 0.65, ly + 0.3);
      curveVertex(lx - 0.2, ly + 0.8);
      curveVertex(lx - 0.7, ly + 0.3);
      curveVertex(lx - 0.3, ly - 0.5);
      curveVertex(lx + 0.5, ly - 0.5);
      endShape(CLOSE);
      //RightEyePatch
      beginShape();
      curveVertex(rx - 0.65, ry); 
      curveVertex(rx - 0.65, ry + 0.3);
      curveVertex(rx + 0.2, ry + 0.8);
      curveVertex(rx + 0.7, ry + 0.3);
      curveVertex(rx + 0.3, ry - 0.5);
      curveVertex(rx - 0.5, ry - 0.5);
      endShape(CLOSE);
   

      //EyeBalls
      fill(eye);
      ellipse(lx, ly, 0.55, 0.55);
      ellipse(rx, ry, 0.55, 0.55);
        
      fill(100);
      ellipse(lx, ly, 0.48, 0.48);
      ellipse(rx, ry, 0.48, 0.48);

      fill(black);
      ellipse(lx, ly, 0.4, 0.4);
      ellipse(rx, ry, 0.4, 0.4);

      fill(175);
      ellipse(lx +0.075, ly-0.075, 0.08, 0.05);
      ellipse(rx +0.075, ry-0.075, 0.08, 0.05);

  }

  //Hair
  this.drawHair = function(hair, length){
    fill(hair);
    beginShape();
    vertex(-0.25, -1.5);
    vertex(0.25, -1.5);
    vertex(0, -2*length);
    endShape(CLOSE);
    beginShape();
    vertex(-0.22, -1.5);
    vertex(0.22, -1.5);
    vertex(-0.25 - (0.1*length), -1.85*length);
    endShape(CLOSE);
    beginShape();
    vertex(-0.2, -1.5);
    vertex(0.2, -1.5);
    vertex(0.25 + (0.1*length), -1.8*length);
    endShape(CLOSE);
  }


  /* set internal properties based on list numbers 0-100 */
  this.setProperties = function(settings) {
    //this.eye_value = int(map(settings[0], 0, 100, 2, 3));
    //TiltValue
    this.tilt_value = map(settings[0], 0, 100, -30, 30);

    //FACE SHAPE
    this.face_width = map(settings[1], 0, 100, 3.6, 4);

    //FACE COLOUR
    this.face_colour = int(map(settings[2], 0, 100, 1, 5));

    //EARS
    this.hair_style = map(settings[3], 0, 100, 1, 1.2);

    //EAR COLOUR
    this.ear_colour = int(map(settings[4], 0, 100, 1, 7));

    //EYE COLOUR - brown, light brown, green, blue
    this.eye_colour = int(map(settings[5], 0, 100, 1, 4));

    // BLUSH
    this.blush_value = int(map(settings[6], 0, 100, 1, 2));

  }

  /* get internal properties as list of numbers 0-100 */
  this.getProperties = function() {
    let settings = new Array(7);
    settings[0] = map(this.tilt_value, -30, 30, 0, 100);
    settings[1] = map(this.face_width, 3.6, 4, 0, 100);
    settings[2] = map(this.face_colour, 1, 5, 0, 100);
    settings[3] = map(this.hair_style, 1, 1.2, 0, 100);
    settings[4] = map(this.ear_colour, 1, 7, 0, 100);
    settings[5] = map(this.eye_colour, 1, 4, 0, 100);
    settings[6] = map(this.blush_value, 1, 2, 0, 100);
    return settings;
  }


}

//given an array of [x,y] points, return the average
function average_point(list){
  var sum_x = 0;
  var sum_y = 0;
  var num_points = 0;
  for(var i = 0; i<list.length; i++){
    sum_x += list[i][0];
    sum_y += list[i][1];
    num_points +- 1;
  }
  return [sum_x / num_points, sum_y /num_points];
}


sample_images.json

[
	"williams.jpg",
	"oscar_selfie.jpg"
]

sketch.html

<head>
    <style> body {padding: 0; margin: 0;} </style>
</head>
<body style="background-color:white">
<img src="same_pose.jpg" width="960" height="500"/><br>
Same Pose
<hr>
<img src="same_subject.jpg" width="960" height="500"/><br>
Same Subject
<p>
<a href="index.html">program</a>
</body>

training_values.json

    
{
  "000001": [
    50,
    24.00000000000003,
    25,
    100,
    33.33333333333333,
    0,
    0
  ],
  "000002": [
    50,
    49.99999999999994,
    0,
    100,
    50,
    0,
    0
  ],
  "000005": [
    50,
    42.00000000000005,
    25,
    36.000000000000036,
    33.33333333333333,
    100,
    0
  ],
  "000006": [
    50,
    42.00000000000005,
    75,
    100,
    66.66666666666666,
    0,
    0
  ],
  "000007": [
    42,
    53.00000000000006,
    50,
    16.000000000000018,
    100,
    0,
    100
  ],
  "000009": [
    60,
    14.000000000000016,
    25,
    100,
    83.33333333333334,
    0,
    0
  ],
  "000010": [
    50,
    10.000000000000012,
    25,
    100,
    33.33333333333333,
    100,
    0
  ],
  "000013": [
    50,
    82.99999999999999,
    25,
    45.00000000000005,
    16.666666666666664,
    100,
    100
  ],
  "000014": [
    56.00000000000001,
    4.000000000000004,
    75,
    42.00000000000005,
    100,
    0,
    0
  ],
  "000015": [
    45,
    57.99999999999995,
    25,
    46.00000000000005,
    83.33333333333334,
    100,
    100
  ],
  "000016": [
    50,
    29.000000000000032,
    50,
    43.00000000000005,
    83.33333333333334,
    0,
    100
  ],
  "000018": [
    50,
    39.00000000000004,
    25,
    100,
    33.33333333333333,
    100,
    0
  ],
  "000020": [
    50,
    61.99999999999996,
    25,
    90.99999999999999,
    83.33333333333334,
    0,
    100
  ],
  "000023": [
    53,
    55.00000000000006,
    25,
    41.00000000000005,
    100,
    100,
    100
  ],
  "000025": [
    50,
    55.99999999999995,
    25,
    18.000000000000018,
    66.66666666666666,
    0,
    100
  ],
  "000028": [
    47,
    42.00000000000005,
    50,
    100,
    66.66666666666666,
    0,
    0
  ],
  "000029": [
    50,
    25.00000000000003,
    25,
    100,
    16.666666666666664,
    100,
    0
  ],
  "000030": [
    56.99999999999999,
    37.000000000000036,
    25,
    52.99999999999995,
    16.666666666666664,
    0,
    100
  ],
  "000031": [
    50,
    30.000000000000032,
    25,
    67.99999999999996,
    66.66666666666666,
    100,
    0
  ],
  "000032": [
    50,
    37.000000000000036,
    25,
    43.00000000000005,
    66.66666666666666,
    0,
    100
  ],
  "000035": [
    50,
    44.00000000000005,
    0,
    100,
    100,
    0,
    0
  ],
  "000037": [
    50,
    55.99999999999995,
    75,
    26.00000000000003,
    100,
    0,
    100
  ],
  "000038": [
    50,
    43.00000000000005,
    25,
    50.00000000000006,
    83.33333333333334,
    100,
    100
  ],
  "000040": [
    50,
    39.00000000000004,
    25,
    100,
    100,
    0,
    0
  ],
  "000041": [
    38,
    49.99999999999994,
    50,
    63.99999999999996,
    100,
    0,
    100
  ],
  "000042": [
    50,
    28.000000000000032,
    0,
    77.99999999999999,
    66.66666666666666,
    100,
    0
  ],
  "000043": [
    50,
    49.99999999999994,
    25,
    100,
    83.33333333333334,
    0,
    0
  ],
  "000044": [
    50,
    35.000000000000036,
    75,
    93,
    100,
    0,
    0
  ],
  "000045": [
    50,
    23.000000000000025,
    50,
    83.99999999999999,
    50,
    0,
    0
  ],
  "000047": [
    46,
    49.99999999999994,
    50,
    100,
    100,
    0,
    0
  ],
  "000048": [
    50,
    49.99999999999994,
    0,
    31.000000000000032,
    100,
    66.66666666666666,
    100
  ],
  "000050": [
    50,
    34.000000000000036,
    25,
    27.00000000000003,
    100,
    0,
    100
  ],
  "000051": [
    50,
    49.99999999999994,
    50,
    0,
    0,
    0,
    100
  ],
  "000052": [
    50,
    23.000000000000025,
    25,
    20.000000000000025,
    83.33333333333334,
    0,
    100
  ],
  "000054": [
    50,
    31.000000000000032,
    25,
    100,
    16.666666666666664,
    100,
    0
  ],
  "000055": [
    50,
    49.99999999999994,
    25,
    22.000000000000025,
    100,
    0,
    100
  ],
  "000056": [
    50,
    36.000000000000036,
    0,
    62.99999999999996,
    100,
    100,
    100
  ],
  "000058": [
    50,
    39.00000000000004,
    0,
    100,
    100,
    100,
    0
  ],
  "000060": [
    50,
    49.99999999999994,
    75,
    20.000000000000025,
    100,
    0,
    100
  ],
  "000064": [
    50,
    32.000000000000036,
    25,
    29.000000000000032,
    83.33333333333334,
    100,
    100
  ],
  "000065": [
    50,
    37.000000000000036,
    25,
    39.00000000000004,
    100,
    0,
    100
  ],
  "000068": [
    52,
    31.000000000000032,
    50,
    58.99999999999995,
    0,
    33.33333333333333,
    100
  ],
  "000069": [
    50,
    43.00000000000005,
    25,
    13.000000000000014,
    100,
    0,
    100
  ],
  "000071": [
    50,
    28.000000000000032,
    25,
    68.99999999999996,
    33.33333333333333,
    33.33333333333333,
    0
  ],
  "000073": [
    50,
    35.000000000000036,
    25,
    100,
    83.33333333333334,
    0,
    0
  ],
  "000076": [
    50,
    29.000000000000032,
    25,
    80.99999999999999,
    100,
    0,
    100
  ],
  "000077": [
    50,
    37.000000000000036,
    25,
    97,
    100,
    0,
    0
  ],
  "000078": [
    50,
    49.99999999999994,
    50,
    100,
    100,
    0,
    0
  ],
  "000079": [
    50,
    12.000000000000014,
    50,
    0,
    83.33333333333334,
    0,
    100
  ],
  "000080": [
    50,
    36.000000000000036,
    25,
    29.000000000000032,
    100,
    0,
    100
  ],
  "000081": [
    50,
    35.000000000000036,
    50,
    16.000000000000018,
    100,
    0,
    100
  ],
  "000083": [
    50,
    49.99999999999994,
    25,
    68.99999999999996,
    50,
    66.66666666666666,
    0
  ],
  "000085": [
    50,
    32.000000000000036,
    25,
    94,
    50,
    66.66666666666666,
    0
  ],
  "000086": [
    50,
    34.000000000000036,
    25,
    73.99999999999997,
    66.66666666666666,
    33.33333333333333,
    0
  ],
  "000088": [
    50,
    49.99999999999994,
    25,
    100,
    50,
    0,
    0
  ],
  "000091": [
    50,
    68.99999999999996,
    25,
    27.00000000000003,
    100,
    0,
    100
  ],
  "000092": [
    50,
    40.00000000000005,
    25,
    71.99999999999996,
    33.33333333333333,
    66.66666666666666,
    0
  ],
  "000096": [
    50,
    58.99999999999995,
    25,
    100,
    100,
    0,
    0
  ],
  "000097": [
    50,
    39.00000000000004,
    25,
    100,
    66.66666666666666,
    100,
    0
  ],
  "000099": [
    50,
    42.00000000000005,
    25,
    100,
    66.66666666666666,
    100,
    0
  ],
  "000100": [
    50,
    49.99999999999994,
    0,
    29.000000000000032,
    33.33333333333333,
    0,
    0
  ],
  "000103": [
    50,
    49.99999999999994,
    25,
    84.99999999999999,
    100,
    66.66666666666666,
    0
  ],
  "000104": [
    50,
    49.99999999999994,
    0,
    38.00000000000004,
    33.33333333333333,
    100,
    100
  ],
  "000106": [
    50,
    31.000000000000032,
    25,
    70.99999999999996,
    66.66666666666666,
    0,
    0
  ],
  "000108": [
    50,
    49.99999999999994,
    25,
    56.000000000000064,
    16.666666666666664,
    100,
    0
  ],
  "000109": [
    50,
    19.00000000000002,
    25,
    26.00000000000003,
    33.33333333333333,
    100,
    100
  ],
  "000110": [
    50,
    49.99999999999994,
    0,
    59.99999999999995,
    66.66666666666666,
    100,
    0
  ],
  "000111": [
    50,
    49.99999999999994,
    75,
    30.000000000000032,
    66.66666666666666,
    0,
    0
  ],
  "000114": [
    50,
    41.00000000000005,
    25,
    36.000000000000036,
    100,
    0,
    100
  ],
  "000115": [
    50,
    49.99999999999994,
    0,
    0,
    16.666666666666664,
    66.66666666666666,
    100
  ],
  "000116": [
    50,
    49.99999999999994,
    25,
    58.99999999999995,
    83.33333333333334,
    0,
    100
  ],
  "000117": [
    50,
    24.00000000000003,
    100,
    83.99999999999999,
    100,
    0,
    0
  ],
  "000118": [
    50,
    18.000000000000018,
    50,
    24.00000000000003,
    100,
    0,
    0
  ],
  "000121": [
    50,
    49.99999999999994,
    50,
    100,
    100,
    0,
    0
  ],
  "000122": [
    50,
    32.000000000000036,
    0,
    0,
    16.666666666666664,
    0,
    0
  ],
  "000125": [
    50,
    26.00000000000003,
    25,
    0,
    0,
    100,
    100
  ],
  "000126": [
    50,
    49.99999999999994,
    25,
    100,
    16.666666666666664,
    100,
    0
  ],
  "000129": [
    50,
    18.000000000000018,
    25,
    32.000000000000036,
    100,
    100,
    100
  ],
  "000131": [
    50,
    43.00000000000005,
    50,
    100,
    100,
    0,
    0
  ],
  "000132": [
    50,
    51.00000000000006,
    50,
    100,
    100,
    0,
    0
  ],
  "000133": [
    50,
    49.99999999999994,
    25,
    24.00000000000003,
    66.66666666666666,
    100,
    0
  ],
  "000134": [
    50,
    49.99999999999994,
    100,
    0,
    100,
    0,
    100
  ],
  "000135": [
    50,
    23.000000000000025,
    50,
    12.000000000000014,
    100,
    0,
    100
  ],
  "000137": [
    50,
    71.99999999999996,
    25,
    32.000000000000036,
    83.33333333333334,
    100,
    100
  ],
  "000140": [
    50,
    35.000000000000036,
    25,
    100,
    16.666666666666664,
    100,
    0
  ],
  "000142": [
    50,
    16.000000000000018,
    25,
    100,
    83.33333333333334,
    100,
    0
  ],
  "000143": [
    50,
    33.000000000000036,
    25,
    22.000000000000025,
    66.66666666666666,
    100,
    100
  ],
  "000145": [
    50,
    49.99999999999994,
    25,
    100,
    100,
    0,
    0
  ],
  "000146": [
    50,
    49.99999999999994,
    50,
    100,
    50,
    0,
    0
  ],
  "000147": [
    50,
    38.00000000000004,
    25,
    100,
    33.33333333333333,
    100,
    0
  ],
  "000148": [
    50,
    38.00000000000004,
    0,
    97,
    100,
    100,
    0
  ],
  "000150": [
    50,
    19.00000000000002,
    0,
    52.00000000000006,
    50,
    100,
    100
  ],
  "000151": [
    50,
    31.000000000000032,
    25,
    100,
    100,
    0,
    0
  ],
  "000152": [
    50,
    49.99999999999994,
    25,
    23.000000000000025,
    66.66666666666666,
    66.66666666666666,
    100
  ],
  "000153": [
    50,
    23.000000000000025,
    25,
    20.000000000000025,
    83.33333333333334,
    33.33333333333333,
    100
  ],
  "000155": [
    46,
    22.000000000000025,
    25,
    100,
    50,
    0,
    0
  ],
  "000156": [
    50,
    30.000000000000032,
    25,
    10.000000000000012,
    33.33333333333333,
    100,
    0
  ],
  "000157": [
    50,
    32.000000000000036,
    25,
    100,
    16.666666666666664,
    100,
    0
  ],
  "000160": [
    50,
    36.000000000000036,
    25,
    24.00000000000003,
    100,
    0,
    100
  ],
  "000161": [
    50,
    27.00000000000003,
    0,
    100,
    100,
    100,
    0
  ]
}


    

x_clown.js

const bg_color = [225, 206, 187];
const fg_color = [151, 102, 52];
const stroke_color = [95, 52, 8];

    
function Face() {
  // these are state variables for a face
  // (your variables may be different)


  /*
   * Draw a face with position lists that include:
   *    chin, right_eye, left_eye, right_eyebrow, left_eyebrow
   *    bottom_lip, top_lip, nose_tip, nose_bridge, 
   */  


  this.draw = function(positions) {
    let eyeChange = this.eyesSHape;
        //position for land marks
    let rightePx = positions.right_eye[0][0]; //0.48
    let rightePy = positions.right_eye[0][1]
    let leftePx = positions.left_eye[3][0]; //0.48
    let leftePy = positions.left_eye[3][1]; 
    let skinco = this.skinC;
    let Gender = this.Genders;
    let smooth;
    let sk1 = '#F2C6AE';
    let sk2 = '#dbb58c';
    let sk3 = '#F5E5C9';
    let sk4 = '#c48d52';
    let sk5 = '#36210d';
    let teethP= this.teethPos;
    let decoration = this.dec;
    let eyecolor = this.ecolor;
    let rightEyebrow = this.rightbrow;
    let leftEyebrow = this.leftbrow;
    fill(255 - this.mouth_value, 200)
    beginShape();
    if(skinco == 1){
      fill(sk1);
    }else if(skinco ==2){
      fill(sk2)
    }else if(skinco == 3){
      fill(sk3)
    }else if(skinco ==4){
      fill(sk4)
    }else if(skinco ==5){
      fill(sk5)
    }


    //face
    for(let i=0; i<positions.chin.length;i += Gender) { 
      vertex(positions.chin[i][0], positions.chin[i][1]);
    }
    for(let i=positions.right_eyebrow.length-2; i>=0;i-=2) {
      vertex(positions.right_eyebrow[i][0], positions.right_eyebrow[i][1]-1.2);
    }
    for(let i=positions.left_eyebrow.length-2; i>=0;i-=2) {
      vertex(positions.left_eyebrow[i][0], positions.left_eyebrow[i][1]-1.2);
    }
    endShape(CLOSE);
    push();
    fill(255);
    beginShape();
    noStroke()
    for(let i = 0; i<positions.nose_tip.length; i++){
      vertex(positions.nose_tip[i][0], positions.nose_tip[i][1]);
    }
    for(let i=14; i>1; i -= Gender) { 
      vertex(positions.chin[i][0], positions.chin[i][1]);
    }
    endShape(CLOSE);
    pop();
    //eye

    push()
    let eyeP = this.eyePos;
    
    fill(100);
    ellipse(leftePx-0.52, leftePy-0.3,1,2)
    ellipse(rightePx+0.52, rightePy-0.3,1,2)
    translate(0,eyeP)
    if(eyeChange == 1){

      push()
      strokeWeight(0.04)
      fill(255)
      beginShape();
      vertex(rightePx+1.12, -1.12);
      bezierVertex(rightePx+1.04, -0.8, rightePx+0.92, -0.68, rightePx+0.76, -0.6)
      bezierVertex(rightePx+0.4, -0.6, rightePx+0.24, -0.68, rightePx+0.08, -0.8)
      vertex(rightePx, -1);
      endShape(CLOSE);

      beginShape();
      vertex(leftePx-1.12, -1.12);
      bezierVertex(leftePx-1.04, -0.8, leftePx-0.92, -0.68, leftePx-0.76, -0.6)
      bezierVertex(leftePx-0.4, -0.6, leftePx-0.24,  -0.68, leftePx-0.08, -0.8)
      vertex(leftePx, -1);
      endShape(CLOSE);

      noStroke();
      push();
      colorMode(HSB);
      fill(eyecolor,100,71)
      ellipse(rightePx+0.52, -1.04, 0.6, 0.52)
      ellipse(leftePx-0.52, -1.04, 0.6, 0.52)
      pop();
      fill(0);
      ellipse(rightePx+0.52,-1.04, 0.4, 0.32)
      ellipse(leftePx-0.52,-1.04, 0.4, 0.32)

      pop();

      push();

      push();
      strokeWeight(0)
      fill('#FF9D85')
      beginShape();
      vertex(rightePx+1.2, -1.08);
      vertex(rightePx+0.92, -1.4);
      vertex(rightePx+0.32, -1.4);
      vertex(rightePx, -1)
      endShape();

      beginShape();
      vertex(leftePx-1.2, -1.08);
      vertex(leftePx-0.92, -1.4);
      vertex(leftePx-0.32, -1.4);
      vertex(leftePx, -1)
      endShape();
      pop();

      beginShape();
      strokeWeight(0.08);
      vertex(rightePx, -1);
      bezierVertex(rightePx+0.24, -1.08, rightePx+1.12, -1.12, rightePx+1.2, -1.08);
      endShape();

      beginShape();
      vertex(leftePx, -1);
      bezierVertex(leftePx-0.24, -1.08, leftePx-1.12, -1.12, leftePx-1.2, -1.08);
      endShape();
      pop();
    }else if(eyeChange ==2){
      //rightePx = 0.48
      //rightePy = 0.2
      strokeWeight(0.04);
      fill(255);
      beginShape();
      vertex(rightePx+1.12, rightePy-0.52);
      bezierVertex(rightePx+1, rightePy, rightePx+0.72, rightePy, rightePx,rightePy);
      bezierVertex(rightePx+0.28, rightePy-0.8, rightePx+0.44, rightePy - 0.64, rightePx +0.68, rightePy - 0.68);
      endShape();

      beginShape();
      vertex(leftePx-1.12, leftePy-0.52);
      bezierVertex(leftePx-1, leftePy, leftePx-0.72, leftePy, leftePx,leftePy);
      bezierVertex(leftePx-0.28, leftePy-0.8, leftePx-0.44, leftePy - 0.64, leftePx -0.68, leftePy - 0.68);
      endShape();
      push();
      colorMode(HSB);
      fill(eyecolor,100,71)
      ellipse(rightePx+0.52, rightePy-0.3, 0.6, 0.6)
      pop();
      fill(0);
      ellipse(rightePx+0.52, rightePy-0.3, 0.3, 0.3)
      beginShape();
      vertex(rightePx,rightePy);
      bezierVertex(rightePx+0.08, rightePy-0.72, rightePx + 0.44, rightePy-0.8, rightePx + 0.68, rightePy-0.78);
      bezierVertex(rightePx + 0.92, rightePy-0.72, rightePx + 1.12, rightePy-0.52, rightePx + 1.2, rightePy-0.64);
      bezierVertex(rightePx + 1.12, rightePy-0.52, rightePx + 0.92, rightePy-0.56, rightePx + 0.68, rightePy-0.68);
      bezierVertex(rightePx + 0.44, rightePy-0.64, rightePx+0.28, rightePy-0.6, rightePx, rightePy-0.2);
      endShape()
      push();
      colorMode(HSB);
      fill(eyecolor,100,71)
      ellipse(leftePx-0.52, leftePy-0.3, 0.6, 0.6)
      pop();
      fill(0);
      ellipse(leftePx-0.52, leftePy-0.3, 0.3, 0.3)
      beginShape();
      vertex(leftePx,leftePy);
      bezierVertex(leftePx-0.08, leftePy-0.72, leftePx - 0.44, leftePy-0.8, leftePx - 0.68, leftePy-0.78);
      bezierVertex(leftePx - 0.92, leftePy-0.72, leftePx - 1.12, leftePy-0.52, leftePx - 1.2, leftePy-0.64);
      bezierVertex(leftePx - 1.12, leftePy-0.52, leftePx - 0.92, leftePy-0.56, leftePx - 0.68, leftePy-0.68);
      bezierVertex(leftePx - 0.44, leftePy-0.64, leftePx-0.28, leftePy-0.6, leftePx, leftePy-0.2);
      endShape();
    }else if(eyeChange == 3){
      translate(0,-0.8)
      // scale(0.4)
      beginShape();//right eye inside
      fill(250)
      //rightePx = 0.48
      vertex(rightePx, -0.216);
      bezierVertex(rightePx+0.144, -0.364, rightePx+1.2, -0.676, rightePx+1.08,-0.24)
      bezierVertex(rightePx+0.96, 0, rightePx+0.916, 0.048, rightePx + 0.72, 0.192);
      bezierVertex(rightePx, 0.24, rightePx-0.12, 0.12, rightePx, -0.216)
      endShape(CLOSE);
      push();
      colorMode(HSB);
      fill(eyecolor,100,71);
      ellipse(rightePx+0.432, -0.12, 0.648, 0.576);
      pop();
      fill(36, 73, 83)
      ellipse(rightePx+0.432, -0.12, 0.24, 0.24)

      push();
      noFill();

      beginShape();
      fill(0)
      vertex(rightePx, -0.216);
      bezierVertex(rightePx+0.144, -0.368, rightePx+1.2, -0.676, rightePx+1.08, -0.24)
      vertex(rightePx+1.2, -0.48);
      bezierVertex(rightePx + 1.08, -0.6, rightePx+0.72, -0.6, rightePx, -0.24)
      endShape(CLOSE);
      pop();

      beginShape();//left eye inside
      fill(250)
      vertex(leftePx, -0.216);
      bezierVertex(leftePx-0.144, -0.364, leftePx-1.2, -0.676, leftePx-1.08,-0.24)
      bezierVertex(leftePx-0.96, 0, leftePx-0.916, 0.048, leftePx - 0.72, 0.192);
      bezierVertex(leftePx, 0.24, leftePx-0.12, 0.12, leftePx, -0.216)
      endShape(CLOSE);
      push();
      colorMode(HSB);
      fill(eyecolor,100,71);
      ellipse(leftePx-0.432, -0.12, 0.648, 0.576);
      pop();
      fill(36, 73, 83)
      ellipse(leftePx-0.432, -0.12, 0.24, 0.24)


      beginShape();
      fill(0)
      vertex(leftePx, -0.216);
      bezierVertex(leftePx-0.144, -0.368, leftePx-1.2, -0.676, leftePx-1.08, -0.24)
      vertex(leftePx-1.2, -0.48);
      bezierVertex(leftePx - 1.08, -0.6, leftePx-0.72, -0.6, leftePx, -0.24)
      endShape(CLOSE);
    }
    pop()

    push();
    scale(0.4)
    translate(0,-2.5)
    if(decoration == 1){
      //freckle
      fill("#B57738")
      noStroke()
      ellipse(1,0,0.1,0.1);
      ellipse(0.2,0.5,0.2,0.2)
      ellipse(-1.1,0.5,0.2,0.15);
      ellipse(1.4,0.8,0.15,0.15);
      ellipse(-1.1,0.3,0.15,0.1);
      ellipse(-1.4,1.3,0.15,0.15);
      ellipse(-0.8,0.5,0.2,0.15);
      ellipse(1.1,0.3,0.15,0.15);
      ellipse(-1,0.9,0.15,0.2);
      ellipse(-2,1,0.15,0.15);
      ellipse(2,1,0.2,0.15);
      ellipse(0, 1, 0.3, 0.15);
      ellipse(1.5, 1.4, 0.25, 0.25);
      ellipse(-1.8, 0.8, 0.2, 0.2);
      ellipse(1.8, 0.8, 0.2, 0.2);
    }else if(decoration==2){

    }else if(decoration==3){
    
      // translate(3,0)
      fill('#EB4762');
      noStroke();
      ellipse(3,2,2,2);
      ellipse(-3,2,2,2);
    }else if(decoration==4){
      //glass
      noFill();
      strokeWeight(0.2)
      ellipse(2.5,0,3.5,2.5);
      ellipse(-2.5,0,3.5,2.5);
      beginShape();
      vertex(0.8,0);
      vertex(0,-0.5)
      vertex(-0.8,0)
      endShape();
    }
    pop();

    push()
    beginShape();// botom lip
    noFill();
    vertex(positions.bottom_lip[2][0],positions.bottom_lip[2][1]);
    bezierVertex(positions.bottom_lip[2][0],positions.bottom_lip[2][1],positions.bottom_lip[3][0],positions.bottom_lip[3][1],positions.bottom_lip[4][0],positions.bottom_lip[4][1])
    endShape();
    pop();

    beginShape();//mouth
    noFill();
    for(let i = 7; i <positions.top_lip.length; i ++){
      vertex(positions.top_lip[i][0],positions.top_lip[i][1]);
    }
    endShape();

    beginShape();
    fill('#543B33');
    vertex(positions.top_lip[7][0],positions.top_lip[7][1]);
    vertex(positions.top_lip[8][0],positions.top_lip[8][1]+this.teethPos);
    vertex(positions.top_lip[9][0],positions.top_lip[9][1]+this.teethPos);
    vertex(positions.top_lip[10][0],positions.top_lip[10][1]+this.teethPos);
    vertex(positions.top_lip[11][0],positions.top_lip[11][1]);
    for(let i = 7; i <positions.bottom_lip.length; i ++){
       vertex(positions.bottom_lip[i][0],positions.bottom_lip[i][1])
    }
    endShape();

    beginShape();
    noStroke();
    fill('red');
    vertex(positions.top_lip[0][0]-0.6,positions.top_lip[0][1]);
    for(let i = 1; i <6; i ++){
      vertex(positions.top_lip[i][0],positions.top_lip[i][1]);
    }
    vertex(positions.top_lip[6][0]+0.6,positions.top_lip[6][1]);
    for(let i = 7; i <12; i ++){
      vertex(positions.top_lip[i][0],positions.top_lip[i][1]);
    }
    endShape()
    beginShape();
    vertex(positions.top_lip[6][0]+0.6,positions.top_lip[6][1]);
    for(let i = 1; i <6; i ++){
       vertex(positions.bottom_lip[i][0],positions.bottom_lip[i][1])
    }
    vertex(positions.top_lip[0][0]-0.6,positions.top_lip[0][1]);
    vertex(positions.bottom_lip[6][0],positions.bottom_lip[6][1]);
    for(let i = 7; i <12; i ++){
       vertex(positions.bottom_lip[i][0],positions.bottom_lip[i][1])
    }
    endShape();

   
    beginShape()//teeth
    fill(255)

    vertex(positions.top_lip[7][0],positions.top_lip[7][1]);
    vertex(positions.top_lip[8][0],positions.top_lip[8][1]+this.teethPos);
    vertex(positions.top_lip[9][0],positions.top_lip[9][1]+this.teethPos);
    vertex(positions.top_lip[10][0],positions.top_lip[10][1]+this.teethPos);
    vertex(positions.top_lip[11][0],positions.top_lip[11][1]);
    for(let i = 11; i >6 ; i --){
      vertex(positions.top_lip[i][0],positions.top_lip[i][1]);
    }
    endShape();

    push();
    stroke(0);
    fill('red');
    ellipse(positions.nose_bridge[3][0], positions.nose_bridge[3][1],1,1)
    pop();


    push(); 
    translate(0,-1)
    
    scale(0.4);
    noStroke();
    fill(25);
    beginShape();//right eye brow
    vertex(0.72, -2.1+0.2*rightEyebrow);
    bezierVertex(0.72, -2.16+0.2*rightEyebrow, 2.4, -3.3-0.15*rightEyebrow, 4.2, -2.4-0.15*rightEyebrow);
    vertex(3.6, -2.46-0.15*rightEyebrow);
    vertex(2.1, -2.4+0.01*rightEyebrow);
    endShape(CLOSE);
    beginShape();//Left eye brow
    vertex(-0.72, -2.1+0.15*leftEyebrow);
    bezierVertex(-0.72, -2.16+0.2*leftEyebrow, -2.4, -3.3-0.15*leftEyebrow, -4.2, -2.4-0.15*leftEyebrow);
    vertex(-3.6, -2.46-0.15*leftEyebrow);
    vertex(-2.1, -2.4+0.01*leftEyebrow);
    endShape(CLOSE);
    pop();
    fill(20);
    // for(let i = 0; i <positions.top_lip.length; i++){ 

    //   textSize(0.3);
    //   text(i, positions.top_lip[i][0], positions.top_lip[i][1]);
    // } //11 10 9 8 7

    // for(let i = 0; i <positions.bottom_lip.length; i++){    
    //   textSize(0.2);
    //   text(i, positions.bottom_lip[i][0], positions.bottom_lip[i][1]);
    // } //7 8 9 10  11
    // // // for(let i = 0; i <positions.chin.length; i++){    
    // // //   textSize(0.2);
    // // //   text(i, positions.chin[i][0], positions.chin[i][1]);
    // // // }
    // // for(let i = 0; i <positions.nose_tip.length; i++){    
    // //   textSize(0.2);
    // //   text(i, positions.nose_tip[i][0], positions.nose_tip[i][1]);
    // // }

    // // for(let i = 0; i <positions.nose_bridge.length; i++){    
    // //   textSize(0.2);
    // //   text(i, positions.nose_bridge[i][0], positions.nose_bridge[i][1]);
    // // }
    // fill(0);
    // for(let i = 0; i <positions.left_eyebrow.length; i++){    
    //   textSize(0.4);
    //   text(i, positions.left_eyebrow[i][0], positions.left_eyebrow[i][1]);
    // }
  }

  /* set internal properties based on list numbers 0-100 */
  this.setProperties = function(settings) {
    this.eyesSHape = int(map(settings[0], 0, 100, 1,3));
    this.skinC = int(map(settings[1], 0, 100, 1, 5));
    this.Genders = int(map(settings[2], 0, 100, 1, 2));
    this.teethPos = map(settings[3], 0, 100,0, 0.5);
    this.dec = int(map(settings[4], 0, 100,1, 4));
    this.ecolor = map(settings[5], 0, 100,0, 360);
    this.rightbrow = map(settings[6], 0, 100,0, 3);
    this.leftbrow = map(settings[7], 0, 100,0, 3);
    this.eyePos = map(settings[8], 0, 100,-0.3,0.3)

  }

  /* get internal properties as list of numbers 0-100 */
  this.getProperties = function() {
    let settings = new Array(9);
    settings[0] = map(this.eyesSHape, 1, 3, 0, 100);
    settings[1] = map(this.skinC, 1, 5, 0, 100);
    settings[2] = map(this.Genders, 1, 2, 0, 100);
    settings[3] = map(this.teethPos, 0, 0.5, 0, 100);
    settings[4] = map(this.dec, 1, 4, 0, 100);
    settings[5]= map(this.ecolor,0,360,0,100);
    settings[6]= map(this.rightbrow,0,3,0,100);
    settings[7]= map(this.leftbrow,0,3,0,100);
    settings[8]= map(this.eyePos,-0.3,0.3,0,100);
    
    return settings;
  }
}

function average_point(list) {
  var sum_x = 0;
  var sum_y = 0;
  var num_points = 0;
  for(var i=0; i<list.length; i++) {
    sum_x += list[i][0];
    sum_y += list[i][1];
    num_points += 1; 
  }
  return [sum_x / num_points, sum_y / num_points];
}


x_example_face.js

/*
 * FaceMap class - holds all informaiton about one mapped
 * face and is able to draw itself.
 */  

// other variables can be in here too
// these control the colors used
const bg_color = [225, 206, 187];
const fg_color = [151, 102, 52];
const stroke_color = [95, 52, 8];

function segment_average(segment) {
  let sum_x = 0;
  let sum_y = 0;
  let s_len = segment.length;
  for (let i=0; i<s_len; i++) {
    sum_x = sum_x + segment[i][0];
    sum_y = sum_y + segment[i][1];
  }
  return [sum_x / s_len , sum_y / s_len ];
}


function Face() {
  // these are state variables for a face
  // (your variables may be different)
  this.eye_value = 2;    // can be either 2 (eyes) or 3 (no eyes)
  this.mouth_value = 1;  // range is 0.5 to 8
  this.tilt_value = 0;   // range is -30 to 30

  this.draw_segment = function(segment, do_loop) {
    // print(segment);
    for(let i=0; i<segment.length; i++) {
        let px = segment[i][0];
        let py = segment[i][1];
        ellipse(px, py, 0.1);
        if(i < segment.length - 1) {
          let nx = segment[i+1][0];
          let ny = segment[i+1][1];
          line(px, py, nx, ny);
        }
        else if(do_loop) {
          let nx = segment[0][0];
          let ny = segment[0][1];
          line(px, py, nx, ny);
        }
    }
  };

  /*
   * Draw a face with position lists that include:
   *    chin, right_eye, left_eye, right_eyebrow, left_eyebrow
   *    bottom_lip, top_lip, nose_tip, nose_bridge, 
   */  
  this.draw = function(positions) {
    rotate(this.tilt_value);

    // head
    stroke(stroke_color);
    fill(fg_color);
    ellipse(0, 0, 3, 4);
    noStroke();

    // mouth
    fill(bg_color);
    ellipse(0, 0.64, 1.36, 0.25 * this.mouth_value);

    // eyebrows
    fill(0);
    stroke(0);
    strokeWeight(0.08);
    this.draw_segment(positions.left_eyebrow);
    this.draw_segment(positions.right_eyebrow);

    fill(128);
    stroke(128);
    this.draw_segment(positions.chin);

    fill(100, 0, 100);
    stroke(100, 0, 100);
    this.draw_segment(positions.nose_bridge);
    this.draw_segment(positions.nose_tip);

    strokeWeight(0.03);

    fill(200, 0, 0);
    stroke(200, 0, 0);
    this.draw_segment(positions.top_lip);
    this.draw_segment(positions.bottom_lip);

    fill(255);
    stroke(255);
    // this.draw_segment(positions.left_eye, true);
    // this.draw_segment(positions.right_eye, true);


    // print(Object.keys(positions))


    let left_eye_pos = segment_average(positions.left_eye);
    let right_eye_pos = segment_average(positions.right_eye);

    // eyes
    noStroke();
    fill(bg_color);
    ellipse(left_eye_pos[0], left_eye_pos[1], 0.45, 0.27);
    ellipse(right_eye_pos[0], right_eye_pos[1], 0.45, 0.27);

    fill(fg_color);
    ellipse(left_eye_pos[0] - 0.1, left_eye_pos[1], 0.18);
    ellipse(right_eye_pos[0] - 0.1, right_eye_pos[1], 0.18);
  }

  /* set internal properties based on list numbers 0-100 */
  this.setProperties = function(settings) {
    this.eye_value = int(map(settings[0], 0, 100, 2, 3));
    this.mouth_value = map(settings[1], 0, 100, 0.5, 8);
    this.tilt_value = map(settings[2], 0, 100, -30, 30);
  }

  /* get internal properties as list of numbers 0-100 */
  this.getProperties = function() {
    let settings = new Array(3);
    settings[0] = map(this.eye_value, 2, 3, 0, 100);
    settings[1] = map(this.mouth_value, 0.5, 8, 0, 100);
    settings[2] = map(this.tilt_value, -30, 30, 0, 100);
    return settings;
  }
}

x_face2.js

/*
 * FaceMap class - holds all informaiton about one mapped
 * face and is able to draw itself.
 */

// other variables can be in here too
// these control the colors used

const stroke_color = [95, 52, 8];

function Face() {

  // state variables for a face

  this.cheek_size = 0;
  this.cheek_colour = 0;
  this.face_colour = 0;
  this.lip_colour = 0;
  this.eyelid_colour = 0;

  //-----------------------------------

  this.cheek_colour = ["#ffd1d1", "#ffb5b5", "#ffc1a6", "#fcb6e9", "#ffe6f2"];
  this.face_colour = ["#ffe9de", "#ffe3c2", "#fcd7c7", "#eba386", "#b37156"];
  this.lip_colour = ["#ff5c5c", "#fc8686", "#ffb3b3", "#ffc2e5", "#fa98d0", "#bf3459"];
  this.eyelid_colour =["#ff9696", "#ff7070", "#f5abff", "#945f9c", "#fca2cd", "#ffd9eb", "#ad6a89"];

  /*
   * Draw a face with position lists that include:
   *    chin, right_eye, left_eye, right_eyebrow, left_eyebrow
   *    bottom_lip, top_lip, nose_tip, nose_bridge, 
   */
  this.draw = function (positions) {

    //----------------------------
    // FACE SHAPE
    //----------------------------

    stroke(0);
    strokeWeight(0.07)

    fill(this.face_colour[this.skin_tone]);

    beginShape();

    for(let i = 0; i < positions.chin.length; i++) {
      curveVertex(positions.chin[i][0], positions.chin[i][1]);
      curveVertex(positions.chin[i][0], positions.chin[i][1]);
    }

    for(let i = 4; i >= 0; i--) {
      curveVertex(positions.right_eyebrow[i][0], positions.right_eyebrow[i][1]);
    }

    for(let i = 4; i >= 0; i--) {
      curveVertex(positions.left_eyebrow[i][0], positions.left_eyebrow[i][1]);
    }

    curveVertex(positions.chin[1][0], positions.chin[0][1]);
    curveVertex(positions.chin[0][0], positions.chin[0][1]);
    curveVertex(positions.chin[0][0], positions.chin[0][1]);
    
    endShape();

    //------------------------------
    // CHEEKS
    //------------------------------

    noStroke();

    fill(this.cheek_colour[this.blush]);
    
    // LEFT CHEEK
    let left_cheek = average_point(positions.left_eye);
    let right_cheek = average_point(positions.right_eye);
    let cheek_sizes = [left_cheek[0]*0.75, left_cheek[0], left_cheek[0]*1.3];

    if(this.cheek_size == 0) {

      ellipse(left_cheek[0], left_cheek[1] + 0.8, cheek_sizes[this.cheek_size],
    cheek_sizes[this.cheek_size]);

      ellipse(right_cheek[0], right_cheek[1] + 0.8, cheek_sizes[this.cheek_size],
    cheek_sizes[this.cheek_size]);

    }

    else if(this.cheek_size == 1) {

      ellipse(left_cheek[0], left_cheek[1] + 0.8, cheek_sizes[this.cheek_size],
    cheek_sizes[this.cheek_size]);

      ellipse(right_cheek[0], right_cheek[1] + 0.8, cheek_sizes[this.cheek_size],
    cheek_sizes[this.cheek_size]);

    }

    else if(this.cheek_size == 2) {

      ellipse(left_cheek[0], left_cheek[1] + 0.8, cheek_sizes[this.cheek_size],
    cheek_sizes[this.cheek_size]);

      ellipse(right_cheek[0], right_cheek[1] + 0.8, cheek_sizes[this.cheek_size],
    cheek_sizes[this.cheek_size]);

    }



    //------------------------------
    // EYEBROWS
    //------------------------------

    fill(0);
    stroke(0);
    strokeWeight(0.07);

    // LEFT EYEBROW

    beginShape();

    for(let i = 1; i < positions.left_eyebrow.length; i++) {
      curveVertex(positions.left_eyebrow[i][0], positions.left_eyebrow[i][1]);
      curveVertex(positions.left_eyebrow[i][0], positions.left_eyebrow[i][1]);
    }
    
    endShape();

    // RIGHT EYEBROW

    beginShape();

    for(let i = 0; i < 4; i++) {
      curveVertex(positions.right_eyebrow[i][0], positions.right_eyebrow[i][1]);
      curveVertex(positions.right_eyebrow[i][0], positions.right_eyebrow[i][1]);
    }
    
    endShape();

    //----------------------------
    // EYELIDS
    //----------------------------

    let left_eyelid = average_point(positions.left_eye);
    let right_eyelid = average_point(positions.right_eye);
    
    noStroke();
    fill(this.eyelid_colour[this.eyeshadow]);

    arc(left_eyelid[0] + 0.03, left_eyelid[1] + 0.03, 0.6, 0.6, 180, 360);
    arc(right_eyelid[0] + 0.03, right_eyelid[1] + 0.03, 0.6, 0.6, 180, 360);

    //----------------------------
    // EYE SHAPE
    //----------------------------

    fill(255);
    stroke(0);
    strokeWeight(0.05);

    // LEFT EYE

    beginShape();

    for(let i = 0; i < positions.left_eye.length; i++) {
      curveVertex(positions.left_eye[i][0], positions.left_eye[i][1]);
      curveVertex(positions.left_eye[i][0], positions.left_eye[i][1]);
    }

    curveVertex(positions.left_eye[0][0], positions.left_eye[0][1]);
    curveVertex(positions.left_eye[0][0], positions.left_eye[0][1]);

    endShape();

    //------------------

    // RIGHT EYE

    beginShape();

    for(let i = 0; i < positions.left_eye.length; i++) {
      curveVertex(positions.right_eye[i][0], positions.right_eye[i][1]);
      curveVertex(positions.right_eye[i][0], positions.right_eye[i][1]);
    }

    curveVertex(positions.right_eye[0][0], positions.right_eye[0][1]);
    curveVertex(positions.right_eye[0][0], positions.right_eye[0][1]);

    endShape();

    //----------------------------
    // IRIS
    //----------------------------

    let left_eye = average_point(positions.left_eye);
    let right_eye = average_point(positions.right_eye);

    noStroke();
    fill(0);
    ellipse(left_eye[0], left_eye[1], 0.2, 0.15);
    ellipse(right_eye[0], right_eye[1], 0.2, 0.15);

    //----------------------------
    // TEETH
    //----------------------------

    fill(255);
    noStroke();

    beginShape();

    for(let i = 0; i < 7; i++) {
      curveVertex(positions.top_lip[i][0], positions.top_lip[i][1]);
      curveVertex(positions.top_lip[i][0], positions.top_lip[i][1]);
    }

    for(let i = 0; i < 7; i++) {
      curveVertex(positions.bottom_lip[i][0], positions.bottom_lip[i][1]);
      curveVertex(positions.bottom_lip[i][0], positions.bottom_lip[i][1]);
    }

    endShape();

    //----------------------------
    // TOP LIP
    //----------------------------

    fill(this.lip_colour[this.lipstick]);
    noStroke();

    beginShape();

    for(let i = 0; i < positions.top_lip.length; i++) {
      curveVertex(positions.top_lip[i][0], positions.top_lip[i][1]);
      curveVertex(positions.top_lip[i][0], positions.top_lip[i][1]);
    }

    curveVertex(positions.top_lip[0][0], positions.top_lip[0][1]);
    curveVertex(positions.top_lip[0][0], positions.top_lip[0][1]);

    endShape();

    //-------------------------------
    // BOTTOM LIP
    //-------------------------------

    beginShape();

    for (let i = 0; i < positions.bottom_lip.length; i++) {
      curveVertex(positions.bottom_lip[i][0], positions.bottom_lip[i][1]);
      curveVertex(positions.bottom_lip[i][0], positions.bottom_lip[i][1]);
    }

    curveVertex(positions.bottom_lip[0][0], positions.bottom_lip[0][1]);
    curveVertex(positions.bottom_lip[0][0], positions.bottom_lip[0][1]);

    endShape();

    //-------------------------------
    // NOSE BRIDGE
    //-------------------------------
    noFill();
    stroke(0);
    strokeWeight(0.08);

    beginShape();

    curveVertex(positions.nose_bridge[0][0], positions.nose_bridge[0][0]);
    curveVertex(positions.nose_bridge[0][0], positions.nose_bridge[0][1]);
    curveVertex(positions.nose_bridge[1][0], positions.nose_bridge[1][1]);
    curveVertex(positions.nose_bridge[2][0], positions.nose_bridge[2][1]);
    curveVertex(positions.nose_bridge[3][0], positions.nose_bridge[3][1]);

    endShape();

    //-------------------------------
    // NOSE TIP
    //-------------------------------

    stroke(0);
    strokeWeight(0.08);

    beginShape();

    curveVertex(positions.nose_tip[0][0], positions.nose_tip[0][1] -0.1);
    curveVertex(positions.nose_tip[1][0], positions.nose_tip[1][1] -0.1);
    curveVertex(positions.nose_tip[2][0], positions.nose_tip[2][1] -0.1);
    curveVertex(positions.nose_tip[3][0], positions.nose_tip[3][1] -0.1);
    curveVertex(positions.nose_tip[4][0], positions.nose_tip[4][1] -0.1);

    endShape();

  }

  //------------------------------------

  /* set internal properties based on list numbers 0-100 */
  this.setProperties = function (settings) {
    this.cheek_size = int(map(settings[0], 0, 100, 0, 2));
    this.blush = int(map(settings[1], 0, 100, 0, 4));
    this.skin_tone = int(map(settings[2], 0, 100, 0, 4));
    this.lipstick = int(map(settings[3], 0, 100, 0, 5));
    this.eyeshadow = int(map(settings[4], 0, 100, 0, 6));
  }

  /* get internal properties as list of numbers 0-100 */
  this.getProperties = function () {
    let settings = new Array(5);
    settings[0] = map(this.cheek_size, 0, 2, 0, 100);
    settings[1] = map(this.blush, 0, 4, 0, 100);
    settings[2] = map(this.skin_tone, 0, 4, 0, 100);
    settings[3] = map(this.lipstick, 0, 5, 0, 100);
    settings[4] = map(this.eyeshadow, 0, 6, 0, 100);
    return settings;
  }
}

// given an array of [x,y] points, return the average
function average_point(list) {
  var sum_x = 0;
  var sum_y = 0;
  var num_points = 0;
  for (var i = 0; i < list.length; i++) {
    sum_x += list[i][0];
    sum_y += list[i][1];
    num_points += 1;
  }
  return [sum_x / num_points, sum_y / num_points];
}

x_foxface.js

/*
 * FaceMap class - holds all informaiton about one mapped
 * face and is able to draw itself.
 */  

// other variables can be in here too
// these control the colors used
const bg_color = [225, 206, 187];
const fg_color = [151, 102, 52];
const stroke_color = [95, 52, 8];

// skin colors
let outLine1 = '#F2C7A9';
let skin1 = '#FDF0E7';  // linen
let skinLt1 = 'FEF9F6' // snow

let outLine2 = '#CC7A51';
let skin2 = '#FBBB9B';  // peach-orange
let skinLt2 = '#FDDDCD';  // unbleached silk
    
let outLine3 = '#9C531D';
let skin3 = '#F58D3F';  // tyger's eye
let skinLt3 = '#FBD1B2';  // apricot

let outLine4 = '#723502';
let skin4 = '#B25303';  // windsor tan
let skinLt4 = '#DCB08C';  // tumbleweed
    
let outLine5 = '#451405';
let skin5 = '#6C1E07';  // kenyan copper
let skinLt5 = '#D6C1BB';  // pale silver

let outLine;
let skinColor;
let skinLt;
     
// mouth colors
let mouthColor = '#BA2E2E';

function Face() {
    // these are state variables for a face

    // variables change from 0 - 1
    this.skin = 2;
    this.eyeOpenL = 0.5;
    this.eyeOpenR = 0.5;
    this.earL = 0.5;
    this.earR = 0.5;
    this.gender = 1;
    this.blush = 1;
    this.freckles = 1;
    this.mouthOpen = 0;


  /*
   * Draw a face with position lists that include:
   *    chin, right_eye, left_eye, right_eyebrow, left_eyebrow
   *    bottom_lip, top_lip, nose_tip, nose_bridge, 
   */  
    
  this.draw = function(positions) {
      
      // vars
      // eyes positon
      let eyePosL = average_point(positions.left_eye);
      let eyePosR = average_point(positions.right_eye);

      // chin position
      let chinLs = [positions.chin[7], positions.chin[8], positions.chin[9]];
      let chinBt = average_point(chinLs);
      
      // nose position
      let nose = average_point(positions.nose_tip);
      
      // mouth position
      let mouthUp = average_point(positions.top_lip);
      let mouthDw = average_point(positions.bottom_lip);
      
      // overall face width
      let faceWidth = abs(positions.chin[0][0] - positions.chin[16][0]);
      let faceScaleX = map(faceWidth, 3, 4.5, 0.8, 1.1);

      // center point of the face
      let centerX = (eyePosL[0] + eyePosR[0]) / 2;
      let centerY = (eyePosL[1] + eyePosR[1]) / 1.0;

      angleMode(DEGREES);
      strokeWeight(0.1);
    
      // set up skin colours
      if(this.skin == 1){
          outLine = outLine1;
          skinColor = skin1;
          skinLt = skinLt1;
      }else if(this.skin == 2){
          outLine = outLine2;
          skinColor = skin2;
          skinLt = skinLt2;
      }else if(this.skin == 3){
          outLine = outLine3;
          skinColor = skin3;
          skinLt = skinLt3;
      }else if(this.skin == 4){
          outLine = outLine4;
          skinColor = skin4;
          skinLt = skinLt4;
      }else{
          outLine = outLine5;
          skinColor = skin5;
          skinLt = skinLt5;
      }
    
    push();

    // ears
    stroke(outLine);
    fill(skinColor);
    let earOffsetX = map(faceWidth, 3, 4.5, -0.2, 0.1);
    
    // left ear
    push();
    let earRotL = map(this.earL, 0, 1, -10, 10);
    
    translate(centerX - (1 + earOffsetX), centerY );
    rotate(earRotL);
      
    bezier(-0.7, 0.3, -0.9, -2, 0.1, -4, 0.9, 0.3);
      
    translate(0.05, 0);
    scale(0.7, 0.6);
      
    fill(skinLt);
    noStroke();
      
    bezier(-0.7, 0.3, -0.9, -2, 0.1, -4, 0.9, 0.3);
    pop();
    
    // right ear
    push();
    let earRotR = map(this.earR, 0, 1, 10, -10);
      
    translate(centerX + (1 + earOffsetX), centerY );
    rotate(earRotR);
      
    bezier(-0.9, 0.3, -0.1, -4, 0.9, -2, 0.7, 0.3);
      
    translate(-0.05, 0);
    scale(0.7, 0.6);
      
    fill(skinLt);
    noStroke();
      
    bezier(-0.9, 0.3, -0.1, -4, 0.9, -2, 0.7, 0.3);
    pop();

    // face (color)
    push(); //push for whole face change
    translate(0, -0.1);
    scale(faceScaleX, 0.9);
      
    push();
    noStroke();
    fill(skinColor);
    
    arc(0, -1.45, 3.7, 3.5, 200, 340, OPEN);
      
    beginShape();
    vertex(-1.71, - 2.11);
    vertex( 1.71, - 2.11);
    vertex( 2.01, chinBt[1] - 1.49);
    vertex(-2.01, chinBt[1] - 1.49);
    endShape();
      
    bezier(-1.7, -2.1, -1.9, -1.9, -2.25, -0.1, -2, chinBt[1] - 1.5);
    bezier( 1.7, -2.1,  1.9, -1.9,  2.25, -0.1,  2, chinBt[1] - 1.5);
      
    bezier(-2, chinBt[1] - 1.5, -1.8, chinBt[1], 1.8, chinBt[1], 2, chinBt[1] - 1.5);
    
    // light-color parts 
    fill(skinLt);
    bezier(-1.6, chinBt[1] - 1.59, -1.0, -0.8, 1.0, -0.8, 1.6, chinBt[1] - 1.59);
       
    bezier(-1.6, chinBt[1] - 1.6, -2.2, chinBt[1], 2.2, chinBt[1], 1.6, chinBt[1] - 1.6);
    pop();
      
    // face (stroke)
    push();
    stroke(outLine);
    noFill();
      
    arc(0, -1.45, 3.7, 3.5, 240, 250, OPEN);
    arc(0, -1.45, 3.7, 3.5, 280, 295, OPEN);
      
    arc(0, -1.45, 3.7, 3.5, 210, 215, OPEN);
    arc(0, -1.45, 3.7, 3.5, 325, 330, OPEN);
      
    bezier(-1.8, -1.8, -1.93, -1.6, -2.02, -1.3, -2.0, -1.1);
    bezier(-2.0, -1.1, -2.1, -0.9, -2.15, -0.4, -2.05, -0.2);
      
    bezier( 1.8, -1.8,  1.93, -1.6,  2.02, -1.3,  2.0, -1.1);
    bezier( 2.0, -1.1,  2.1, -0.9,  2.15, -0.4,  2.05, -0.2);
    
    bezier(-2, chinBt[1] - 1.5, -1.8, chinBt[1], 1.8, chinBt[1], 2, chinBt[1] - 1.5);
    pop();
      
    // fur
    fill(skinColor);
    
    //left cheek fur
    push();
    translate(-2.05, -0.08);
    rotate(-90);
      
    noStroke();
    arc(0, 0, 0.2, 0.15, 180, 360, OPEN);
      
    stroke(outLine);
    arc(0, 0, 0.2, 0.15, 280, 360, OPEN);
    pop();
      
    push();
    translate(-2.0, 0.25);
    rotate(-95);
    
    noStroke();
    arc(0, 0, 0.35, 0.35, 180, 360, OPEN);
    
    stroke(outLine);
    arc(0, 0, 0.35, 0.35, 250, 360, OPEN);
    pop();
      
    push();
    translate(-2.0, 0.45);
    rotate(-95);
      
    noStroke();
    arc(0, 0, 0.15, 0.1, 180, 360, OPEN);
      
    stroke(outLine);
    arc(0, 0, 0.15, 0.1, 270, 360, OPEN);
    pop();
     
      
    //right cheek fur
    push();
    translate( 2.05, -0.08);
    rotate( 90);
      
    noStroke();
    arc(0, 0, 0.2, 0.15, 180, 360, OPEN);
      
    stroke(outLine);
    arc(0, 0, 0.2, 0.15, 180, 280, OPEN);
    pop();
      
    push();
    translate( 2.0, 0.25);
    rotate( 95);
    
    noStroke();
    arc(0, 0, 0.35, 0.35, 180, 360, OPEN);
    
    stroke(outLine);
    arc(0, 0, 0.35, 0.35, 180, 300, OPEN);
    pop();
      
    push();
    translate( 2.0, 0.45);
    rotate( 95);
      
    noStroke();
    arc(0, 0, 0.15, 0.1, 180, 360, OPEN);
      
    stroke(outLine);
    arc(0, 0, 0.15, 0.1, 180, 260, OPEN);
    pop();
      
    // left ear fur
    push();
    translate( -1.65, -2.1);
    rotate( 100);
    
    noStroke();
    arc(0, 0, 0.45, 0.4, 0, 180, OPEN);
    
    stroke(outLine);
    arc(0, 0, 0.45, 0.5, 80, 180, OPEN);
    pop();
      
    push();
    translate( -1.75, -1.95);
    rotate(80);
    
    noStroke();
    arc(0, 0, 0.2, 0.15, 0, 180, OPEN);
    
    stroke(outLine);
    arc(0, 0, 0.2, 0.15, 90, 180, OPEN);
    pop();
      
    // right ear fur
    push();
    translate( 1.65, -2.1);
    rotate(80);
    
    noStroke();
    arc(0, 0, 0.45, 0.4, 180, 0, OPEN);
    
    stroke(outLine);
    arc(0, 0, 0.45, 0.5, 180, 260, OPEN);
    pop();
      
    push();
    translate( 1.75, -1.95);
    rotate( 100);
    
    noStroke();
    arc(0, 0, 0.2, 0.15, 180, 0, OPEN);
    
    stroke(outLine);
    arc(0, 0, 0.2, 0.15, 180, 270, OPEN);
    pop();
      
    // head-top fur
    push();
    translate(-0.45, -3.15);
    rotate(-9);
      
    noStroke();
      
    arc(0, 0, 0.2, 0.15, 175, 5, OPEN); 
      
    stroke(outLine);
    noFill();
      
    arc(0, 0, 0.2, 0.15, 180, 270, OPEN); 
    pop();
      
    push();
    translate(-0.15, -3.18);
      
    noStroke();
      
    arc(0, 0, 0.4, 0.35, 175, 5, OPEN); 
      
    stroke(outLine);
    noFill();
      
    arc(0, 0, 0.4, 0.35, 190, 280, OPEN); 
    pop();
      
    push();
    translate(0.15, -3.15);
    rotate(5);
      
    noStroke();
      
    arc(0, 0, 0.25, 0.2, 175, 5, OPEN); 
      
    stroke(outLine);
    noFill();
      
    arc(0, 0, 0.25, 0.2, 200, 280, OPEN); 
    pop();
      
    // left up fur
    push();
    translate(-0.96, -2.94);
    rotate(-30);
      
    noStroke();
      
    arc(0, 0, 0.15, 0.1, 175, 5, OPEN); 
      
    stroke(outLine);
    noFill();
      
    arc(0, 0, 0.15, 0.1, 320, 360, OPEN); 
    pop();
      
    push();
    translate(-1.2, -2.79);
    rotate(-40);
      
    noStroke();
      
    arc(0, 0, 0.4, 0.35, 175, 5, OPEN); 
      
    stroke(outLine);
    noFill();
      
    arc(0, 0, 0.4, 0.35, 285, 360, OPEN); 
    pop();
      
    push();
    translate(-1.36, -2.62);
    rotate(-45);
      
    noStroke();
      
    arc(0, 0, 0.28, 0.25, 175, 5, OPEN); 
      
    stroke(outLine);
    noFill();
      
    arc(0, 0, 0.28, 0.25, 300, 360, OPEN); 
    pop();
      
    // right up fur
    push();
    translate(0.9, -2.98);
    rotate(30);
      
    noStroke();
      
    arc(0, 0, 0.25, 0.2, 175, 5, OPEN); 
      
    stroke(outLine);
    noFill();
      
    arc(0, 0, 0.25, 0.2, 180, 280, OPEN); 
    pop();
      
    push();
    translate(1.2, -2.79);
    rotate(40);
      
    noStroke();
      
    arc(0, 0, 0.4, 0.35, 175, 5, OPEN); 
      
    stroke(outLine);
    noFill();
      
    arc(0, 0, 0.4, 0.35, 185, 280, OPEN); 
    pop();
      
    push();
    translate(1.36, -2.62);
    rotate(45);
      
    noStroke();
      
    arc(0, 0, 0.25, 0.2, 175, 5, OPEN); 
      
    stroke(outLine);
    noFill();
      
    arc(0, 0, 0.25, 0.2, 240, 280, OPEN); 
    pop();
      
    pop(); // pop for whole face change

    // eye-borws
    push();
    stroke(skinLt);
    strokeWeight(0.15);
    noFill();
      
    translate(0, 0.1);
      
    beginShape();
    curveVertex(positions.left_eyebrow[1][0], positions.left_eyebrow[1][1]);
    curveVertex(positions.left_eyebrow[2][0] + 0.08, positions.left_eyebrow[2][1]);
    curveVertex(positions.left_eyebrow[3][0] - 0.08, positions.left_eyebrow[3][1]);
    curveVertex(positions.left_eyebrow[4][0], positions.left_eyebrow[4][1]);
    endShape(); 
      
    beginShape();
    curveVertex(positions.right_eyebrow[3][0], positions.right_eyebrow[3][1]);
    curveVertex(positions.right_eyebrow[2][0] - 0.08, positions.right_eyebrow[2][1]);
    curveVertex(positions.right_eyebrow[1][0] + 0.08, positions.right_eyebrow[1][1]);
    curveVertex(positions.right_eyebrow[0][0], positions.right_eyebrow[0][1]);
    endShape();
    pop();
      
    push();
    noFill();
      
    beginShape();
    curveVertex(positions.left_eyebrow[1][0], positions.left_eyebrow[1][1]);
    curveVertex(positions.left_eyebrow[2][0], positions.left_eyebrow[2][1]);
    curveVertex(positions.left_eyebrow[3][0], positions.left_eyebrow[3][1]);
    curveVertex(positions.left_eyebrow[4][0], positions.left_eyebrow[4][1]);
    endShape(); 
      
    beginShape();
    curveVertex(positions.right_eyebrow[3][0], positions.right_eyebrow[3][1]);
    curveVertex(positions.right_eyebrow[2][0], positions.right_eyebrow[2][1]);
    curveVertex(positions.right_eyebrow[1][0], positions.right_eyebrow[1][1]);
    curveVertex(positions.right_eyebrow[0][0], positions.right_eyebrow[0][1]);
    endShape(); 
    pop();

    // left eye   
    push();
    translate(eyePosL[0] - 0.05, eyePosL[1] + 0.1);
    noStroke();
    
    let eyeLipL = map (this.eyeOpenL, 0, 1, -0.09, 0.12);
    
    // eye base
    let fixerL = map(this.eyeOpenL, 0, 1, - 0.5, - 0.3);
    noStroke();
    fill(skinLt);
    
    arc(0, 0, 0.6, 0.5, 0, 180);
    bezier(- 0.3, 0, -0.32, fixerL, 0.32, fixerL, 0.3, 0);
    
    // pupil
    fill(10);
    ellipse(0, 0, 0.5);
      
    // eye-lip
    push();
    stroke(10);
    strokeWeight(0.08);
    noFill();
    rotate(-5);
      
    arc(0, -0.15 + eyeLipL, 0.6, 0.4, 200, 340);
    
    // eye-lash
    if(this.gender == 0){
        // female, eye-lashes
        arc(-0.04, -0.4 + eyeLipL, 0.5, 0.5, 148, 165);
    }else{
        // male, nothing
    }
    pop();

    // fixer
    noStroke();

    let fixL1 = map (this.eyeOpenL, 0, 1, 180, 269);
    let fixL2 = map (this.eyeOpenL, 0, 1, 360, 270);

    pop();

    // right eye
    push();
    translate(eyePosR[0] + 0.05, eyePosR[1] + 0.1);
    noStroke();

    let eyeLipR = map (this.eyeOpenR, 0, 1, -0.09, 0.12);

    // eye base
    let fixerR = map(this.eyeOpenR, 0, 1, - 0.5, - 0.3);
    noStroke();
    fill(skinLt);
    
    arc(0, 0, 0.6, 0.5, 0, 180);
    bezier(- 0.3, 0, -0.32, fixerR, 0.32, fixerR, 0.3, 0);
    
    // pupil
    fill(10);
    ellipse(0, 0, 0.5);
      
    // eye-lip
    push();
    stroke(10);
    strokeWeight(0.08);
    noFill();
    rotate(5);
      
    arc(0, -0.15 + eyeLipR, 0.6, 0.4, 200, 340);
    
    // eye-lash
    if(this.gender == 0){
        // female, eye-lashes
        arc( 0.04, -0.4 + eyeLipR, 0.5, 0.5, 10, 27);
    }else{
        // male, nothing
    }
    pop();

    pop();
                
    // nose
    push();
    noStroke();
    strokeJoin(ROUND);
    fill(10);
      
    push();
    translate(nose[0] - 0.05, nose[1]);
    rotate(-45);
      
    arc(0, 0, 0.25, 0.4, 180, 360, OPEN);
    pop();
      
    push();
    translate(nose[0] + 0.05, nose[1]);
    rotate( 45);
      
    arc(0, 0, 0.25, 0.4, 180, 360, OPEN);
    pop();
      
    push();
    translate(nose[0], nose[1] + 0.04);
      
    arc(0, 0, 0.31, 0.2, 0, 180, OPEN);
    pop();

    quad(nose[0] - 0.15, nose[1] - 0.15, nose[0] + 0.15, nose[1] - 0.15, nose[0] + 0.1, nose[1] + 0.05, nose[0] - 0.1, nose[1] + 0.05);
    pop();
      

    // mouth
    let mouthDis = abs(mouthUp[1] - mouthDw[1]);
    
    push();
    translate(mouthUp[0], mouthUp[1]);
      
    noStroke();
    fill(mouthColor);
    arc(0, 0.01, 0.6, mouthDis * 2, 10, 170, OPEN);
    
    fill(skinLt);
    stroke(10);
    strokeWeight(0.08);
    arc(-0.2, 0, 0.4, 0.3, 0, 170);
    arc( 0.2, 0, 0.4, 0.3, 10, 180);
    pop();

    // gender
    if(this.gender == 0){
        // female
        push();
        translate(-1.35, -2.55);
        rotate(10);
        
        noStroke();
        fill('#5fa55a');
        
          for (let i = 0; i < 3; i ++) {
            ellipse(0, 0, 0.45, 0.22);
              
            rotate(60);
          }
        fill(255);
        ellipse(0, 0, 0.1);
        pop();
        
        push();
        translate(-1, -2.78);
        
        noStroke();
        fill('#f6d51f');
        
          for (let i = 0; i < 3; i ++) {
            ellipse(0, 0, 0.7, 0.35);
              
            rotate(60);
          }
        fill(255);
        ellipse(0, 0, 0.15);
        pop();
        
        push();
        translate(-0.6, -2.9);
        rotate(20);
        
        noStroke();
        fill('#fa8925');
        
          for (let i = 0; i < 3; i ++) {
            ellipse(0, 0, 0.45, 0.22);
              
            rotate(60);
          }
        fill(255);
        ellipse(0, 0, 0.1);
        pop();
        
        push();
        translate( 1.2, -2.65);
        rotate(20);
        
        noStroke();
        fill('#01b4bc');
        
          for (let i = 0; i < 3; i ++) {
            ellipse(0, 0, 0.45, 0.22);
              
            rotate(60);
          }
        fill(255);
        ellipse(0, 0, 0.1);
        pop();
        
        push();
        translate( 0.8, -2.85);
        rotate(15);
        
        noStroke();
        fill('#fa5457');
        
          for (let i = 0; i < 3; i ++) {
            ellipse(0, 0, 0.7, 0.35);
              
            rotate(60);
          }
        fill(255);
        ellipse(0, 0, 0.15);
        pop();
    }else{
        // male
        fill(50);
        stroke(0);
        
        rectMode(CENTER);
        
        push();
        translate(chinBt[0] - 0.45, chinBt[1] - 0.52);
        
        rotate(-15);
        rect(0, 0.2, 0.6, 0.28, 0.1);
        
        rotate(20);
        rect(0, 0, 0.6, 0.38, 0.1);
        
        line(0.1, 0, 0.2, 0);
        pop();
        
        push();
        translate(chinBt[0] + 0.45, chinBt[1] - 0.52);
        
        rotate(15);
        rect(0, 0.2, 0.6, 0.28, 0.1);
        
        rotate(-20);
        rect(0, 0, 0.6, 0.38, 0.1);
        
        line(- 0.1, 0, - 0.2, 0);
        pop();
        
        rect(chinBt[0], chinBt[1] - 0.47, 0.4, 0.5, 0.1);
    } 
    
    // blush
    if(this.blush == 0){
        push();
        stroke('#EC6054');
        strokeWeight(0.18);
        
        line(nose[0] - 0.6, nose[1] - 0.35, nose[0] - 0.8, nose[1] - 0.15);
        line(nose[0] - 0.9, nose[1] - 0.35, nose[0] - 1.1, nose[1] - 0.15);
        
        line(nose[0] + 0.8, nose[1] - 0.35, nose[0] + 0.6, nose[1] - 0.15);
        line(nose[0] + 1.1, nose[1] - 0.35, nose[0] + 0.9, nose[1] - 0.15);

        pop();
    }else{
        // do not do anything
    }
    
    // freckles
    if(this.freckles == 0){
        push();
        fill(outLine);
        noStroke();
        
        ellipse(nose[0] - 0.4, nose[1] - 0.4, 0.08);
        ellipse(nose[0] - 0.6, nose[1] - 0.15, 0.08);
        ellipse(nose[0] - 0.8, nose[1] - 0.35, 0.08);
        ellipse(nose[0] - 1.1, nose[1] - 0.2, 0.08);
        
        ellipse(nose[0] + 0.4, nose[1] - 0.4, 0.08);
        ellipse(nose[0] + 0.6, nose[1] - 0.15, 0.08);
        ellipse(nose[0] + 0.8, nose[1] - 0.35, 0.08);
        ellipse(nose[0] + 1.1, nose[1] - 0.2, 0.08);

        pop();
    }else{
        // do not do anything
    }

    pop();
  }

  /* set internal properties based on list numbers 0-100 */
  this.setProperties = function(settings) {
    this.skin = int(map(settings[0], 0, 100, 1, 6));
    this.eyeOpenL = map(settings[1], 0, 100, 0, 1);
    this.eyeOpenR = map(settings[2], 0, 100, 0, 1);
    this.earL = map(settings[3], 0, 100, 0, 1);
    this.earR = map(settings[4], 0, 100, 0, 1);
    this.gender = int(map(settings[5], 0, 100, 0, 2));
    this.blush = int(map(settings[6], 0, 100, 0, 2));
    this.freckles = int(map(settings[7], 0, 100, 0, 2));
  }

  /* get internal properties as list of numbers 0-100 */
  this.getProperties = function() {
    let settings = new Array(3);
    settings[0] = map(this.skin, 1, 6, 0, 100);
    settings[1] = map(this.eyeOpenL, 0, 1, 0, 100);
    settings[2] = map(this.eyeOpenR, 0, 1, 0, 100);
    settings[3] = map(this.earL, 0, 1, 0, 100);
    settings[4] = map(this.earR, 0, 1, 0, 100);
    settings[5] = map(this.gender, 0, 2, 0, 100);
    settings[6] = map(this.blush, 0, 2, 0, 100);
    settings[7] = map(this.freckles, 0, 2, 0, 100);
    return settings;
  }
}

// given an array of [x,y] points, return the average
function average_point(list) {
  var sum_x = 0;
  var sum_y = 0;
  var num_points = 0;
  for(var i=0; i<list.length; i++) {
    sum_x += list[i][0];
    sum_y += list[i][1];
    num_points += 1; 
  }
  return [sum_x / num_points, sum_y / num_points];
}

z_face_landmark_68_model-weights_manifest.json

[{"weights":[{"name":"dense0/conv0/filters","shape":[3,3,3,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004853619781194949,"min":-0.5872879935245888}},{"name":"dense0/conv0/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004396426443960153,"min":-0.7298067896973853}},{"name":"dense0/conv1/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00635151559231328,"min":-0.5589333721235686}},{"name":"dense0/conv1/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009354315552057004,"min":-1.2628325995276957}},{"name":"dense0/conv1/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0029380727048013726,"min":-0.5846764682554731}},{"name":"dense0/conv2/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0049374802439820535,"min":-0.6171850304977566}},{"name":"dense0/conv2/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009941946758943446,"min":-1.3421628124573652}},{"name":"dense0/conv2/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0030300481062309416,"min":-0.5272283704841838}},{"name":"dense0/conv3/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005672684837790097,"min":-0.7431217137505026}},{"name":"dense0/conv3/pointwise_filter","shape":[1,1,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010712201455060173,"min":-1.5639814124387852}},{"name":"dense0/conv3/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0030966934035806097,"min":-0.3839899820439956}},{"name":"dense1/conv0/depthwise_filter","shape":[3,3,32,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0039155554537679636,"min":-0.48161332081345953}},{"name":"dense1/conv0/pointwise_filter","shape":[1,1,32,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01023082966898002,"min":-1.094698774580862}},{"name":"dense1/conv0/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0027264176630506327,"min":-0.3871513081531898}},{"name":"dense1/conv1/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004583378632863362,"min":-0.5454220573107401}},{"name":"dense1/conv1/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00915846403907327,"min":-1.117332612766939}},{"name":"dense1/conv1/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003091680419211294,"min":-0.5966943209077797}},{"name":"dense1/conv2/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005407439727409214,"min":-0.708374604290607}},{"name":"dense1/conv2/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00946493943532308,"min":-1.2399070660273235}},{"name":"dense1/conv2/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004409168514550901,"min":-0.9788354102303}},{"name":"dense1/conv3/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004478132958505668,"min":-0.6493292789833219}},{"name":"dense1/conv3/pointwise_filter","shape":[1,1,64,64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.011063695888893277,"min":-1.2501976354449402}},{"name":"dense1/conv3/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003909627596537272,"min":-0.6646366914113363}},{"name":"dense2/conv0/depthwise_filter","shape":[3,3,64,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003213915404151468,"min":-0.3374611174359041}},{"name":"dense2/conv0/pointwise_filter","shape":[1,1,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010917326048308728,"min":-1.4520043644250609}},{"name":"dense2/conv0/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002800439152063108,"min":-0.38085972468058266}},{"name":"dense2/conv1/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0050568851770139206,"min":-0.6927932692509071}},{"name":"dense2/conv1/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01074961213504567,"min":-1.3222022926106174}},{"name":"dense2/conv1/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0030654204242369708,"min":-0.5487102559384177}},{"name":"dense2/conv2/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00591809165244009,"min":-0.917304206128214}},{"name":"dense2/conv2/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01092823346455892,"min":-1.366029183069865}},{"name":"dense2/conv2/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.002681120470458386,"min":-0.36463238398234055}},{"name":"dense2/conv3/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0048311497650894465,"min":-0.5797379718107336}},{"name":"dense2/conv3/pointwise_filter","shape":[1,1,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.011227761062921263,"min":-1.4483811771168429}},{"name":"dense2/conv3/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0034643323982463162,"min":-0.3360402426298927}},{"name":"dense3/conv0/depthwise_filter","shape":[3,3,128,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003394978887894574,"min":-0.49227193874471326}},{"name":"dense3/conv0/pointwise_filter","shape":[1,1,128,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010051267287310432,"min":-1.2765109454884247}},{"name":"dense3/conv0/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003142924752889895,"min":-0.4588670139219247}},{"name":"dense3/conv1/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00448304671867221,"min":-0.5872791201460595}},{"name":"dense3/conv1/pointwise_filter","shape":[1,1,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.016063522357566685,"min":-2.3613377865623026}},{"name":"dense3/conv1/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00287135781026354,"min":-0.47664539650374765}},{"name":"dense3/conv2/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006002906724518421,"min":-0.7923836876364315}},{"name":"dense3/conv2/pointwise_filter","shape":[1,1,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.017087187019048954,"min":-1.6061955797906016}},{"name":"dense3/conv2/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.003124481205846749,"min":-0.46242321846531886}},{"name":"dense3/conv3/depthwise_filter","shape":[3,3,256,1],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006576311588287353,"min":-1.0193282961845398}},{"name":"dense3/conv3/pointwise_filter","shape":[1,1,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015590153955945782,"min":-1.99553970636106}},{"name":"dense3/conv3/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.004453541601405424,"min":-0.6546706154065973}},{"name":"fc/weights","shape":[256,136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010417488509533453,"min":-1.500118345372817}},{"name":"fc/bias","shape":[136],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0025084222648658005,"min":0.07683877646923065}}],"paths":["z_face_landmark_68_model-shard1"]}]

z_face_recognition_model-weights_manifest.json

[{"weights":[{"name":"conv32_down/conv/filters","shape":[7,7,3,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0005260649557207145,"min":-0.07101876902229645}},{"name":"conv32_down/conv/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":8.471445956577858e-7,"min":-0.00014740315964445472}},{"name":"conv32_down/scale/weights","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.06814416062598135,"min":5.788674831390381}},{"name":"conv32_down/scale/biases","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008471635042452345,"min":-0.931879854669758}},{"name":"conv32_1/conv1/conv/filters","shape":[3,3,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0007328585666768691,"min":-0.0974701893680236}},{"name":"conv32_1/conv1/conv/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":1.5952091238361e-8,"min":-0.000001978059313556764}},{"name":"conv32_1/conv1/scale/weights","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.02146628510718252,"min":3.1103382110595703}},{"name":"conv32_1/conv1/scale/biases","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0194976619645661,"min":-2.3787147596770644}},{"name":"conv32_1/conv2/conv/filters","shape":[3,3,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0004114975824075587,"min":-0.05267169054816751}},{"name":"conv32_1/conv2/conv/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":4.600177166424806e-9,"min":-5.70421968636676e-7}},{"name":"conv32_1/conv2/scale/weights","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.03400764932819441,"min":2.1677730083465576}},{"name":"conv32_1/conv2/scale/biases","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010974494616190593,"min":-1.240117891629537}},{"name":"conv32_2/conv1/conv/filters","shape":[3,3,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0005358753251094444,"min":-0.0760942961655411}},{"name":"conv32_2/conv1/conv/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":5.9886454383719385e-9,"min":-7.366033889197485e-7}},{"name":"conv32_2/conv1/scale/weights","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.014633869657329485,"min":2.769575357437134}},{"name":"conv32_2/conv1/scale/biases","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.022131107367721257,"min":-2.5229462399202234}},{"name":"conv32_2/conv2/conv/filters","shape":[3,3,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00030145110452876373,"min":-0.03949009469326805}},{"name":"conv32_2/conv2/conv/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":6.8779549306497095e-9,"min":-9.010120959151119e-7}},{"name":"conv32_2/conv2/scale/weights","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.03929369870354148,"min":4.8010945320129395}},{"name":"conv32_2/conv2/scale/biases","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010553357180427103,"min":-1.2452961472903983}},{"name":"conv32_3/conv1/conv/filters","shape":[3,3,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0003133527642371608,"min":-0.040735859350830905}},{"name":"conv32_3/conv1/conv/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":4.1064200719547974e-9,"min":-3.0387508532465503e-7}},{"name":"conv32_3/conv1/scale/weights","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.009252088210161994,"min":2.333256721496582}},{"name":"conv32_3/conv1/scale/biases","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.007104101251153385,"min":-0.34810096130651585}},{"name":"conv32_3/conv2/conv/filters","shape":[3,3,32,32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00029995629892629733,"min":-0.031195455088334923}},{"name":"conv32_3/conv2/conv/bias","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":5.62726418316814e-9,"min":-6.921534945296811e-7}},{"name":"conv32_3/conv2/scale/weights","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0467432975769043,"min":5.362040996551514}},{"name":"conv32_3/conv2/scale/biases","shape":[32],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010314425300149357,"min":-1.268674311918371}},{"name":"conv64_down/conv1/conv/filters","shape":[3,3,32,64],"dtype":"float32"},{"name":"conv64_down/conv1/conv/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":8.373908033218849e-10,"min":-1.172347124650639e-7}},{"name":"conv64_down/conv1/scale/weights","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0066875364266189875,"min":2.5088400840759277}},{"name":"conv64_down/conv1/scale/biases","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01691421620986041,"min":-2.0973628100226906}},{"name":"conv64_down/conv2/conv/filters","shape":[3,3,64,64],"dtype":"float32"},{"name":"conv64_down/conv2/conv/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":2.3252014483766877e-9,"min":-2.673981665633191e-7}},{"name":"conv64_down/conv2/scale/weights","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.032557439804077146,"min":2.6351239681243896}},{"name":"conv64_down/conv2/scale/biases","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015429047509735706,"min":-1.5429047509735707}},{"name":"conv64_1/conv1/conv/filters","shape":[3,3,64,64],"dtype":"float32"},{"name":"conv64_1/conv1/conv/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":1.1319172039756998e-9,"min":-1.4941307092479238e-7}},{"name":"conv64_1/conv1/scale/weights","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.007802607031429515,"min":3.401733160018921}},{"name":"conv64_1/conv1/scale/biases","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01425027146058924,"min":-0.6982633015688727}},{"name":"conv64_1/conv2/conv/filters","shape":[3,3,64,64],"dtype":"float32"},{"name":"conv64_1/conv2/conv/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":2.5635019893325435e-9,"min":-2.717312108692496e-7}},{"name":"conv64_1/conv2/scale/weights","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.04062801716374416,"min":3.542381525039673}},{"name":"conv64_1/conv2/scale/biases","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.007973166306813557,"min":-0.7415044665336609}},{"name":"conv64_2/conv1/conv/filters","shape":[3,3,64,64],"dtype":"float32"},{"name":"conv64_2/conv1/conv/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":1.2535732661062331e-9,"min":-1.8302169685151004e-7}},{"name":"conv64_2/conv1/scale/weights","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.005631206549850164,"min":2.9051668643951416}},{"name":"conv64_2/conv1/scale/biases","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01859012585060269,"min":-2.3795361088771445}},{"name":"conv64_2/conv2/conv/filters","shape":[3,3,64,64],"dtype":"float32"},{"name":"conv64_2/conv2/conv/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":2.486726369919351e-9,"min":-3.5311514452854786e-7}},{"name":"conv64_2/conv2/scale/weights","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.03740917467603497,"min":5.571568965911865}},{"name":"conv64_2/conv2/scale/biases","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006418555858088475,"min":-0.5263215803632549}},{"name":"conv64_3/conv1/conv/filters","shape":[3,3,64,64],"dtype":"float32"},{"name":"conv64_3/conv1/conv/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":7.432564576875473e-10,"min":-8.47312361763804e-8}},{"name":"conv64_3/conv1/scale/weights","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006400122362024644,"min":2.268010377883911}},{"name":"conv64_3/conv1/scale/biases","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010945847922680425,"min":-1.3353934465670119}},{"name":"conv64_3/conv2/conv/filters","shape":[3,3,64,64],"dtype":"float32"},{"name":"conv64_3/conv2/conv/bias","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":2.278228722014533e-9,"min":-3.212302498040492e-7}},{"name":"conv64_3/conv2/scale/weights","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.029840927498013366,"min":7.038398265838623}},{"name":"conv64_3/conv2/scale/biases","shape":[64],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.010651412197187834,"min":-1.161003929493474}},{"name":"conv128_down/conv1/conv/filters","shape":[3,3,64,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00020040544662989823,"min":-0.022245004575918704}},{"name":"conv128_down/conv1/conv/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":4.3550543563576545e-10,"min":-4.311503812794078e-8}},{"name":"conv128_down/conv1/scale/weights","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.007448580685783835,"min":2.830846071243286}},{"name":"conv128_down/conv1/scale/biases","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01211262824488621,"min":-1.6957679542840696}},{"name":"conv128_down/conv2/conv/filters","shape":[3,3,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00022380277514457702,"min":-0.02484210804104805}},{"name":"conv128_down/conv2/conv/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":9.031058637304466e-10,"min":-1.1650065642122761e-7}},{"name":"conv128_down/conv2/scale/weights","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.027663578706629135,"min":3.1111555099487305}},{"name":"conv128_down/conv2/scale/biases","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.008878476946961646,"min":-1.029903325847551}},{"name":"conv128_1/conv1/conv/filters","shape":[3,3,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00022380667574265425,"min":-0.032899581334170175}},{"name":"conv128_1/conv1/conv/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":4.4147297756478345e-10,"min":-5.253528433020923e-8}},{"name":"conv128_1/conv1/scale/weights","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.013599334978589825,"min":3.634530782699585}},{"name":"conv128_1/conv1/scale/biases","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.014059314073300829,"min":-1.4059314073300828}},{"name":"conv128_1/conv2/conv/filters","shape":[3,3,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00021715293474057143,"min":-0.02909849325523657}},{"name":"conv128_1/conv2/conv/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":9.887046963276768e-10,"min":-1.1370104007768284e-7}},{"name":"conv128_1/conv2/scale/weights","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.029993299409454943,"min":3.630716562271118}},{"name":"conv128_1/conv2/scale/biases","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00782704236460667,"min":-0.7200878975438136}},{"name":"conv128_2/conv1/conv/filters","shape":[3,3,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00017718105923895743,"min":-0.022324813464108636}},{"name":"conv128_2/conv1/conv/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":3.567012027797675e-10,"min":-5.243507680862582e-8}},{"name":"conv128_2/conv1/scale/weights","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.007940645778880399,"min":4.927767753601074}},{"name":"conv128_2/conv1/scale/biases","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.015933452867994122,"min":-1.5614783810634238}},{"name":"conv128_2/conv2/conv/filters","shape":[3,3,128,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0001451439717236687,"min":-0.01712698866339291}},{"name":"conv128_2/conv2/conv/bias","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":1.0383988570966347e-9,"min":-1.2356946399449953e-7}},{"name":"conv128_2/conv2/scale/weights","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.02892604528688917,"min":4.750600814819336}},{"name":"conv128_2/conv2/scale/biases","shape":[128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00797275748907351,"min":-0.7414664464838364}},{"name":"conv256_down/conv1/conv/filters","shape":[3,3,128,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0002698827827093648,"min":-0.03994265184098599}},{"name":"conv256_down/conv1/conv/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":5.036909834755123e-10,"min":-6.396875490139006e-8}},{"name":"conv256_down/conv1/scale/weights","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.014870181738161573,"min":4.269900798797607}},{"name":"conv256_down/conv1/scale/biases","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.022031106200872685,"min":-3.1063859743230484}},{"name":"conv256_down/conv2/conv/filters","shape":[3,3,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00046430734150549946,"min":-0.03946612402796745}},{"name":"conv256_down/conv2/conv/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":6.693064577513153e-10,"min":-7.630093618364995e-8}},{"name":"conv256_down/conv2/scale/weights","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.03475512242784687,"min":3.608360528945923}},{"name":"conv256_down/conv2/scale/biases","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01290142021927179,"min":-1.1482263995151893}},{"name":"conv256_1/conv1/conv/filters","shape":[3,3,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00037147209924810076,"min":-0.04234781931428348}},{"name":"conv256_1/conv1/conv/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":3.2105515457510146e-10,"min":-3.467395669411096e-8}},{"name":"conv256_1/conv1/scale/weights","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.043242172166412955,"min":5.28542947769165}},{"name":"conv256_1/conv1/scale/biases","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01643658619300992,"min":-1.3149268954407936}},{"name":"conv256_1/conv2/conv/filters","shape":[3,3,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0003289232651392619,"min":-0.041773254672686264}},{"name":"conv256_1/conv2/conv/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":9.13591691187321e-10,"min":-1.2333487831028833e-7}},{"name":"conv256_1/conv2/scale/weights","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0573908618852204,"min":4.360693454742432}},{"name":"conv256_1/conv2/scale/biases","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0164216583850337,"min":-1.3958409627278647}},{"name":"conv256_2/conv1/conv/filters","shape":[3,3,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00010476927912118389,"min":-0.015610622589056398}},{"name":"conv256_2/conv1/conv/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":2.418552539068639e-10,"min":-2.539480166022071e-8}},{"name":"conv256_2/conv1/scale/weights","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.06024209564807368,"min":6.598613739013672}},{"name":"conv256_2/conv1/scale/biases","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.01578534350675695,"min":-1.1049740454729864}},{"name":"conv256_2/conv2/conv/filters","shape":[3,3,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.00005543030908002573,"min":-0.007427661416723448}},{"name":"conv256_2/conv2/conv/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":1.0822061852320308e-9,"min":-1.515088659324843e-7}},{"name":"conv256_2/conv2/scale/weights","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.04302893993901272,"min":2.2855491638183594}},{"name":"conv256_2/conv2/scale/biases","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.006792667566561232,"min":-0.8083274404207865}},{"name":"conv256_down_out/conv1/conv/filters","shape":[3,3,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.000568966465253456,"min":-0.05632768006009214}},{"name":"conv256_down_out/conv1/conv/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":4.5347887884881677e-10,"min":-6.530095855422961e-8}},{"name":"conv256_down_out/conv1/scale/weights","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.017565592597512638,"min":4.594101905822754}},{"name":"conv256_down_out/conv1/scale/biases","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.04850864223405427,"min":-6.306123490427055}},{"name":"conv256_down_out/conv2/conv/filters","shape":[3,3,256,256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.0003739110687199761,"min":-0.06954745878191555}},{"name":"conv256_down_out/conv2/conv/bias","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":1.2668428328152895e-9,"min":-2.2549802424112154e-7}},{"name":"conv256_down_out/conv2/scale/weights","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.04351314469879749,"min":4.31956672668457}},{"name":"conv256_down_out/conv2/scale/biases","shape":[256],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.021499746921015722,"min":-1.2039858275768804}},{"name":"fc","shape":[256,128],"dtype":"float32","quantization":{"dtype":"uint8","scale":0.000357687911566566,"min":-0.04578405268052045}}],"paths":["z_face_recognition_model-shard1","z_face_recognition_model-shard2"]}]

z_face_system.js

// Arithmetic mean
let getMean = function (data) {
    return data.reduce(function (a, b) {
        return Number(a) + Number(b);
    }) / data.length;
};

// Standard deviation
let getSD = function (data) {
    let m = getMean(data);
    return Math.sqrt(data.reduce(function (sq, n) {
            return sq + Math.pow(n - m, 2);
        }, 0) / (data.length - 1));
};

const LM_LEN = 68;
const LM_parts = ['chin', 'left_eyebrow', 'right_eyebrow',
'nose_bridge', 'nose_tip', 'left_eye', 'right_eye',
'top_lip', 'bottom_lip'];
const LM_names = [
'chin', 'chin', 'chin', 'chin', 'chin',
'chin', 'chin', 'chin', 'chin', 'chin',
'chin', 'chin', 'chin', 'chin', 'chin',
'chin', 'chin',
'left_eyebrow', 'left_eyebrow', 'left_eyebrow', 'left_eyebrow', 'left_eyebrow',
'right_eyebrow', 'right_eyebrow', 'right_eyebrow', 'right_eyebrow', 'right_eyebrow',
'nose_bridge', 'nose_bridge', 'nose_bridge', 'nose_bridge',
'nose_tip', 'nose_tip', 'nose_tip', 'nose_tip', 'nose_tip',
'left_eye', 'left_eye', 'left_eye', 'left_eye', 'left_eye', 'left_eye',
'right_eye', 'right_eye', 'right_eye', 'right_eye', 'right_eye', 'right_eye',
'lip', 'lip', 'lip', 'lip',
'lip', 'lip', 'lip', 'lip',
'lip', 'lip', 'lip', 'lip',
'lip', 'lip', 'lip', 'lip',
'lip', 'lip', 'lip', 'lip'
]
// https://github.com/ageitgey/face_recognition/blob/d34c622bf42e2c619505a4884017051ecf61ac77/face_recognition/api.py#L190
const top_lip_indices =    [48, 49, 50, 51, 52, 53, 54, 64, 63, 62, 61, 60];
const bottom_lip_indices = [54, 55, 56, 57, 58, 59, 48, 60, 67, 66, 65, 64];

function get_landmarks(faceDescriptions) {
  let landmarks = []
  for(let i=0; i<faceDescriptions.length; i++) {
    let curLM = {
      'chin': [],
      'left_eyebrow': [],
      'right_eyebrow': [],
      'nose_bridge': [],
      'nose_tip': [],
      'left_eye': [],
      'right_eye': [],
      'top_lip': [],
      'bottom_lip': []
    };
    let lm = faceDescriptions[i].landmarks;
    let lpts = lm.positions;
    let x_points = [];
    let y_points = [];
    // print(lpts.length)
    for(let j=0; j<LM_LEN; j++) {
      x_points.push(lpts[j].x)
      y_points.push(lpts[j].y)
    }
    let mean_x = getMean(x_points);
    let mean_y = getMean(y_points);
    for(let j=0; j<LM_LEN; j++) {
      x_points[j] = x_points[j] - mean_x;
      y_points[j] = y_points[j] - mean_y;
    }
    let sdev_x = getSD(x_points);
    let sdev_y = getSD(y_points);
    let sdev = sdev_x > sdev_y ? sdev_x : sdev_y;
    // let p1 = lpts[27]
    // let p2 = lpts[28]
    // EYES VERSION
    // let p1 = lpts[36];
    // let p2 = lpts[42];
    // EARS VERSION
    let p1 = lpts[2];
    let p2 = lpts[14];
    let xd = p1.x - p2.x;
    let yd = p1.y - p2.y;
    let angle = Math.atan2(-yd, -xd);
    let s_a = Math.sin(-angle);
    let c_a = Math.cos(-angle);
    let raw_points = []
    for(let j=0; j<LM_LEN; j++) {
      let pt = [0, 0]
      pt[0] = lpts[j].x - mean_x;
      pt[1] = lpts[j].y - mean_y;
      pt[0] = pt[0] / sdev;
      pt[1] = pt[1] / sdev;
      let x_new = pt[0] * c_a - pt[1] * s_a;
      let y_new = pt[0] * s_a + pt[1] * c_a;
      pt[0] = x_new;
      pt[1] = y_new;
      raw_points.push(pt);
    }
    // put all raw points into landmarks objects
    // first everything but the lips (which start at 48)
    for(let j=0; j<48; j++) {
      let key = LM_names[j];
      curLM[key].push(raw_points[j]);
    }
    // now the lips, which have dupes
    for(let j=0; j<top_lip_indices.length; j++) {
      let cur_ix = top_lip_indices[j];
      let cur_pt = raw_points[cur_ix];
      let pt_copy = [cur_pt[0], cur_pt[1]];
      curLM['top_lip'].push(pt_copy);
    }
    for(let j=0; j<bottom_lip_indices.length; j++) {
      let cur_ix = bottom_lip_indices[j];
      let cur_pt = raw_points[cur_ix];
      let pt_copy = [cur_pt[0], cur_pt[1]];
      curLM['bottom_lip'].push(pt_copy);
    }
    curLM['transform'] = {
      'center': [mean_x, mean_y],
      'scale': sdev,
      'angle': angle
    }
    landmarks.push(curLM);
  }
  // print(JSON.stringify(landmarks));
  return landmarks;
}

function get_latents(faceDescriptions) {
  latents = [];
  for(let i=0; i<faceDescriptions.length; i++) {
    let lm = faceDescriptions[i].descriptor;
    latents.push(lm);
    // print(lm);
  }
  return latents;
}

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

z_kdTree.js

/**
 * k-d Tree JavaScript - V 1.01
 *
 * https://github.com/ubilabs/kd-tree-javascript
 *
 * @author Mircea Pricop <pricop@ubilabs.net>, 2012
 * @author Martin Kleppe <kleppe@ubilabs.net>, 2012
 * @author Ubilabs http://ubilabs.net, 2012
 * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
 */

 (function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['exports'], factory);
    } else if (typeof exports === 'object') {
        factory(exports);
    } else {
        factory((root.commonJsStrict = {}));
    }
}(this, function (exports) {
  function Node(obj, dimension, parent) {
    this.obj = obj;
    this.left = null;
    this.right = null;
    this.parent = parent;
    this.dimension = dimension;
  }

  function kdTree(points, metric, dimensions) {

    var self = this;
    
    function buildTree(points, depth, parent) {
      var dim = depth % dimensions.length,
        median,
        node;

      if (points.length === 0) {
        return null;
      }
      if (points.length === 1) {
        return new Node(points[0], dim, parent);
      }

      points.sort(function (a, b) {
        return a[dimensions[dim]] - b[dimensions[dim]];
      });

      median = Math.floor(points.length / 2);
      node = new Node(points[median], dim, parent);
      node.left = buildTree(points.slice(0, median), depth + 1, node);
      node.right = buildTree(points.slice(median + 1), depth + 1, node);

      return node;
    }

    // Reloads a serialied tree
    function loadTree (data) {
      // Just need to restore the `parent` parameter
      self.root = data;

      function restoreParent (root) {
        if (root.left) {
          root.left.parent = root;
          restoreParent(root.left);
        }

        if (root.right) {
          root.right.parent = root;
          restoreParent(root.right);
        }
      }

      restoreParent(self.root);
    }
    
    // If points is not an array, assume we're loading a pre-built tree
    if (!Array.isArray(points)) loadTree(points, metric, dimensions);
    else this.root = buildTree(points, 0, null);

    // Convert to a JSON serializable structure; this just requires removing 
    // the `parent` property
    this.toJSON = function (src) {
      if (!src) src = this.root;
      var dest = new Node(src.obj, src.dimension, null);
      if (src.left) dest.left = self.toJSON(src.left);
      if (src.right) dest.right = self.toJSON(src.right);
      return dest;
    };

    this.insert = function (point) {
      function innerSearch(node, parent) {

        if (node === null) {
          return parent;
        }

        var dimension = dimensions[node.dimension];
        if (point[dimension] < node.obj[dimension]) {
          return innerSearch(node.left, node);
        } else {
          return innerSearch(node.right, node);
        }
      }

      var insertPosition = innerSearch(this.root, null),
        newNode,
        dimension;

      if (insertPosition === null) {
        this.root = new Node(point, 0, null);
        return;
      }

      newNode = new Node(point, (insertPosition.dimension + 1) % dimensions.length, insertPosition);
      dimension = dimensions[insertPosition.dimension];

      if (point[dimension] < insertPosition.obj[dimension]) {
        insertPosition.left = newNode;
      } else {
        insertPosition.right = newNode;
      }
    };

    this.remove = function (point) {
      var node;

      function nodeSearch(node) {
        if (node === null) {
          return null;
        }

        if (node.obj === point) {
          return node;
        }

        var dimension = dimensions[node.dimension];

        if (point[dimension] < node.obj[dimension]) {
          return nodeSearch(node.left, node);
        } else {
          return nodeSearch(node.right, node);
        }
      }

      function removeNode(node) {
        var nextNode,
          nextObj,
          pDimension;

        function findMin(node, dim) {
          var dimension,
            own,
            left,
            right,
            min;

          if (node === null) {
            return null;
          }

          dimension = dimensions[dim];

          if (node.dimension === dim) {
            if (node.left !== null) {
              return findMin(node.left, dim);
            }
            return node;
          }

          own = node.obj[dimension];
          left = findMin(node.left, dim);
          right = findMin(node.right, dim);
          min = node;

          if (left !== null && left.obj[dimension] < own) {
            min = left;
          }
          if (right !== null && right.obj[dimension] < min.obj[dimension]) {
            min = right;
          }
          return min;
        }

        if (node.left === null && node.right === null) {
          if (node.parent === null) {
            self.root = null;
            return;
          }

          pDimension = dimensions[node.parent.dimension];

          if (node.obj[pDimension] < node.parent.obj[pDimension]) {
            node.parent.left = null;
          } else {
            node.parent.right = null;
          }
          return;
        }

        // If the right subtree is not empty, swap with the minimum element on the
        // node's dimension. If it is empty, we swap the left and right subtrees and
        // do the same.
        if (node.right !== null) {
          nextNode = findMin(node.right, node.dimension);
          nextObj = nextNode.obj;
          removeNode(nextNode);          
          node.obj = nextObj;
        } else {
          nextNode = findMin(node.left, node.dimension);
          nextObj = nextNode.obj;
          removeNode(nextNode);
          node.right = node.left;
          node.left = null;
          node.obj = nextObj;
        }

      }

      node = nodeSearch(self.root);

      if (node === null) { return; }

      removeNode(node);
    };

    this.nearest = function (point, maxNodes, maxDistance) {
      var i,
        result,
        bestNodes;

      bestNodes = new BinaryHeap(
        function (e) { return -e[1]; }
      );

      function nearestSearch(node) {
        var bestChild,
          dimension = dimensions[node.dimension],
          ownDistance = metric(point, node.obj),
          linearPoint = {},
          linearDistance,
          otherChild,
          i;

        function saveNode(node, distance) {
          bestNodes.push([node, distance]);
          if (bestNodes.size() > maxNodes) {
            bestNodes.pop();
          }
        }

        for (i = 0; i < dimensions.length; i += 1) {
          if (i === node.dimension) {
            linearPoint[dimensions[i]] = point[dimensions[i]];
          } else {
            linearPoint[dimensions[i]] = node.obj[dimensions[i]];
          }
        }

        linearDistance = metric(linearPoint, node.obj);

        if (node.right === null && node.left === null) {
          if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
            saveNode(node, ownDistance);
          }
          return;
        }

        if (node.right === null) {
          bestChild = node.left;
        } else if (node.left === null) {
          bestChild = node.right;
        } else {
          if (point[dimension] < node.obj[dimension]) {
            bestChild = node.left;
          } else {
            bestChild = node.right;
          }
        }

        nearestSearch(bestChild);

        if (bestNodes.size() < maxNodes || ownDistance < bestNodes.peek()[1]) {
          saveNode(node, ownDistance);
        }

        if (bestNodes.size() < maxNodes || Math.abs(linearDistance) < bestNodes.peek()[1]) {
          if (bestChild === node.left) {
            otherChild = node.right;
          } else {
            otherChild = node.left;
          }
          if (otherChild !== null) {
            nearestSearch(otherChild);
          }
        }
      }

      if (maxDistance) {
        for (i = 0; i < maxNodes; i += 1) {
          bestNodes.push([null, maxDistance]);
        }
      }

      if(self.root)
        nearestSearch(self.root);

      result = [];

      for (i = 0; i < Math.min(maxNodes, bestNodes.content.length); i += 1) {
        if (bestNodes.content[i][0]) {
          result.push([bestNodes.content[i][0].obj, bestNodes.content[i][1]]);
        }
      }
      return result;
    };

    this.balanceFactor = function () {
      function height(node) {
        if (node === null) {
          return 0;
        }
        return Math.max(height(node.left), height(node.right)) + 1;
      }

      function count(node) {
        if (node === null) {
          return 0;
        }
        return count(node.left) + count(node.right) + 1;
      }

      return height(self.root) / (Math.log(count(self.root)) / Math.log(2));
    };
  }

  // Binary heap implementation from:
  // http://eloquentjavascript.net/appendix2.html

  function BinaryHeap(scoreFunction){
    this.content = [];
    this.scoreFunction = scoreFunction;
  }

  BinaryHeap.prototype = {
    push: function(element) {
      // Add the new element to the end of the array.
      this.content.push(element);
      // Allow it to bubble up.
      this.bubbleUp(this.content.length - 1);
    },

    pop: function() {
      // Store the first element so we can return it later.
      var result = this.content[0];
      // Get the element at the end of the array.
      var end = this.content.pop();
      // If there are any elements left, put the end element at the
      // start, and let it sink down.
      if (this.content.length > 0) {
        this.content[0] = end;
        this.sinkDown(0);
      }
      return result;
    },

    peek: function() {
      return this.content[0];
    },

    remove: function(node) {
      var len = this.content.length;
      // To remove a value, we must search through the array to find
      // it.
      for (var i = 0; i < len; i++) {
        if (this.content[i] == node) {
          // When it is found, the process seen in 'pop' is repeated
          // to fill up the hole.
          var end = this.content.pop();
          if (i != len - 1) {
            this.content[i] = end;
            if (this.scoreFunction(end) < this.scoreFunction(node))
              this.bubbleUp(i);
            else
              this.sinkDown(i);
          }
          return;
        }
      }
      throw new Error("Node not found.");
    },

    size: function() {
      return this.content.length;
    },

    bubbleUp: function(n) {
      // Fetch the element that has to be moved.
      var element = this.content[n];
      // When at 0, an element can not go up any further.
      while (n > 0) {
        // Compute the parent element's index, and fetch it.
        var parentN = Math.floor((n + 1) / 2) - 1,
            parent = this.content[parentN];
        // Swap the elements if the parent is greater.
        if (this.scoreFunction(element) < this.scoreFunction(parent)) {
          this.content[parentN] = element;
          this.content[n] = parent;
          // Update 'n' to continue at the new position.
          n = parentN;
        }
        // Found a parent that is less, no need to move it further.
        else {
          break;
        }
      }
    },

    sinkDown: function(n) {
      // Look up the target element and its score.
      var length = this.content.length,
          element = this.content[n],
          elemScore = this.scoreFunction(element);

      while(true) {
        // Compute the indices of the child elements.
        var child2N = (n + 1) * 2, child1N = child2N - 1;
        // This is used to store the new position of the element,
        // if any.
        var swap = null;
        // If the first child exists (is inside the array)...
        if (child1N < length) {
          // Look it up and compute its score.
          var child1 = this.content[child1N],
              child1Score = this.scoreFunction(child1);
          // If the score is less than our element's, we need to swap.
          if (child1Score < elemScore)
            swap = child1N;
        }
        // Do the same checks for the other child.
        if (child2N < length) {
          var child2 = this.content[child2N],
              child2Score = this.scoreFunction(child2);
          if (child2Score < (swap == null ? elemScore : child1Score)){
            swap = child2N;
          }
        }

        // If the element needs to be moved, swap it, and continue.
        if (swap != null) {
          this.content[n] = this.content[swap];
          this.content[swap] = element;
          n = swap;
        }
        // Otherwise, we are done.
        else {
          break;
        }
      }
    }
  };
  
  this.kdTree = kdTree;
  
  exports.kdTree = kdTree;
  exports.BinaryHeap = BinaryHeap;
}));

z_purview_helper.js

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