Code viewer for World: Improved Doodle Recognitio...
'use strict';
const colourMap = {
  GREEN : "#00FF00",
  YELLOW : "#FFFF00",
  ORANGE : "#FFA500",
  RED : "#B20000",
  WHITE : "#FFFFFF"
};
/**
 * @return {undefined}
 */
function clearCanvas() {
  var ctx = document.getElementById("doodleCanvas").getContext("2d");
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  /** @type {!Array} */
  clickX = new Array;
  /** @type {!Array} */
  clickY = new Array;
  /** @type {!Array} */
  clickDrag = new Array;
  /** @type {string} */
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  ctx.fill();
}
document.write('\n\x3c!-- Bootstrap CSS --\x3e\n<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">\n<style>\ntable, td, th {\n  border: 1px solid black;\n    text-align: center;\n}\n\ntd:hover {\n    background-color: lightblue;\n}\n\n.table {\n  border-collapse: collapse;\n  width: 100%;\n}\n\n.slider {\n  -webkit-appearance: none;\n  height: 25px;\n  background: #d3d3d3;\n  outline: none;\n  opacity: 0.7;\n  -webkit-transition: .2s;\n  transition: opacity .2s;\n}\n\n.slider:hover {\n  opacity: 1;\n}\n\n.slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 25px;\n  height: 25px;\n  background: #4CAF50;\n  cursor: pointer;\n}\n\n.slider::-moz-range-thumb {\n  width: 25px;\n  height: 25px;\n  background: #4CAF50;\n  cursor: pointer;\n}\n\n</style>\n<div style="text-align: center;">\n    <canvas id="doodleCanvas" width="280" height="280" style="border:1px solid black;"></canvas>\n    <div>\n        <button type="button" style="margin:5px" class="btn btn-secondary" onclick="clearCanvas()">Clear or click spacebar</button>\n    </div>\n    \n    <div>\n        <canvas id="smallCanvas" width="28" height="28" style="height:100;width:100; border:1px solid black;" >\n    </div>\n    \n    <p>Brush Size:</p>\n    <input type="range" min="1" max="100" value="25" class="slider" id="brushSize" onchange="updateBrushSize(this.value)">\n    \n    <p>Brush Type:</p>\n    <input type="range" min="0" max="2" value="0" class="slider" id="brushType" onchange="updateBrushType(this.value)">\n    \n    <div>\n        <h2>Select Correct Result To Retrain</h2>\n        <table class="table" id="predictionTable">\n        </table>\n    </div>\n</div>\n\n');
/** @type {!Array} */
var lineJoin = ["round", "bevel", "miter"];
/** @type {number} */
var BRUSH_STYLE = 0;
/** @type {number} */
var BRUSH_SIZE = 25;
const PIXELS = 28;
const PIXELSSQUARED = 784;
var inputs;
var paint;
/** @type {!Array} */
var models = [];
/** @type {!Array} */
var clickX = [];
/** @type {!Array} */
var clickY = [];
/** @type {!Array} */
var clickDrag = [];
/**
 * @param {number} canCreateDiscussions
 * @return {undefined}
 */
function updateBrushSize(canCreateDiscussions) {
  /** @type {number} */
  BRUSH_SIZE = canCreateDiscussions;
  redraw();
  makePredictions();
}
/**
 * @param {number} canCreateDiscussions
 * @return {undefined}
 */
function updateBrushType(canCreateDiscussions) {
  /** @type {number} */
  BRUSH_STYLE = canCreateDiscussions;
  redraw();
  makePredictions();
}
/**
 * @return {?}
 */
function loadTensorFlowLibraries() {
  return $.when($.getScript("https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"), $.getScript("https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis@1.0.2/dist/tfjs-vis.umd.min.js"), $.getScript("uploads/michaelryan/data.js"));
}
/**
 * @param {number} s
 * @return {?}
 */
