From e5f0788da59f1ea799b5bfcb46fd93d974715c78 Mon Sep 17 00:00:00 2001
From: Jake Read <jake.read@cba.mit.edu>
Date: Wed, 18 Dec 2019 23:19:29 -0500
Subject: [PATCH] clean pathmaking, webcam->ouput looks great

---
 hunks/image/displayImageData.js       |  54 +-
 hunks/image/edgeDetect.js             | 918 +++++++++++++++++--------
 hunks/image/imgToPath2D.js            | 919 ++++++++++++++++++++++++++
 hunks/image/renderVectors.js          |   2 +-
 hunks/image/vectorize.js              |  12 +-
 hunks/interface/threejsPathDisplay.js |  98 +++
 hunks/interface/threejsPathStream.js  |   2 +-
 7 files changed, 1695 insertions(+), 310 deletions(-)
 create mode 100644 hunks/image/imgToPath2D.js
 create mode 100644 hunks/interface/threejsPathDisplay.js

diff --git a/hunks/image/displayImageData.js b/hunks/image/displayImageData.js
index b458d29..cddf0e4 100644
--- a/hunks/image/displayImageData.js
+++ b/hunks/image/displayImageData.js
@@ -21,50 +21,42 @@ export default function DisplayImageData() {
 
   let dom = this.document()
   let canvas = $('<canvas>').get(0)
+  let context = canvas.getContext('2d')
   canvas.height = 100
   canvas.width = 100
-  let context = canvas.getContext('2d')
   $(dom).append(canvas)
+  let vcanvas = $('<canvas>').get(0)
+  let vcontext = vcanvas.getContext('2d')
+  // vcanvas,
 
   let canvasUid, descriptionUid;
   let idealWidth = 395;
 
   this.init = () => {
     //
-  };
+  }
+
 
   this.loop = () => {
     if (imageIn.io()) {
-      let imageData = imageIn.get();
-      canvas.height = imageData.height
-      canvas.width = imageData.width
-      context.putImageData(imageData, 0, 0)
+      let img = imageIn.get();
+      vcanvas.height = img.height
+      vcanvas.width = img.width
+      vcontext.putImageData(img, 0, 0)
+      // and scale ... ?
+      let scale = idealWidth / img.width
+      canvas.width = img.width * scale
+      canvas.height = img.height * scale
+      //context.clearRect(0, 0, canvas.width, canvas.height)
+      context.scale(scale, scale)
+      context.drawImage(vcanvas, 0, 0)
       /*
-      old work, now easier.
-      next improvement: display to variable size canvas ... or / just do that w/ html handles ?
-      only trick: w/ resize handles, wouldn't be stored to state var, so ... 
-      // we can also get by searching through our local dom...
-      let canvas = $(this.dom)
-        .find("canvas")
-        .get(0);
-
-      let scale = idealWidth / imageData.width;
-      // scaling these is a pain, we could use a better way (maybe css transform just-one master canvas?)
-      let vCanvas = $("<canvas>").get(0);
-      vCanvas.width = imageData.width;
-      vCanvas.height = imageData.height;
-      vCanvas.getContext("2d").putImageData(imageData, 0, 0);
-      vCanvas.getContext("2d").scale(scale, scale);
-      // now we can write over,
-      canvas.width = imageData.width * scale;
-      canvas.height = imageData.height * scale;
-      // here's the underlying context we're drawing into:
-      let ctx = canvas.getContext("2d");
-      ctx.clearRect(0, 0, canvas.width, canvas.height);
-      // definitely want some cleaner ideas about canvases / drawing / sizes / etc
-      // probably it is in drawing real-size canvases, and scaling those with css.
-      ctx.scale(scale, scale);
-      ctx.drawImage(vCanvas, 0, 0);
+      createImageBitmap(img).then((image) => {
+        //context.clearRect(0, 0, canvas.width, canvas.height)
+        context.scale(scale, scale)
+        context.drawImage(image, 0, 0)
+        rendering = false
+      })
       */
     }
   };
diff --git a/hunks/image/edgeDetect.js b/hunks/image/edgeDetect.js
index e235f74..706a53d 100644
--- a/hunks/image/edgeDetect.js
+++ b/hunks/image/edgeDetect.js
@@ -1,7 +1,7 @@
 /*
 hunks/image/edgeDetect.js
 
-find edges 
+find edges
 
 Jake Read at the Center for Bits and Atoms with Neil Gershenfeld and Leo McElroy
 (c) Massachusetts Institute of Technology 2019
@@ -10,11 +10,6 @@ This work may be reproduced, modified, distributed, performed, and
 displayed for any purpose, but must acknowledge the squidworks and cuttlefish projects.
 Copyright is retained and must be preserved. The work is provided as is;
 no warranty is provided, and users accept all liability.
-*/
-/*
-
-hunk template
-
 */
 
 // these are ES6 modules
