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