function getColourForPredictionValue(s) {
  let color;
  return s >= .8 ? color = colourMap.GREEN : s >= .6 && s < .8 ? color = colourMap.YELLOW : s >= .4 && s < .6 ? color = colourMap.ORANGE : s >= .2 && s < .4 && (color = colourMap.RED), color;
}
/**
 * @param {number} indent
 * @param {!NodeList} arr
 * @return {undefined}
 */
function fillPredictionTable(indent, arr) {
  indent = indent + 1;
  let i = 0;
  for (let j = 0; j < arr.length; j++) {
    if (arr[j] > arr[i]) {
      i = j;
    }
    $(`#${indent}_${j}`).css("background-color", colourMap.WHITE);
    $(`#${indent}_${j}`).html(arr[j].toFixed(2));
  }
  let meterPos = getColourForPredictionValue(arr[i]);
  $(`#${indent}_${i}`).css("background-color", meterPos);
}
/**
 * @param {?} e
 * @return {undefined}
 */
async function selectCorrectValue(e) {
  console.log(e);
  let row = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
  /** @type {number} */
  row[e] = 1;
  const [n, sc] = tf.tidy(() => {
    const options = data.nextTrainBatch(1);
    return [options.xs, options.labels];
  });
  tf.tensor2d(inputs, [1, 784]);
  tf.tensor2d(row, [1, 10]);
  let tr = data.nextTrainArray();
  tr.batchImagesArray.set(inputs, IMAGE_SIZE);
  tr.batchLabelsArray.set(row, NUM_CLASSES);
  const hugeDocs = tf.tensor2d(tr.batchImagesArray, [2, IMAGE_SIZE]);
  const sortOption = tf.tensor2d(tr.batchLabelsArray, [2, NUM_CLASSES]);
  console.log("Retraining models....");
  await retrainModels(hugeDocs, sortOption);
  console.log("Complete....");
  makePredictions();
}
/**
 * @param {?} docs
 * @param {?} sortOption
 * @return {undefined}
 */
async function retrainModels(docs, sortOption) {
  const pingPromises = models.map(async(indexer) => {
    await indexer.fit(docs.reshape([2, 28, 28, 1]), sortOption);
  });
  await Promise.all(pingPromises);
}
/**
 * @return {undefined}
 */
function createPredictionTable() {
  var e = $("#predictionTable");
  /** @type {string} */
  var tt = "Model 500";
  for (let i = 0; i <= models.length; i++) {
    /** @type {string} */
    var filename = "<tr>";
    for (let endAccelerationPoint = -1; endAccelerationPoint < 10; endAccelerationPoint++) {
      if (0 === i) {
        filename = filename + (-1 === endAccelerationPoint ? "<td></td>" : `<td onclick="selectCorrectValue(${endAccelerationPoint})">${endAccelerationPoint}</td>`);
      } else {
        if (-1 === endAccelerationPoint) {
          filename = filename + `<td>${tt}</td>`;
          /** @type {string} */
          tt = tt + "0";
        } else {
          filename = filename + `<td id=${i}_${endAccelerationPoint}>0.00</td>`;
        }
      }
    }
    /** @type {string} */
    filename = filename + "</tr>";
    e.append(filename);
  }
}
/**
 * @return {undefined}
 */
function makePredictions() {
  let captureContext = document.getElementById("smallCanvas").getContext("2d");
  let imgData = captureContext.getImageData(0, 0, captureContext.canvas.width, captureContext.canvas.height);
  /** @type {!Float32Array} */
  inputs = new Float32Array(PIXELSSQUARED);
  for (let i = 0; i < PIXELSSQUARED; i++) {
    /** @type {number} */
    inputs[i] = imgData.data[4 * i] / 255;
  }
  currentTensor = tf.tensor2d(inputs, [1, PIXELSSQUARED]);
  for (let i = 0; i < models.length; i++) {
    fillPredictionTable(i, models[i].predict(currentTensor.reshape([1, 28, 28, 1])).dataSync());
  }
}
/**
 * @return {undefined}
 */