@@ -36,133 +31,35 @@ export default function edgeDetect() {
   let imageIn = new Input("ImageData", "image", this);
   this.inputs.push(imageIn);
 
-  // states
-
   // outputs
   let imageOut = new Output("ImageData", "image", this);
   this.outputs.push(imageOut);
 
-  // Helper Functions
-  const edgeDetectHelper = imageRGBA => {
-    var h = imageRGBA.height;
-    var w = imageRGBA.width;
-    var input = imageRGBA.data;
-    var output = new Uint8ClampedArray(h * w * 4);
-    var i00, i0m, i0p, im0, ip0, imm, imp, ipm, ipp, row, col;
-    //
-    // find edges - interior
-    //
-    for (row = 1; row < h - 1; ++row) {
-      for (col = 1; col < w - 1; ++col) {
-        i00 =
-          input[(h - 1 - row) * w * 4 + col * 4 + 0] +
-          input[(h - 1 - row) * w * 4 + col * 4 + 1] +
-          input[(h - 1 - row) * w * 4 + col * 4 + 2];
-        i0p =
-          input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
-          input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
-          input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
-        ip0 =
-          input[(h - 2 - row) * w * 4 + col * 4 + 0] +
-          input[(h - 2 - row) * w * 4 + col * 4 + 1] +
-          input[(h - 2 - row) * w * 4 + col * 4 + 2];
-        ipp =
-          input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
-          input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
-          input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
-        i0m =
-          input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
-          input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
-          input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
-        im0 =
-          input[(h - row) * w * 4 + col * 4 + 0] +
-          input[(h - row) * w * 4 + col * 4 + 1] +
-          input[(h - row) * w * 4 + col * 4 + 2];
-        imm =
-          input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
-          input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
-          input[(h - row) * w * 4 + (col - 1) * 4 + 2];
-        imp =
-          input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
-          input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
-          input[(h - row) * w * 4 + (col + 1) * 4 + 2];
-        ipm =
-          input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
-          input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
-          input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
-        if (
-          i00 != i0p ||
-          i00 != ip0 ||
-          i00 != ipp ||
-          i00 != i0m ||
-          i00 != im0 ||
-          i00 != imm ||
-          i00 != imp ||
-          i00 != ipm
-        ) {
-          output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
-          output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
-          output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
-          output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-        } else if (i00 == 0) {
-          output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
-          output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
-          output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
-          output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-        } else {
-          output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
-          output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
-          output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
-          output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-        }
-      }
+  // view
+  // this.dom = {}
+  this.init = () => {
+    //this.dom = $('<div>').get(0)
+  };
+
+  //loop
+  this.loop = () => {
+    if (imageIn.io() && !imageOut.io()) {
+      imageOut.put(edgeDetectHelper(imageIn.get()))
     }
-    //
-    // left and right edges
-    //
-    for (row = 1; row < h - 1; ++row) {
-      col = w - 1;
-      i00 =
-        input[(h - 1 - row) * w * 4 + col * 4 + 0] +
-        input[(h - 1 - row) * w * 4 + col * 4 + 1] +
-        input[(h - 1 - row) * w * 4 + col * 4 + 2];
-      i0m =
-        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
-        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
-        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
-      imm =
-        input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
-        input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
-        input[(h - row) * w * 4 + (col - 1) * 4 + 2];
-      ipm =
-        input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
-        input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
-        input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
-      im0 =
-        input[(h - row) * w * 4 + col * 4 + 0] +
-        input[(h - row) * w * 4 + col * 4 + 1] +
-        input[(h - row) * w * 4 + col * 4 + 2];
-      ip0 =
-        input[(h - 2 - row) * w * 4 + col * 4 + 0] +
-        input[(h - 2 - row) * w * 4 + col * 4 + 1] +
-        input[(h - 2 - row) * w * 4 + col * 4 + 2];
-      if (i00 != i0m || i00 != ip0 || i00 != ipm || i00 != im0 || i00 != imm) {
-        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
-        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-      } else if (i00 == 0) {
-        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
-        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-      } else {
-        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
-        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-      }
-      col = 0;
+  };
+}
+
+const edgeDetectHelper = (imageRGBA) => {
+  var h = imageRGBA.height;
+  var w = imageRGBA.width;
+  var input = imageRGBA.data;
+  var output = new Uint8ClampedArray(h * w * 4);
+  var i00, i0m, i0p, im0, ip0, imm, imp, ipm, ipp, row, col;
+  //
+  // find edges - interior
+  //
+  for (row = 1; row < h - 1; ++row) {
+    for (col = 1; col < w - 1; ++col) {
       i00 =
         input[(h - 1 - row) * w * 4 + col * 4 + 0] +
         input[(h - 1 - row) * w * 4 + col * 4 + 1] +
@@ -171,110 +68,44 @@ export default function edgeDetect() {
         input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
         input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
         input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
-      imp =
-        input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
-        input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
-        input[(h - row) * w * 4 + (col + 1) * 4 + 2];
-      ipp =
-        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
-        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
-        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
-      im0 =
-        input[(h - row) * w * 4 + col * 4 + 0] +
-        input[(h - row) * w * 4 + col * 4 + 1] +
-        input[(h - row) * w * 4 + col * 4 + 2];
       ip0 =
         input[(h - 2 - row) * w * 4 + col * 4 + 0] +
         input[(h - 2 - row) * w * 4 + col * 4 + 1] +
         input[(h - 2 - row) * w * 4 + col * 4 + 2];
-      if (i00 != i0p || i00 != ip0 || i00 != ipp || i00 != im0 || i00 != imp) {
-        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
-        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-      } else if (i00 == 0) {
-        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
-        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-      } else {
-        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
-        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-      }
-    }
-    //
-    // top and bottom edges
-    //
-    for (col = 1; col < w - 1; ++col) {
-      row = h - 1;
-      i00 =
-        input[(h - 1 - row) * w * 4 + col * 4 + 0] +
-        input[(h - 1 - row) * w * 4 + col * 4 + 1] +
-        input[(h - 1 - row) * w * 4 + col * 4 + 2];
+      ipp =
+        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
+        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
+        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
       i0m =
         input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
         input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
         input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
-      i0p =
-        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
-        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
-        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
-      imm =
-        input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
-        input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
-        input[(h - row) * w * 4 + (col - 1) * 4 + 2];
       im0 =
         input[(h - row) * w * 4 + col * 4 + 0] +
         input[(h - row) * w * 4 + col * 4 + 1] +
         input[(h - row) * w * 4 + col * 4 + 2];
+      imm =
+        input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
+        input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
+        input[(h - row) * w * 4 + (col - 1) * 4 + 2];
       imp =
         input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
         input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
         input[(h - row) * w * 4 + (col + 1) * 4 + 2];
-      if (i00 != i0m || i00 != i0p || i00 != imm || i00 != im0 || i00 != imp) {
-        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
-        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-      } else if (i00 == 0) {
-        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
-        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-      } else {
-        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
-        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
-        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
-      }
-      row = 0;
-      i00 =
-        input[(h - 1 - row) * w * 4 + col * 4 + 0] +
-        input[(h - 1 - row) * w * 4 + col * 4 + 1] +
-        input[(h - 1 - row) * w * 4 + col * 4 + 2];
-      i0m =
-        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
-        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
-        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
-      i0p =
-        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
-        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
-        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
       ipm =
         input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
         input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
         input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
-      ip0 =
-        input[(h - 2 - row) * w * 4 + col * 4 + 0] +
-        input[(h - 2 - row) * w * 4 + col * 4 + 1] +
-        input[(h - 2 - row) * w * 4 + col * 4 + 2];
-      ipp =
-        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
-        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
-        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
-      if (i00 != i0m || i00 != i0p || i00 != ipm || i00 != ip0 || i00 != ipp) {
+      if (
+        i00 != i0p ||
+        i00 != ip0 ||
+        i00 != ipp ||
+        i00 != i0m ||
+        i00 != im0 ||
+        i00 != imm ||
+        i00 != imp ||
+        i00 != ipm
+      ) {
         output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
         output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
         output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
@@ -291,28 +122,37 @@ export default function edgeDetect() {
         output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
       }
     }
-    //
-    // corners
-    //
-    row = 0;
-    col = 0;
+  }
+  //
+  // left and right edges
+  //
+  for (row = 1; row < h - 1; ++row) {
+    col = w - 1;
     i00 =
       input[(h - 1 - row) * w * 4 + col * 4 + 0] +
       input[(h - 1 - row) * w * 4 + col * 4 + 1] +
       input[(h - 1 - row) * w * 4 + col * 4 + 2];
-    i0p =
-      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
-      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
-      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+    i0m =
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+    imm =
+      input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - row) * w * 4 + (col - 1) * 4 + 2];
+    ipm =
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
+    im0 =
+      input[(h - row) * w * 4 + col * 4 + 0] +
+      input[(h - row) * w * 4 + col * 4 + 1] +
+      input[(h - row) * w * 4 + col * 4 + 2];
     ip0 =
       input[(h - 2 - row) * w * 4 + col * 4 + 0] +
       input[(h - 2 - row) * w * 4 + col * 4 + 1] +
       input[(h - 2 - row) * w * 4 + col * 4 + 2];
-    ipp =
-      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
-      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
-      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
-    if (i00 != i0p || i00 != ip0 || i00 != ipp) {
+    if (i00 != i0m || i00 != ip0 || i00 != ipm || i00 != im0 || i00 != imm) {
       output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
       output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
       output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
@@ -328,25 +168,32 @@ export default function edgeDetect() {
       output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
       output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
     }
-    row = 0;
-    col = w - 1;
+    col = 0;
     i00 =
       input[(h - 1 - row) * w * 4 + col * 4 + 0] +
       input[(h - 1 - row) * w * 4 + col * 4 + 1] +
       input[(h - 1 - row) * w * 4 + col * 4 + 2];
-    i0m =
-      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
-      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
-      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+    i0p =
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+    imp =
+      input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - row) * w * 4 + (col + 1) * 4 + 2];
+    ipp =
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
+    im0 =
+      input[(h - row) * w * 4 + col * 4 + 0] +
+      input[(h - row) * w * 4 + col * 4 + 1] +
+      input[(h - row) * w * 4 + col * 4 + 2];
     ip0 =
       input[(h - 2 - row) * w * 4 + col * 4 + 0] +
       input[(h - 2 - row) * w * 4 + col * 4 + 1] +
       input[(h - 2 - row) * w * 4 + col * 4 + 2];
-    ipm =
-      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
-      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
-      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
-    if (i00 != i0m || i00 != ip0 || i00 != ipm) {
+    if (i00 != i0p || i00 != ip0 || i00 != ipp || i00 != im0 || i00 != imp) {
       output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
       output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
       output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
@@ -362,16 +209,28 @@ export default function edgeDetect() {
       output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
       output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
     }
+  }
+  //
+  // top and bottom edges
+  //
+  for (col = 1; col < w - 1; ++col) {
     row = h - 1;
-    col = 0;
     i00 =
       input[(h - 1 - row) * w * 4 + col * 4 + 0] +
       input[(h - 1 - row) * w * 4 + col * 4 + 1] +
       input[(h - 1 - row) * w * 4 + col * 4 + 2];
+    i0m =
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
     i0p =
       input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
       input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
       input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+    imm =
+      input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - row) * w * 4 + (col - 1) * 4 + 2];
     im0 =
       input[(h - row) * w * 4 + col * 4 + 0] +
       input[(h - row) * w * 4 + col * 4 + 1] +
@@ -380,7 +239,7 @@ export default function edgeDetect() {
       input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
       input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
       input[(h - row) * w * 4 + (col + 1) * 4 + 2];
-    if (i00 != i0p || i00 != im0 || i00 != imp) {
+    if (i00 != i0m || i00 != i0p || i00 != imm || i00 != im0 || i00 != imp) {
       output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
       output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
       output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
@@ -396,8 +255,7 @@ export default function edgeDetect() {
       output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
       output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
     }
-    row = h - 1;
-    col = w - 1;
+    row = 0;
     i00 =
       input[(h - 1 - row) * w * 4 + col * 4 + 0] +
       input[(h - 1 - row) * w * 4 + col * 4 + 1] +
@@ -406,15 +264,23 @@ export default function edgeDetect() {
       input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
       input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
       input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
-    im0 =
-      input[(h - row) * w * 4 + col * 4 + 0] +
-      input[(h - row) * w * 4 + col * 4 + 1] +
-      input[(h - row) * w * 4 + col * 4 + 2];
-    imm =
-      input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
-      input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
-      input[(h - row) * w * 4 + (col - 1) * 4 + 2];
-    if (i00 != i0m || i00 != im0 || i00 != imm) {
+    i0p =
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+    ipm =
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
+    ip0 =
+      input[(h - 2 - row) * w * 4 + col * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + col * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + col * 4 + 2];
+    ipp =
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
+    if (i00 != i0m || i00 != i0p || i00 != ipm || i00 != ip0 || i00 != ipp) {
       output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
       output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
       output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
@@ -430,25 +296,545 @@ export default function edgeDetect() {
       output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
       output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
     }
+  }
+  //
+  // corners
+  //
+  row = 0;
+  col = 0;
+  i00 =
+    input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 2];
+  i0p =
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+  ip0 =
+    input[(h - 2 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 2 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 2 - row) * w * 4 + col * 4 + 2];
+  ipp =
+    input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
+    input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
+    input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
+  if (i00 != i0p || i00 != ip0 || i00 != ipp) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else if (i00 == 0) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  }
+  row = 0;
+  col = w - 1;
+  i00 =
+    input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 2];
+  i0m =
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+  ip0 =
+    input[(h - 2 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 2 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 2 - row) * w * 4 + col * 4 + 2];
+  ipm =
+    input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
+    input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
+    input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
+  if (i00 != i0m || i00 != ip0 || i00 != ipm) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else if (i00 == 0) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  }
+  row = h - 1;
+  col = 0;
+  i00 =
+    input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 2];
+  i0p =
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+  im0 =
+    input[(h - row) * w * 4 + col * 4 + 0] +
+    input[(h - row) * w * 4 + col * 4 + 1] +
+    input[(h - row) * w * 4 + col * 4 + 2];
+  imp =
+    input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
+    input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
+    input[(h - row) * w * 4 + (col + 1) * 4 + 2];
+  if (i00 != i0p || i00 != im0 || i00 != imp) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else if (i00 == 0) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  }
+  row = h - 1;
+  col = w - 1;
+  i00 =
+    input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 2];
+  i0m =
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+  im0 =
+    input[(h - row) * w * 4 + col * 4 + 0] +
+    input[(h - row) * w * 4 + col * 4 + 1] +
+    input[(h - row) * w * 4 + col * 4 + 2];
+  imm =
+    input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
+    input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
+    input[(h - row) * w * 4 + (col - 1) * 4 + 2];
+  if (i00 != i0m || i00 != im0 || i00 != imm) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else if (i00 == 0) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  }
 
-    const imgData = new ImageData(output, w, h);
+  const imgData = new ImageData(output, w, h);
 
-    return imgData;
-  };
-  // view
-  // this.dom = {}
-  this.init = () => {
-    //this.dom = $('<div>').get(0)
-  };
+  return imgData;
+};
 
-  /*
-    this.onload = () => {}
-    */
-
-  //loop
-  this.loop = () => {
-    if (imageIn.io() && !imageOut.io()) {
-      imageOut.put(edgeDetectHelper(imageIn.get()));
+const orientEdgesHelper = imageRGBA => {
+  var h = imageRGBA.height;
+  var w = imageRGBA.width;
+  var input = imageRGBA.data;
+  var output = new Uint8ClampedArray(h * w * 4);
+  var row, col;
+  var boundary = 0;
+  var interior = 1;
+  var exterior = 2;
+  var alpha = 3;
+  var northsouth = 0;
+  var north = 128;
+  var south = 64;
+  var eastwest = 1;
+  var east = 128;
+  var west = 64;
+  var startstop = 2;
+  var start = 128;
+  var stop = 64;
+  //
+  // orient body states
+  //
+  for (row = 1; row < h - 1; ++row) {
+    for (col = 1; col < w - 1; ++col) {
+      output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+      if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+        if (
+          input[(h - 1 - (row + 1)) * w * 4 + col * 4 + boundary] != 0 &&
+          (input[(h - 1 - row) * w * 4 + (col + 1) * 4 + interior] != 0 ||
+            input[(h - 1 - (row + 1)) * w * 4 + (col + 1) * 4 + interior] !=
+              0)
+        )
+          output[(h - 1 - row) * w * 4 + col * 4 + northsouth] |= north;
+        if (
+          input[(h - 1 - (row - 1)) * w * 4 + col * 4 + boundary] != 0 &&
+          (input[(h - 1 - row) * w * 4 + (col - 1) * 4 + interior] != 0 ||
+            input[(h - 1 - (row - 1)) * w * 4 + (col - 1) * 4 + interior] !=
+              0)
+        )
+          output[(h - 1 - row) * w * 4 + col * 4 + northsouth] |= south;
+        if (
+          input[(h - 1 - row) * w * 4 + (col + 1) * 4 + boundary] != 0 &&
+          (input[(h - 1 - (row - 1)) * w * 4 + col * 4 + interior] != 0 ||
+            input[(h - 1 - (row - 1)) * w * 4 + (col + 1) * 4 + interior] !=
+              0)
+        )
+          output[(h - 1 - row) * w * 4 + col * 4 + eastwest] |= east;
+        if (
+          input[(h - 1 - row) * w * 4 + (col - 1) * 4 + boundary] != 0 &&
+          (input[(h - 1 - (row + 1)) * w * 4 + col * 4 + interior] != 0 ||
+            input[(h - 1 - (row + 1)) * w * 4 + (col - 1) * 4 + interior] !=
+              0)
+        )
+          output[(h - 1 - row) * w * 4 + col * 4 + eastwest] |= west;
+      }
+    }
+  }
+  //
+  // orient edge states
+  //
+  for (col = 1; col < w - 1; ++col) {
+    row = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+    if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+      if (
+        input[(h - 1 - (row + 1)) * w * 4 + col * 4 + boundary] != 0 &&
+        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + interior] != 0
+      ) {
+        output[(h - 1 - row) * w * 4 + col * 4 + northsouth] |= north;
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= start;
+      }
+      if (input[(h - 1 - row) * w * 4 + (col - 1) * 4 + interior] != 0)
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= stop;
+    }
+    row = h - 1;
+    output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+    if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+      if (input[(h - 1 - row) * w * 4 + (col + 1) * 4 + interior] != 0)
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= stop;
+      if (
+        input[(h - 1 - (row - 1)) * w * 4 + col * 4 + boundary] != 0 &&
+        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + interior] != 0
+      ) {
+        output[(h - 1 - row) * w * 4 + col * 4 + northsouth] |= south;
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= start;
+      }
     }
