diff --git a/files.html b/files.html index ccb783e678a55cf425152c551169df63fa545b40..8bb411daf2fdcdac496928bc60dc1dd2f92b60df 100644 --- a/files.html +++ b/files.html @@ -132,6 +132,7 @@ <i> machines</i><br> <i> Epilog</i><br> <a href='./programs/machines/Epilog/cut%20png'>cut png</a><br> + <a href='./programs/machines/Epilog/cut%20svg'>cut svg</a><br> <i> G-code</i><br> <a href='./programs/machines/G-code/mill%202D%20png'>mill 2D png</a><br> <i> Roland</i><br> diff --git a/modules/convert/svg/image b/modules/convert/svg/image index 074437a6e3569e7df34585f624e9bf718967a924..10b359edec7ac96285f79780b82c14f4315778dd 100755 --- a/modules/convert/svg/image +++ b/modules/convert/svg/image @@ -25,7 +25,7 @@ var name = 'convert SVG image' // initialization // var init = function() { - mod.dpi.value = 300 + mod.dpi.value = 100 } // // inputs diff --git a/programs/index.html b/programs/index.html index 34341c4e60545369dd138a1ed421dca2541b830d..27139d7182b3d7744ca9818109eb5302b4e40ea2 100644 --- a/programs/index.html +++ b/programs/index.html @@ -17,6 +17,7 @@ <i>machines</i><br> <i> Epilog</i><br> <a href="javascript:handler('programs/machines/Epilog/cut%20png')">cut png</a><br> + <a href="javascript:handler('programs/machines/Epilog/cut%20svg')">cut svg</a><br> <i> G-code</i><br> <a href="javascript:handler('programs/machines/G-code/mill%202D%20png')">mill 2D png</a><br> <i> Roland</i><br> diff --git a/programs/machines/Epilog/cut svg b/programs/machines/Epilog/cut svg new file mode 100644 index 0000000000000000000000000000000000000000..9027b05c514c6f32b1e263cf5873cee21e57b81c --- /dev/null +++ b/programs/machines/Epilog/cut svg @@ -0,0 +1 @@ +{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"139","left":"1760","inputs":{},"outputs":{}},"0.07944144280928633":{"definition":"//\n// edge detect\n// green = interior, blue = exterior, red = boundary\n// assumes input is thresholded\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'edge detect'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n edge_detect()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:interior, blue:exterior, red:boundary'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// edge detect\n//\nfunction edge_detect() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({worker:worker.toString(),\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var i00,i0m,i0p,im0,ip0,imm,imp,ipm,ipp,row,col\n //\n // find edges - interior\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != i0m) || (i00 != im0) || (i00 != imm)\n || (i00 != imp) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // left and right edges\n //\n for (row = 1; row < (h-1); ++row) {\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm) \n || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // top and bottom edges\n //\n for (col = 1; col < (w-1); ++col) {\n row = h-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != imm) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != ipm) \n || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // corners\n //\n row = 0\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"505","left":"2290","inputs":{},"outputs":{}},"0.8903773266711255":{"definition":"//\n// orient edges\n// input is green:interior, blue:exterior, red:boundary\n// output is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'orient edges'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n var ctx = mod.display.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n orient_edges()\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // off-screen display canvas\n //\n var canvas = document.createElement('canvas')\n mod.display = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('red:north, dark red:south'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:east, dark green:west'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('blue:start, dark blue:stop'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.display,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// orient edges\n//\nfunction orient_edges() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n var disp = new Uint8ClampedArray(evt.data.display)\n var dispdata = new ImageData(disp,w,h)\n var ctx = mod.display.getContext(\"2d\")\n ctx.putImageData(dispdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var w = mod.canvas.width\n var h = mod.canvas.height\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,w,h)\n ctx.drawImage(mod.display,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var row,col\n var boundary = 0\n var interior = 1\n var exterior = 2\n var alpha = 3\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n //\n // orient body states\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && ((input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && ((input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n }\n }\n }\n //\n // orient edge states\n //\n for (col = 1; col < (w-1); ++col) {\n row = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n row = h-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n for (row = 1; row < (h-1); ++row) {\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n //\n // orient corner states (todo)\n //\n row = 0\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = 0\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n //\n // invert background for display\n //\n var display = new Uint8ClampedArray(h*w*4)\n var r,g,b,i\n for (row = 0; row < h; ++row) {\n for (col = 0; col < w; ++col) {\n r = output[(h-1-row)*w*4+col*4+0]\n g = output[(h-1-row)*w*4+col*4+1]\n b = output[(h-1-row)*w*4+col*4+2]\n i = r+g+b\n if (i != 0) { \n display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]\n display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]\n display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]\n display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]\n }\n else {\n display[(h-1-row)*w*4+col*4+0] = 255\n display[(h-1-row)*w*4+col*4+1] = 255\n display[(h-1-row)*w*4+col*4+2] = 255\n display[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // return output\n //\n self.postMessage({buffer:output.buffer,display:display.buffer},[output.buffer,display.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"588","left":"1814","inputs":{},"outputs":{}},"0.3135579179893032":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'offset'\n//\n// initialization\n//\nvar init = function() {\n mod.offset.value = ''\n }\n//\n// inputs\n//\nvar inputs = {\n distances:{type:'F32',\n event:function(evt){\n mod.distances = evt.detail\n var h = mod.distances.height\n var w = mod.distances.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.height = mod.distances.height \n ctx.canvas.width = mod.distances.width\n if (mod.offset.value != '')\n offset()\n }},\n offset:{type:'number',\n event:function(evt){\n mod.offset.value = evt.detail\n offset()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"16","left":"2249","inputs":{},"outputs":{}},"0.17844113591127098":{"definition":"//\n// cut raster\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'cut raster'\n//\n// initialization\n//\nvar init = function() {\n mod.in.value = 0.010\n mod.mm.value = 25.4*0.010\n }\n//\n// inputs\n//\nvar inputs = {\n imageInfo:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n }},\n path:{type:'array',\n event:function(evt){\n if (mod.label.nodeValue == 'calculating') {\n mod.label.nodeValue = 'calculate'\n mod.labelspan.style.fontWeight = 'normal'\n mod.path = evt.detail\n outputs.toolpath.event()\n }\n }}}\n//\n// outputs\n//\nvar outputs = {\n offset:{type:'number',\n event:function(){\n var pixels = 0.5*parseFloat(mod.in.value)*mod.dpi\n mods.output(mod,'offset',pixels)\n }},\n toolpath:{type:'object',\n event:function(){\n cmd = {}\n cmd.path = mod.path\n cmd.name = mod.name\n cmd.dpi = mod.dpi\n cmd.width = mod.width\n cmd.height = mod.height\n mods.output(mod,'toolpath',cmd)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n div.appendChild(document.createTextNode('tool diameter'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mm: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.in.value = parseFloat(mod.mm.value)/25.4\n })\n div.appendChild(input)\n mod.mm = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.mm.value = parseFloat(mod.in.value)*25.4\n })\n div.appendChild(input)\n mod.in = input\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n var span = document.createElement('span')\n var text = document.createTextNode('calculate')\n mod.label = text\n span.appendChild(text)\n mod.labelspan = span\n btn.appendChild(span)\n btn.addEventListener('click',function(){\n mod.label.nodeValue = 'calculating'\n mod.labelspan.style.fontWeight = 'bold'\n outputs.offset.event()\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n;\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"531","left":"934","inputs":{},"outputs":{}},"0.6488303557466412":{"definition":"//\n// image threshold\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'image threshold'\n//\n// initialization\n//\nvar init = function() {\n mod.threshold.value = 0.5\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n threshold_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // threshold value\n //\n div.appendChild(document.createTextNode('threshold (0-1): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n threshold_image()\n })\n div.appendChild(input)\n mod.threshold = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// threshold image\n//\nfunction threshold_image() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var t = parseFloat(mod.threshold.value)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,threshold:t,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var t = evt.data.threshold\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,i\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n r = buf[(h-1-row)*w*4+col*4+0] \n g = buf[(h-1-row)*w*4+col*4+1] \n b = buf[(h-1-row)*w*4+col*4+2] \n a = buf[(h-1-row)*w*4+col*4+3] \n i = (r+g+b)/(3*255)\n if (a == 0)\n val = 255\n else if (i > t)\n var val = 255\n else\n var val = 0\n buf[(h-1-row)*w*4+col*4+0] = val\n buf[(h-1-row)*w*4+col*4+1] = val\n buf[(h-1-row)*w*4+col*4+2] = val\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"54","left":"1280","inputs":{},"outputs":{}},"0.749132408760488":{"definition":"//\n// vectorize\n// input is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = 1\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n vectorize()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'array',\n event:function(){\n mods.output(mod,'path',mod.path)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br')) \n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // error value\n //\n div.appendChild(document.createTextNode('vector fit (pixels): '))\n //div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n vectorize()\n })\n div.appendChild(input)\n mod.error = input\n div.appendChild(document.createElement('br'))\n //\n // sort\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n input.checked = true\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// vectorize\n//\nfunction vectorize() {\n //\n // draw path\n //\n function draw_path(path) {\n window.URL.revokeObjectURL(url)\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute('viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment in path) {\n if (path[segment].length > 1) {\n if (xend != null) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = xend\n var y1 = yend\n var x2 = path[segment][0][0]\n var y2 = h-path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n svg.appendChild(g)\n }\n //\n // set up worker\n //\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n webworker.terminate()\n mod.path = evt.data.path\n draw_path(mod.path)\n outputs.path.event()\n })\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,sort:mod.sort.checked,\n error:parseFloat(mod.error.value),\n buffer:mod.input.data.buffer})\n }\n//\n// vectorize worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var sort = evt.data.sort\n var input = new Uint8ClampedArray(evt.data.buffer)\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n var path = []\n //\n // edge follower\n //\n function follow_edges(row,col) {\n if ((input[(h-1-row)*w*4+col*4+northsouth] != 0)\n || (input[(h-1-row)*w*4+col*4+eastwest] != 0)) {\n path[path.length] = [[col,row]]\n while (1) {\n if (input[(h-1-row)*w*4+col*4+northsouth] & north) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~north\n row += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+northsouth] & south) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~south\n row -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & east) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~east\n col += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & west) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~west\n col -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else\n break\n }\n }\n }\n //\n // follow boundary starts\n //\n for (var row = 1; row < (h-1); ++row) {\n col = 0\n follow_edges(row,col)\n col = w-1\n follow_edges(row,col)\n }\n for (var col = 1; col < (w-1); ++col) {\n row = 0\n follow_edges(row,col)\n row = h-1 \n follow_edges(row,col)\n }\n //\n // follow interior paths\n //\n for (var row = 1; row < (h-1); ++row) {\n for (var col = 1; col < (w-1); ++col) {\n follow_edges(row,col)\n }\n }\n //\n // vectorize path\n //\n var error = evt.data.error\n var vecpath = []\n for (var seg = 0; seg < path.length; ++seg) {\n var x0 = path[seg][0][0]\n var y0 = path[seg][0][1]\n vecpath[vecpath.length] = [[x0,y0]]\n var xsum = x0\n var ysum = y0\n var sum = 1\n for (var pt = 1; pt < path[seg].length; ++pt) {\n var xold = x\n var yold = y\n var x = path[seg][pt][0]\n var y = path[seg][pt][1]\n if (sum == 1) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n var xmean = xsum/sum\n var ymean = ysum/sum\n var dx = xmean-x0\n var dy = ymean-y0\n var d = Math.sqrt(dx*dx+dy*dy)\n var nx = dy/d\n var ny = -dx/d\n var l = Math.abs(nx*(x-x0)+ny*(y-y0))\n if (l < error) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [xold,yold]\n x0 = xold\n y0 = yold\n xsum = xold\n ysum = yold\n sum = 1\n }\n }\n if (pt == (path[seg].length-1)) {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [x,y]\n }\n }\n }\n //\n // sort path\n //\n if ((vecpath.length > 0) && (sort == true)) {\n var dmin = w*w+h*h\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][0]\n var d = x*x+y*y\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n var sortpath = [vecpath[segmin]]\n vecpath.splice(segmin,1)\n }\n while (vecpath.length > 0) {\n var dmin = w*w+h*h\n var x0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][0]\n var y0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][1]\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][1]\n var d = (x-x0)*(x-x0)+(y-y0)*(y-y0)\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n sortpath[sortpath.length] = vecpath[segmin]\n vecpath.splice(segmin,1)\n }\n }\n }\n else if ((vecpath.length > 0) && (sort == false))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"495","left":"1423","inputs":{},"outputs":{}},"0.4852429882871653":{"definition":"//\n// printer server module\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2017\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'WebSocket printer'\n//\n// initialization\n//\nvar init = function() {\n mod.address.value = '127.0.0.1'\n mod.port.value = 1234\n mod.printer.value = 'laser'\n mod.socket = null\n socket_open()\n }\n//\n// inputs\n//\nvar inputs = {\n file:{type:'',\n event:function(evt){\n if (evt.detail.type == 'file') {\n mod.job = evt.detail\n mod.label.nodeValue = 'send file to printer'\n mod.labelspan.style.fontWeight = 'bold'\n }\n }}}\n//\n// outputs\n//\nvar outputs = {\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // server\n //\n var a = document.createElement('a')\n a.href = './js/printserver.js'\n a.innerHTML = 'printerserver:'\n a.target = '_blank'\n div.appendChild(a)\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('address: '))\n input = document.createElement('input')\n input.type = 'text'\n input.size = 10\n div.appendChild(input)\n mod.address = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0port: '))\n input = document.createElement('input')\n input.type = 'text'\n input.size = 10\n div.appendChild(input)\n mod.port = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('\\u00a0\\u00a0status: '))\n input = document.createElement('input')\n input.type = 'text'\n input.size = 10\n div.appendChild(input)\n mod.status = input\n div.appendChild(document.createElement('br'))\n //\n // open/close\n //\n var btn = document.createElement('button')\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('open'))\n btn.addEventListener('click',function() {\n socket_open()\n })\n div.appendChild(btn)\n var btn = document.createElement('button')\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('close'))\n btn.addEventListener('click',function() {\n socket_close()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // printer\n //\n div.appendChild(document.createTextNode('printer:'))\n div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 10\n div.appendChild(input)\n mod.printer = input\n div.appendChild(document.createElement('br')) \n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n var span = document.createElement('span')\n var text = document.createTextNode('waiting for file')\n mod.label = text\n span.appendChild(text)\n mod.labelspan = span\n btn.appendChild(span)\n btn.addEventListener('click',function(){\n if (mod.socket == null) {\n mod.status.value = \"can't send, not open\"\n }\n else if (mod.label.nodeValue == 'send file to printer') {\n mod.job.printer = mod.printer.value\n socket_send(JSON.stringify(mod.job))\n mod.label.nodeValue = 'cancel'\n }\n else if (mod.label.nodeValue == 'cancel') {\n socket_send('cancel')\n }\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\nfunction socket_open() {\n var url = \"ws://\"+mod.address.value+':'+mod.port.value\n mod.socket = new WebSocket(url)\n mod.socket.onopen = function(event) {\n mod.status.value = \"socket opened\"\n }\n mod.socket.onerror = function(event) {\n mod.status.value = \"can not open socket\"\n mod.socket = null\n }\n mod.socket.onmessage = function(event) {\n mod.status.value = event.data\n if ((event.data == 'done') || (event.data == 'cancel')\n || (event.data.slice(0,5) == 'error')) {\n mod.label.nodeValue = 'waiting for file'\n mod.labelspan.style.fontWeight = 'normal'\n }\n }\n }\nfunction socket_close() {\n mod.socket.close()\n mod.status.value = \"socket closed\"\n mod.socket = null\n }\nfunction socket_send(msg) {\n if (mod.socket != null) {\n mod.status.value = \"send\"\n mod.socket.send(msg)\n }\n else {\n mod.status.value = \"can't send, not open\"\n }\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"607","left":"145","inputs":{},"outputs":{}},"0.030171947156024226":{"definition":"//\n// Epilog laser cutter\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2017\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'Epilog laser cutter'\n//\n// initialization\n//\nvar init = function() {\n mod.power.value = 25\n mod.speed.value = 75\n mod.rate.value = 100\n mod.xpos.value = 10\n mod.ypos.value = 10\n mod.topleft.checked = true\n }\n//\n// inputs\n//\nvar inputs = {\n toolpath:{type:'',\n event:function(evt){\n mod.name = evt.detail.name\n mod.path = evt.detail.path\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n make_path()\n }},\n settings:{type:'',\n event:function(evt){\n //set_values(evt.detail) // todo\n }}}\n//\n// outputs\n//\nvar outputs = {\n file:{type:'',\n event:function(str){\n obj = {}\n obj.type = 'file'\n obj.name = mod.name+'.epi'\n obj.contents = str\n mods.output(mod,'file',obj)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n div.appendChild(document.createTextNode('auto focus: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n div.appendChild(input)\n mod.focus = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('power (%): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.power = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('speed (%): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.speed = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('rate (pps): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.rate = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('position (mm):'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('x: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.xpos = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('y: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.ypos = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('alignment:'))\n div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'origin'\n input.id = mod.div.id+'topleft'\n div.appendChild(input)\n mod.topleft = input\n div.appendChild(document.createTextNode(' left \\u00A0\\u00A0 top \\u00A0\\u00A0 right '))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'origin'\n input.id = mod.div.id+'topright'\n div.appendChild(input)\n mod.topright = input\n div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'origin'\n input.id = mod.div.id+'botleft'\n div.appendChild(input)\n mod.botleft = input\n div.appendChild(document.createTextNode(' left bottom right '))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'origin'\n input.id = mod.div.id+'botright'\n div.appendChild(input)\n mod.botright = input\n }\n//\n// local functions\n//\nfunction make_path() {\n var dx = mod.width/mod.dpi\n var dy = mod.height/mod.dpi\n var nx = mod.width\n var ny = mod.height\n var scale = 600.0*dx/(nx-1) // 600 DPI\n var power = parseFloat(mod.power.value)\n var speed = parseFloat(mod.speed.value)\n var rate = parseFloat(mod.rate.value)\n var ox = parseFloat(mod.xpos.value)/25.4\n var oy = parseFloat(mod.ypos.value)/25.4\n if (mod.botleft.checked) {\n var xoffset = 600.0*ox\n var yoffset = 600.0*(oy-dy)\n }\n else if (mod.botright.checked) {\n var xoffset = 600.0*(ox-dx)\n var yoffset = 600.0*(oy-dy)\n }\n else if (mod.topleft.checked) {\n var xoffset = 600.0*ox\n var yoffset = 600.0*oy\n }\n else if (mod.topright.checked) {\n var xoffset = 600.0*(ox-dx)\n var yoffset = 600.0*oy\n }\n var str = \"\u001b%-12345X@PJL JOB NAME=\" + mod.name + \"\\r\\n\u001b\"\n str += \"E@PJL ENTER LANGUAGE=PCL\\r\\n\u001b\"\n if (mod.focus.checked)\n //\n // init with autofocus on\n //\n str += \"&y1A\u001b\"\n else\n // \n // init with autofocus off\n //\n str += \"&y0A\u001b\"\n str += \"&l0U\u001b&l0Z\u001b&u600D\u001b*p0X\u001b*p0Y\u001b*t600R\u001b*r0F\u001b&y50P\u001b&z50S\u001b*r6600T\u001b*r5100S\u001b*r1A\u001b*rC\u001b%1BIN;\"\n str += \"XR\"+rate+\";YP\"+power+\";ZS\"+speed+\";\\n\"\n //\n // loop over segments\n //\n for (var seg = 0; seg < mod.path.length; ++seg) {\n //\n // loop over points\n //\n x = xoffset+scale*mod.path[seg][0][0]\n y = yoffset+scale*(ny-mod.path[seg][0][1])\n if (x < 0) x = 0\n if (y < 0) y = 0\n str += \"PU\"+x.toFixed(0)+\",\"+y.toFixed(0)+\";\" // move up to start point\n for (var pt = 1; pt < mod.path[seg].length; ++pt) {\n x = xoffset+scale*mod.path[seg][pt][0]\n y = yoffset+scale*(ny-mod.path[seg][pt][1])\n if (x < 0) x = 0\n if (y < 0) y = 0\n str += \"PD\"+x.toFixed(0)+\",\"+y.toFixed(0)+\";\" // move down\n }\n str += \"\\n\"\n }\n str += \"\u001b%0B\u001b%1BPU\u001bE\u001b%-12345X@PJL EOJ \\r\\n\"\n //\n // end-of-file padding hack from Epilog print driver\n //\n for (var i = 0; i < 10000; ++i)\n str += \" \"\n outputs.file.event(str)\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"586","left":"450","inputs":{},"outputs":{}},"0.0006820493131942662":{"definition":"//\n// read SVG\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'read SVG'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n SVG:{type:'string',\n event:function(evt) {\n svg_load_handler({target:{result:evt.detail}})\n }}}\n//\n// outputs\n//\nvar outputs = {\n SVG:{type:'string',\n event:function(){\n mods.output(mod,'SVG',mod.str)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // file input control\n //\n var file = document.createElement('input')\n file.setAttribute('type','file')\n file.setAttribute('id',div.id+'file_input')\n file.style.position = 'absolute'\n file.style.left = 0\n file.style.top = 0\n file.style.width = 0\n file.style.height = 0\n file.style.opacity = 0\n file.addEventListener('change',function() {\n svg_read_handler()\n })\n div.appendChild(file)\n mod.file = file\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // file select button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('select SVG file'))\n btn.addEventListener('click',function(){\n var file = document.getElementById(div.id+'file_input')\n file.value = null\n file.click()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // info div\n //\n var info = document.createElement('div')\n info.setAttribute('id',div.id+'info')\n var text = document.createTextNode('file:')\n info.appendChild(text)\n mod.name = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('width:')\n info.appendChild(text)\n mod.width = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('height:')\n info.appendChild(text)\n mod.height = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('units per inch:')\n info.appendChild(text)\n mod.units = text\n div.appendChild(info)\n }\n//\n// local functions\n//\n// read handler\n//\nfunction svg_read_handler(event) {\n //\n // read as text\n //\n var file_reader = new FileReader()\n file_reader.onload = svg_load_handler\n var input_file = mod.file.files[0]\n var file_name = input_file.name\n mod.name.nodeValue = \"file: \"+file_name\n file_reader.readAsText(input_file)\n }\n//\n// load handler\n//\nfunction svg_load_handler(event) {\n mod.str = event.target.result\n //\n // parse size\n //\n var i = mod.str.indexOf(\"width\")\n if (i == -1) {\n mod.width.nodeValue = \"width: not found\"\n mod.height.nodeValue = \"height: not found\"\n }\n else {\n var i1 = mod.str.indexOf(\"\\\"\",i+1)\n var i2 = mod.str.indexOf(\"\\\"\",i1+1)\n var width = mod.str.substring(i1+1,i2)\n i = mod.str.indexOf(\"height\")\n i1 = mod.str.indexOf(\"\\\"\",i+1)\n i2 = mod.str.indexOf(\"\\\"\",i1+1)\n var height = mod.str.substring(i1+1,i2)\n ih = mod.str.indexOf(\"height\")\n if (width.indexOf(\"px\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 90\n }\n else if (width.indexOf(\"mm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 25.4\n }\n else if (width.indexOf(\"cm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 2.54\n }\n else if (width.indexOf(\"in\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 1\n }\n else {\n var units = 90\n }\n mod.width.nodeValue = \"width: \"+width\n mod.height.nodeValue = \"height: \"+height\n mod.units.nodeValue = \"units per inch: \"+units\n }\n //\n // display\n //\n var img = new Image()\n var src = \"data:image/svg+xml;base64,\"+window.btoa(mod.str)\n img.setAttribute(\"src\",src)\n img.onload = function() {\n if (img.width > img.height) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-img.height/img.width)\n var w = mod.canvas.width\n var h = mod.canvas.width*img.height/img.width\n }\n else {\n var x0 = mod.canvas.width*.5*(1-img.width/img.height)\n var y0 = 0\n var w = mod.canvas.height*img.width/img.height\n var h = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(img,x0,y0,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = img.width\n ctx.canvas.height = img.height \n ctx.drawImage(img,0,0)\n outputs.SVG.event()\n }\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"72","left":"208","inputs":{},"outputs":{}},"0.07794749880174023":{"definition":"//\n// convert SVG image\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'convert SVG image'\n//\n// initialization\n//\nvar init = function() {\n mod.dpi.value = 100\n }\n//\n// inputs\n//\nvar inputs = {\n SVG:{type:'string',\n event:function(evt){\n mod.svg = evt.detail\n get_size()\n draw_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}},\n imageInfo:{type:'object',\n event:function(){\n var obj = {}\n obj.name = \"SVG image\"\n obj.dpi = parseFloat(mod.dpi.value)\n obj.width = mod.img.width\n obj.height = mod.img.height\n mods.output(mod,'imageInfo',obj)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n //\n // dpi\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('dpi: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n draw_image()\n })\n div.appendChild(input)\n mod.dpi = input\n div.appendChild(document.createTextNode(' (enter)'))\n //\n // units\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('units: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n draw_image()\n })\n div.appendChild(input)\n mod.unitstext = input\n div.appendChild(document.createTextNode(' (enter)'))\n //\n // size\n //\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('image size:')\n div.appendChild(text)\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(pixels)')\n div.appendChild(text)\n mod.pixels = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(inches)')\n div.appendChild(text)\n mod.inches = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(mm)')\n div.appendChild(text)\n mod.mm = text\n }\n//\n// local functions\n//\n// get size\n//\nfunction get_size() {\n var i = mod.svg.indexOf(\"width\")\n if (i == -1) {\n var width = 1\n var height = 1\n var units = 90\n }\n else {\n var i1 = mod.svg.indexOf(\"\\\"\",i+1)\n var i2 = mod.svg.indexOf(\"\\\"\",i1+1)\n var width = mod.svg.substring(i1+1,i2)\n i = mod.svg.indexOf(\"height\")\n i1 = mod.svg.indexOf(\"\\\"\",i+1)\n i2 = mod.svg.indexOf(\"\\\"\",i1+1)\n var height = mod.svg.substring(i1+1,i2)\n ih = mod.svg.indexOf(\"height\")\n if (width.indexOf(\"px\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 90\n }\n else if (width.indexOf(\"mm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 25.4\n }\n else if (width.indexOf(\"cm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 2.54\n }\n else if (width.indexOf(\"in\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 1\n }\n else {\n var units = 90\n }\n }\n mod.width = parseFloat(width)\n mod.height = parseFloat(height)\n mod.units = units\n mod.unitstext.value = units\n }\n//\n// draw image\n//\nfunction draw_image() {\n var dpi = parseFloat(mod.dpi.value)\n var units = parseFloat(mod.unitstext.value)\n var width = parseInt(dpi*mod.width/units)\n var height = parseInt(dpi*mod.height/units)\n mod.pixels.nodeValue = width+' x '+height+\" (pixels)\"\n mod.inches.nodeValue = (width/dpi).toFixed(3)+' x '+(height/dpi).toFixed(3)+\" (inches)\"\n mod.mm.nodeValue = (25.4*width/dpi).toFixed(3)+' x '+(25.4*height/dpi).toFixed(3)+\" (mm)\"\n var src = \"data:image/svg+xml;base64,\"+window.btoa(mod.svg)\n var img = new Image()\n img.setAttribute(\"src\",src)\n img.onload = function() {\n if (width > height) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-height/width)\n var w = mod.canvas.width\n var h = mod.canvas.width*height/width\n }\n else {\n var x0 = mod.canvas.width*.5*(1-width/height)\n var y0 = 0\n var w = mod.canvas.height*width/height\n var h = mod.canvas.height\n }\n mod.img.width = width\n mod.img.height = height\n var ctx = mod.img.getContext(\"2d\")\n ctx.clearRect(0,0,width,height)\n ctx.drawImage(img,0,0,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,w,h)\n outputs.image.event()\n outputs.imageInfo.event()\n }\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"46","left":"680","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.17844113591127098\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.17844113591127098\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.17844113591127098\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.030171947156024226\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.030171947156024226\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4852429882871653\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.0006820493131942662\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVG\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07794749880174023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVG\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.07794749880174023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.07794749880174023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.17844113591127098\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}"]} \ No newline at end of file