{"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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"584.1762899924108","left":"2531.337440127956","filename":"undefined","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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"908.9565326487034","left":"3662.182771573178","filename":"undefined","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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1057.1155229683727","left":"4150.298016531761","filename":"undefined","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 = '13.779522440900001'\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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"739.8964389076857","left":"3126.4978680843415","filename":"undefined","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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"465.6927251214147","left":"2010.9537429563275","filename":"undefined","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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1459.7960658732918","left":"3844.490670999341","filename":"undefined","inputs":{},"outputs":{}},"0.4793941661670936":{"definition":"//\n// save file\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 = 'save file'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n file:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.contents = evt.detail.contents\n save_file()\n }}}\n//\n// outputs\n//\nvar outputs = {}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name:')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('size:')\n div.appendChild(text)\n mod.sizetext = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\nfunction save_file() {\n var a = document.createElement('a')\n a.setAttribute('href','data:text/plain;charset=utf-8,'+ \n encodeURIComponent(mod.contents))\n a.setAttribute('download',mod.name)\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n mod.nametext.nodeValue = 'name: '+mod.name\n mods.fit(mod.div)\n mod.sizetext.nodeValue = 'size: '+mod.contents.length\n mods.fit(mod.div)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1472.0146760881094","left":"3282.4049744294944","filename":"undefined","inputs":{},"outputs":{}},"0.4592074909554409":{"definition":"//\n// convert SVG image\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2018\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 = '500'\n }\n//\n// inputs\n//\nvar inputs = {\n SVG:{type:'string',\n event:function(evt){\n mod.svg = evt.detail\n get_size()\n load_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 // invert button\n //\n div.appendChild(document.createTextNode(' '))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('invert'))\n btn.addEventListener('click',function(){\n invert_image()\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 load_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 load_image()\n })\n div.appendChild(input)\n mod.unitstext = input\n div.appendChild(document.createTextNode(' (enter)'))\n //\n // fill\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('fill background: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.checked = true\n input.id = mod.div.id+'fill'\n div.appendChild(input)\n mod.fill= input\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// load image\n//\nfunction load_image() {\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 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 =\n width+' x '+height+\" (pixels)\"\n mod.inches.nodeValue =\n (width/dpi).toFixed(3)+' x '+(height/dpi).toFixed(3)+\" (inches)\"\n mod.mm.nodeValue =\n (25.4*width/dpi).toFixed(3)+' x '+(25.4*height/dpi).toFixed(3)+\" (mm)\"\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 if (mod.fill.checked)\n fill_image()\n else\n output_image()\n }\n }\n//\n// output_image\n//\nfunction output_image() {\n if (mod.img.width > mod.img.height) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-mod.img.height/mod.img.width)\n var w = mod.canvas.width\n var h = mod.canvas.width*mod.img.height/mod.img.width\n }\n else {\n var x0 = mod.canvas.width*.5*(1-mod.img.width/mod.img.height)\n var y0 = 0\n var w = mod.canvas.height*mod.img.width/mod.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(mod.img,x0,y0,w,h)\n outputs.image.event()\n outputs.imageInfo.event()\n }\n//\n// fill image\n//\nfunction fill_image() {\n var blob = new Blob(['('+fill_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 output_image()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var h = mod.img.height\n var w = mod.img.width\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,w,h)\n webworker.postMessage({\n height:img.height,width:img.width,buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction fill_worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n alpha = buf[(h-1-row)*w*4+col*4+3]/255\n buf[(h-1-row)*w*4+col*4+0] \n = (1-alpha)*255+alpha*buf[(h-1-row)*w*4+col*4+0] \n buf[(h-1-row)*w*4+col*4+1] \n = (1-alpha)*255+alpha*buf[(h-1-row)*w*4+col*4+1] \n buf[(h-1-row)*w*4+col*4+2] \n = (1-alpha)*255+alpha*buf[(h-1-row)*w*4+col*4+2] \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// invert image\n//\nfunction invert_image() {\n var blob = new Blob(['('+invert_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.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 h = mod.img.height\n var w = mod.img.width\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,w,h)\n webworker.postMessage({\n height:img.height,width:img.width,buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction invert_worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n buf[(h-1-row)*w*4+col*4+0] \n buf[(h-1-row)*w*4+col*4+0] \n = 255-buf[(h-1-row)*w*4+col*4+0] \n buf[(h-1-row)*w*4+col*4+1] \n = 255-buf[(h-1-row)*w*4+col*4+1] \n buf[(h-1-row)*w*4+col*4+2] \n = 255-buf[(h-1-row)*w*4+col*4+2] \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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"276.8397260756478","left":"786.6098567965535","filename":"undefined","inputs":{},"outputs":{}},"0.608045696421487":{"definition":"//\n// mill raster 2D\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2016\n// Modified by Francisco Sanchez Arroyo 31-Jan-2020\n// Modified by Sol Bekic 21-Jul-2020\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 //\n var mod = {}\n //\n // name\n //\n var name = 'mill raster 2D'\n //\n // initialization\n //\n var init = function() {\n mod.dia_in.value = 0.0156\n mod.dia_mm.value = 25.4 * parseFloat(mod.dia_in.value)\n mod.cut_in.value = 0.004\n mod.cut_mm.value = 25.4 * parseFloat(mod.cut_in.value)\n mod.max_in.value = 0.004\n mod.max_mm.value = 25.4 * parseFloat(mod.max_in.value)\n mod.number.value = 4\n mod.stepover.value = 0.5\n mod.merge.value = 1\n mod.sort.checked = true\n }\n //\n // inputs\n //\n var inputs = {\n imageInfo: {\n 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 var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.width\n ctx.canvas.height = mod.height\n }\n },\n path: {\n type: 'array',\n event: function(evt) {\n if (mod.label.nodeValue == 'calculating') {\n draw_path(evt.detail)\n accumulate_path(evt.detail)\n mod.offsetCount += 1\n if ((mod.offsetCount != parseInt(mod.number.value)) && (evt.detail.length > 0)) {\n mod.offset += parseFloat(mod.stepover.value)\n outputs.offset.event()\n } else {\n mod.label.nodeValue = 'calculate'\n mod.labelspan.style.fontWeight = 'normal'\n merge_path()\n clear_path()\n draw_path(mod.path)\n draw_connections()\n add_depth()\n outputs.toolpath.event()\n }\n }\n }\n },\n settings: {\n type: 'object',\n event: function(evt) {\n set_values(evt.detail)\n }\n }\n }\n //\n // outputs\n //\n var outputs = {\n diameter: {\n type: 'number',\n event: function() {\n mods.output(mod, 'diameter', Math.ceil(mod.dpi * mod.dia_in.value))\n }\n },\n offset: {\n type: 'number',\n event: function() {\n var pixels = mod.offset * parseFloat(mod.dia_in.value) * mod.dpi\n mods.output(mod, 'offset', pixels)\n }\n },\n toolpath: {\n 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 cmd.depth = mod.depth\n mods.output(mod, 'toolpath', cmd)\n }\n }\n }\n //\n // interface\n //\n var interface = function(div) {\n mod.div = div\n //\n // tool diameter\n //\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.dia_in.value = parseFloat(mod.dia_mm.value) / 25.4\n })\n div.appendChild(input)\n mod.dia_mm = input\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.dia_mm.value = parseFloat(mod.dia_in.value) * 25.4\n })\n div.appendChild(input)\n mod.dia_in = input\n div.appendChild(document.createElement('br'))\n //\n // cut depth\n //\n div.appendChild(document.createTextNode('cut depth'))\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.cut_in.value = parseFloat(mod.cut_mm.value) / 25.4\n })\n div.appendChild(input)\n mod.cut_mm = input\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.cut_mm.value = parseFloat(mod.cut_in.value) * 25.4\n })\n div.appendChild(input)\n mod.cut_in = input\n div.appendChild(document.createElement('br'))\n //\n // max depth\n //\n div.appendChild(document.createTextNode('max depth'))\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.max_in.value = parseFloat(mod.max_mm.value) / 25.4\n })\n div.appendChild(input)\n mod.max_mm = input\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.max_mm.value = parseFloat(mod.max_in.value) * 25.4\n })\n div.appendChild(input)\n mod.max_in = input\n div.appendChild(document.createElement('br'))\n //\n // offset number\n //\n div.appendChild(document.createTextNode('offset number: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.number = input\n div.appendChild(document.createTextNode(' (0 = fill)'))\n div.appendChild(document.createElement('br'))\n //\n // offset stepover\n //\n div.appendChild(document.createTextNode('offset stepover: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.stepover = input\n div.appendChild(document.createTextNode(' (1 = diameter)'))\n div.appendChild(document.createElement('br'))\n //\n // direction\n //\n div.appendChild(document.createTextNode('direction: '))\n div.appendChild(document.createTextNode('climb'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id + 'direction'\n input.id = mod.div.id + 'climb'\n input.checked = true\n div.appendChild(input)\n mod.climb = input\n div.appendChild(document.createTextNode(' conventional'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id + 'direction'\n input.id = mod.div.id + 'conventional'\n div.appendChild(input)\n mod.conventional = input\n div.appendChild(document.createElement('br'))\n //\n // path merge\n //\n div.appendChild(document.createTextNode('path merge: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.merge = input\n div.appendChild(document.createTextNode(' (1 = diameter)'))\n div.appendChild(document.createElement('br'))\n //\n // path order\n //\n div.appendChild(document.createTextNode('path order: '))\n div.appendChild(document.createTextNode('forward'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id + 'order'\n input.id = mod.div.id + 'forward'\n input.checked = true\n div.appendChild(input)\n mod.forward = input\n div.appendChild(document.createTextNode(' reverse'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id + 'order'\n input.id = mod.div.id + 'reverse'\n div.appendChild(input)\n mod.reverse = input\n div.appendChild(document.createElement('br'))\n //\n // sort distance\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 div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // calculate\n //\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 mod.offset = 0.5\n mod.offsetCount = 0\n mod.path = []\n clear_path()\n outputs.diameter.event()\n outputs.offset.event()\n })\n div.appendChild(btn)\n div.appendChild(document.createTextNode(' '))\n //\n // view\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 div.appendChild(document.createElement('br'))\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 //\n // local functions\n //\n // set_values\n //\n function set_values(settings) {\n for (var s in settings) {\n switch (s) {\n case 'tool diameter (in)':\n mod.dia_in.value = settings[s]\n mod.dia_mm.value = parseFloat(mod.dia_in.value) * 25.4\n break\n case 'cut depth (in)':\n mod.cut_in.value = settings[s]\n mod.cut_mm.value = parseFloat(mod.cut_in.value) * 25.4\n break\n case 'max depth (in)':\n mod.max_in.value = settings[s]\n mod.max_mm.value = parseFloat(mod.max_in.value) * 25.4\n break\n case 'tool diameter (mm)':\n mod.dia_mm.value = settings[s]\n mod.dia_in.value = parseFloat(mod.dia_mm.value) / 25.4\n break\n case 'cut depth (mm)':\n mod.cut_mm.value = settings[s]\n mod.cut_in.value = parseFloat(mod.cut_mm.value) / 25.4\n break\n case 'max depth (mm)':\n mod.max_mm.value = settings[s]\n mod.max_in.value = parseFloat(mod.max_mm.value) / 25.4\n break\n case 'offset number':\n mod.number.value = settings[s]\n case 'overlap':\n case 'offset stepover':\n mod.stepover.value = settings[s]\n break\n }\n }\n }\n //\n // clear_path\n //\n function clear_path() {\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 svg.appendChild(g)\n }\n //\n // accumulate_path\n // todo: replace inefficient insertion sort\n // todo: move sort out of main thread\n //\n function accumulate_path(path) {\n var forward = mod.forward.checked\n var conventional = mod.conventional.checked\n var sort = mod.sort.checked\n for (var segnew = 0; segnew < path.length; ++segnew) {\n if (conventional)\n path[segnew].reverse()\n if (mod.path.length == 0)\n mod.path.splice(0, 0, path[segnew])\n else if (sort) {\n var xnew = path[segnew][0][0]\n var ynew = path[segnew][0][1]\n var dmin = Number.MAX_VALUE\n var segmin = -1\n for (var segold = 0; segold < mod.path.length; ++segold) {\n var xold = mod.path[segold][0][0]\n var yold = mod.path[segold][0][1]\n var dx = xnew - xold\n var dy = ynew - yold\n var d = Math.sqrt(dx * dx + dy * dy)\n if (d < dmin) {\n dmin = d\n segmin = segold\n }\n }\n if (forward)\n mod.path.splice(segmin + 1, 0, path[segnew])\n else\n mod.path.splice(segmin, 0, path[segnew])\n } else {\n if (forward)\n mod.path.splice(mod.path.length, 0, path[segnew])\n else\n mod.path.splice(0, 0, path[segnew])\n }\n }\n }\n //\n // merge_path\n //\n function merge_path() {\n var dmerge = mod.dpi * parseFloat(mod.merge.value) * parseFloat(mod.dia_in.value)\n var seg = 0\n while (seg < (mod.path.length - 1)) {\n var xold = mod.path[seg][mod.path[seg].length - 1][0]\n var yold = mod.path[seg][mod.path[seg].length - 1][1]\n var xnew = mod.path[seg + 1][0][0]\n var ynew = mod.path[seg + 1][0][1]\n var dx = xnew - xold\n var dy = ynew - yold\n var d = Math.sqrt(dx * dx + dy * dy)\n if (d < dmerge)\n mod.path.splice(seg, 2, mod.path[seg].concat(mod.path[seg + 1]))\n else\n seg += 1\n }\n }\n //\n // add_depth\n //\n function add_depth() {\n var cut = parseFloat(mod.cut_in.value)\n var max = parseFloat(mod.max_in.value)\n var newpath = []\n for (var seg = 0; seg < mod.path.length; ++seg) {\n var depth = cut\n if ((mod.path[seg][0][0] == mod.path[seg][mod.path[seg].length - 1][0]) &&\n (mod.path[seg][0][0] == mod.path[seg][mod.path[seg].length - 1][0])) {\n var newseg = []\n while (depth <= max) {\n var idepth = -Math.round(mod.dpi * depth)\n for (var pt = 0; pt < mod.path[seg].length; ++pt) {\n var point = mod.path[seg][pt].concat(idepth)\n newseg.splice(newseg.length, 0, point)\n }\n if (depth == max)\n break\n depth += cut\n if (depth > max)\n depth = max\n }\n newpath.splice(newpath.length, 0, newseg)\n } else {\n var newseg = []\n while (depth <= max) {\n var idepth = -Math.round(mod.dpi * depth)\n for (var pt = 0; pt < mod.path[seg].length; ++pt) {\n var point = mod.path[seg][pt].concat(idepth)\n newseg.splice(newseg.length, 0, point)\n }\n newpath.splice(newpath.length, 0, newseg)\n newseg = []\n if (depth == max)\n break\n depth += cut\n if (depth > max)\n depth = max\n }\n }\n }\n mod.path = newpath\n mod.depth = Math.round(parseFloat(mod.max_in.value) * mod.dpi)\n }\n //\n // draw_path\n //\n function draw_path(path) {\n var g = document.getElementById(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 = 0; segment < path.length; ++segment) {\n if (path[segment].length > 1) {\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 }\n //\n // draw_connections\n //\n function draw_connections() {\n var g = document.getElementById(mod.div.id + 'g')\n var h = mod.img.height\n var w = mod.img.width\n //\n // loop over segments\n //\n for (var segment = 1; segment < mod.path.length; ++segment) {\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 = mod.path[segment - 1][mod.path[segment - 1].length - 1][0]\n var y1 = h - mod.path[segment - 1][mod.path[segment - 1].length - 1][1] - 1\n var x2 = mod.path[segment][0][0]\n var y2 = h - mod.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 //\n // return values\n //\n return ({\n name: name,\n init: init,\n inputs: inputs,\n outputs: outputs,\n interface: interface\n })\n}())\n","top":"900.7594989741781","left":"658.9399050658019","filename":"modules/15%20processes/20%20mill%20raster/2D.js","inputs":{},"outputs":{}},"0.6248369051648597":{"definition":"//\n// view toolpath\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// Modified by Francisco Sanchez Arroyo 27-March-2022 (r139)\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// todo:\n// erase and update new path\n// show depth info\n// show size\n// calculate camera far\n//\n// Fran notes:\n// From r125 you can no longer use the geometry contructor to create lines,\n// now you must use the buffer geometry constructor. The process is slightly\n// different.\n//\n//\n// closure\n//\n(function(){\n //\n // module globals\n //\n var mod = {}\n //\n // name\n //\n var name = 'view toolpath'\n //\n // initialization\n //\n var init = function() {\n }\n //\n // inputs\n //\n var inputs = {\n toolpath:{type:'object',\n event:function(evt){\n mod.path = evt.detail.path\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 mod.depth = evt.detail.depth\n show_path_info()\n show_path()\n outputs.toolpath.event()\n }}}\n //\n // outputs\n //\n var outputs = {\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 //\n var interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name: ')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(mm)')\n div.appendChild(text)\n mod.mmtext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(in)')\n div.appendChild(text)\n mod.intext = text\n //\n // view\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 var span = document.createElement('span')\n var text = document.createTextNode('view')\n span.appendChild(text)\n btn.appendChild(span)\n btn.addEventListener('click',function(){\n open_view_window()\n })\n div.appendChild(btn)\n }\n //\n // local functions\n //\n // show_path_info\n //\n function show_path_info() {\n mod.nametext.nodeValue = 'name: '+mod.name\n var width = (25.4*mod.width/mod.dpi).toFixed(3)\n var height = (25.4*mod.height/mod.dpi).toFixed(3)\n var depth = (25.4*mod.depth/mod.dpi).toFixed(3)\n if (mod.depth == undefined)\n mod.mmtext.nodeValue = width+' x '+height+' (mm)'\n else\n mod.mmtext.nodeValue = width+' x '+height+' x '+depth+' (mm)'\n var width = (mod.width/mod.dpi).toFixed(3)\n var height = (mod.height/mod.dpi).toFixed(3)\n var depth = (mod.depth/mod.dpi).toFixed(3)\n if (mod.depth == undefined)\n mod.intext.nodeValue = width+' x '+height+' (in)'\n else\n mod.intext.nodeValue = width+' x '+height+' x '+depth+' (in)'\n mods.fit(mod.div)\n }\n //\n // show_path\n //\n function show_path() {\n var scene = mod.scene\n var camera = mod.camera\n var renderer = mod.renderer\n //\n // check if view window open\n //\n if (mod.win == undefined) {\n open_view_window()\n return\n }\n //\n // check for path\n //\n if (mod.path == undefined)\n return\n //\n // clear scene, leave camera\n //\n var length = scene.children.length\n for (var c = (length-1); c > 1; --c) {\n scene.remove(scene.children[c])\n }\n //\n // fit camera\n //\n mod.thetaxy = 0\n mod.thetaz = 0\n mod.r = mod.height/2\n mod.x0 = mod.width/2\n mod.y0 = mod.height/2\n camera.position.set(mod.x0,mod.y0,mod.r)\n camera.up = new THREE.Vector3(0,1,0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n //\n // draw segments\n //\n var arrow_size = 1+mod.width/200\n var path = mod.path\n for (var segment = 0; segment < path.length; ++segment) {\n if (segment > 0)\n add_arrow(path[segment-1][path[segment-1].length-1],path[segment][0],0xff0000,arrow_size) \n for (var point = 1; point < path[segment].length; ++point) {\n add_arrow(path[segment][point-1],path[segment][point],0x0000ff,arrow_size)\n }\n }\n //\n // add axes\n //\n var length = mod.height/10\n add_arrow([0,0,0],[length,0,0],0xff0000,arrow_size)\n add_arrow([0,0,0],[0,length,0],0x00ff00,arrow_size)\n add_arrow([0,0,0],[0,0,length],0x0000ff,arrow_size)\n //\n // render\n //\n update()\n //\n // add_arrow\n //\n function add_arrow(start,stop,color,size) {\n var origin = new THREE.Vector3().fromArray(start)\n if (mod.depth == undefined)\n origin.z = 0\n var end = new THREE.Vector3().fromArray(stop)\n if (mod.depth == undefined)\n end.z = 0\n var length = new THREE.Vector3().subVectors(end,origin).length()\n if (length <= size) {\n add_line(origin,end,color)\n //length = 1.1*size\n return\n }\n var direction = new THREE.Vector3().subVectors(end,origin).normalize()\n var arrow = new THREE.ArrowHelper(direction,origin,length,color,size,size)\n scene.add(arrow)\n }\n //\n // add_line\n //\n function add_line(start,stop,colorhex) {\n var vertices = []\n vertices.push(start,stop)\n var geometry = new THREE.BufferGeometry().setFromPoints(vertices)\n var material = new THREE.LineBasicMaterial({color:colorhex})\n var line = new THREE.Line(geometry,material)\n scene.add(line)\n }\n //\n // update\n //\n function update() {\n renderer.render(scene,camera)\n }\n }\n //\n // open_view_window\n //\n function open_view_window() {\n //\n // globals\n //\n var container,scene,camera,renderer,win,controls\n //\n // open the window\n //\n open_window()\n //\n // open_window\n //\n function open_window() {\n //\n // open window\n //\n win = window.open('')\n mod.win = win\n //\n // load three.js\n //\n var script = document.createElement('script')\n script.type = 'text/javascript'\n script.onload = init_window\n script.src = 'js/three.js/three.min.js'\n mod.div.appendChild(script)\n }\n //\n // init_window\n //\n function init_window() {\n //\n // close button\n //\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 mod.win = undefined\n })\n win.document.body.appendChild(btn)\n //\n // label text\n //\n var text = win.document.createTextNode(' left: pan, right: rotate, scroll: zoom')\n win.document.body.appendChild(text)\n //\n // GL container\n //\n win.document.body.appendChild(document.createElement('br')) \n container = win.document.createElement('div')\n container.style.overflow = 'hidden'\n win.document.body.appendChild(container)\n //\n // event handlers\n //\n container.addEventListener('contextmenu',context_menu)\n container.addEventListener('mousedown',mouse_down)\n container.addEventListener('mouseup',mouse_up)\n container.addEventListener('mousemove',mouse_move)\n container.addEventListener('wheel',mouse_wheel)\n //\n // add scene\n //\n scene = new THREE.Scene()\n mod.scene = scene\n var width = win.innerWidth\n var height = win.innerHeight\n var aspect = width/height\n var near = 0.1\n var far = 1000000\n camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n mod.camera = camera\n scene.add(camera)\n //\n // add renderer\n //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n renderer.setSize(width,height)\n container.appendChild(renderer.domElement)\n //\n // show the path if available\n //\n show_path()\n }\n //\n // context_menu\n //\n function context_menu(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n return (false)\n }\n //\n // mouse_down\n //\n function mouse_down(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n mod.button = evt.button\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_up\n //\n function mouse_up(evt) {\n mod.button = undefined\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_move\n //\n function mouse_move(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dx = evt.clientX-mod.x\n var dy = evt.clientY-mod.y\n mod.x = evt.clientX\n mod.y = evt.clientY\n if (mod.button == 0) {\n mod.x0 += \n Math.sin(mod.thetaz)*mod.height*dy/win.innerHeight\n -Math.cos(mod.thetaz)*mod.width*dx/win.innerWidth\n mod.y0 += \n Math.cos(mod.thetaz)*mod.height*dy/win.innerHeight\n +Math.sin(mod.thetaz)*mod.width*dx/win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n else if (mod.button == 2) {\n mod.thetaxy += dy/win.innerHeight\n mod.thetaz += dx/win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n //\n // mouse_wheel\n //\n function mouse_wheel(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dy = evt.deltaY/win.innerHeight\n mod.r += mod.height*dy\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n //\n // return values\n //\n return ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n }())\n ","top":"1232.7143395199862","left":"2100.79288398158","filename":"undefined","inputs":{},"outputs":{}},"0.8018489687859153":{"definition":"//\n// on-off module\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2020\n// Modified by Francisco Sanchez Arroyo 27-March-2022\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 //\n var mod = {}\n //\n // name\n //\n var name = 'on/off'\n //\n // initialization\n //\n var init = function() {}\n //\n // inputs\n //\n var inputs = {\n in: {\n type: '',\n event: function(evt) {\n outputs.out.event(evt.detail)\n }\n }\n }\n //\n // outputs\n //\n var outputs = {\n out: {\n type: '',\n event: function(obj) {\n checkSwitch(obj)\n }\n }\n }\n //\n // interface\n //\n var interface = function(div) {\n mod.div = div\n var label = document.createElement('label')\n label.className = 'switch'\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.checked = true\n label.appendChild(input)\n mod.switch = input\n var span = document.createElement('span')\n span.className = 'slider round'\n label.appendChild(span)\n div.appendChild(label)\n }\n //\n // local functions\n //\n\n // switch\n function checkSwitch(obj) {\n if (mod.switch.checked) {\n mods.output(mod, 'out', obj)\n }\n }\n\n\n //\n // return values\n //\n return ({\n mod: mod,\n name: name,\n init: init,\n inputs: inputs,\n outputs: outputs,\n interface: interface\n })\n}())\n","top":"1222.0385686580853","left":"2998.6276866626467","filename":"modules/40%20ui/switch.js","inputs":{},"outputs":{}},"0.1970821212694227":{"definition":"//\n// label\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\n// Modified by Francisco Sanchez Arroyo 02-Feb-2020\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 //\n var mod = {}\n //\n // name\n //\n var name = 'label'\n //\n // initialization\n //\n var init = function() {\n mod.size.value = '400'\n mod.text.value = 'G-Code Mill 2D PCB'\n update_text()\n }\n //\n // inputs\n //\n var inputs = {}\n //\n // outputs\n //\n var outputs = {}\n //\n //\n // interface\n //\n var interface = function(div) {\n mod.div = div\n //div.appendChild(document.createTextNode('font size: '))\n input = document.createElement('input')\n input.type = 'text'\n input.size = 3\n input.addEventListener('input', function(evt) {\n update_text()\n })\n //div.appendChild(input)\n mod.size = input\n //div.appendChild(document.createTextNode(' (%)'))\n //div.appendChild(document.createElement('br'))\n //div.appendChild(document.createTextNode('text: '))\n input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input', function(evt) {\n update_text()\n })\n //div.appendChild(input)\n mod.text = input\n //div.appendChild(document.createElement('br'))\n var span = document.createElement('span')\n var text = document.createTextNode('')\n span.appendChild(text)\n mod.label = text\n div.appendChild(span)\n mod.span = span\n }\n //\n // local functions\n //\n function update_text() {\n mod.label.nodeValue = mod.text.value\n mod.span.style.fontSize = mod.size.value + '%'\n mods.fit(mod.div)\n }\n //\n // return values\n //\n return ({\n mod: mod,\n name: name,\n init: init,\n inputs: inputs,\n outputs: outputs,\n interface: interface\n })\n}())\n","top":"42.00878523157155","left":"258.50898789779126","filename":"modules/40%20ui/label%20simple.js","inputs":{},"outputs":{}},"0.8650805658939553":{"definition":"//\n// read png\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// Modified by Fran Sanchez June 2021 (add flip v) and ChatGPT Feb 23 (add rotate cw)\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 //\n var mod = {}\n //\n // name\n //\n var name = 'read png'\n //\n // initialization\n //\n var init = function() {}\n //\n // inputs\n //\n var inputs = {}\n //\n // outputs\n //\n var outputs = {\n image: {\n 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 },\n imageInfo: {\n type: 'object',\n event: function() {\n var obj = {}\n obj.name = mod.name.nodeValue\n obj.dpi = parseFloat(mod.dpitext.value)\n obj.width = mod.img.width\n obj.height = mod.img.height\n mods.output(mod, 'imageInfo', obj)\n }\n }\n }\n //\n // interface\n //\n var 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 png_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(245,245,245)'\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 png 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.createTextNode(' '))\n //\n // invert 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('invert'))\n btn.addEventListener('click', function() {\n invert_image()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // flip button\n //\n var flip_btn = document.createElement('button')\n flip_btn.style.padding = mods.ui.padding\n flip_btn.style.margin = 1\n flip_btn.appendChild(document.createTextNode('flip H'))\n flip_btn.addEventListener('click', function() {\n flip_image()\n })\n div.appendChild(flip_btn)\n div.appendChild(document.createTextNode(' '))\n //\n //\n // flipv button\n //\n var flipv_btn = document.createElement('button')\n flipv_btn.style.padding = mods.ui.padding\n flipv_btn.style.margin = 1\n flipv_btn.appendChild(document.createTextNode('flip V'))\n flipv_btn.addEventListener('click', function() {\n flipv_image()\n })\n div.appendChild(flipv_btn)\n div.appendChild(document.createTextNode(' '))\n //\n // rotate button\n //\n var rot_btn = document.createElement('button')\n rot_btn.style.padding = mods.ui.padding\n rot_btn.style.margin = 1\n rot_btn.disabled = false\n rot_btn.appendChild(document.createTextNode('rotate 90CW'))\n rot_btn.addEventListener('click', function() {\n rotate90cw_image()\n })\n div.appendChild(rot_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 info.appendChild(document.createTextNode('dpi: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input', function() {\n mod.dpi = parseFloat(mod.dpitext.value)\n mod.mmtext.nodeValue = (25.4 * mod.img.width / mod.dpi).toFixed(3) +\n ' x ' + (25.4 * mod.img.height / mod.dpi).toFixed(3) + ' mm'\n mod.intext.nodeValue = (mod.img.width / mod.dpi).toFixed(3) +\n ' x ' + (mod.img.height / mod.dpi).toFixed(3) + ' in'\n outputs.imageInfo.event()\n })\n info.appendChild(input)\n mod.dpitext = input\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('px: ')\n info.appendChild(text)\n mod.pxtext = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('mm: ')\n info.appendChild(text)\n mod.mmtext = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('in: ')\n info.appendChild(text)\n mod.intext = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('')\n info.appendChild(text)\n mod.name = text\n div.appendChild(info)\n }\n //\n // local functions\n //\n // read handler\n //\n function png_read_handler(event) {\n var file_reader = new FileReader()\n file_reader.onload = png_binary_handler\n input_file = mod.file.files[0]\n file_name = input_file.name\n mod.name.nodeValue = file_name\n file_reader.readAsArrayBuffer(input_file)\n }\n //\n // binary load handler\n //\n function png_binary_handler(event) {\n //\n // get DPI\n //\n // 8 header\n // 4 len, 4 type, data, 4 crc\n // pHYs 4 ppx, 4 ppy, 1 unit: 0 ?, 1 meter\n // IEND\n //\n var units = ppx = ppy = 0\n var buf = event.target.result\n var view = new DataView(buf)\n var ptr = 8\n if (!((view.getUint8(1) == 80) && (view.getUint8(2) == 78) && (view.getUint8(3) == 71))) {\n set_prompt(\"error: PNG header not found\")\n return\n }\n while (1) {\n var length = view.getUint32(ptr)\n ptr += 4\n var type = String.fromCharCode(\n view.getUint8(ptr), view.getUint8(ptr + 1),\n view.getUint8(ptr + 2), view.getUint8(ptr + 3))\n ptr += 4\n if (type == \"pHYs\") {\n ppx = view.getUint32(ptr)\n ppy = view.getUint32(ptr + 4)\n units = view.getUint8(ptr + 8)\n }\n if (type == \"IEND\")\n break\n ptr += length + 4\n }\n if (units == 0) {\n set_prompt(\"no PNG units not found, assuming 72 DPI\")\n ppx = 72 * 1000 / 25.4\n }\n dpi = ppx * 25.4 / 1000\n //\n // read as URL for display\n //\n var file_reader = new FileReader()\n file_reader.onload = png_URL_handler\n file_reader.readAsDataURL(input_file)\n }\n //\n // URL load handler\n //\n function png_URL_handler(event) {\n var img = new Image()\n img.setAttribute(\"src\", event.target.result)\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 } 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 mod.dpitext.value = dpi.toFixed(3)\n mod.pxtext.nodeValue = img.width + ' x ' + img.height + ' px'\n mod.mmtext.nodeValue = (25.4 * img.width / dpi).toFixed(3) +\n ' x ' + (25.4 * img.height / dpi).toFixed(3) + ' mm'\n mod.intext.nodeValue = (img.width / dpi).toFixed(3) +\n ' x ' + (img.height / dpi).toFixed(3) + ' in'\n outputs.image.event()\n outputs.imageInfo.event()\n }\n }\n //\n // invert image\n //\n function invert_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) //canvas pixels contain 4 elements: RGBA\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 } 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.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 h = mod.img.height\n var w = mod.img.width\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0, 0, w, h)\n webworker.postMessage({\n height: img.height,\n width: img.width,\n buffer: img.data.buffer\n }, [img.data.buffer])\n }\n\n function worker() {\n self.addEventListener('message', function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n buf[(h - 1 - row) * w * 4 + col * 4 + 0] = 255 - buf[(h - 1 - row) * w * 4 + col * 4 + 0]\n buf[(h - 1 - row) * w * 4 + col * 4 + 1] = 255 - buf[(h - 1 - row) * w * 4 + col * 4 + 1]\n buf[(h - 1 - row) * w * 4 + col * 4 + 2] = 255 - buf[(h - 1 - row) * w * 4 + col * 4 + 2]\n buf[(h - 1 - row) * w * 4 + col * 4 + 3] = 255\n }\n }\n self.postMessage({\n buffer: buf.buffer\n }, [buf.buffer])\n })\n }\n //\n // flip image\n //\n function flip_image() {\n var blob = new Blob(['(' + flip_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 } 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.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 h = mod.img.height\n var w = mod.img.width\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0, 0, w, h)\n webworker.postMessage({\n height: img.height,\n width: img.width,\n buffer: img.data.buffer\n }, [img.data.buffer])\n }\n\n function flip_worker() {\n self.addEventListener('message', function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n // image stored as RGBA array first row 0 of w*4 elements followed by row 1... until row h-1\n var index = 0 // index for the 1D array image\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w / 2; ++col) {\n index = row * w * 4 + col * 4 // given row,col coordinates it returns its index in the 1D array\n r_index = row* w * 4 + (w - 1 - col) * 4 // the index where this value should be\n // Replace RGB values\n temp_1 = buf[index + 0] //R\n temp_2 = buf[index + 1] //G\n temp_3 = buf[index + 2] //B\n buf[index + 0] = buf[r_index + 0] //R\n buf[index + 1] = buf[r_index + 1] //G\n buf[index + 2] = buf[r_index + 2] //B\n buf[index + 3] = 255 //A\n buf[r_index + 0] = temp_1 //R\n buf[r_index + 1] = temp_2 //G\n buf[r_index + 2] = temp_3 //B \n buf[r_index + 3] = 255 //A\n }\n }\n self.postMessage({\n buffer: buf.buffer\n }, [buf.buffer])\n })\n }\n\n\n //\n // flip v image\n //\n function flipv_image() {\n var blob = new Blob(['(' + flipv_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 } 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.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 h = mod.img.height\n var w = mod.img.width\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0, 0, w, h)\n webworker.postMessage({\n height: img.height,\n width: img.width,\n buffer: img.data.buffer\n }, [img.data.buffer])\n }\n\n function flipv_worker() {\n self.addEventListener('message', function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var index = 0\n for (var col = 0; col < w; ++col) {\n for (var row = 0; row < h / 2; ++row) {\n index = row * w * 4 + col * 4\n r_index = (h - 1 - row) * w * 4 + col * 4\n temp_1 = buf[index + 0]\n temp_2 = buf[index + 1]\n temp_3 = buf[index + 2]\n buf[index + 0] = buf[r_index + 0]\n buf[index + 1] = buf[r_index + 1]\n buf[index + 2] = buf[r_index + 2]\n buf[r_index + 0] = temp_1\n buf[r_index + 1] = temp_2\n buf[r_index + 2] = temp_3\n buf[index + 3] = 255\n buf[r_index + 3] = 255\n }\n }\n self.postMessage({\n buffer: buf.buffer\n }, [buf.buffer])\n })\n }\n\n //\n // rotate image 90 CW by ChatGPT\n //\n function rotate90cw_worker() {\n // by ChatGPT\n self.addEventListener('message', function(evt) {\n var h = evt.data.height;\n var w = evt.data.width;\n var img = new ImageData(new Uint8ClampedArray(evt.data.buffer), w, h);\n var rotated = new ImageData(new Uint8ClampedArray(w * h * 4), h, w);\n \n for (var y = 0; y < h; y++) {\n for (var x = 0; x < w; x++) {\n var srcPos = (y * w + x) * 4;\n var destPos = (x * h + (h - y - 1)) * 4;\n rotated.data[destPos + 0] = img.data[srcPos + 0];\n rotated.data[destPos + 1] = img.data[srcPos + 1];\n rotated.data[destPos + 2] = img.data[srcPos + 2];\n rotated.data[destPos + 3] = img.data[srcPos + 3];\n }\n }\n \n self.postMessage({\n height: rotated.height,\n width: rotated.width,\n buffer: rotated.data.buffer\n }, [rotated.data.buffer]);\n });\n }\n \n\n\n function rotate90cw_image() {\n // by ChatGPT\n var blob = new Blob(['(' + rotate90cw_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 = evt.data.height\n var w = evt.data.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf, w, h)\n var canvas = mod.img\n canvas.width = w\n canvas.height = h\n var ctx = canvas.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 } 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(canvas, x0, y0, wd, hd)\n webworker.terminate()\n outputs.image.event()\n })\n var canvas = mod.canvas\n var ctx = canvas.getContext(\"2d\")\n ctx.clearRect(0, 0, canvas.width, canvas.height)\n var h = mod.img.height\n var w = mod.img.width\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0, 0, w, h)\n webworker.postMessage({\n height: img.height,\n width: img.width,\n buffer: img.data.buffer\n }, [img.data.buffer])\n }\n \n //\n // return values\n //\n return ({\n name: name,\n init: init,\n inputs: inputs,\n outputs: outputs,\n interface: interface\n })\n}())\n","top":"928.22790988985","left":"-324.81130864699264","filename":"modules/00%20read/png.js","inputs":{},"outputs":{}},"0.49202738148831227":{"definition":"//\n// image tabs \n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2023\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 //\n var mod = {}\n //\n // name\n //\n var name = 'image tabs'\n //\n // initialization\n //\n var init = function() {\n mod.length_in.value = 0\n mod.length_mm.value = 25.4*parseFloat(mod.length_in.value)\n mod.width_in.value = 0\n mod.width_mm.value = 25.4*parseFloat(mod.width_in.value)\n mod.E.checked = true\n mod.W.checked = true\n //mod.N.checked = false // NS path bug?\n //mod.S.checked = false\n }\n //\n // inputs\n //\n var inputs = {\n image: {\n 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 }\n },\n imageInfo: {\n 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 image_tabs()\n // race condition, assuming imageInfo comes 2nd\n }\n },\n settings: {\n type: 'object',\n event: function(evt) {\n set_values(evt.detail)\n image_tabs()\n }\n }\n }\n //\n // outputs\n //\n var outputs = {\n image: {\n 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 }\n }\n //\n // interface\n //\n var 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 // tab width\n //\n div.appendChild(document.createTextNode('tab width'))\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.width_in.value = parseFloat(mod.width_mm.value)/25.4\n image_tabs()\n })\n div.appendChild(input)\n mod.width_mm = input\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.width_mm.value = parseFloat(mod.width_in.value)*25.4\n image_tabs()\n })\n div.appendChild(input)\n mod.width_in = input\n div.appendChild(document.createElement('br'))\n //\n // tab length\n //\n div.appendChild(document.createTextNode('tab length'))\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.length_in.value = parseFloat(mod.length_mm.value)/25.4\n image_tabs()\n })\n div.appendChild(input)\n mod.length_mm = input\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.length_mm.value = parseFloat(mod.length_in.value)*25.4\n image_tabs()\n })\n div.appendChild(input)\n mod.length_in = input\n div.appendChild(document.createElement('br'))\n //\n // tab sides\n //\n div.appendChild(document.createTextNode('tab sides: '))\n div.appendChild(document.createTextNode('E'))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.addEventListener('change',function() {\n image_tabs()\n })\n div.appendChild(input)\n mod.E = input\n div.appendChild(document.createTextNode(' W'))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.addEventListener('change',function() {\n image_tabs()\n })\n div.appendChild(input)\n mod.W = input\n /*\n div.appendChild(document.createTextNode(' N'))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.addEventListener('change',function() {\n image_tabs()\n })\n div.appendChild(input)\n mod.N = input\n div.appendChild(document.createTextNode(' S'))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.addEventListener('change',function() {\n image_tabs()\n })\n div.appendChild(input)\n mod.S = input\n */\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 // set_values\n //\n function set_values(settings) {\n for (var s in settings) {\n switch (s) {\n case 'tab width (in)':\n mod.width_in.value = settings[s]\n mod.width_mm.value = parseFloat(mod.width_in.value)*25.4\n break\n case 'tab width (mm)':\n mod.width_mm.value = settings[s]\n mod.width_in.value = parseFloat(mod.width_mm.value)/25.4\n break\n case 'tab length (in)':\n mod.length_in.value = settings[s]\n mod.length_mm.value = parseFloat(mod.length_in.value)*25.4\n break\n case 'tab length (mm)':\n mod.length_mm.value = settings[s]\n mod.length_in.value = parseFloat(mod.length_mm.value)/25.4\n break\n }\n }\n }\n //\n // image tabs\n //\n function image_tabs() {\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 } 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 E = parseFloat(mod.E.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,\n width: mod.input.width,\n E: mod.E.checked,\n W: mod.W.checked,\n //N: mod.N.checked,\n //S: mod.S.checked,\n dpi: mod.dpi,\n lenin: mod.length_in.value,\n widin: mod.width_in.value,\n buffer: img.data.buffer\n }, [img.data.buffer])\n }\n\n function worker() {\n self.addEventListener('message', function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n console.log(evt.data.lenin,evt.data.dpi)\n var dl = (evt.data.lenin*evt.data.dpi)\n var dw = (evt.data.widin*evt.data.dpi)\n var E = evt.data.E\n var W = evt.data.W\n //var N = evt.data.N\n //var S = evt.data.S\n var buf = new Uint8ClampedArray(evt.data.buffer)\n console.log(evt.data.widin,dl,dw)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (E) {\n if ((row > (h/2-dw/2)) && (row < (h/2+dw/2))\n && (col > (w-dl))) {\n buf[(h-1-row)*w*4+col*4+0] = 255\n buf[(h-1-row)*w*4+col*4+1] = 255\n buf[(h-1-row)*w*4+col*4+2] = 255\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n if (W) {\n if ((row > (h/2-dw/2)) && (row < (h/2+dw/2))\n && (col < dl)) {\n buf[(h-1-row)*w*4+col*4+0] = 255\n buf[(h-1-row)*w*4+col*4+1] = 255\n buf[(h-1-row)*w*4+col*4+2] = 255\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n /*\n if (N) {\n if ((col > (w/2-dw/2)) && (col < (w/2+dw/2))\n && (row > (h-dl))) {\n buf[(h-1-row)*w*4+col*4+0] = 255\n buf[(h-1-row)*w*4+col*4+1] = 255\n buf[(h-1-row)*w*4+col*4+2] = 255\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n if (S) {\n if ((col > (w/2-dw/2)) && (col < (w/2+dw/2))\n && (row < dl)) {\n buf[(h-1-row)*w*4+col*4+0] = 255\n buf[(h-1-row)*w*4+col*4+1] = 255\n buf[(h-1-row)*w*4+col*4+2] = 255\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n */\n }\n }\n self.postMessage({\n buffer: buf.buffer\n }, [buf.buffer])\n })\n }\n //\n // return values\n //\n return ({\n mod: mod,\n name: name,\n init: init,\n inputs: inputs,\n outputs: outputs,\n interface: interface\n })\n}())\n","top":"364.86373507058715","left":"1530.8662728330005","filename":"modules/image/tabs.js","inputs":{},"outputs":{}},"0.3947110891103697":{"definition":"//\n// path to G-code\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\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// Updated: Quentin Bolsee\n// Date: Oct 26 2023\n// Comments: time estimate\n//\n// Updated: Neil Gershenfeld\n// Date: Aug 26 2023\n// Comments: compatibility tweaks\n//\n// Updated: Neil Gershenfeld\n// Date: Oct 28 2020\n// Comments: added mm/s vs mm/min option\n//\n// Updated: Steven Chew\n// Date: Feb 20 2019\n// Comments: Added option to output in inch or mm\n// Date:... Oct 28 2019\n// Comments: Corrected feedrate conversion\n// - inch/s to inch/min\n//...........- mm/s to mm/min\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'path to G-code'\n//\n// initialization\n//\nvar init = function() {\n mod.cutspeed.value = '2.5'\n mod.plungespeed.value = '2.5'\n mod.jogheight.value = '2'\n mod.spindlespeed.value = '11000'\n mod.dwell.value = '0'\n mod.tool.value = '0'\n mod.coolantoff.checked = true\n mod.formatMm.checked = true\n }\n//\n// inputs\n//\nvar inputs = {\n path:{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//\n// outputs\n//\nvar outputs = {\n file:{type:'',\n event:function(str){\n obj = {}\n obj.name = mod.name+\".nc\"\n obj.contents = str\n mods.output(mod,'file',obj)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // cut speed\n //\n div.appendChild(document.createTextNode('cut speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.cutspeed = input\n div.appendChild(document.createTextNode(' (mm/s)'))\n div.appendChild(document.createElement('br'))\n //\n // plunge speed\n //\n div.appendChild(document.createTextNode('plunge speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.plungespeed = input\n div.appendChild(document.createTextNode(' (mm/s)'))\n div.appendChild(document.createElement('br'))\n //\n // jog height\n //\n div.appendChild(document.createTextNode('jog height: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.jogheight = input\n div.appendChild(document.createTextNode(' (mm)'))\n div.appendChild(document.createElement('br'))\n //\n // spindle speed\n //\n div.appendChild(document.createTextNode('spindle: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.spindlespeed = input\n div.appendChild(document.createTextNode(' (RPM)'))\n div.appendChild(document.createElement('br'))\n //\n // dwell\n //\n div.appendChild(document.createTextNode('dwell: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.dwell = input\n div.appendChild(document.createTextNode(' (0:skip)'))\n div.appendChild(document.createElement('br'))\n //\n // tool\n //\n div.appendChild(document.createTextNode('tool: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.tool = input\n div.appendChild(document.createTextNode(' (0:skip)'))\n div.appendChild(document.createElement('br'))\n //\n // coolant\n //\n div.appendChild(document.createTextNode('coolant:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'coolant'\n input.id = mod.div.id+'coolanton'\n div.appendChild(input)\n mod.coolanton = input\n div.appendChild(document.createTextNode('on'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'coolant'\n input.id = mod.div.id+'coolantoff'\n div.appendChild(input)\n mod.coolantoff = input\n div.appendChild(document.createTextNode('off'))\n div.appendChild(document.createElement('br'))\n //\n // inch or mm format\n //\n div.appendChild(document.createTextNode('format:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'format'\n input.id = mod.div.id+'formatInch'\n input.checked = true\n div.appendChild(input)\n mod.formatInch = input\n div.appendChild(document.createTextNode('inch'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'format'\n input.id = mod.div.id+'formatMm'\n div.appendChild(input)\n mod.formatMm = input\n div.appendChild(document.createTextNode('mm'))\n div.appendChild(document.createElement('br'))\n //\n // time\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('Estimated time: '))\n var timeTxt = document.createTextNode('--:--:--')\n div.appendChild(timeTxt)\n mod.timeTxt = timeTxt\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\nfunction toHHMMSS(sec_num) {\n sec_num = Math.floor(sec_num)\n var hours = Math.floor(sec_num / 3600);\n var minutes = Math.floor((sec_num - (hours * 3600)) / 60);\n var seconds = sec_num - (hours * 3600) - (minutes * 60);\n\n if (hours < 10) {hours = \"0\"+hours;}\n if (minutes < 10) {minutes = \"0\"+minutes;}\n if (seconds < 10) {seconds = \"0\"+seconds;}\n return hours+':'+minutes+':'+seconds;\n }\nfunction make_path() {\n var total_minutes = 0.0 // in minutes\n var dx = 25.4*mod.width/mod.dpi\n var cut_speed = parseFloat(mod.cutspeed.value)\n var plunge_speed = parseFloat(mod.plungespeed.value)\n var jog_height = parseFloat(mod.jogheight.value)\n var nx = mod.width\n var scale = dx/(nx-1)\n var in_mm_scale = 1\n if (mod.formatInch.checked) {\n dx /= 25.4\n scale /= 25.4\n cut_speed /= 25.4\n plunge_speed /= 25.4\n jog_height /= 25.4\n }\n var spindle_speed = parseFloat(mod.spindlespeed.value)\n var dwell = parseFloat(mod.dwell.value)\n var tool = parseInt(mod.tool.value)\n str = \"%\\n\" // tape start\n str += \"G17\\n\" // xy plane\n if (mod.formatInch.checked)\n str += \"G20\\n\" // inches\n if (mod.formatMm.checked)\n str += \"G21\\n\" // mm\n str += \"G40\\n\" // cancel tool radius compensation\n str += \"G49\\n\" // cancel tool length compensation\n str += \"G54\\n\" // coordinate system 1\n str += \"G80\\n\" // cancel canned cycles\n str += \"G90\\n\" // absolute coordinates\n str += \"G94\\n\" // feed/minute units\n cut_speed *= 60 // feed/sec -> /minute units\n plunge_speed *= 60 // feed/sec -> /minute units\n if (tool != 0)\n str += \"T\"+tool+\"M06\\n\" // tool selection, tool change\n str += \"F\"+cut_speed.toFixed(4)+\"\\n\" // feed rate\n str += \"S\"+spindle_speed+\"\\n\" // spindle speed\n if (mod.coolanton.checked)\n str += \"M08\\n\" // coolant on\n str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\" // move up before starting spindle\n str += \"M03\\n\" // spindle on clockwise\n if (dwell != 0)\n str += \"G04 P\"+dwell+\"\\n\" // give spindle time to spin up\n //\n // simulate tool motion to estimate time\n //\n var toolxyz = {\n xp: null,\n yp: null,\n zp: null,\n move: function(xn, yn, zn) {\n var dist = 0\n if ((this.xp !== null) && (this.yp !== null) && (this.zp !== null)) {\n dist = Math.sqrt((xn-this.xp)**2+(yn-this.yp)**2+(zn-this.zp)**2)\n }\n this.xp = xn\n this.yp = yn\n this.zp = zn\n return dist\n }\n }\n //\n // follow segments\n //\n for (var seg = 0; seg < mod.path.length; ++seg) {\n //\n // move up to starting point\n //\n x = scale*mod.path[seg][0][0]\n y = scale*mod.path[seg][0][1]\n z = jog_height\n str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\"\n str += \"G00X\"+x.toFixed(4)+\"Y\"+y.toFixed(4)+\"Z\"+jog_height.toFixed(4)+\"\\n\"\n total_minutes += toolxyz.move(x, y, z) / cut_speed\n //\n // move down\n //\n z = scale*mod.path[seg][0][2]\n str += \"G01Z\"+z.toFixed(4)+\" F\"+plunge_speed.toFixed(4)+\"\\n\"\n str += \"F\"+cut_speed.toFixed(4)+\"\\n\" //restore xy feed rate\n for (var pt = 1; pt < mod.path[seg].length; ++pt) {\n //\n // move to next point\n //\n x = scale*mod.path[seg][pt][0]\n y = scale*mod.path[seg][pt][1]\n z = scale*mod.path[seg][pt][2]\n str += \"G01X\"+x.toFixed(4)+\"Y\"+y.toFixed(4)+\"Z\"+z.toFixed(4)+\"\\n\"\n total_minutes += toolxyz.move(x, y, z) / cut_speed\n }\n }\n //\n // finish\n //\n z = jog_height\n str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\" // move up\n str += \"G00X0.0000Y0.0000\"+\"Z\"+jog_height.toFixed(4)+\"\\n\" // finish at origin\n str += \"M05\\n\" // spindle stop\n total_minutes += toolxyz.move(x, y, z) / cut_speed\n if (mod.coolanton.checked)\n str += \"M09\\n\" // coolant off\n str += \"M30\\n\" // program end and reset\n str += \"%\\n\" // tape end\n //\n // print time\n //\n mod.timeTxt.textContent = toHHMMSS(total_minutes * 60)\n //\n // output file\n //\n outputs.file.event(str)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1119.9043770630365","left":"2624.9521667089825","filename":"modules/10%20toolpath/20%20formats/g-code.js","inputs":{},"outputs":{}},"0.4983010755806615":{"definition":"//\n// set object\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\n// Modified by Francisco Sanchez Arroyo 31-Jan-2020\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 //\n var mod = {}\n //\n // name\n //\n var name = 'set PCB defaults'\n //\n // initialization\n //\n var init = function() {\n //\n add_output('isolate traces (1/64)')\n add_variable('tool diameter (in)', 'var00')\n mod.var00.value = '0.0156'\n add_variable('cut depth (in)', 'var01')\n mod.var01.value = '0.006'\n add_variable('max depth (in)', 'var02')\n mod.var02.value = '0.006'\n add_variable('offset number', 'var03')\n mod.var03.value = '2'\n add_variable('offset stepover', 'var04')\n mod.var04.value = '0.5'\n add_variable('speed (mm/s)', 'var05')\n mod.var05.value = '4'\n add_variable('tab width (in)', 'var06')\n mod.var06.value = '0'\n add_variable('tab length (in)', 'var07')\n mod.var07.value = '0'\n //\n add_output('clear copper (1/32)')\n add_variable('tool diameter (in)', 'var00')\n mod.var00.value = '0.0312'\n add_variable('cut depth (in)', 'var01')\n mod.var01.value = '0.006'\n add_variable('max depth (in)', 'var02')\n mod.var02.value = '0.006'\n add_variable('offset number', 'var03')\n mod.var03.value = '0'\n add_variable('offset stepover', 'var04')\n mod.var04.value = '0.5'\n add_variable('speed (mm/s)', 'var05')\n mod.var05.value = '4'\n add_variable('tab width (in)', 'var06')\n mod.var06.value = '0'\n add_variable('tab length (in)', 'var07')\n mod.var07.value = '0'\n //\n add_output('mill outline (1/32)')\n add_variable('tool diameter (in)', 'var10')\n mod.var10.value = '0.0312'\n add_variable('cut depth (in)', 'var11')\n mod.var11.value = '0.024'\n add_variable('max depth (in)', 'var12')\n mod.var12.value = '0.072'\n add_variable('offset number', 'var13')\n mod.var13.value = '1'\n add_variable('offset stepover', 'var14')\n mod.var14.value = '0.5'\n add_variable('speed (mm/s)', 'var15')\n mod.var15.value = '4'\n add_variable('tab width (in)', 'var16')\n mod.var16.value = '0.01'\n add_variable('tab length (in)', 'var17')\n mod.var17.value = '0.2'\n //\n add_output('mill traces (10 mil)')\n add_variable('tool diameter (in)', 'var20')\n mod.var20.value = '0.01'\n add_variable('cut depth (in)', 'var21')\n mod.var21.value = '0.004'\n add_variable('max depth (in)', 'var22')\n mod.var22.value = '0.004'\n add_variable('offset number', 'var23')\n mod.var23.value = '4'\n add_variable('offset stepover', 'var24')\n mod.var24.value = '0.5'\n add_variable('speed (mm/s)', 'var25')\n mod.var25.value = '2'\n add_variable('tab width (in)', 'var26')\n mod.var26.value = '0'\n add_variable('tab length (in)', 'var27')\n mod.var27.value = '0'\n }\n //\n // inputs\n //\n var inputs = {}\n //\n // outputs\n //\n var outputs = {\n settings: {\n type: '',\n event: function(vars) {\n mods.output(mod, 'settings', vars)\n }\n }\n }\n //\n // interface\n //\n var interface = function(div) {\n mod.div = div\n }\n //\n // local functions\n //\n function add_output(label) {\n if (mod.settings == undefined) {\n mod.settings = {}\n }\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(label)\n span.appendChild(text)\n btn.appendChild(span)\n var f = function(label) {\n btn.addEventListener('click', function() {\n for (var s in mod.settings)\n mod.settings[s].span.style.fontWeight = 'normal'\n mod.settings[label].span.style.fontWeight = 'bold'\n var vars = {}\n for (var v in mod.settings[label].variables)\n vars[v] = mod.settings[label].variables[v].value\n outputs.settings.event(vars)\n })\n }(label)\n mod.settings[label] = {\n span: span,\n variables: {}\n }\n mod.div.appendChild(btn)\n mod.setting = label\n mod.div.appendChild(document.createElement('br'))\n }\n\n function add_variable(label, variable) {\n var text = document.createTextNode(label)\n mod.div.appendChild(text)\n mod.div.appendChild(document.createTextNode(': '))\n input = document.createElement('input')\n input.type = 'text'\n input.size = 10\n mod[variable] = input\n mod.div.appendChild(input)\n mod.settings[mod.setting].variables[label] = input\n mod.div.appendChild(document.createElement('br'))\n }\n //\n // return values\n //\n return ({\n mod: mod,\n name: name,\n init: init,\n inputs: inputs,\n outputs: outputs,\n interface: interface\n })\n}())\n","top":"923.6376989371713","left":"35.88851870611197","filename":"modules/70%20defaults/PCB%20defaults.js","inputs":{},"outputs":{}},"0.36020256178778576":{"definition":"//\n// V-bit calculator\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\n// Modified by Francisco Sanchez Arroyo 28-March-2022\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 //\n var mod = {}\n //\n // name\n //\n var name = 'V-bit calculator'\n //\n // initialization\n //\n var init = function() {\n // bit parameters\n mod.tip_dia_mm.value = 0.1\n mod.tip_dia_in.value = parseFloat(mod.tip_dia_mm.value)/25.4\n mod.angle.value = 30\n //'tool diameter (in)'\n mod.cut_width_mm.value = 0.3048\n mod.cut_width_in.value = parseFloat(mod.cut_width_mm.value)/25.4\n // 'offset number'\n mod.offset_number.value = 2\n // 'offset stepover'\n mod.offset_stepover.value = 0.5\n // 'speed (mm/s)'\n mod.speed.value = 4\n //'cut depth (in)' // 'max depth (in)'\n mod.cut_depth_mm.value = get_depth_mm()\n mod.cut_depth_in.value = parseFloat(mod.cut_depth_mm.value)/25.4\n\n }\n //\n // inputs\n //\n var inputs = {}\n //\n // outputs\n //\n var outputs = {\n settings: {\n type: '',\n event: function(vars) {\n mods.output(mod, 'settings', vars)\n }\n }\n }\n //\n // interface\n //\n var interface = function(div) {\n mod.div = div\n //\n // load d3.js\n //\n var script = document.createElement('script')\n script.type = 'text/javascript'\n script.onload = draw\n script.src = 'js/d3.js/d3.min.js'\n document.head.appendChild(script)\n //\n //\n // send settings\n //\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('send calculated settings')\n span.appendChild(text)\n btn.appendChild(span)\n btn.addEventListener('click', function() {\n var vars = {\n \"tool diameter (in)\": mod.cut_width_in.value,\n \"cut depth (in)\": mod.cut_depth_in.value,\n \"max depth (in)\": mod.cut_depth_in.value,\n \"offset number\": mod.offset_number.value,\n \"offset stepover\": mod.offset_stepover.value,\n \"speed (mm/s)\":mod.speed.value\n }\n outputs.settings.event(vars)\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('hr'))\n div.appendChild(document.createTextNode('V-bit parameters:'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('tip diameter: '))\n var input = document.createElement('input')\n input.addEventListener('input',function(){\n mod.tip_dia_in.value = parseFloat(mod.tip_dia_mm.value)/25.4\n })\n input.type = 'text'\n input.size = 4\n div.appendChild(input)\n mod.tip_dia_mm = input\n div.appendChild(document.createTextNode('\\u00a0mm '))\n var input = document.createElement('input')\n input.addEventListener('input',function(){\n mod.tip_dia_mm.value = parseFloat(mod.tip_dia_in.value)*25.4\n })\n input.type = 'text'\n input.size = 7\n div.appendChild(input)\n mod.tip_dia_in = input\n div.appendChild(document.createTextNode('\\u00a0in'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('angle: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 4\n div.appendChild(input)\n mod.angle = input\n div.appendChild(document.createTextNode('\\u00a0degrees '))\n div.appendChild(document.createElement('hr'))\n div.appendChild(document.createTextNode('Milling parameters:'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('Offset number: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 4\n div.appendChild(input)\n mod.offset_number = input\n div.appendChild(document.createTextNode(' (0 = fill)\\u00a0\\u00a0'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('Offset stepover: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 4\n div.appendChild(input)\n mod.offset_stepover = input\n div.appendChild(document.createTextNode(' (1 = diameter)'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('Feed speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 4\n div.appendChild(input)\n mod.speed = input\n div.appendChild(document.createTextNode(' mm/s\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0'))\n div.appendChild(document.createElement('hr'))\n div.appendChild(document.createTextNode('Calculator:'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('cut width: '))\n var input = document.createElement('input')\n input.addEventListener('input',function(){\n mod.cut_width_in.value = parseFloat(mod.cut_width_mm.value)/25.4\n mod.cut_depth_in.value = get_depth_in()\n mod.cut_depth_mm.value = get_depth_mm()\n })\n input.type = 'text'\n input.size = 4\n div.appendChild(input)\n mod.cut_width_mm = input\n div.appendChild(document.createTextNode('\\u00a0mm '))\n var input = document.createElement('input')\n input.addEventListener('input',function(){\n mod.cut_width_mm.value = parseFloat(mod.cut_width_in.value)*25.4\n mod.cut_depth_mm.value = get_depth_mm()\n mod.cut_depth_in.value = get_depth_in()\n })\n input.type = 'text'\n input.size = 7\n div.appendChild(input)\n mod.cut_width_in = input\n div.appendChild(document.createTextNode('\\u00a0in'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('cut depth: '))\n var input = document.createElement('input')\n input.addEventListener('input',function(){\n mod.cut_depth_in.value = parseFloat(mod.cut_depth_mm.value)/25.4\n mod.cut_width_in.value = get_width_in()\n mod.cut_width_mm.value = get_width_mm()\n })\n input.type = 'text'\n input.size = 4\n div.appendChild(input)\n mod.cut_depth_mm = input\n div.appendChild(document.createTextNode('\\u00a0mm '))\n var input = document.createElement('input')\n input.addEventListener('input',function(){\n mod.cut_depth_mm.value = parseFloat(mod.cut_depth_in.value)*25.4\n mod.cut_width_mm.value = get_width_mm()\n mod.cut_width_in.value = get_width_in()\n })\n input.type = 'text'\n input.size = 7\n div.appendChild(input)\n mod.cut_depth_in = input\n div.appendChild(document.createTextNode('\\u00a0in'))\n div.appendChild(document.createElement('br'))\n\n\n //\n //\n // draw\n //\n function draw() {\n //\n // data\n //\n var data = [{x: 35, y: 10}, {x: 35, y: 30}, {x: 70, y: 110}, {x: 80, y: 110}, {x: 115, y: 30}, {x: 115, y: 10}]\n var data2 = [{x: -5, y: 200}, {x: -5, y: 160}, {x: 59, y: 160}, {x: 70, y: 185}, {x: 80, y: 185}, {x: 91, y: 160}, {x: 175, y: 160}, {x: 175, y: 200}]\n var data3 =[{x: 39, y: 160}, {x: 50, y: 185}, {x: 60, y: 185}, {x: 71, y: 160}]\n \n console.log('good')\n var div2 = document.createElement('div')\n div2.id = 'd3svg'\n div.appendChild(div2)\n \n var svg = d3.select(\"#d3svg\") \n .append(\"svg\") \n .attr(\"width\", 160) \n .attr(\"height\", 195)\n \n // prepare a helper function\n var lineFunc = d3.line()\n .x(function(d) { return d.x })\n .y(function(d) { return d.y })\n \n // Add the path using this helper function\n svg.append('path')\n .attr('d', lineFunc(data))\n .attr('stroke', 'black')\n .attr('stroke-width', 2)\n .attr('fill', 'lightgray');\n \n svg.append('path')\n .attr('d', lineFunc(data2))\n .attr('stroke', 'black')\n .attr('stroke-width', 2)\n .attr('fill', 'tan');\n \n svg.append('path')\n .attr('d', lineFunc(data3))\n .attr('stroke', 'red')\n .attr('stroke-width', 2)\n .attr('fill', 'none');\n \n svg.append('text')\n .attr('x', 35)\n .attr('y', 125)\n .text(\"tip diameter \")\n \n svg.append('text')\n .attr('x', 57)\n .attr('y', 60)\n .text(\"angle\")\n \n svg.append('text')\n .attr('x', 90)\n .attr('y', 180)\n .text(\"cut depth\")\n \n svg.append('text')\n .attr('x', 5)\n .attr('y', 150)\n .attr('fill', 'red')\n .text(\"stepover\") \n \n svg.append('text')\n .attr('x', 72)\n .attr('y', 150)\n .text(\"cut width\") \n \n mods.fit(mod.div)\n }\n //\n\n\n }\n //\n // local functions\n //\n\n //\n // get width and depth\n //\n\n function get_depth_mm() {\n return (parseFloat(mod.cut_width_mm.value) - parseFloat(mod.tip_dia_mm.value))/(2 * Math.tan(parseFloat(mod.angle.value)*Math.PI/360))\n }\n function get_depth_in() {\n return (parseFloat(mod.cut_width_in.value) - parseFloat(mod.tip_dia_in.value))/(2 * Math.tan(parseFloat(mod.angle.value)*Math.PI/360))\n }\n function get_width_mm() {\n return parseFloat(mod.tip_dia_mm.value) + (2 * parseFloat(mod.cut_depth_mm.value) * Math.tan(parseFloat(mod.angle.value)*Math.PI/360))\n }\n function get_width_in() {\n return parseFloat(mod.tip_dia_in.value) + (2 * parseFloat(mod.cut_depth_in.value) * Math.tan(parseFloat(mod.angle.value)*Math.PI/360))\n }\n\n \n\n // function add_output(label) {\n // if (mod.settings == undefined) {\n // mod.settings = {}\n // }\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(label)\n // span.appendChild(text)\n // btn.appendChild(span)\n // var f = function(label) {\n // btn.addEventListener('click', function() {\n // for (var s in mod.settings)\n // mod.settings[s].span.style.fontWeight = 'normal'\n // mod.settings[label].span.style.fontWeight = 'bold'\n // var vars = {}\n // for (var v in mod.settings[label].variables)\n // vars[v] = mod.settings[label].variables[v].value\n // outputs.settings.event(vars)\n // })\n // }(label)\n // mod.settings[label] = {\n // span: span,\n // variables: {}\n // }\n // mod.div.appendChild(btn)\n // mod.setting = label\n // mod.div.appendChild(document.createElement('br'))\n // }\n\n\n\n\n //\n // return values\n //\n return ({\n mod: mod,\n name: name,\n init: init,\n inputs: inputs,\n outputs: outputs,\n interface: interface\n })\n}())\n","top":"1000.1424083342091","left":"294.11895696088806","filename":"modules/defaults/vbit%20calculator.js","inputs":{},"outputs":{}},"0.01382071743564961":{"definition":"//\n// note\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2017\n// Modified by Francisco Sanchez 2019\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 //\n var mod = {}\n //\n // name\n //\n var name = 'note'\n //\n // initialization\n //\n var init = function() {\n mod.text.value = 'use \"set pcb defaults\" for typical endmill presets or use \"V-bit calculator\" as a substitute for \"isolate traces (1/64)\"'\n }\n //\n // inputs\n //\n var inputs = {}\n //\n // outputs\n //\n var outputs = {}\n //\n // interface\n //\n var interface = function(div) {\n mod.div = div\n //\n // text\n //\n var text = document.createElement('textarea')\n text.style.backgroundColor = '#D4E157';\n text.setAttribute('spellcheck', 'false')\n text.setAttribute('rows', mods.ui.rows)\n text.setAttribute('cols', mods.ui.cols)\n //\n // watch textarea for resize\n //\n new MutationObserver(update_module).observe(text, {\n attributes: true,\n attributeFilter: [\"style\"]\n })\n div.appendChild(text)\n mod.text = text\n div.appendChild(document.createElement('br'))\n }\n //\n // local functions\n //\n //\n // update module\n //\n function update_module() {\n mods.fit(mod.div)\n }\n //\n // return values\n //\n return ({\n mod: mod,\n name: name,\n init: init,\n inputs: inputs,\n outputs: outputs,\n interface: interface\n })\n}())\n","top":"1623.2008752587524","left":"1417.5537844602782","filename":"modules/ui/note.js","inputs":{},"outputs":{}},"0.30606865676281036":{"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 //\n var mod = {}\n //\n // name\n //\n var name = 'read SVG'\n //\n // initialization\n //\n var init = function() {\n window.addEventListener(\"message\", (e) => {\n let svgString = e.data;\n let evt = {\n target: {\n result: svgString\n }\n };\n\n // Notify SvgPcb that we received the message and there is no need to retry\n e.source.postMessage(\"ready\", e.origin);\n\n svg_load_handler(evt);\n });\n }\n //\n // inputs\n //\n var inputs = {\n SVG: {\n type: 'string',\n event: function(evt) {\n svg_load_handler({\n target: {\n result: evt.detail\n }\n })\n }\n }\n }\n //\n // outputs\n //\n var outputs = {\n SVG: {\n type: 'string',\n event: function() {\n mods.output(mod, 'SVG', mod.str)\n }\n }\n }\n //\n // interface\n //\n var 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 // pull from SvgPcb 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('Pull from SvgPcb'))\n btn.addEventListener('click', function() {\n console.log(\"pull...\");\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 //\n function 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 //\n function 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\t // get from viewbox instead\n\t i = mod.str.indexOf(\"viewBox\")\n\t var i1 = mod.str.indexOf(\"\\\"\", i + 1) // get the next \" after viewbox, ex: viewBox=\"0 0 300 300\"\n\t var i2 = mod.str.indexOf(\"\\ \", i1 + 1)\n var i3 = mod.str.indexOf(\"\\ \", i2 + 1)\n\t var i4 = mod.str.indexOf(\"\\ \", i3 + 1)\n var i5 = mod.str.indexOf(\"\\\"\", i4 + 1)\n\t var width = mod.str.substring(i3 + 1, i4)\n var height = mod.str.substring(i4 + 1, i5)\n\t var units = 90\n mod.width.nodeValue = \"width: \" + width\n mod.height.nodeValue = \"height: \" + height\n\t mod.units.nodeValue = \"units per inch: \" + units\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 } else if (width.indexOf(\"mm\") != -1) {\n width = width.slice(0, -2)\n height = height.slice(0, -2)\n var units = 25.4\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 } else if (width.indexOf(\"in\") != -1) {\n width = width.slice(0, -2)\n height = height.slice(0, -2)\n var units = 1\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\timg.setAttribute(\"width\", width)\n img.setAttribute(\"height\", height)\n\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 } 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\t //console.log(ctx)\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 //\n return ({\n mod: mod,\n name: name,\n init: init,\n inputs: inputs,\n outputs: outputs,\n interface: interface\n })\n}())\n","top":"228.6973413772966","left":"237.4778444644412","filename":"modules/read/svg.js","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.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.608045696421487\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.608045696421487\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.4592074909554409\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.608045696421487\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.608045696421487\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6248369051648597\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8018489687859153\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"out\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4793941661670936\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8650805658939553\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.608045696421487\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8650805658939553\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.49202738148831227\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.4592074909554409\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.49202738148831227\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8650805658939553\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.49202738148831227\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.4592074909554409\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.49202738148831227\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.49202738148831227\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6248369051648597\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3947110891103697\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3947110891103697\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8018489687859153\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"in\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.4983010755806615\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"settings\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.608045696421487\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.4983010755806615\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"settings\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.49202738148831227\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.36020256178778576\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"settings\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.608045696421487\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.30606865676281036\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVG\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4592074909554409\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVG\\\"}\"}"]}