diff --git a/hunks/image/displayImageData.js b/hunks/image/displayImageData.js index b458d29fb4b1e43d00500792bb4109059d10e93e..cddf0e42e418e5baaa577bf7c039279466cbbe34 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 e235f748fcf3631165cac19e200886b801467d23..706a53d02b71e4a6dae1b7762b6390879a35b5ab 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 0000000000000000000000000000000000000000..6206b0f56f05dee0acdc0ae1dffa9aabb93570b0 --- /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 9ba1f5d93fb2e93ff1f638b0c712ddd0ea911ab9..7189e262e05f5351e5367489012164cb23a2c3c8 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 abf29c32f140ff160c2dd0af290cf633b3017e27..43391a92fee15fb1e2723b359cf765743725197b 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 0000000000000000000000000000000000000000..05e4b6ebe22c80bbddc9c68a149f42dc33a42708 --- /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 d4cfa3bed871bacafc34930271a3470a1f4dbbb3..43e1001f0d04ba7f2b007553c813081b628b4835 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)