+  }
+  for (row = 1; row < h - 1; ++row) {
+    col = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+    if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+      if (
+        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + boundary] != 0 &&
+        input[(h - 1 - (row - 1)) * w * 4 + col * 4 + interior] != 0
+      ) {
+        output[(h - 1 - row) * w * 4 + col * 4 + eastwest] |= east;
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= start;
+      }
+      if (input[(h - 1 - (row + 1)) * w * 4 + col * 4 + interior] != 0)
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= stop;
+    }
+    col = w - 1;
+    output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+    if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+      if (input[(h - 1 - (row - 1)) * w * 4 + col * 4 + interior] != 0)
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= stop;
+      if (
+        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + boundary] != 0 &&
+        input[(h - 1 - (row + 1)) * w * 4 + col * 4 + interior] != 0
+      ) {
+        output[(h - 1 - row) * w * 4 + col * 4 + eastwest] |= west;
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= start;
+      }
+    }
+  }
+  //
+  // orient corner states (todo)
+  //
+  row = 0;
+  col = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+  row = h - 1;
+  col = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+  row = 0;
+  col = w - 1;
+  output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+  row = h - 1;
+  col = w - 1;
+  output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+
+  // var display = new Uint8ClampedArray(h*w*4)
+  // var r,g,b,i
+  // for (row = 0; row < h; ++row) {
+  //   for (col = 0; col < w; ++col) {
+  //     r = output[(h-1-row)*w*4+col*4+0]
+  //     g = output[(h-1-row)*w*4+col*4+1]
+  //     b = output[(h-1-row)*w*4+col*4+2]
+  //     i = r+g+b
+  //     if (i != 0) {
+  //        display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]
+  //        display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]
+  //        display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]
+  //        display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]
+  //        }
+  //     else {
+  //        display[(h-1-row)*w*4+col*4+0] = 255
+  //        display[(h-1-row)*w*4+col*4+1] = 255
+  //        display[(h-1-row)*w*4+col*4+2] = 255
+  //        display[(h-1-row)*w*4+col*4+3] = 255
+  //        }
+  //     }
+  //  }
+
+  const imgData = new ImageData(output, w, h);
+
+  return imgData;
+};
+
+function vectorWorker() {
+  self.onmessage = function(e) {
+    const vectorizeHelper = (imageRGBA, vectorFit = 1, sort = true) => {
+      var h = imageRGBA.height;
+      var w = imageRGBA.width;
+      var input = imageRGBA.data;
+      var northsouth = 0;
+      var north = 128;
+      var south = 64;
+      var eastwest = 1;
+      var east = 128;
+      var west = 64;
+      var startstop = 2;
+      var start = 128;
+      var stop = 64;
+      var path = [];
+      //
+      // edge follower
+      //
+      function follow_edges(row, col) {
+        if (
+          input[(h - 1 - row) * w * 4 + col * 4 + northsouth] != 0 ||
+          input[(h - 1 - row) * w * 4 + col * 4 + eastwest] != 0
+        ) {
+          path[path.length] = [
+            [col, row]
+          ];
+          while (1) {
+            if (
+              input[(h - 1 - row) * w * 4 + col * 4 + northsouth] & north
+            ) {
+              input[(h - 1 - row) * w * 4 + col * 4 + northsouth] =
+                input[(h - 1 - row) * w * 4 + col * 4 + northsouth] &
+                ~north;
+              row += 1;
+              path[path.length - 1][path[path.length - 1].length] = [
+                col,
+                row
+              ];
+            } else if (
+              input[(h - 1 - row) * w * 4 + col * 4 + northsouth] & south
+            ) {
+              input[(h - 1 - row) * w * 4 + col * 4 + northsouth] =
+                input[(h - 1 - row) * w * 4 + col * 4 + northsouth] &
+                ~south;
+              row -= 1;
+              path[path.length - 1][path[path.length - 1].length] = [
+                col,
+                row
+              ];
+            } else if (
+              input[(h - 1 - row) * w * 4 + col * 4 + eastwest] & east
+            ) {
+              input[(h - 1 - row) * w * 4 + col * 4 + eastwest] =
+                input[(h - 1 - row) * w * 4 + col * 4 + eastwest] & ~east;
+              col += 1;
+              path[path.length - 1][path[path.length - 1].length] = [
+                col,
+                row
+              ];
+            } else if (
+              input[(h - 1 - row) * w * 4 + col * 4 + eastwest] & west
+            ) {
+              input[(h - 1 - row) * w * 4 + col * 4 + eastwest] =
+                input[(h - 1 - row) * w * 4 + col * 4 + eastwest] & ~west;
+              col -= 1;
+              path[path.length - 1][path[path.length - 1].length] = [
+                col,
+                row
+              ];
+            } else break;
+          }
+        }
+      }
+      //
+      // follow boundary starts
+      //
+      for (var row = 1; row < h - 1; ++row) {
+        col = 0;
+        follow_edges(row, col);
+        col = w - 1;
+        follow_edges(row, col);
+      }
+      for (var col = 1; col < w - 1; ++col) {
+        row = 0;
+        follow_edges(row, col);
+        row = h - 1;
+        follow_edges(row, col);
+      }
+      //
+      // follow interior paths
+      //
+      for (var row = 1; row < h - 1; ++row) {
+        for (var col = 1; col < w - 1; ++col) {
+          follow_edges(row, col);
+        }
+      }
+      //
+      // vectorize path
+      //
+      var error = vectorFit;
+      var vecpath = [];
+      for (var seg = 0; seg < path.length; ++seg) {
+        var x0 = path[seg][0][0];
+        var y0 = path[seg][0][1];
+        vecpath[vecpath.length] = [
+          [x0, y0]
+        ];
+        var xsum = x0;
+        var ysum = y0;
+        var sum = 1;
+        for (var pt = 1; pt < path[seg].length; ++pt) {
+          var xold = x;
+          var yold = y;
+          var x = path[seg][pt][0];
+          var y = path[seg][pt][1];
+          if (sum == 1) {
+            xsum += x;
+            ysum += y;
+            sum += 1;
+          } else {
+            var xmean = xsum / sum;
+            var ymean = ysum / sum;
+            var dx = xmean - x0;
+            var dy = ymean - y0;
+            var d = Math.sqrt(dx * dx + dy * dy);
+            var nx = dy / d;
+            var ny = -dx / d;
+            var l = Math.abs(nx * (x - x0) + ny * (y - y0));
+            if (l < error) {
+              xsum += x;
+              ysum += y;
+              sum += 1;
+            } else {
+              vecpath[vecpath.length - 1][
+                vecpath[vecpath.length - 1].length
+              ] = [xold, yold];
+              x0 = xold;
+              y0 = yold;
+              xsum = xold;
+              ysum = yold;
+              sum = 1;
+            }
+          }
+          if (pt == path[seg].length - 1) {
+            vecpath[vecpath.length - 1][
+              vecpath[vecpath.length - 1].length
+            ] = [x, y];
+          }
+        }
+      }
+      //
+      // sort path
+      //
+      if (vecpath.length > 1 && sort == true) {
+        var dmin = w * w + h * h;
+        var segmin = null;
+        for (var seg = 0; seg < vecpath.length; ++seg) {
+          var x = vecpath[seg][0][0];
+          var y = vecpath[seg][0][0];
+          var d = x * x + y * y;
+          if (d < dmin) {
+            dmin = d;
+            segmin = seg;
+          }
+        }
+        if (segmin != null) {
+          var sortpath = [vecpath[segmin]];
+          vecpath.splice(segmin, 1);
+        }
+        while (vecpath.length > 0) {
+          var dmin = w * w + h * h;
+          var x0 =
+            sortpath[sortpath.length - 1][
+              sortpath[sortpath.length - 1].length - 1
+            ][0];
+          var y0 =
+            sortpath[sortpath.length - 1][
+              sortpath[sortpath.length - 1].length - 1
+            ][1];
+          segmin = null;
+          for (var seg = 0; seg < vecpath.length; ++seg) {
+            var x = vecpath[seg][0][0];
+            var y = vecpath[seg][0][1];
+            var d = (x - x0) * (x - x0) + (y - y0) * (y - y0);
+            if (d < dmin) {
+              dmin = d;
+              segmin = seg;
+            }
+          }
+          if (segmin != null) {
+            sortpath[sortpath.length] = vecpath[segmin];
+            vecpath.splice(segmin, 1);
+          }
+        }
+      } else if (
+        (vecpath.length > 1 && sort == false) ||
+        vecpath.length == 1
+      )
+        sortpath = vecpath;
+      else sortpath = [];
+
+      return sortpath;
+    };
+
+    const newOut = vectorizeHelper(e.data);
+    self.postMessage(newOut);
   };
 }