async function setupCanvas() {
  /**
   * @param {number} x
   * @param {number} y
   * @param {boolean} dragging
   * @return {undefined}
   */
  function addClick(x, y, dragging) {
    clickX.push(x);
    clickY.push(y);
    clickDrag.push(dragging);
  }
  var g = document.getElementById("doodleCanvas").getContext("2d");
  document.getElementById("smallCanvas").getContext("2d").scale(.1, .1);
  /** @type {string} */
  g.fillStyle = "black";
  g.fillRect(0, 0, g.canvas.width, g.canvas.height);
  g.fill();
  createPredictionTable();
  $("#doodleCanvas").mousedown(function(event) {
    event.pageX;
    this.offsetLeft;
    event.pageY;
    this.offsetTop;
    /** @type {boolean} */
    paint = true;
    addClick(event.pageX - this.offsetLeft, event.pageY - this.offsetTop);
    redraw(g);
  });
  $("#doodleCanvas").mousemove(function(event) {
    if (paint) {
      addClick(event.pageX - this.offsetLeft, event.pageY - this.offsetTop, true);
      redraw(g);
    }
  });
  $("#doodleCanvas").mouseup(function(canCreateDiscussions) {
    /** @type {boolean} */
    paint = false;
    makePredictions();
  });
  $("#doodleCanvas").mouseleave(function(canCreateDiscussions) {
    /** @type {boolean} */
    paint = false;
  });
  $("body").keyup(function(event) {
    if (32 == event.keyCode) {
      clearCanvas();
    }
  });
}
/**
 * @param {!Object} context
 * @return {undefined}
 */
function redraw(context) {
  (context = document.getElementById("doodleCanvas").getContext("2d")).clearRect(0, 0, context.canvas.width, context.canvas.height);
  /** @type {string} */
  context.fillStyle = "black";
  context.fillRect(0, 0, context.canvas.width, context.canvas.height);
  context.fill();
  /** @type {string} */
  context.strokeStyle = "white";
  context.lineJoin = lineJoin[BRUSH_STYLE];
  context.lineWidth = BRUSH_SIZE;
  /** @type {number} */
  var i = 0;
  for (; i < clickX.length; i++) {
    context.beginPath();
    if (clickDrag[i] && i) {
      context.moveTo(clickX[i - 1], clickY[i - 1]);
    } else {
      context.moveTo(clickX[i] - 1, clickY[i]);
    }
    context.lineTo(clickX[i], clickY[i]);
    context.closePath();
    context.stroke();
  }
  /** @type {string} */
  context.filter = "blur(2px)";
  drawSmallCanvas(doodleCanvas);
}
/**
 * @param {?} canCreateDiscussions
 * @return {undefined}
 */
function drawSmallCanvas(canCreateDiscussions) {
  const ctx = document.getElementById("smallCanvas").getContext("2d");
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  /** @type {string} */
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  ctx.fill();
  ctx.drawImage(document.getElementById("doodleCanvas"), 0, 0);
}
/**
 * @param {!Array} items
 * @return {undefined}
 */
function compileModels(items) {
  console.log("compiling...");
  const optimizer = tf.train.adam();
  for (let i = 0; i < items.length; i++) {
    items[i].compile({
      optimizer : optimizer,
      loss : "categoricalCrossentropy",
      metrics : ["accuracy"]
    });
  }
  console.log("Neural Networks loaded...");
}
let data;
/**
 * @return {undefined}
 */
async function setup() {
  console.log("Load external scripts...");
  await loadTensorFlowLibraries();
  console.log("Scripts loaded...");
  models.push(await tf.loadLayersModel("uploads/michaelryan/500-model.json"));
  models.push(await tf.loadLayersModel("uploads/michaelryan/5000-model.json"));
  models.push(await tf.loadLayersModel("uploads/michaelryan/50000-model.json"));
  compileModels(models);
  data = new MnistData;
  await data.load();
  setupCanvas();
  console.log("Ready to go");
}
setup();