diff --git a/hunks/image/imgToPath2D.js b/hunks/image/imgToPath2D.js
new file mode 100644
index 0000000..6206b0f
--- /dev/null
+++ b/hunks/image/imgToPath2D.js
@@ -0,0 +1,919 @@
+/*
+hunks/image/edgeDetect.js
+
+find edges
+
+Jake Read at the Center for Bits and Atoms with Neil Gershenfeld and Leo McElroy
+(c) Massachusetts Institute of Technology 2019
+
+This work may be reproduced, modified, distributed, performed, and
+displayed for any purpose, but must acknowledge the squidworks and cuttlefish projects.
+Copyright is retained and must be preserved. The work is provided as is;
+no warranty is provided, and users accept all liability.
+*/
+
+// these are ES6 modules
+import { Hunkify, Input, Output, State } from "../hunks.js";
+
+export default function imgToPath2D() {
+  Hunkify(this)
+
+  let imageIn = this.input("ImageData", "image")
+  let idealWidthInput = this.input("number", "width")
+
+  let thresholdOut = this.output("ImageData", "threshold")
+  let edgesOut = this.output("ImageData", "edges")
+  let pathOut = this.output("reference", "path")
+
+  let threshValue = this.state("number", "threshold", 0.5)
+  let idealWidth = this.state("number", "width", 220) // machine-space width,
+  let zUp = this.state('number', 'clearance', 10)
+  let zDown = this.state('number', 'depth', -5)
+
+  this.init = () => {}
+
+  // ok, we'll work on inputs, this will take some time,
+  // we'll store outputs, wait for them to clear
+  let working = false
+  let store = null
+
+  //loop
+  this.loop = () => {
+    if(idealWidthInput.io()){
+      idealWidth.set(idealWidthInput.get())
+    }
+    if (imageIn.io() && !working) {
+      working = true
+      let img = imageIn.get()
+      let threshold = thresholdRGBA(img, threshValue.value)
+      if(!thresholdOut.io()) thresholdOut.put(threshold)
+      let edges = edgeDetectHelper(threshold)
+      if(!edgesOut.io()) edgesOut.put(edges)
+      let oriented = orientEdgesHelper(edges)
+      // now the worker,
+      let blob = new Blob(["(" + vectorWorker.toString() + "())"])
+      let url = window.URL.createObjectURL(blob)
+      let worker = new Worker(url)
+      worker.onmessage = (e) => {
+        store = e.data
+        worker.terminate()
+        // now, this has done path-to-array in pixel space. we want those in
+        // to be related to the width of our image,
+        // and we want to add those jog features,
+        store = unfPath(e.data, img.width, idealWidth.value, zUp.value, zDown.value)
+        working = false
+      }
+      worker.postMessage(oriented)
+    }
+    if(!pathOut.io() && store != null){
+      pathOut.put(store)
+      store = null
+    }
+  };
+}
+
+// Helper Functions
+const thresholdRGBA = (imageRGBA, threshold) => {
+  const w = imageRGBA.width;
+  const h = imageRGBA.height;
+  const buf = imageRGBA.data;
+  const t = threshold;
+
+  let r, g, b, a, i;
+  for (var row = 0; row < h; ++row) {
+    for (var col = 0; col < w; ++col) {
+      r = buf[(h - 1 - row) * w * 4 + col * 4 + 0];
+      g = buf[(h - 1 - row) * w * 4 + col * 4 + 1];
+      b = buf[(h - 1 - row) * w * 4 + col * 4 + 2];
+      a = buf[(h - 1 - row) * w * 4 + col * 4 + 3];
+      i = (r + g + b) / (3 * 255);
+
+      let val;
+      if (a === 0) {
+        val = 255;
+      } else if (i > t) {
+        val = 255;
+      } else {
+        val = 0;
+      }
+
+      buf[(h - 1 - row) * w * 4 + col * 4 + 0] = val;
+      buf[(h - 1 - row) * w * 4 + col * 4 + 1] = val;
+      buf[(h - 1 - row) * w * 4 + col * 4 + 2] = val;
+      buf[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    }
+  }
+
+  const imgdata = new ImageData(buf, w, h);
+
+  return imgdata;
+};
+
+const edgeDetectHelper = (imageRGBA) => {
+  var h = imageRGBA.height;
+  var w = imageRGBA.width;
+  var input = imageRGBA.data;
+  var output = new Uint8ClampedArray(h * w * 4);
+  var i00, i0m, i0p, im0, ip0, imm, imp, ipm, ipp, row, col;
+  //
+  // find edges - interior
+  //
+  for (row = 1; row < h - 1; ++row) {
+    for (col = 1; col < w - 1; ++col) {
+      i00 =
+        input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+        input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+        input[(h - 1 - row) * w * 4 + col * 4 + 2];
+      i0p =
+        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+      ip0 =
+        input[(h - 2 - row) * w * 4 + col * 4 + 0] +
+        input[(h - 2 - row) * w * 4 + col * 4 + 1] +
+        input[(h - 2 - row) * w * 4 + col * 4 + 2];
+      ipp =
+        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
+        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
+        input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
+      i0m =
+        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+      im0 =
+        input[(h - row) * w * 4 + col * 4 + 0] +
+        input[(h - row) * w * 4 + col * 4 + 1] +
+        input[(h - row) * w * 4 + col * 4 + 2];
+      imm =
+        input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
+        input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
+        input[(h - row) * w * 4 + (col - 1) * 4 + 2];
+      imp =
+        input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
+        input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
+        input[(h - row) * w * 4 + (col + 1) * 4 + 2];
+      ipm =
+        input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
+        input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
+        input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
+      if (
+        i00 != i0p ||
+        i00 != ip0 ||
+        i00 != ipp ||
+        i00 != i0m ||
+        i00 != im0 ||
+        i00 != imm ||
+        i00 != imp ||
+        i00 != ipm
+      ) {
+        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+      } else if (i00 == 0) {
+        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+      } else {
+        output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+        output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+        output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+        output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+      }
+    }
+  }
+  //
+  // left and right edges
+  //
+  for (row = 1; row < h - 1; ++row) {
+    col = w - 1;
+    i00 =
+      input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + col * 4 + 2];
+    i0m =
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+    imm =
+      input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - row) * w * 4 + (col - 1) * 4 + 2];
+    ipm =
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
+    im0 =
+      input[(h - row) * w * 4 + col * 4 + 0] +
+      input[(h - row) * w * 4 + col * 4 + 1] +
+      input[(h - row) * w * 4 + col * 4 + 2];
+    ip0 =
+      input[(h - 2 - row) * w * 4 + col * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + col * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + col * 4 + 2];
+    if (i00 != i0m || i00 != ip0 || i00 != ipm || i00 != im0 || i00 != imm) {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    } else if (i00 == 0) {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    } else {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    }
+    col = 0;
+    i00 =
+      input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + col * 4 + 2];
+    i0p =
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+    imp =
+      input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - row) * w * 4 + (col + 1) * 4 + 2];
+    ipp =
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
+    im0 =
+      input[(h - row) * w * 4 + col * 4 + 0] +
+      input[(h - row) * w * 4 + col * 4 + 1] +
+      input[(h - row) * w * 4 + col * 4 + 2];
+    ip0 =
+      input[(h - 2 - row) * w * 4 + col * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + col * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + col * 4 + 2];
+    if (i00 != i0p || i00 != ip0 || i00 != ipp || i00 != im0 || i00 != imp) {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    } else if (i00 == 0) {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    } else {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    }
+  }
+  //
+  // top and bottom edges
+  //
+  for (col = 1; col < w - 1; ++col) {
+    row = h - 1;
+    i00 =
+      input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + col * 4 + 2];
+    i0m =
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+    i0p =
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+    imm =
+      input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - row) * w * 4 + (col - 1) * 4 + 2];
+    im0 =
+      input[(h - row) * w * 4 + col * 4 + 0] +
+      input[(h - row) * w * 4 + col * 4 + 1] +
+      input[(h - row) * w * 4 + col * 4 + 2];
+    imp =
+      input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - row) * w * 4 + (col + 1) * 4 + 2];
+    if (i00 != i0m || i00 != i0p || i00 != imm || i00 != im0 || i00 != imp) {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    } else if (i00 == 0) {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    } else {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    }
+    row = 0;
+    i00 =
+      input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + col * 4 + 2];
+    i0m =
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+    i0p =
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+    ipm =
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
+    ip0 =
+      input[(h - 2 - row) * w * 4 + col * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + col * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + col * 4 + 2];
+    ipp =
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
+      input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
+    if (i00 != i0m || i00 != i0p || i00 != ipm || i00 != ip0 || i00 != ipp) {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    } else if (i00 == 0) {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    } else {
+      output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+      output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+    }
+  }
+  //
+  // corners
+  //
+  row = 0;
+  col = 0;
+  i00 =
+    input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 2];
+  i0p =
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+  ip0 =
+    input[(h - 2 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 2 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 2 - row) * w * 4 + col * 4 + 2];
+  ipp =
+    input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 0] +
+    input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 1] +
+    input[(h - 2 - row) * w * 4 + (col + 1) * 4 + 2];
+  if (i00 != i0p || i00 != ip0 || i00 != ipp) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else if (i00 == 0) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  }
+  row = 0;
+  col = w - 1;
+  i00 =
+    input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 2];
+  i0m =
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+  ip0 =
+    input[(h - 2 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 2 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 2 - row) * w * 4 + col * 4 + 2];
+  ipm =
+    input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 0] +
+    input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 1] +
+    input[(h - 2 - row) * w * 4 + (col - 1) * 4 + 2];
+  if (i00 != i0m || i00 != ip0 || i00 != ipm) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else if (i00 == 0) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  }
+  row = h - 1;
+  col = 0;
+  i00 =
+    input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 2];
+  i0p =
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + (col + 1) * 4 + 2];
+  im0 =
+    input[(h - row) * w * 4 + col * 4 + 0] +
+    input[(h - row) * w * 4 + col * 4 + 1] +
+    input[(h - row) * w * 4 + col * 4 + 2];
+  imp =
+    input[(h - row) * w * 4 + (col + 1) * 4 + 0] +
+    input[(h - row) * w * 4 + (col + 1) * 4 + 1] +
+    input[(h - row) * w * 4 + (col + 1) * 4 + 2];
+  if (i00 != i0p || i00 != im0 || i00 != imp) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else if (i00 == 0) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  }
+  row = h - 1;
+  col = w - 1;
+  i00 =
+    input[(h - 1 - row) * w * 4 + col * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + col * 4 + 2];
+  i0m =
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 0] +
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 1] +
+    input[(h - 1 - row) * w * 4 + (col - 1) * 4 + 2];
+  im0 =
+    input[(h - row) * w * 4 + col * 4 + 0] +
+    input[(h - row) * w * 4 + col * 4 + 1] +
+    input[(h - row) * w * 4 + col * 4 + 2];
+  imm =
+    input[(h - row) * w * 4 + (col - 1) * 4 + 0] +
+    input[(h - row) * w * 4 + (col - 1) * 4 + 1] +
+    input[(h - row) * w * 4 + (col - 1) * 4 + 2];
+  if (i00 != i0m || i00 != im0 || i00 != imm) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else if (i00 == 0) {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  } else {
+    output[(h - 1 - row) * w * 4 + col * 4 + 0] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 1] = 255;
+    output[(h - 1 - row) * w * 4 + col * 4 + 2] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + 3] = 255;
+  }
+
+  const imgData = new ImageData(output, w, h);
+
+  return imgData;
+};
+
+const orientEdgesHelper = imageRGBA => {
+  var h = imageRGBA.height;
+  var w = imageRGBA.width;
+  var input = imageRGBA.data;
+  var output = new Uint8ClampedArray(h * w * 4);
+  var row, col;
+  var boundary = 0;
+  var interior = 1;
+  var exterior = 2;
+  var alpha = 3;
+  var northsouth = 0;
+  var north = 128;
+  var south = 64;
+  var eastwest = 1;
+  var east = 128;
+  var west = 64;
+  var startstop = 2;
+  var start = 128;
+  var stop = 64;
+  //
+  // orient body states
+  //
+  for (row = 1; row < h - 1; ++row) {
+    for (col = 1; col < w - 1; ++col) {
+      output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+      output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+      if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+        if (
+          input[(h - 1 - (row + 1)) * w * 4 + col * 4 + boundary] != 0 &&
+          (input[(h - 1 - row) * w * 4 + (col + 1) * 4 + interior] != 0 ||
+            input[(h - 1 - (row + 1)) * w * 4 + (col + 1) * 4 + interior] !=
+              0)
+        )
+          output[(h - 1 - row) * w * 4 + col * 4 + northsouth] |= north;
+        if (
+          input[(h - 1 - (row - 1)) * w * 4 + col * 4 + boundary] != 0 &&
+          (input[(h - 1 - row) * w * 4 + (col - 1) * 4 + interior] != 0 ||
+            input[(h - 1 - (row - 1)) * w * 4 + (col - 1) * 4 + interior] !=
+              0)
+        )
+          output[(h - 1 - row) * w * 4 + col * 4 + northsouth] |= south;
+        if (
+          input[(h - 1 - row) * w * 4 + (col + 1) * 4 + boundary] != 0 &&
+          (input[(h - 1 - (row - 1)) * w * 4 + col * 4 + interior] != 0 ||
+            input[(h - 1 - (row - 1)) * w * 4 + (col + 1) * 4 + interior] !=
+              0)
+        )
+          output[(h - 1 - row) * w * 4 + col * 4 + eastwest] |= east;
+        if (
+          input[(h - 1 - row) * w * 4 + (col - 1) * 4 + boundary] != 0 &&
+          (input[(h - 1 - (row + 1)) * w * 4 + col * 4 + interior] != 0 ||
+            input[(h - 1 - (row + 1)) * w * 4 + (col - 1) * 4 + interior] !=
+              0)
+        )
+          output[(h - 1 - row) * w * 4 + col * 4 + eastwest] |= west;
+      }
+    }
+  }
+  //
+  // orient edge states
+  //
+  for (col = 1; col < w - 1; ++col) {
+    row = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+    if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+      if (
+        input[(h - 1 - (row + 1)) * w * 4 + col * 4 + boundary] != 0 &&
+        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + interior] != 0
+      ) {
+        output[(h - 1 - row) * w * 4 + col * 4 + northsouth] |= north;
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= start;
+      }
+      if (input[(h - 1 - row) * w * 4 + (col - 1) * 4 + interior] != 0)
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= stop;
+    }
+    row = h - 1;
+    output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+    if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+      if (input[(h - 1 - row) * w * 4 + (col + 1) * 4 + interior] != 0)
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= stop;
+      if (
+        input[(h - 1 - (row - 1)) * w * 4 + col * 4 + boundary] != 0 &&
+        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + interior] != 0
+      ) {
+        output[(h - 1 - row) * w * 4 + col * 4 + northsouth] |= south;
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= start;
+      }
+    }
+  }
+  for (row = 1; row < h - 1; ++row) {
+    col = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+    if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+      if (
+        input[(h - 1 - row) * w * 4 + (col + 1) * 4 + boundary] != 0 &&
+        input[(h - 1 - (row - 1)) * w * 4 + col * 4 + interior] != 0
+      ) {
+        output[(h - 1 - row) * w * 4 + col * 4 + eastwest] |= east;
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= start;
+      }
+      if (input[(h - 1 - (row + 1)) * w * 4 + col * 4 + interior] != 0)
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= stop;
+    }
+    col = w - 1;
+    output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+    output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+    if (input[(h - 1 - row) * w * 4 + col * 4 + boundary] != 0) {
+      if (input[(h - 1 - (row - 1)) * w * 4 + col * 4 + interior] != 0)
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= stop;
+      if (
+        input[(h - 1 - row) * w * 4 + (col - 1) * 4 + boundary] != 0 &&
+        input[(h - 1 - (row + 1)) * w * 4 + col * 4 + interior] != 0
+      ) {
+        output[(h - 1 - row) * w * 4 + col * 4 + eastwest] |= west;
+        output[(h - 1 - row) * w * 4 + col * 4 + startstop] |= start;
+      }
+    }
+  }
+  //
+  // orient corner states (todo)
+  //
+  row = 0;
+  col = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+  row = h - 1;
+  col = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+  row = 0;
+  col = w - 1;
+  output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+  row = h - 1;
+  col = w - 1;
+  output[(h - 1 - row) * w * 4 + col * 4 + northsouth] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + eastwest] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + startstop] = 0;
+  output[(h - 1 - row) * w * 4 + col * 4 + alpha] = 255;
+
+  // var display = new Uint8ClampedArray(h*w*4)
+  // var r,g,b,i
+  // for (row = 0; row < h; ++row) {
+  //   for (col = 0; col < w; ++col) {
+  //     r = output[(h-1-row)*w*4+col*4+0]
+  //     g = output[(h-1-row)*w*4+col*4+1]
+  //     b = output[(h-1-row)*w*4+col*4+2]
+  //     i = r+g+b
+  //     if (i != 0) {
+  //        display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]
+  //        display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]
+  //        display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]
+  //        display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]
+  //        }
+  //     else {
+  //        display[(h-1-row)*w*4+col*4+0] = 255
+  //        display[(h-1-row)*w*4+col*4+1] = 255
+  //        display[(h-1-row)*w*4+col*4+2] = 255
+  //        display[(h-1-row)*w*4+col*4+3] = 255
+  //        }
+  //     }
+  //  }
+
+  const imgData = new ImageData(output, w, h);
+
+  return imgData;
+};
+
+function vectorWorker() {
+  self.onmessage = function(e) {
+    const vectorizeHelper = (imageRGBA, vectorFit = 1, sort = true) => {
+      var h = imageRGBA.height;
+      var w = imageRGBA.width;
+      var input = imageRGBA.data;
+      var northsouth = 0;
+      var north = 128;
+      var south = 64;
+      var eastwest = 1;
+      var east = 128;
+      var west = 64;
+      var startstop = 2;
+      var start = 128;
+      var stop = 64;
+      var path = [];
+      //
+      // edge follower
+      //
+      function follow_edges(row, col) {
+        if (
+          input[(h - 1 - row) * w * 4 + col * 4 + northsouth] != 0 ||
+          input[(h - 1 - row) * w * 4 + col * 4 + eastwest] != 0
+        ) {
+          path[path.length] = [
+            [col, row]
+          ];
+          while (1) {
+            if (
+              input[(h - 1 - row) * w * 4 + col * 4 + northsouth] & north
+            ) {
+              input[(h - 1 - row) * w * 4 + col * 4 + northsouth] =
+                input[(h - 1 - row) * w * 4 + col * 4 + northsouth] &
+                ~north;
+              row += 1;
+              path[path.length - 1][path[path.length - 1].length] = [
+                col,
+                row
+              ];
+            } else if (
+              input[(h - 1 - row) * w * 4 + col * 4 + northsouth] & south
+            ) {
+              input[(h - 1 - row) * w * 4 + col * 4 + northsouth] =
+                input[(h - 1 - row) * w * 4 + col * 4 + northsouth] &
+                ~south;
+              row -= 1;
+              path[path.length - 1][path[path.length - 1].length] = [
+                col,
+                row
+              ];
+            } else if (
+              input[(h - 1 - row) * w * 4 + col * 4 + eastwest] & east
+            ) {
+              input[(h - 1 - row) * w * 4 + col * 4 + eastwest] =
+                input[(h - 1 - row) * w * 4 + col * 4 + eastwest] & ~east;
+              col += 1;
+              path[path.length - 1][path[path.length - 1].length] = [
+                col,
+                row
+              ];
+            } else if (
+              input[(h - 1 - row) * w * 4 + col * 4 + eastwest] & west
+            ) {
+              input[(h - 1 - row) * w * 4 + col * 4 + eastwest] =
+                input[(h - 1 - row) * w * 4 + col * 4 + eastwest] & ~west;
+              col -= 1;
+              path[path.length - 1][path[path.length - 1].length] = [
+                col,
+                row
+              ];
+            } else break;
+          }
+        }
+      }
+      //
+      // follow boundary starts
+      //
+      for (var row = 1; row < h - 1; ++row) {
+        col = 0;
+        follow_edges(row, col);
+        col = w - 1;
+        follow_edges(row, col);
+      }
+      for (var col = 1; col < w - 1; ++col) {
+        row = 0;
+        follow_edges(row, col);
+        row = h - 1;
+        follow_edges(row, col);
+      }
+      //
+      // follow interior paths
+      //
+      for (var row = 1; row < h - 1; ++row) {
+        for (var col = 1; col < w - 1; ++col) {
+          follow_edges(row, col);
+        }
+      }
+      //
+      // vectorize path
+      //
+      var error = vectorFit;
+      var vecpath = [];
+      for (var seg = 0; seg < path.length; ++seg) {
+        var x0 = path[seg][0][0];
+        var y0 = path[seg][0][1];
+        vecpath[vecpath.length] = [
+          [x0, y0]
+        ];
+        var xsum = x0;
+        var ysum = y0;
+        var sum = 1;
+        for (var pt = 1; pt < path[seg].length; ++pt) {
+          var xold = x;
+          var yold = y;
+          var x = path[seg][pt][0];
+          var y = path[seg][pt][1];
+          if (sum == 1) {
+            xsum += x;
+            ysum += y;
+            sum += 1;
+          } else {
+            var xmean = xsum / sum;
+            var ymean = ysum / sum;
+            var dx = xmean - x0;
+            var dy = ymean - y0;
+            var d = Math.sqrt(dx * dx + dy * dy);
+            var nx = dy / d;
+            var ny = -dx / d;
+            var l = Math.abs(nx * (x - x0) + ny * (y - y0));
+            if (l < error) {
+              xsum += x;
+              ysum += y;
+              sum += 1;
+            } else {
+              vecpath[vecpath.length - 1][
+                vecpath[vecpath.length - 1].length
+              ] = [xold, yold];
+              x0 = xold;
+              y0 = yold;
+              xsum = xold;
+              ysum = yold;
+              sum = 1;
+            }
+          }
+          if (pt == path[seg].length - 1) {
+            vecpath[vecpath.length - 1][
+              vecpath[vecpath.length - 1].length
+            ] = [x, y];
+          }
+        }
+      }
+      //
+      // sort path
+      //
+      if (vecpath.length > 1 && sort == true) {
+        var dmin = w * w + h * h;
+        var segmin = null;
+        for (var seg = 0; seg < vecpath.length; ++seg) {
+          var x = vecpath[seg][0][0];
+          var y = vecpath[seg][0][0];
+          var d = x * x + y * y;
+          if (d < dmin) {
+            dmin = d;
+            segmin = seg;
+          }
+        }
+        if (segmin != null) {
+          var sortpath = [vecpath[segmin]];
+          vecpath.splice(segmin, 1);
+        }
+        while (vecpath.length > 0) {
+          var dmin = w * w + h * h;
+          var x0 =
+            sortpath[sortpath.length - 1][
+              sortpath[sortpath.length - 1].length - 1
+            ][0];
+          var y0 =
+            sortpath[sortpath.length - 1][
+              sortpath[sortpath.length - 1].length - 1
+            ][1];
+          segmin = null;
+          for (var seg = 0; seg < vecpath.length; ++seg) {
+            var x = vecpath[seg][0][0];
+            var y = vecpath[seg][0][1];
+            var d = (x - x0) * (x - x0) + (y - y0) * (y - y0);
+            if (d < dmin) {
+              dmin = d;
+              segmin = seg;
+            }
+          }
+          if (segmin != null) {
+            sortpath[sortpath.length] = vecpath[segmin];
+            vecpath.splice(segmin, 1);
+          }
+        }
+      } else if (
+        (vecpath.length > 1 && sort == false) ||
+        vecpath.length == 1
+      )
+        sortpath = vecpath;
+      else sortpath = [];
+
+      return sortpath;
+    };
+
+    const newOut = vectorizeHelper(e.data);
+    self.postMessage(newOut);
+  };
+}
+
+const unfPath = (path, pwidth, mmwidth, zu, zd) => {
+  // RIP oldboy
+  let unfp = []
+  // expansion,
+  let scale = mmwidth / pwidth
+  // flatten, adding z-moves
+  for(let leg of path){
+    // start each leg up top, above the first point,
+    unfp.push([scale * leg[0][0], scale * leg[0][1], zu])
+    for(let point of leg){
+      unfp.push([scale * point[0], scale * point[1], zd])
+    }
+    // and the lift, to tail
+    let last = leg[leg.length - 1]
+    unfp.push([scale * last[0], scale * last[1], zu])
+  }
+  return unfp
+}
diff --git a/hunks/image/renderVectors.js b/hunks/image/renderVectors.js
index 9ba1f5d..7189e26 100644
--- a/hunks/image/renderVectors.js
+++ b/hunks/image/renderVectors.js
@@ -1,7 +1,7 @@
 /*
 hunks/image/renderVectors.js
 
-display vectors 
+display vectors
 
 Jake Read at the Center for Bits and Atoms with Neil Gershenfeld and Leo McElroy
 (c) Massachusetts Institute of Technology 2019
diff --git a/hunks/image/vectorize.js b/hunks/image/vectorize.js
index abf29c3..43391a9 100644
--- a/hunks/image/vectorize.js
+++ b/hunks/image/vectorize.js
@@ -15,14 +15,6 @@ no warranty is provided, and users accept all liability.
 // these are ES6 modules
 import { Hunkify, Input, Output, State } from "../hunks.js";
 
-/*
-  import {
-    html,
-    svg,
-    render
-  } from 'https://unpkg.com/lit-html?module';
-  */
-
 export default function Vectorize() {
   // this fn attaches handles to our function-object,
   Hunkify(this);
@@ -31,8 +23,6 @@ export default function Vectorize() {
   let imageIn = new Input("ImageData", "image", this);
   this.inputs.push(imageIn);
 
-  // states
-
   // outputs
   let vectors = new Output("array", "Vectors", this);
   this.outputs.push(vectors);
@@ -264,7 +254,7 @@ export default function Vectorize() {
 
       worker.onmessage = e => {
         stash = e.data
-        haveData = true 
+        haveData = true
         worker.terminate();
         working = false
       };
diff --git a/hunks/interface/threejsPathDisplay.js b/hunks/interface/threejsPathDisplay.js
new file mode 100644
index 0000000..05e4b6e
--- /dev/null
+++ b/hunks/interface/threejsPathDisplay.js
@@ -0,0 +1,98 @@
+/*
+hunks/interface/threejs_ghost.js
+
+takes array for point, draws history of segements in tail
+a-la snake, but you can never win or loose
+
+Jake Read at the Center for Bits and Atoms
+(c) Massachusetts Institute of Technology 2019
+
+This work may be reproduced, modified, distributed, performed, and
+displayed for any purpose, but must acknowledge the squidworks and cuttlefish projects.
+Copyright is retained and must be preserved. The work is provided as is;
+no warranty is provided, and users accept all liability.
+*/
+
+import {
+  Hunkify,
+  Input,
+  Output,
+  State
+} from '../hunks.js'
+
+import * as THREE from '../../libs/three.module.js'
+import { OrbitControls } from '../../libs/three_orbitcontrols.module.js'
+// really hacking the import here... apologies, works though!
+import { MeshLine, MeshLineMaterial } from '../../libs/three_meshline.module.js'
+
+export default function ThreeGhosts() {
+  Hunkify(this)
+
+  let ptin = this.input('reference', 'point')
+
+  let lineWidth = this.state('number', 'line width', 5)
+
+  let dom = this.document()
+
+  this.init = () => {
+
+  }
+
+  let width = 1000
+  let height = 1000
+
+  let camera = new THREE.PerspectiveCamera(75, width / height, 1, 10000)
+  camera.up.set(0, 0, 1)
+  let scene = new THREE.Scene()
+
+  // fires when loaded into the dom,
+  this.init = () => {
+    scene.background = new THREE.Color(0xc8e6e2)
+
+    let renderer = new THREE.WebGLRenderer()
+    renderer.setSize(width, height)
+
+    dom.appendChild(renderer.domElement)
+    let axesHelper = new THREE.AxesHelper(10)
+    scene.add(axesHelper)
+
+    let controls = new OrbitControls(camera, this.dom)
+    controls.update()
+    camera.position.set(-100, -150, 400)
+    camera.lookAt(new THREE.Vector3(100, 100, 0))
+
+    let animate = function() {
+      requestAnimationFrame(animate)
+      //setTimeout(animate, 500)
+      controls.update()
+      renderer.render(scene, camera)
+    }
+    // kickstart
+    animate()
+  }
+
+  let last = {}
+
+  this.loop = () => {
+    if (ptin.io()) {
+      // local copy,
+      let lp = JSON.parse(JSON.stringify(ptin.get()))
+      let lw = lineWidth.value
+      // redraw top->bottom every time ?
+      let geometry = new THREE.Geometry()
+      for(let i = 0; i < lp.length; i ++){
+        geometry.vertices.push(new THREE.Vector3(lp[i][0], lp[i][1], lp[i][2]))
+      }
+      let line = new MeshLine() // first arg is pts, 2nd is width setting fn
+      line.setGeometry(geometry, function(p) { return lw })
+      let material = new MeshLineMaterial({
+        color: 0x000000,
+        resolution: new THREE.Vector2(width, height)
+      })
+      let mesh = new THREE.Mesh(line.geometry, material)
+      scene.remove(last)
+      scene.add(mesh)
+      last = mesh
+    }
+  }
+}
diff --git a/hunks/interface/threejsPathStream.js b/hunks/interface/threejsPathStream.js
index d4cfa3b..43e1001 100644
--- a/hunks/interface/threejsPathStream.js
+++ b/hunks/interface/threejsPathStream.js
@@ -25,7 +25,7 @@ import { OrbitControls } from '../../libs/three_orbitcontrols.module.js'
 // really hacking the import here... apologies, works though!
 import { MeshLine, MeshLineMaterial } from '../../libs/three_meshline.module.js'
 
-let numSegs = 100
+let numSegs = 100 
 
 export default function ThreeGhosts() {
   Hunkify(this)
-- 
GitLab