diff --git a/docs/Instruction/images/proverXL/30x30mm_cutout.jpg b/docs/Instruction/images/proverXL/30x30mm_cutout.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0a522316f345c9884e49babc4f456b57c838b0eb Binary files /dev/null and b/docs/Instruction/images/proverXL/30x30mm_cutout.jpg differ diff --git a/docs/Instruction/images/proverXL/SVG image.nc b/docs/Instruction/images/proverXL/SVG image.nc index 6fa8850a3aba21e590cdef6034e9fb6fcddb1a7e..7dafcfa56be407484305ce471e68b67ecc007394 100644 --- a/docs/Instruction/images/proverXL/SVG image.nc +++ b/docs/Instruction/images/proverXL/SVG image.nc @@ -1,4 +1,3 @@ -% G17 G21 G40 @@ -7,12 +6,9 @@ G54 G80 G90 G94 -T1M06 F150.0000 S11000 G00Z2.0000 -M03 -G04 P1000 G00Z2.0000 G00X5.0324Y4.7528Z2.0000 G01Z-0.2542 F150.0000 diff --git a/docs/Instruction/images/proverXL/gsender_homed.png b/docs/Instruction/images/proverXL/gsender_homed.png new file mode 100644 index 0000000000000000000000000000000000000000..1acc8c4b18bad5850d7849a5d0a5bbac02c08c41 Binary files /dev/null and b/docs/Instruction/images/proverXL/gsender_homed.png differ diff --git a/docs/Instruction/images/proverXL/gsender_jog.png b/docs/Instruction/images/proverXL/gsender_jog.png new file mode 100644 index 0000000000000000000000000000000000000000..2b7d3f30afa287c58be6dcdb2f46ff08d9d264ce Binary files /dev/null and b/docs/Instruction/images/proverXL/gsender_jog.png differ diff --git a/docs/Instruction/images/proverXL/job_start.png b/docs/Instruction/images/proverXL/job_start.png new file mode 100644 index 0000000000000000000000000000000000000000..b25e5b876032fd71e907b294ce95c1d68c2e8b21 Binary files /dev/null and b/docs/Instruction/images/proverXL/job_start.png differ diff --git a/docs/Instruction/images/proverXL/load_file.png b/docs/Instruction/images/proverXL/load_file.png new file mode 100644 index 0000000000000000000000000000000000000000..a8f1677e77c07dab44a7caf49c02c86e928d7e71 Binary files /dev/null and b/docs/Instruction/images/proverXL/load_file.png differ diff --git a/docs/Instruction/images/proverXL/mill_2D_PCB_svg.txt b/docs/Instruction/images/proverXL/mill_2D_PCB_svg.txt new file mode 100644 index 0000000000000000000000000000000000000000..9cd2ee7fee28a107ee8bc657d1787dbb62eb6851 --- /dev/null +++ b/docs/Instruction/images/proverXL/mill_2D_PCB_svg.txt @@ -0,0 +1 @@ +{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"390.58350726962306","left":"3347.994408259124","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1007.5835072696229","left":"3805.994408259124","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"877.5835072696229","left":"3374.994408259124","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 = ''\n }\n//\n// inputs\n//\nvar inputs = {\n distances:{type:'F32',\n event:function(evt){\n mod.distances = evt.detail\n var h = mod.distances.height\n var w = mod.distances.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.height = mod.distances.height \n ctx.canvas.width = mod.distances.width\n if (mod.offset.value != '')\n offset()\n }},\n offset:{type:'number',\n event:function(evt){\n mod.offset.value = evt.detail\n offset()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"490.58350726962306","left":"3806.994408259124","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"523.583507269623","left":"2835.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.2892270043957246":{"definition":"//\n// view toolpath\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// todo:\n// erase and update new path\n// show depth info\n// show size\n// calculate camera far\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'view toolpath'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar 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//\nvar 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//\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('(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//\nfunction 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//\nfunction 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 geometry = new THREE.Geometry()\n geometry.vertices.push(start,stop)\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\t renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction 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\t scene = new THREE.Scene()\n\t mod.scene = scene\n\t var width = win.innerWidth\n\t var height = win.innerHeight\n\t var aspect = width/height\n\t var near = 0.1\n\t var far = 1000000\n\t camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n\t mod.camera = camera\n\t scene.add(camera)\n\t //\n\t // add renderer\n\t //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n\t renderer.setSize(width,height)\n\t 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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1227.583507269623","left":"2165.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.9557599338778935":{"definition":"//\n// mill raster 2D\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 = 'mill raster 2D'\n//\n// initialization\n//\nvar 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//\nvar inputs = {\n imageInfo:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.width\n ctx.canvas.height = mod.height\n }},\n path:{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 }\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:{type:'object',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n diameter:{type:'number',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{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:{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//\nvar 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//\nfunction 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 'offset number':\n mod.number.value = settings[s]\n break\n }\n }\n }\n//\n// clear_path\n//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"448.58350726962306","left":"2188.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.10309904694903338":{"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 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 = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = 1\n mod.sort.checked = true\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 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 > 1) && (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 > 1) && (sort == false)) || (vecpath.length == 1))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"993.5835072696229","left":"2879.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.992472539363189":{"definition":"//\n// set object\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 = 'set PCB defaults'\n//\n// initialization\n//\nvar init = function() {\n //\n add_output('mill 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.004'\n add_variable('max depth (in)','var02')\n mod.var02.value = '0.004'\n add_variable('offset number','var03')\n mod.var03.value = '4'\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.010'\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 //\n }\n//\n// inputs\n//\nvar inputs = {}\n//\n// outputs\n//\nvar outputs = {\n settings:{type:'',\n event:function(vars){\n mods.output(mod,'settings',vars)\n }\n }\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n }\n//\n// local functions\n//\nfunction 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] = {span:span,variables:{}}\n mod.div.appendChild(btn)\n mod.setting = label\n mod.div.appendChild(document.createElement('br'))\n }\nfunction 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//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"368.58350726962306","left":"1644.9944082591242","filename":"undefined","inputs":{},"outputs":{}},"0.8622681794886853":{"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":"1215.9473065265367","left":"1684.9337776993061","filename":"modules/file/save","inputs":{},"outputs":{}},"0.057765477464730264":{"definition":"//\n// read SVG\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'read SVG'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n SVG:{type:'string',\n event:function(evt) {\n svg_load_handler({target:{result:evt.detail}})\n }}}\n//\n// outputs\n//\nvar outputs = {\n SVG:{type:'string',\n event:function(){\n mods.output(mod,'SVG',mod.str)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // file input control\n //\n var file = document.createElement('input')\n file.setAttribute('type','file')\n file.setAttribute('id',div.id+'file_input')\n file.style.position = 'absolute'\n file.style.left = 0\n file.style.top = 0\n file.style.width = 0\n file.style.height = 0\n file.style.opacity = 0\n file.addEventListener('change',function() {\n svg_read_handler()\n })\n div.appendChild(file)\n mod.file = file\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // file select button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('select SVG file'))\n btn.addEventListener('click',function(){\n var file = document.getElementById(div.id+'file_input')\n file.value = null\n file.click()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // info div\n //\n var info = document.createElement('div')\n info.setAttribute('id',div.id+'info')\n var text = document.createTextNode('file:')\n info.appendChild(text)\n mod.name = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('width:')\n info.appendChild(text)\n mod.width = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('height:')\n info.appendChild(text)\n mod.height = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('units per inch:')\n info.appendChild(text)\n mod.units = text\n div.appendChild(info)\n }\n//\n// local functions\n//\n// read handler\n//\nfunction svg_read_handler(event) {\n //\n // read as text\n //\n var file_reader = new FileReader()\n file_reader.onload = svg_load_handler\n var input_file = mod.file.files[0]\n var file_name = input_file.name\n mod.name.nodeValue = \"file: \"+file_name\n file_reader.readAsText(input_file)\n }\n//\n// load handler\n//\nfunction svg_load_handler(event) {\n mod.str = event.target.result\n //\n // parse size\n //\n var i = mod.str.indexOf(\"width\")\n if (i == -1) {\n mod.width.nodeValue = \"width: not found\"\n mod.height.nodeValue = \"height: not found\"\n }\n else {\n var i1 = mod.str.indexOf(\"\\\"\",i+1)\n var i2 = mod.str.indexOf(\"\\\"\",i1+1)\n var width = mod.str.substring(i1+1,i2)\n i = mod.str.indexOf(\"height\")\n i1 = mod.str.indexOf(\"\\\"\",i+1)\n i2 = mod.str.indexOf(\"\\\"\",i1+1)\n var height = mod.str.substring(i1+1,i2)\n ih = mod.str.indexOf(\"height\")\n if (width.indexOf(\"px\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 90\n }\n else if (width.indexOf(\"mm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 25.4\n }\n else if (width.indexOf(\"cm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 2.54\n }\n else if (width.indexOf(\"in\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 1\n }\n else {\n var units = 90\n }\n mod.width.nodeValue = \"width: \"+width\n mod.height.nodeValue = \"height: \"+height\n mod.units.nodeValue = \"units per inch: \"+units\n }\n //\n // display\n //\n var img = new Image()\n var src = \"data:image/svg+xml;base64,\"+window.btoa(mod.str)\n img.setAttribute(\"src\",src)\n img.onload = function() {\n if (img.width > img.height) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-img.height/img.width)\n var w = mod.canvas.width\n var h = mod.canvas.width*img.height/img.width\n }\n else {\n var x0 = mod.canvas.width*.5*(1-img.width/img.height)\n var y0 = 0\n var w = mod.canvas.height*img.width/img.height\n var h = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(img,x0,y0,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = img.width\n ctx.canvas.height = img.height \n ctx.drawImage(img,0,0)\n outputs.SVG.event()\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"332.03488793999406","left":"837.5723969709061","filename":"modules/read/svg","inputs":{},"outputs":{}},"0.5022735462618486":{"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 = '1000'\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":"438.69493521948414","left":"1211.852199242571","filename":"modules/convert/svg/image","inputs":{},"outputs":{}},"0.9604471250218796":{"definition":"//\n// path to G-code\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2018\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// Updated: Neil Gershenfeld\n// Date: Oct 28 2020\n// Comments: added mm/s vs mm/min option\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 = '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.tool.value = '1'\n mod.coolantoff.checked = true\n mod.formatMm.checked = true\n mod.unitMinutes.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 speed: '))\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 // 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.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 // second or minute rate units \n //\n div.appendChild(document.createTextNode('rate units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitSeconds'\n input.checked = true\n div.appendChild(input)\n mod.unitSeconds = input\n div.appendChild(document.createTextNode('second'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitMinutes'\n div.appendChild(input)\n mod.unitMinutes = input\n div.appendChild(document.createTextNode('minute'))\n }\n//\n// local functions\n//\nfunction make_path() {\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 if (mod.unitMinutes.checked) {\n cut_speed *= 60\n plunge_speed *= 60\n }\n var spindle_speed = parseFloat(mod.spindlespeed.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 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 //str += \"G04 P1000\\n\" // give spindle 1 second to spin up................comment out\n //str += \"G04 P1\\n\" // give spindle 1 Millisecond to spin up..............comment out\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 str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\"\n str += \"G00X\"+x.toFixed(4)+\"Y\"+y.toFixed(4)+\"Z\"+jog_height.toFixed(4)+\"\\n\"\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 }\n }\n //\n // finish\n //\n str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\" // move up before stopping spindle\n str += \"M05\\n\" // spindle stop\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 // 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\n","top":"764.1683166499946","left":"1656.839479173399","filename":"modules/path/formats/g-code","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.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.992472539363189\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"settings\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5022735462618486\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5022735462618486\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.057765477464730264\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVG\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5022735462618486\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVG\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9604471250218796\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9604471250218796\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8622681794886853\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}"]} \ No newline at end of file diff --git a/docs/Instruction/images/proverXL/milled_bords_svg_png.jpg b/docs/Instruction/images/proverXL/milled_bords_svg_png.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b4935ae8c3602c3aeb6b19e2111e8ef2f9e144f6 Binary files /dev/null and b/docs/Instruction/images/proverXL/milled_bords_svg_png.jpg differ diff --git a/docs/Instruction/images/proverXL/mods/mill_2D_PCB_png.html b/docs/Instruction/images/proverXL/mods/mill_2D_PCB_png.html new file mode 100644 index 0000000000000000000000000000000000000000..1624982511f1e1b9c4ea1db3d482f732daeb8014 --- /dev/null +++ b/docs/Instruction/images/proverXL/mods/mill_2D_PCB_png.html @@ -0,0 +1,1915 @@ +<html> +<head><meta charset='utf-8'> +<title>mods</title> +</head> +<body link='black' alink='black' vlink='black'> +<script> +// +// mods.js +// +// Neil Gershenfeld +// (c) Massachusetts Institute of Technology 2018 +// +// This work may be reproduced, modified, distributed, performed, and +// displayed for any purpose, but must acknowledge the mods +// project. Copyright is retained and must be preserved. The work is +// provided as is; no warranty is provided, and users accept all +// liability. +// +// closure +// +(function(){ +// +// globals +// +var mods = {} +mods.mod = {} +mods.globals = {} +mods.ui = {source:null, + progname:'', + padding:7, + bezier:100, + canvas:250, + rows:5, + cols:20, + link_color:'rgb(0,0,128)', + link_highlight:'rgb(255,0,0)', + header:50, + selected:{}, + mousedown:null, + menu:null, + top:null, + left:null, + xstart:null, + ystart:null, + xtrans:null, + ytrans:null + } +// +// UI +// +document.body.style.overflow = "hidden" +function mods_transform() { + var transform = document.body.style.transform + var m = new DOMMatrix(getComputedStyle(document.body).transform) + var s = m.m11 + var tx = m.m41/s + var ty = m.m42/s + var origin = document.body.style.transformOrigin + var pxx = origin.indexOf('px') + var ox = parseFloat(origin.slice(0,pxx)) + var pxy = origin.indexOf('px',pxx+2) + var oy = parseFloat(origin.slice(pxx+2,pxy)) + return({s:s,tx:tx,ty:ty,ox:ox,oy:oy}) + } +document.body.style.transform = 'scale(1) translate(0px,0px)' +document.body.style.transformOrigin = '0px 0px' +// +// scroll wheel event +// +window.addEventListener('wheel',function(evt) { + /* + (xw+tx-ox)*s+ox = xs + xw = ox-tx+(xs-ox)/s + (xw+tx0-ox0)*s+ox0 = (xw+tx1-ox1)*s+ox1 + (tx0-ox0)*s+ox0 = (tx1-ox1)*s+ox1 + tx0+(ox1-ox0)+(ox0-ox1)/s = tx1 + tx0+(ox1-ox0)*(1-1/s) = tx1 + */ + var el = document.elementFromPoint(evt.pageX,evt.pageY) + if ((el.tagName == "HTML") || (el.tagName == "BODY")) { + set_prompt('scroll to zoom') + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + if (evt.deltaY > 0) + var scale = t.s*1.1 + else + var scale = t.s*0.9 + var tx = t.tx+(evt.pageX-t.ox)*(1-1/t.s) + var ty = t.ty+(evt.pageY-t.oy)*(1-1/t.s) + document.body.style.transform = `scale(${scale}) translate(${tx}px,${ty}px)` + document.body.style.transformOrigin = `${evt.pageX}px ${evt.pageY}px` + } + }) +// +// body mouse events +// +window.addEventListener('mousedown',function(evt) { + // + // get element mouse is over + // + var el = document.elementFromPoint(evt.pageX,evt.pageY) + // + // check if on body + // + if ((el.tagName == "HTML") || (el.tagName == "BODY")) { + // + // remember button + // + mods.ui.mousedown = evt.button + if (mods.ui.mousedown == 0) { + set_prompt('left-drag to pan, right-drag to select') + if (mods.ui.menu != null) { + document.body.removeChild(mods.ui.menu) + mods.ui.menu = null + } + } + else if (mods.ui.mousedown == 2) { + set_prompt('menu; left-drag to pan, right-drag to select') + } + // + // remember position + // + var t = mods_transform() + mods.ui.xstart = evt.pageX + mods.ui.ystart = evt.pageY + mods.ui.xtrans = t.tx + mods.ui.ytrans = t.ty + } + }) +window.addEventListener('mousemove',function(evt) { + // + // mouse move + // + if (mods.ui.mousedown != null) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + if (mods.ui.mousedown == 0) { + // + // pan on left drag + // + xtrans = mods.ui.xtrans+(evt.pageX-mods.ui.xstart)/t.s + ytrans = mods.ui.ytrans+(evt.pageY-mods.ui.ystart)/t.s + document.body.style.transform = `scale(${t.s}) translate(${xtrans}px,${ytrans}px)` + } + else if (mods.ui.mousedown == 2) { + // + // select on right drag + // + var rect = document.getElementById('svgrect') + if (rect == undefined) { + // + // start dragging + // + if (mods.ui.menu != null) { + document.body.removeChild(mods.ui.menu) + mods.ui.menu = null + } + set_prompt('right-drag to select') + var t = mods_transform() + var rect = document.createElementNS('http://www.w3.org/2000/svg','rect') + rect.setAttribute('id','svgrect') + rect.setAttribute('x',t.ox-t.tx+(evt.pageX-t.ox)/t.s) + rect.setAttribute('y',t.oy-t.ty+(evt.pageY-t.oy)/t.s-mods.ui.header) + rect.setAttribute('width',0) + rect.setAttribute('height',0) + rect.setAttribute('fill','rgb(200,200,200)') + rect.setAttribute('stroke','none') + var svg = document.getElementById('svg') + svg.insertBefore(rect,svg.firstChild) + } + else { + // + // continue dragging + // + var rect = document.getElementById('svgrect') + var xp = t.ox-t.tx+(mods.ui.xstart-t.ox)/t.s + var yp = t.oy-t.ty+(mods.ui.ystart-t.oy)/t.s-mods.ui.header + var xw = t.ox-t.tx+(evt.pageX-t.ox)/t.s + var yw = t.oy-t.ty+(evt.pageY-t.oy)/t.s-mods.ui.header + if (xw < xp) { + rect.setAttribute('x',xw) + rect.setAttribute('width',xp-xw) + } + else + rect.setAttribute('width',xw-xp) + if (yw < yp) { + rect.setAttribute('y',yw) + rect.setAttribute('height',yp-yw) + } + else + rect.setAttribute('height',yw-yp) + } + } + } + }) +window.addEventListener('mouseup',function(evt) { + // + // mouse up + // + mods.ui.mousedown = null + // + // check for selection rectangle + // + var rect = document.getElementById('svgrect') + if (rect != null) { + // + // rectangle exists, selecting modules + // + var x = parseFloat(rect.getAttribute('x')) + var y = parseFloat(rect.getAttribute('y')) + var width = parseFloat(rect.getAttribute('width')) + var height = parseFloat(rect.getAttribute('height')) + svg.removeChild(rect) + var modules = document.getElementById('modules') + // + // loop to find selected modules + // + mods.ui.selected = {} + for (var module in modules.childNodes) { + var container = modules.childNodes[module] + var id = container.id + if (id != undefined) { + var name = container.firstChild + var left = parseFloat(container.dataset.left) + var top = parseFloat(container.dataset.top) + if ((x <= left) && (left <= x+width) && (y <= top) && (top <= y+height)) { + // + // module is in selection rectangle + // + name.style.fontWeight = "bold" + mods.ui.selected[id] = true + } + else { + // + // module is not in selection rectangle + // + name.style.fontWeight = "normal" + } + } + } + } + }) +// +// context menu +// +window.addEventListener('contextmenu',function(evt){ + evt.stopPropagation() + evt.preventDefault() + if (mods.ui.menu != null) { + document.body.removeChild(mods.ui.menu) + mods.ui.menu = null + } + var div = document.createElement('div') + make_menu(div) + add_menu(div,'modules',modules) + add_menu(div,'programs',programs) + add_menu(div,'edit',edit) + add_menu(div,'options',options) + document.body.appendChild(div) + function make_menu(div) { + mods.ui.menu = div + div.style.position = "absolute" + var t = mods_transform() + div.style.top = t.oy-t.ty+(evt.pageY-t.oy)/t.s + div.style.left = t.ox-t.tx+(evt.pageX-t.ox)/t.s + div.style.zIndex = 0 + div.style.cursor = 'default' + div.style.backgroundColor = "rgb(220,255,255)" + div.style.padding = 1.5*mods.ui.padding + div.style.textAlign = 'left' + div.style.border = '2px solid' + div.style.borderRadius = '10px' + } + function add_menu(div,text,click) { + var textdiv = document.createElement('div') + textdiv.appendChild(document.createTextNode(text)) + textdiv.appendChild(document.createElement('br')) + textdiv.addEventListener('mouseover',function(evt){ + evt.target.style.fontWeight = 'bold'}) + textdiv.addEventListener('mouseout',function(evt){ + evt.target.style.fontWeight = 'normal'}) + textdiv.addEventListener('mousedown',click) + textdiv.addEventListener('touchend',click) + div.appendChild(textdiv) + } + // + // modules menu + // + function modules(evt) { + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + var div = document.createElement('div') + make_menu(div) + set_prompt('modules') + // + // open server module + // + add_menu(div,'open server module',function(evt){ + function module_label(label) { + var div = document.createElement('div') + var i = document.createElement('i') + i.appendChild(document.createTextNode(label)) + div.appendChild(i) + div.appendChild(document.createElement('br')) + menu.appendChild(div) + } + function module_menu(label,module) { + var div = document.createElement('div') + div.appendChild( + document.createTextNode('\u00A0\u00A0\u00A0'+label)) + div.addEventListener('mouseover',function(evt){ + evt.target.style.fontWeight = 'bold'}) + div.addEventListener('mouseout',function(evt){ + evt.target.style.fontWeight = 'normal'}) + div.addEventListener('mousedown',function(evt){ + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var t = mods_transform() + mod_message_handler(module, + t.oy-t.ty+(evt.pageY-t.oy)/t.s, + t.ox-t.tx+(evt.pageX-t.ox)/t.s)}) + div.appendChild(document.createElement('br')) + menu.appendChild(div) + } + document.body.removeChild(evt.target.parentNode) + var menu = document.createElement('div') + make_menu(menu) + document.body.appendChild(menu) + menu.style.width = mods.ui.canvas + menu.style.height = mods.ui.canvas + menu.style.overflow = 'auto' + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + var str = req.response + eval(str) + } + } + req.open('GET','modules/index.js'+'?rnd='+Math.random()) + req.send() + }) + // + // open local module + // + add_menu(div,'open local module',function(evt){ + var t = mods_transform() + mods.ui.top = t.oy-t.ty+(evt.pageY-t.oy)/t.s + mods.ui.left = t.ox-t.tx+(evt.pageX-t.ox)/t.s + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var file = document.getElementById('mod_input') + file.value = null + file.click() + }) + // + // open remote module + // + add_menu(div,'open remote module',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + set_prompt('remotes not yet implemented') + }) + document.body.appendChild(div) + } + // + // programs menu + // + function programs(evt) { + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + var div = document.createElement('div') + make_menu(div) + set_prompt('programs') + // + // open local program + // + add_menu(div,'open local program',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var file = document.getElementById('prog_input') + file.value = null + file.click() + }) + // + // open server program + // + add_menu(div,'open server program',function(evt){ + function program_label(label) { + var div = document.createElement('div') + var i = document.createElement('i') + i.appendChild(document.createTextNode(label)) + div.appendChild(i) + div.appendChild(document.createElement('br')) + menu.appendChild(div) + } + function program_menu(label,program) { + var div = document.createElement('div') + div.appendChild( + document.createTextNode('\u00A0\u00A0\u00A0'+label)) + div.addEventListener('mouseover',function(evt){ + evt.target.style.fontWeight = 'bold'}) + div.addEventListener('mouseout',function(evt){ + evt.target.style.fontWeight = 'normal'}) + div.addEventListener('mousedown',function(evt){ + evt.preventDefault() + evt.stopPropagation() + if (location.port == 80) + var uri = 'http://'+location.hostname + +'?program='+program + else + var uri = 'http://'+location.hostname+':' + +location.port+'?program='+program + set_prompt('<a href='+uri+'>program link</a>') + prog_message_handler(program) + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + }) + div.appendChild(document.createElement('br')) + menu.appendChild(div) + } + document.body.removeChild(evt.target.parentNode) + var menu = document.createElement('div') + make_menu(menu) + document.body.appendChild(menu) + menu.style.width = mods.ui.canvas + menu.style.height = mods.ui.canvas + menu.style.overflow = 'auto' + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + var str = req.response + eval(str) + } + } + req.open('GET','programs/index.js'+'?rnd='+Math.random()) + req.send() + }) + // + // open remote program + // + add_menu(div,'open remote program',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + set_prompt('remotes not yet implemented') + }) + // + // save local program + // + add_menu(div,'save local program',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + save_program() + }) + // + // save local page + // + add_menu(div,'save local page',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + save_page() + }) + document.body.appendChild(div) + } + // + // edit menu + // + function edit(evt) { + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + var div = document.createElement('div') + make_menu(div) + set_prompt('edit') + // + // cut + // + add_menu(div,'cut',function(evt){ + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + if ((Object.keys(mods.ui.selected).length) == 0) { + set_prompt("nothing selected") + } + else { + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + delete_module(id) + mods.ui.selected = {} + } + } + }) + // + // copy + // + add_menu(div,'copy',function(evt){ + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + set_prompt('copy not yet implemented') + }) + // + // paste + // + add_menu(div,'paste',function(evt){ + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + set_prompt('paste not yet implemented') + }) + document.body.appendChild(div) + } + // + // options menu + // + function options(evt) { + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var div = document.createElement('div') + make_menu(div) + set_prompt('options') + // + // view files + // + add_menu(div,'view files',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var win = window.open('files.html') + }) + document.body.appendChild(div) + // + // view project + // + add_menu(div,'view project',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var win = window.open('https://gitlab.cba.mit.edu/pub/mods') + }) + document.body.appendChild(div) + } + }) +// +// prompt +// +document.body.appendChild(document.createTextNode(' ')) +var span = document.createElement('span') + span.setAttribute('id','logo') + span.style.display = 'inline-block' + span.style.verticalAlign = 'middle' + span.style.width = 20 + span.style.height = 20 + span.style.padding = mods.ui.padding + span.appendChild(logo(1)) + document.body.appendChild(span) +document.body.appendChild(document.createTextNode(' ')) +var span = document.createElement('span') + span.setAttribute('id','prompt') + span.style.display = 'inline-block' + span.style.verticalAlign = 'middle' + var innerspan = document.createElement('span') + span.appendChild(innerspan) + document.body.appendChild(span) +function logo(size) { + var x = 0 + var y = 2.8*size/3.8 + var svgNS = "http://www.w3.org/2000/svg" + var logo = document.createElementNS(svgNS,"svg") + logo.setAttributeNS("http://www.w3.org/2000/xmlns/", + "xmlns:xlink","http://www.w3.org/1999/xlink") + logo.setAttributeNS(null,'viewBox',"0 0 "+size+" "+size) + var new_rect = document.createElementNS(svgNS,"rect"); + new_rect.setAttribute("width",size/3.8) + new_rect.setAttribute("height",size/3.8) + new_rect.setAttribute("x",x) + new_rect.setAttribute("y",y) + new_rect.setAttribute("fill","blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS,"rect"); + new_rect.setAttribute("width",size/3.8) + new_rect.setAttribute("height",size/3.8) + new_rect.setAttribute("x",x+1.4*size/3.8) + new_rect.setAttribute("y",y) + new_rect.setAttribute("fill","blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS,"rect"); + new_rect.setAttribute("width",size/3.8) + new_rect.setAttribute("height",size/3.8) + new_rect.setAttribute("x",x+2.8*size/3.8) + new_rect.setAttribute("y",y) + new_rect.setAttribute("fill","blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS, "rect"); + new_rect.setAttribute("width",size/3.8) + new_rect.setAttribute("height",size/3.8) + new_rect.setAttribute("x",x) + new_rect.setAttribute("y",y-1.4*size/3.8) + new_rect.setAttribute("fill","blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS, "rect"); + new_rect.setAttribute("width", size / 3.8) + new_rect.setAttribute("height", size / 3.8) + new_rect.setAttribute("x", x + 2.8 * size / 3.8) + new_rect.setAttribute("y", y - 1.4 * size / 3.8) + new_rect.setAttribute("fill", "blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS, "rect"); + new_rect.setAttribute("width", size / 3.8) + new_rect.setAttribute("height", size / 3.8) + new_rect.setAttribute("x", x + 1.4 * size / 3.8) + new_rect.setAttribute("y", y - 2.8 * size / 3.8) + new_rect.setAttribute("fill", "blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS, "rect"); + new_rect.setAttribute("width", size / 3.8) + new_rect.setAttribute("height", size / 3.8) + new_rect.setAttribute("x", x + 2.8 * size / 3.8) + new_rect.setAttribute("y", y - 2.8 * size / 3.8) + new_rect.setAttribute("fill", "blue") + logo.appendChild(new_rect) + var new_circ = document.createElementNS(svgNS, "circle"); + new_circ.setAttribute("r", size / (2 * 3.8)) + new_circ.setAttribute("cx", x + size / (2 * 3.8)) + new_circ.setAttribute("cy", y + size / (2 * 3.8) - 2.8 * size / 3.8) + new_circ.setAttribute("fill", "red") + logo.appendChild(new_circ) + var new_circ = document.createElementNS(svgNS, "circle"); + new_circ.setAttribute("r", size / (2 * 3.8)) + new_circ.setAttribute("cx", x + size / (2 * 3.8) + 1.4 * size / 3.8) + new_circ.setAttribute("cy", y + size / (2 * 3.8) - 1.4 * size / 3.8) + new_circ.setAttribute("fill", "red") + logo.appendChild(new_circ) + return logo + } +set_prompt('right click/two finger/long press for menu; scroll for zoom, drag for pan') +// +// SVG canvas for drawing +// +var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg") + svg.style.position = 'absolute' + svg.style.backgroundColor = 'rgb(255,255,255)' + svg.style.top = mods.ui.header + svg.style.left = 0 + svg.style.zIndex = 0 + svg.style.overflow = 'visible' + svg.setAttribute('width',40) + svg.setAttribute('height',40) + svg.setAttribute('id','svg') + svg.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink") + document.body.appendChild(svg) +// +// link container +// +var svg = document.getElementById('svg') +var g = document.createElementNS('http://www.w3.org/2000/svg','g') + g.setAttribute('id','links') + svg.appendChild(g) +// +// file reading controls +// +var file = document.createElement('input') + file.setAttribute('type','file') + file.setAttribute('id','mod_input') + file.style.position = 'absolute' + file.style.left = 0 + file.style.top = 0 + file.style.width = 0 + file.style.height = 0 + file.style.opacity = 0 + file.addEventListener('change',function() { + mod_read_handler() + }) + document.body.appendChild(file) +var file = document.createElement('input') + file.setAttribute('type','file') + file.setAttribute('id','prog_input') + file.style.position = 'absolute' + file.style.left = 0 + file.style.top = 0 + file.style.width = 0 + file.style.height = 0 + file.style.opacity = 0 + file.addEventListener('change',function() { + prog_read_handler() + }) + document.body.appendChild(file) +// +// module container +// +var div = document.createElement('div') + div.setAttribute('id','modules') + document.body.appendChild(div) +// +// check for program load query +// +if (location.search.length > 0) { + var args = location.search.slice(1).split('&') + for (var a in args) { + var arg = args[a].split('=') + if (arg[0] == 'program') + prog_message_handler(arg[1]) + } + } +// +// program routines +// +function prog_read_handler(event) { + var file = document.getElementById('prog_input') + var file_reader = new FileReader() + file_reader.onload = prog_load_handler + file_reader.readAsText(file.files[0]) + mods.ui.progname = file.files[0].name + } +function prog_message_handler(filename) { + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + prog = JSON.parse(req.response) + prog_load(prog) + } + } + var index = filename.lastIndexOf('/') + if (index != -1) { + mods.ui.progname = filename.slice(index+1) + } + else + mods.ui.progname = filename + // + // send request, with random query to prevent caching + // + req.open('GET',filename+'?rnd='+Math.random()) + req.send() + } +function prog_load_handler(event) { + prog = JSON.parse(event.target.result) + prog_load(prog) + } +function prog_load(prog) { + // + // load modules + // + for (var idnumber in prog.modules) { + var module = prog.modules[idnumber] + var str = module.definition + try { + eval('var args = '+str) + } + catch (err) { + console.log(err.message) + return + } + args.definition = str + args.id = idnumber + var t = mods_transform() + var xw = t.ox-t.tx+(mods.ui.xstart-t.ox)/t.s + var yw = t.oy-t.ty+(mods.ui.ystart-t.oy)/t.s-mods.ui.header + args.top = parseFloat(module.top)+yw + args.left = parseFloat(module.left)+xw + args.filename = module.filename + add_module(args) + } + // + // load links + // + for (var linkid in prog.links) { + var str = prog.links[linkid] + eval('var link = '+str) + eval('var linksrc = '+link.source) + eval('var linkdst = '+link.dest) + var src = document.getElementById( + JSON.stringify({id:linksrc.id,type:linksrc.type,name:linksrc.name})) + var dst = document.getElementById( + JSON.stringify({id:linkdst.id,type:linkdst.type,name:linkdst.name})) + add_link(src,dst) + } + } +function save_program() { + set_prompt('program name? ') + get_prompt(mods.ui.progname,function(filename){ + mods.ui.progname = filename + var prog = {modules:{},links:[]} + var modules = document.getElementById('modules') + // + // save modules + // + for (var c = 0; c < modules.childNodes.length; ++c) { + var module = modules.childNodes[c] + var idnumber = module.id + prog.modules[idnumber] = { + definition:update_module_definition( + idnumber,module.dataset.definition), + top:module.dataset.top, + left:module.dataset.left, + filename: module.dataset.filename, + inputs:{}, + outputs:{} + } + } + // + // save links + // + var svg = document.getElementById('svg') + var links = svg.getElementById('links') + for (var l = 0; l < links.childNodes.length; ++l) { + var link = links.childNodes[l] + var linkid = link.id + prog.links.push(linkid) + } + // + // download + // + var text = JSON.stringify(prog) + var a = document.createElement('a') + a.setAttribute('href','data:text/plain;charset=utf-8,'+ + encodeURIComponent(text)) + a.setAttribute('download',filename) + a.style.display = 'none' + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + }) + } +function save_page() { + set_prompt('page name? ') + get_prompt(mods.ui.progname+".html",function(filename){ + mods.ui.progname = filename + var prog = {modules:{},links:[]} + var modules = document.getElementById('modules') + // + // save modules + // + for (var c = 0; c < modules.childNodes.length; ++c) { + var module = modules.childNodes[c] + var idnumber = module.id + prog.modules[idnumber] = { + definition:update_module_definition( + idnumber,module.dataset.definition), + top:module.dataset.top, + left:module.dataset.left, + filename: module.dataset.filename, + inputs:{}, + outputs:{} + } + } + // + // save links + // + var svg = document.getElementById('svg') + var links = svg.getElementById('links') + for (var l = 0; l < links.childNodes.length; ++l) { + var link = links.childNodes[l] + var linkid = link.id + prog.links.push(linkid) + } + // + // read mods.js + // + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + // + // construct page + // + var str = req.response + var text ="<html>\n" + text += "<head><meta charset='utf-8'>\n" + text += "<title>mods</title>\n" + text += "</head>\n" + text += "<body link='black' alink='black' vlink='black'>\n" + text += "<"+"script"+">\n" + text += str + text += "var prog = JSON.parse(JSON.stringify("+JSON.stringify(prog)+"))\n" + text += "window.mods_prog_load(prog)\n" + text += "</"+"script"+">\n" + text += "</body>\n" + text += "</html>\n" + // + // download page + // + var a = document.createElement('a') + a.setAttribute('href','data:text/plain;charset=utf-8,'+ + encodeURIComponent(text)) + a.setAttribute('download',filename) + a.style.display = 'none' + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + } + } + // + // send request, with random query to prevent caching + // + req.open('GET','js/mods.js'+'?rnd='+Math.random()) + req.send() + }) + } +// +// add program load to window +// +window.mods_prog_load = function(prog) { + prog_load(prog) + } +// +// module routines +// +function mod_read_handler(event) { + var file = document.getElementById('mod_input') + var file_reader = new FileReader() + file_reader.onload = mod_load_handler + file_reader.readAsText(file.files[0]) + } +function mod_message_handler(filename,top,left) { + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + var str = req.response + try { + eval('var args = '+str) + } + catch (err) { + console.log(err.message) + return + } + args.definition = str + args.id = String(Math.random()) + args.top = top + args.left = left + args.filename = filename + add_module(args) + } + } + // + // send request, with random query to prevent caching + // + req.open('GET',filename+'?rnd='+Math.random()) + req.send() + } +function mod_load_handler(event) { + str = event.target.result + try { + eval('var args = '+str) + } + catch (err) { + console.log(err.message) + return + } + args.definition = str + args.id = String(Math.random()) + args.top = mods.ui.top + args.left = mods.ui.left + args.filename = "" + var div = add_module(args) + return(div) + } +function add_module(args) { + var idnumber = args.id + mods.mod[idnumber] = args.mod + var modules = document.getElementById('modules') + // + // container + // + var container = document.createElement('div') + container.setAttribute("id",idnumber) + container.style.position = "absolute" + container.style.top = args.top + container.style.left = args.left + container.dataset.top = args.top + container.dataset.left = args.left + container.dataset.filename = args.filename + container.dataset.name = args.name + container.style.zIndex = 0 + container.style.width = window.innerWidth + container.dataset.definition = args.definition + modules.appendChild(container) + // + // name + // + var divname = document.createElement('div') + divname.appendChild(document.createTextNode(args.name)) + divname.addEventListener('mouseover',name_over) + divname.addEventListener('mouseout',name_out) + divname.addEventListener('mousedown',name_mousedown) + divname.addEventListener('touchstart',name_touchdown) + divname.style.backgroundColor = "rgb(210,240,210)" + divname.style.padding = 1.5*mods.ui.padding + divname.style.position = "absolute" + divname.style.cursor = 'default' + divname.style.top = 0 + divname.style.left = 0 + divname.style.textAlign = 'center' + divname.style.border = '2px solid' + divname.style.borderRadius = '10px' + container.appendChild(divname) + // + // controls + // + var divctrl = document.createElement('div') + var editspan = document.createElement('span') + editspan.innerHTML = 'edit' + editspan.style.fontWeight = 'normal' + editspan.addEventListener('mouseover',function(event){ + set_prompt('click to edit') + editspan.style.fontWeight = 'bold'}) + editspan.addEventListener('mouseout',function(event){ + set_prompt('') + editspan.style.fontWeight = 'normal'}) + editspan.addEventListener('mousedown',edit_module) + divctrl.appendChild(editspan) + var delspan = document.createElement('span') + delspan.innerHTML = ' delete ' + delspan.addEventListener('mouseover',function(event){ + set_prompt('click to delete') + delspan.style.fontWeight = 'bold'}) + delspan.addEventListener('mouseout',function(event){ + set_prompt('') + delspan.style.fontWeight = 'normal'}) + delspan.addEventListener('mousedown',function(event){ + delete_module(event.target.parentNode.parentNode.id)}) + divctrl.appendChild(delspan) + divctrl.style.backgroundColor = "rgb(240,220,220)" + divctrl.style.padding = mods.ui.padding + divctrl.style.position = "absolute" + divctrl.style.cursor = 'default' + divctrl.style.top = divname.clientHeight + divctrl.style.left = 0 + divctrl.style.textAlign = 'center' + divctrl.style.border = '2px solid' + divctrl.style.borderRadius = '10px' + container.appendChild(divctrl) + divctrl.style.left = divname.clientWidth/2-divctrl.clientWidth/2 + // + // interface + // + var divint = document.createElement('div') + divint.style.backgroundColor = "rgb(240,240,240)" + divint.style.padding = mods.ui.padding + divint.style.position = "absolute" + divint.style.top = divname.clientHeight+divctrl.clientHeight + divint.style.textAlign = 'center' + divint.style.border = '2px solid' + divint.style.borderRadius = '10px' + divint.setAttribute('id',JSON.stringify({id:idnumber,type:'interface'})) + divint.dataset.id = idnumber + divint.dataset.divNameSize = divname.clientWidth + args.interface(divint) + container.appendChild(divint) + divint.style.left = divname.clientWidth/2-divint.clientWidth/2 + // + // inputs + // + var divin = document.createElement('div') + divin.setAttribute('id',JSON.stringify({id:idnumber,type:'inputs'})) + var b = document.createElement('b') + b.appendChild(document.createTextNode('inputs')) + divin.appendChild(b) + divin.style.backgroundColor = "rgb(240,240,210)" + divin.style.padding = mods.ui.padding + divin.style.paddingLeft = '2px' + divin.style.position= "absolute" + divin.style.top = divname.clientHeight+divctrl.clientHeight + divin.style.textAlign = 'left' + divin.style.border = '2px solid' + divin.style.borderRadius = '10px' + divin.setAttribute('id',JSON.stringify({id:idnumber,type:'inputs'})) + divin.style.cursor = 'default' + divin.addEventListener('mousedown',nothing) + divin.addEventListener('touchstart',nothing) + divin.addEventListener('mouseup',nothing) + divin.addEventListener('touchend',nothing) + for (var v in args.inputs) { + var div = document.createElement('div') + if (args.inputs[v].label != undefined) + div.innerHTML += args.inputs[v].label + else + div.innerHTML += v + if (args.inputs[v].type != '') + div.innerHTML += ' ('+args.inputs[v].type+')' + div.setAttribute('id',JSON.stringify({id:idnumber,type:'inputs',name:v})) + div.addEventListener('mouseover',input_over) + div.addEventListener('mouseout',input_out) + div.addEventListener('mousedown',input_mousedown) + div.addEventListener('touchstart',input_touchdown) + div.dataset.links = JSON.stringify([]) + div.dataset.name = v + divin.appendChild(div) + div.dataset.id = JSON.stringify( + {id:idnumber,type:'input',name:v, + rnd:Math.random()}) // randomize for unique events + window.addEventListener(div.dataset.id,args.inputs[v].event) + } + container.appendChild(divin) + if ((Object.keys(args.inputs).length) == 0) + divin.style.visibility = 'hidden' + divin.style.left = divname.clientWidth/2 + -divint.clientWidth/2-divin.clientWidth + for (var i = 1; i < divin.childNodes.length; ++i) { + divin.childNodes[i].dataset.dx = divin.offsetLeft + +divin.childNodes[i].offsetLeft + divin.childNodes[i].dataset.dy = divin.offsetTop + +divin.childNodes[i].offsetTop + +divin.childNodes[i].offsetHeight/2 + } + // + // outputs + // + var divout = document.createElement('div') + divout.setAttribute('id',JSON.stringify({id:idnumber,type:'outputs'})) + var b = document.createElement('b') + b.appendChild(document.createTextNode('outputs')) + divout.appendChild(b) + divout.style.backgroundColor = "rgb(240,240,210)" + divout.style.padding = mods.ui.padding + divout.style.paddingRight = '2px' + divout.style.position = "absolute" + divout.style.top = divname.clientHeight+divctrl.clientHeight + divout.style.textAlign = 'right' + divout.addEventListener('mousedown',nothing) + divout.style.border = '2px solid' + divout.style.borderRadius = '10px' + divout.setAttribute('id',JSON.stringify({id:idnumber,type:'outputs'})) + divout.style.cursor = 'default' + divout.addEventListener('touchstart',nothing) + divout.addEventListener('mouseup',nothing) + divout.addEventListener('touchend',nothing) + for (var v in args.outputs) { + var div = document.createElement('div') + if (args.outputs[v].label != undefined) + div.innerHTML += args.outputs[v].label + else + div.innerHTML += v + if (args.outputs[v].type != '') + div.innerHTML += ' ('+args.outputs[v].type+')' + div.setAttribute('id',JSON.stringify({id:idnumber,type:'outputs',name:v})) + div.addEventListener('mouseover',output_over) + div.addEventListener('mouseout',output_out) + div.addEventListener('mousedown',output_mousedown) + div.addEventListener('touchstart',output_touchdown) + div.dataset.links = JSON.stringify([]) + div.dataset.name = v + divout.appendChild(div) + div.dataset.id = JSON.stringify( + {id:idnumber,type:'output',name:v, + rnd:Math.random()}) // randomize for unique events + window.addEventListener(div.dataset.id,args.outputs[v].event) + } + container.appendChild(divout) + if ((Object.keys(args.outputs).length) == 0) + divout.style.visibility = 'hidden' + divout.style.left = divname.clientWidth/2 + +divint.clientWidth/2 + for (var i = 1; i < divout.childNodes.length; ++i) { + divout.childNodes[i].dataset.dx = divout.offsetLeft + +divout.childNodes[i].offsetLeft + +divout.childNodes[i].offsetWidth + divout.childNodes[i].dataset.dy = divout.offsetTop + +divout.childNodes[i].offsetTop + +divout.childNodes[i].offsetHeight/2 + } + // + // initialization + // + args.init() + // + // resize to contents + // + container.style.width = divint.clientWidth+divin.clientWidth+divout.clientWidth + mods.fit(divint) + // + // return container + // + return(container) + } +function delete_module(idnumber) { + // + // delete links + // + var ins = document.getElementById( + JSON.stringify({id:idnumber,type:'inputs'})) + var outs = document.getElementById( + JSON.stringify({id:idnumber,type:'outputs'})) + for (var i = 1; i < ins.childNodes.length; ++i) { + var links = JSON.parse(ins.childNodes[i].dataset.links) + for (var l in links) + delete_link(links[l]) + } + for (var i = 1; i < outs.childNodes.length; ++i) { + var links = JSON.parse(outs.childNodes[i].dataset.links) + for (var l in links) + delete_link(links[l]) + } + // + // delete container + // + var modules = document.getElementById('modules') + var container = document.getElementById(idnumber) + modules.removeChild(container) + // + // clear prompt + // + set_prompt('') + } +function update_module_definition(id) { + // + // get definition + // + var module = document.getElementById(id) + var def = module.dataset.definition + // + // check for mod + // + if (mods.mod[id] == undefined) + return def + // + // split definition + // + var lines = def.split('\n') + // + // find init function + // + var line = 0 + while (line < lines.length) { + if (lines[line].indexOf("var init") == 0) + break + line += 1 + } + // + // read initializations up to inputs function + // + while (line < lines.length) { + if (lines[line].indexOf(".value =") != -1) { + var start = 4+lines[line].indexOf("mod.") + var end = lines[line].indexOf(".value") + var key = lines[line].slice(start,end) + var value = mods.mod[id][key]['value'] + if (value.indexOf('\n') != -1) + value = value.replace(/\n/g,"\\n") + lines[line] = " mod."+key+".value = '"+value+"'" + } + else if (lines[line].indexOf(".checked =") != -1) { + var start = 4+lines[line].indexOf("mod.") + var end = lines[line].indexOf(".checked") + var key = lines[line].slice(start,end) + var value = mods.mod[id][key]['checked'] + lines[line] = " mod."+key+".checked = "+value + } + if (lines[line].indexOf("var inputs") == 0) + break + line += 1 + } + return(lines.join('\n')) + } +function edit_module(evt) { + var mod = evt.target.parentNode.parentNode + var idnumber = mod.id + var def = update_module_definition(idnumber) + // + // open edit window + // + var top = mod.dataset.top + var left = mod.dataset.left + var name = mod.dataset.name + var filename = mod.dataset.filename + var fontsize = 100 + var win = window.open('') + var file = document.createElement('input') + file.setAttribute('type','file') + file.setAttribute('id','edit_module_file') + file.style.position = 'absolute' + file.style.left = 0 + file.style.top = 0 + file.style.width = 0 + file.style.height = 0 + file.style.opacity = 0 + file.addEventListener('change',function() { + edit_module_read_handler() + }) + win.document.body.appendChild(file) + function edit_module_read_handler() { + var file = win.document.getElementById('edit_module_file') + var file_reader = new FileReader() + file_reader.onload = edit_module_load_handler + file_reader.readAsText(file.files[0]) + } + function edit_module_load_handler(event) { + str = event.target.result + var text = win.document.getElementById('edit_module_text') + text.value = str + update_module(idnumber) + win.close() + } + if (filename != "undefined" && filename != "") { + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('reload from server')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + text.value = req.response + update_module(idnumber) + } + } + req.open('GET',filename+'?rnd='+Math.random()) + req.send() + win.close() + }) + win.document.body.appendChild(btn) + } + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('load from file')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + var file = win.document.getElementById('edit_module_file') + file.value = null + file.click() + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('update and close')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + update_module(idnumber) + win.close() + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('update')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + update_module(idnumber) + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('close')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + win.close() + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('save')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + var a = document.createElement('a') + a.setAttribute('href','data:text/plain;charset=utf-8,'+ + encodeURIComponent(text.value)) + a.setAttribute('download',name) + a.style.display = 'none' + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('increase font')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + fontsize *= 1.2 + text.style.fontSize = fontsize+'%' + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('decrease font')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + fontsize /= 1.2 + text.style.fontSize = fontsize+'%' + }) + win.document.body.appendChild(btn) + win.document.body.appendChild(document.createElement('br')) + var text = document.createElement('textarea') + text.setAttribute('id','edit_module_text') + text.style.width = '100%' + text.style.height= '100%' + text.value = def + win.document.body.appendChild(text) + function reload_module(idnumber) { + } + function update_module(idnumber) { + // + // save links + // + var ins = document.getElementById( + JSON.stringify({id:idnumber,type:'inputs'})) + var inlinks = [] + for (var i = 1; i < ins.childNodes.length; ++i) { + var links = JSON.parse(ins.childNodes[i].dataset.links) + for (var l in links) + inlinks.push(links[l]) + } + var outs = document.getElementById( + JSON.stringify({id:idnumber,type:'outputs'})) + var outlinks = [] + for (var i = 1; i < outs.childNodes.length; ++i) { + var links = JSON.parse(outs.childNodes[i].dataset.links) + for (var l in links) + outlinks.push(links[l]) + } + // + // delete module + // + delete_module(idnumber) + // + // add module + // + var def = text.value + try { + eval('var args = '+def) + } + catch (err) { + console.log(err.message) + return + } + args.definition = def + args.id = idnumber + args.top = top + args.left = left + args.filename = filename + add_module(args) + // + // add links + // + for (var l in inlinks) { + eval('var link = '+inlinks[l]) + eval('var linksrc = '+link.source) + eval('var linkdst = '+link.dest) + var src = document.getElementById( + JSON.stringify( + {id:linksrc.id,type:linksrc.type,name:linksrc.name})) + var dst = document.getElementById( + JSON.stringify( + {id:linkdst.id,type:linkdst.type,name:linkdst.name})) + add_link(src,dst) + } + for (var l in outlinks) { + eval('var link = '+outlinks[l]) + eval('var linksrc = '+link.source) + eval('var linkdst = '+link.dest) + var src = document.getElementById( + JSON.stringify( + {id:linksrc.id,type:linksrc.type,name:linksrc.name})) + var dst = document.getElementById( + JSON.stringify( + {id:linkdst.id,type:linkdst.type,name:linkdst.name})) + add_link(src,dst) + } + } + } +// +// UI routines +// +function set_prompt(txt) { + var span = document.getElementById('prompt') + span.childNodes[0].innerHTML = ' '+txt + } +function get_prompt(txt,fn) { + var div = document.getElementById('prompt') + if (div.childNodes.length > 2) + // + // already getting a prompt + // + return + var text = document.createElement('input') + text.type = 'text' + text.size = 20 + text.value = txt + text.addEventListener('keydown',function(evt){ + if (evt.key == 'Enter') { + var div = document.getElementById('prompt') + div.removeChild(div.childNodes[1]) + div.removeChild(div.childNodes[1]) + set_prompt('') + fn(text.value) + } + }) + div.appendChild(text) + div.appendChild(document.createTextNode(' (enter)')) + text.focus() + } +function nothing(evt) { + evt.preventDefault() + evt.stopPropagation() + } +// +// link routines +// +mods.add_link = function(src,dst) { + add_link(src,dst) + } +function add_link(src,dst) { + // + // link order from out to in + // + if (src.id.indexOf('outputs') == -1) { + var tmp = src + src = dst + dst = tmp + } + // + // check if link exists + // + var id = JSON.stringify({source:src.id,dest:dst.id}) + var link = document.getElementById(id) + if (link != null) { + // + // yes, remove it and return + // + delete_link(id) + return + } + // + // no, add link + // + var links = JSON.parse(src.dataset.links) + links.push(id) + src.dataset.links = JSON.stringify(links) + var links = JSON.parse(dst.dataset.links) + links.push(id) + dst.dataset.links = JSON.stringify(links) + // + // draw link + // + xsrc = src.parentNode.parentNode.offsetLeft + +parseFloat(src.dataset.dx) + ysrc = src.parentNode.parentNode.offsetTop + +parseFloat(src.dataset.dy) + -mods.ui.header + xdst = dst.parentNode.parentNode.offsetLeft + +parseFloat(dst.dataset.dx) + ydst = dst.parentNode.parentNode.offsetTop + +parseFloat(dst.dataset.dy) + -mods.ui.header + var links = document.getElementById('links') + var path = document.createElementNS('http://www.w3.org/2000/svg','path') + path.setAttribute('id',id) + path.setAttribute('d','M'+xsrc+','+ysrc+' C'+(xsrc+mods.ui.bezier)+',' + +ysrc+' '+(xdst-mods.ui.bezier)+','+ydst+' '+xdst+','+ydst) + path.setAttribute('fill','none') + path.setAttribute('stroke','rgb(0,0,128)') + path.setAttribute('stroke-width',3) + links.appendChild(path) + /* + // + // don't trigger link + // + eval('var evtid = '+src.id) + evtid.type = 'output' + var evtstr = JSON.stringify(evtid) + var evt = new CustomEvent(evtstr) + window.dispatchEvent(evt) + */ + } +function delete_link(linkid) { + // + // delete a link + // + var links = document.getElementById('links') + links.removeChild(document.getElementById(linkid)) + link = JSON.parse(linkid) + src = document.getElementById(link.source) + dst = document.getElementById(link.dest) + var links = JSON.parse(src.dataset.links) + var index = links.indexOf(linkid) + links.splice(index,1) + src.dataset.links = JSON.stringify(links) + var links = JSON.parse(dst.dataset.links) + var index = links.indexOf(linkid) + links.splice(index,1) + dst.dataset.links = JSON.stringify(links) + } +function draw_links(idnumber,color) { + // + // draw a module's links + // + var ins = document.getElementById( + JSON.stringify({id:idnumber,type:'inputs'})) + var outs = document.getElementById( + JSON.stringify({id:idnumber,type:'outputs'})) + for (var i = 1; i < ins.childNodes.length; ++i) { + var links = JSON.parse(ins.childNodes[i].dataset.links) + for (var l in links) + draw_link(links[l],color) + } + for (var i = 1; i < outs.childNodes.length; ++i) { + var links = JSON.parse(outs.childNodes[i].dataset.links) + for (var l in links) + draw_link(links[l],color) + } + } +function draw_link(id,color) { + // + // draw a link + // + var link = JSON.parse(id) + src = document.getElementById(link.source) + dst = document.getElementById(link.dest) + var path = document.getElementById(id) + xsrc = src.parentNode.parentNode.offsetLeft + +parseFloat(src.dataset.dx) + ysrc = src.parentNode.parentNode.offsetTop + +parseFloat(src.dataset.dy) + -mods.ui.header + xdst = dst.parentNode.parentNode.offsetLeft + +parseFloat(dst.dataset.dx) + ydst = dst.parentNode.parentNode.offsetTop + +parseFloat(dst.dataset.dy) + -mods.ui.header + path.setAttribute('d','M'+xsrc+','+ysrc+' C'+(xsrc+mods.ui.bezier)+',' + +ysrc+' '+(xdst-mods.ui.bezier)+','+ydst+' '+xdst+','+ydst) + path.setAttribute('stroke',color) + } +// +// mods routines to be called from modules +// +mods.fit = function(div) { + // + // fit a module + // + div.style.left = div.dataset.divNameSize/2-div.clientWidth/2 + var divin = document.getElementById( + JSON.stringify( + {id:div.dataset.id,type:'inputs'})) + divin.style.left = div.dataset.divNameSize/2-div.clientWidth/2-divin.clientWidth + var divout = document.getElementById( + JSON.stringify( + {id:div.dataset.id,type:'outputs'})) + divout.style.left = div.dataset.divNameSize/2+div.clientWidth/2 + } +mods.output = function(mod,varname,val) { + // + // send module outputs + // + var div = mod.div + var key = JSON.parse(div.id) + var idnumber = key.id + var out = document.getElementById( + JSON.stringify( + {id:idnumber,type:'outputs',name:varname})) + var links = JSON.parse(out.dataset.links) + for (var l in links) { + var link = JSON.parse(links[l]) + var dest = JSON.parse(link.dest) + var divin = document.getElementById(JSON.stringify( + {id:dest.id,type:'inputs',name:dest.name})) + var evt = new CustomEvent(divin.dataset.id,{detail:val}) + window.dispatchEvent(evt) + } + } +// +// module mod-ification calls +// +mods.module_create = function(args) { + var event = {target:{result:args}} + var div = mod_load_handler(event) + return(div) + } +mods.module_delete = function(id) { + delete_module(id) + } +mods.module_id = function(div) { + return div.parentNode.id + } +mods.module_left = function(id) { + var module = document.getElementById(id) + return (parseInt(module.style.left)) + } +mods.module_move = function(id,dx,dy) { + var module = document.getElementById(id) + var top = parseInt(module.style.top) + module.style.top = top+dy + module.dataset.top = top+dy + var left = parseInt(module.style.left) + module.style.left = left+dx + module.dataset.left = left+dx + draw_links(id,mods.ui.link_color) + } +mods.module_inputs = function(id,index) { + var module = document.getElementById(id) + var inputs = document.getElementById( + JSON.stringify({id:id,type:'inputs'})) + console.log(inputs.childNodes[index]) + } +mods.module_outputs = function(id,index) { + var module = document.getElementById(id) + var outputs = document.getElementById( + JSON.stringify({id:id,type:'outputs'})) + console.log(outputs.childNodes[index]) + } +mods.module_position = function(id,x,y) { + var module = document.getElementById(id) + var top = parseInt(module.style.top) + module.style.top = y + module.dataset.top = y + var left = parseInt(module.style.left) + module.style.left = x + module.dataset.left = x + draw_links(id,mods.ui.link_color) + } +mods.module_top = function(id) { + var module = document.getElementById(id) + return (parseInt(module.style.top)) + } +mods.module_width = function(id) { + var module = document.getElementById(id) + return (parseInt(module.clientWidth)) + } +// +// input event handlers +// +function input_over(evt) { + evt.target.style.fontWeight = 'bold' + var links = JSON.parse(evt.target.dataset.links) + for (var l in links) + draw_link(links[l],mods.ui.link_highlight) + if (mods.ui.source == null) + set_prompt('click to link') + } +function input_out(evt) { + evt.target.style.fontWeight = 'normal' + var links = JSON.parse(evt.target.dataset.links) + for (var l in links) + draw_link(links[l],mods.ui.link_color) + if (mods.ui.source == null) + set_prompt('') + } +function input_mousedown(evt) { + if (mods.ui.source == null) { + mods.ui.source = evt.target + set_prompt('variable to link/unlink to?') + } + else { + add_link(mods.ui.source,evt.target) + set_prompt('') + mods.ui.source = null + } + } +function input_touchdown(evt) { + if (mods.ui.source == null) { + mods.ui.source = evt.target + set_prompt('variable to link/unlink to?') + } + else { + add_link(mods.ui.source,evt.target) + set_prompt('') + mods.ui.source = null + } + } +// +// output event handlers +// +function output_over(evt) { + evt.target.style.fontWeight = 'bold' + var links = JSON.parse(evt.target.dataset.links) + for (var l in links) + draw_link(links[l],mods.ui.link_highlight) + if (mods.ui.source == null) + set_prompt('click to link') + } +function output_out(evt) { + evt.target.style.fontWeight = 'normal' + var links = JSON.parse(evt.target.dataset.links) + for (var l in links) + draw_link(links[l],mods.ui.link_color) + if (mods.ui.source == null) + set_prompt('') + } +function output_mousedown(evt) { + if (mods.ui.source == null) { + mods.ui.source = evt.target + set_prompt('variable to link/unlink to?') + } + else { + add_link(mods.ui.source,evt.target) + set_prompt('') + mods.ui.source = null + } + } +function output_touchdown(evt) { + if (mods.ui.source == null) { + mods.ui.source = evt.target + set_prompt('variable to link/unlink to?') + } + else { + add_link(mods.ui.source,evt.target) + set_prompt('') + mods.ui.source = null + } + } +// +// name event handlers +// +function name_over(evt) { + evt.target.style.fontWeight = 'bold' + if (mods.ui.source == null) + set_prompt('click and drag to move') + } +function name_out(evt) { + evt.target.style.fontWeight = 'normal' + if (mods.ui.source == null) + set_prompt('') + } +function name_mousedown(evt) { + evt.preventDefault() + evt.stopPropagation() + var div = document.getElementById(evt.target.parentNode.id) + div.style.zIndex = 1 + mods.ui.xstart = evt.clientX + mods.ui.ystart = evt.clientY + mods.ui.selected[evt.target.parentNode.id] = true + window.addEventListener('mousemove',name_mousemove) + window.addEventListener('mouseup',name_mouseup) + } +function name_mousemove(evt) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + var dx = (evt.clientX-mods.ui.xstart)/t.s + var dy = (evt.clientY-mods.ui.ystart)/t.s + var newleft = parseFloat(div.dataset.left)+dx + var newtop = parseFloat(div.dataset.top)+dy + div.style.left = newleft+'px' + div.style.top = newtop+'px' + draw_links(id,mods.ui.link_color) + } + } +function name_mouseup(evt) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + div.style.zIndex = 0 + div.childNodes[0].style.fontWeight = 'normal' + var dx = (evt.clientX-mods.ui.xstart)/t.s + var dy = (evt.clientY-mods.ui.ystart)/t.s + div.dataset.left = parseFloat(div.dataset.left)+dx + div.dataset.top = parseFloat(div.dataset.top)+dy + window.removeEventListener('mousemove',name_mousemove) + window.removeEventListener('mouseup',name_mouseup) + } + mods.ui.selected = {} + } +function name_touchdown(evt) { + evt.preventDefault() + evt.stopPropagation() + var div = document.getElementById(evt.target.parentNode.id) + div.style.zIndex = 1 + mods.ui.xstart = evt.changedTouches[0].pageX + mods.ui.ystart = evt.changedTouches[0].pageY + mods.ui.selected[evt.target.parentNode.id] = true + window.addEventListener('touchmove',name_touchmove) + window.addEventListener('touchend',name_touchup) + } +function name_touchmove(evt) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + var dx = (evt.changedTouches[0].pageX-mods.ui.xstart)/t.s + var dy = (evt.changedTouches[0].pageY-mods.ui.ystart)/t.s + var newleft = parseFloat(div.dataset.left)+dx + var newtop = parseFloat(div.dataset.top)+dy + div.style.left = newleft+'px' + div.style.top = newtop+'px' + draw_links(id,mods.ui.link_color) + } + } +function name_touchup(evt) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + div.style.zIndex = 0 + var dx = (evt.changedTouches[0].pageX-mods.ui.xstart)/t.s + var dy = (evt.changedTouches[0].pageY-mods.ui.ystart)/t.s + div.dataset.left = parseFloat(div.dataset.left)+dx + div.dataset.top = parseFloat(div.dataset.top)+dy + window.removeEventListener('touchmove',name_touchmove) + window.removeEventListener('touchend',name_touchup) + } + mods.ui.selected = {} + } +})() +var prog = JSON.parse(JSON.stringify({"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1287.611647642926","left":"4490.813999470311","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1904.6116476429258","left":"4948.813999470311","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1774.611647642926","left":"4517.813999470311","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 = ''\n }\n//\n// inputs\n//\nvar inputs = {\n distances:{type:'F32',\n event:function(evt){\n mod.distances = evt.detail\n var h = mod.distances.height\n var w = mod.distances.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.height = mod.distances.height \n ctx.canvas.width = mod.distances.width\n if (mod.offset.value != '')\n offset()\n }},\n offset:{type:'number',\n event:function(evt){\n mod.offset.value = evt.detail\n offset()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1387.611647642926","left":"4949.813999470311","filename":"undefined","inputs":{},"outputs":{}},"0.08127540142793499":{"definition":"//\n// read png\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 = 'read png'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\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 imageInfo:{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// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // file input control\n //\n var file = document.createElement('input')\n file.setAttribute('type','file')\n file.setAttribute('id',div.id+'file_input')\n file.style.position = 'absolute'\n file.style.left = 0\n file.style.top = 0\n file.style.width = 0\n file.style.height = 0\n file.style.opacity = 0\n file.addEventListener('change',function() {\n 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(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 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 // 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//\nfunction 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//\nfunction 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//\nfunction 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 }\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//\nfunction 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)\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 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 = 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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1229.611647642926","left":"2335.8139994703106","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1420.611647642926","left":"3978.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.2892270043957246":{"definition":"//\n// view toolpath\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// todo:\n// erase and update new path\n// show depth info\n// show size\n// calculate camera far\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'view toolpath'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar 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//\nvar 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//\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('(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//\nfunction 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//\nfunction 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 geometry = new THREE.Geometry()\n geometry.vertices.push(start,stop)\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\t renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction 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\t scene = new THREE.Scene()\n\t mod.scene = scene\n\t var width = win.innerWidth\n\t var height = win.innerHeight\n\t var aspect = width/height\n\t var near = 0.1\n\t var far = 1000000\n\t camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n\t mod.camera = camera\n\t scene.add(camera)\n\t //\n\t // add renderer\n\t //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n\t renderer.setSize(width,height)\n\t 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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"2124.611647642926","left":"3308.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.9557599338778935":{"definition":"//\n// mill raster 2D\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 = 'mill raster 2D'\n//\n// initialization\n//\nvar 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//\nvar inputs = {\n imageInfo:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.width\n ctx.canvas.height = mod.height\n }},\n path:{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 }\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:{type:'object',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n diameter:{type:'number',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{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:{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//\nvar 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//\nfunction 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 'offset number':\n mod.number.value = settings[s]\n break\n }\n }\n }\n//\n// clear_path\n//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"1345.611647642926","left":"3331.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.10309904694903338":{"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 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 = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = 1\n mod.sort.checked = true\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 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 > 1) && (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 > 1) && (sort == false)) || (vecpath.length == 1))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1890.6116476429258","left":"4022.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.8622681794886853":{"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":"2112.9754468998394","left":"2827.7533689104926","filename":"modules/file/save","inputs":{},"outputs":{}},"0.992472539363189":{"definition":"//\n// set object\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 = 'set PCB defaults'\n//\n// initialization\n//\nvar init = function() {\n //\n add_output('mill 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.008'\n add_variable('max depth (in)','var02')\n mod.var02.value = '0.008'\n add_variable('offset number','var03')\n mod.var03.value = '4'\n //\n add_output('mill outline (1/32)')\n add_variable('tool diameter (in)','var10')\n mod.var10.value = '0.027559055118110236'\n add_variable('cut depth (in)','var11')\n mod.var11.value = '0.027559055118110236'\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 //\n }\n//\n// inputs\n//\nvar inputs = {}\n//\n// outputs\n//\nvar outputs = {\n settings:{type:'',\n event:function(vars){\n mods.output(mod,'settings',vars)\n }\n }\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n }\n//\n// local functions\n//\nfunction 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] = {span:span,variables:{}}\n mod.div.appendChild(btn)\n mod.setting = label\n mod.div.appendChild(document.createElement('br'))\n }\nfunction 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//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1265.611647642926","left":"2787.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.8764917270856811":{"definition":"//\n// path to G-code\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2018\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// Updated: Neil Gershenfeld\n// Date: Oct 28 2020\n// Comments: added mm/s vs mm/min option\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 = '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.tool.value = '1'\n mod.coolantoff.checked = true\n mod.formatMm.checked = true\n mod.unitMinutes.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 speed: '))\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 // 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.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 // second or minute rate units \n //\n div.appendChild(document.createTextNode('rate units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitSeconds'\n input.checked = true\n div.appendChild(input)\n mod.unitSeconds = input\n div.appendChild(document.createTextNode('second'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitMinutes'\n div.appendChild(input)\n mod.unitMinutes = input\n div.appendChild(document.createTextNode('minute'))\n }\n//\n// local functions\n//\nfunction make_path() {\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 if (mod.unitMinutes.checked) {\n cut_speed *= 60\n plunge_speed *= 60\n }\n var spindle_speed = parseFloat(mod.spindlespeed.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 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 //str += \"G04 P1\\n\" // give spindle 1 second to spin up...........................comment out\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 str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\"\n str += \"G00X\"+x.toFixed(4)+\"Y\"+y.toFixed(4)+\"Z\"+jog_height.toFixed(4)+\"\\n\"\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 }\n }\n //\n // finish\n //\n str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\" // move up before stopping spindle\n //\n str += \"G1X0Y0Z0\\n\" // movetoXYZ0\n //\n str += \"M05\\n\" // spindle stop\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 // 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\n","top":"1644.892543840524","left":"2796.9482640838332","filename":"modules/path/formats/g-code","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.08127540142793499\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.08127540142793499\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.992472539363189\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"settings\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8764917270856811\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8764917270856811\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8622681794886853\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}"]})) +window.mods_prog_load(prog) +</script> +</body> +</html> diff --git a/docs/Instruction/images/proverXL/mods/mill_2D_PCB_png.txt b/docs/Instruction/images/proverXL/mods/mill_2D_PCB_png.txt new file mode 100644 index 0000000000000000000000000000000000000000..3fdbc2c7efd6026ea73d24b2871225647237fbbb --- /dev/null +++ b/docs/Instruction/images/proverXL/mods/mill_2D_PCB_png.txt @@ -0,0 +1 @@ +{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1209.611647642926","left":"3989.8139994703106","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1826.6116476429258","left":"4447.813999470311","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1696.611647642926","left":"4016.8139994703106","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 = ''\n }\n//\n// inputs\n//\nvar inputs = {\n distances:{type:'F32',\n event:function(evt){\n mod.distances = evt.detail\n var h = mod.distances.height\n var w = mod.distances.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.height = mod.distances.height \n ctx.canvas.width = mod.distances.width\n if (mod.offset.value != '')\n offset()\n }},\n offset:{type:'number',\n event:function(evt){\n mod.offset.value = evt.detail\n offset()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1309.611647642926","left":"4448.813999470311","filename":"undefined","inputs":{},"outputs":{}},"0.08127540142793499":{"definition":"//\n// read png\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 = 'read png'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\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 imageInfo:{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// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // file input control\n //\n var file = document.createElement('input')\n file.setAttribute('type','file')\n file.setAttribute('id',div.id+'file_input')\n file.style.position = 'absolute'\n file.style.left = 0\n file.style.top = 0\n file.style.width = 0\n file.style.height = 0\n file.style.opacity = 0\n file.addEventListener('change',function() {\n 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(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 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 // 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//\nfunction 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//\nfunction 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//\nfunction 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 }\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//\nfunction 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)\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 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 = 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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1151.611647642926","left":"1834.8139994703106","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1342.611647642926","left":"3477.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.2892270043957246":{"definition":"//\n// view toolpath\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// todo:\n// erase and update new path\n// show depth info\n// show size\n// calculate camera far\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'view toolpath'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar 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//\nvar 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//\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('(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//\nfunction 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//\nfunction 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 geometry = new THREE.Geometry()\n geometry.vertices.push(start,stop)\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\t renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction 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\t scene = new THREE.Scene()\n\t mod.scene = scene\n\t var width = win.innerWidth\n\t var height = win.innerHeight\n\t var aspect = width/height\n\t var near = 0.1\n\t var far = 1000000\n\t camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n\t mod.camera = camera\n\t scene.add(camera)\n\t //\n\t // add renderer\n\t //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n\t renderer.setSize(width,height)\n\t 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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"2046.6116476429258","left":"2807.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.9557599338778935":{"definition":"//\n// mill raster 2D\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 = 'mill raster 2D'\n//\n// initialization\n//\nvar 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//\nvar inputs = {\n imageInfo:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.width\n ctx.canvas.height = mod.height\n }},\n path:{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 }\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:{type:'object',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n diameter:{type:'number',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{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:{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//\nvar 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//\nfunction 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 'offset number':\n mod.number.value = settings[s]\n break\n }\n }\n }\n//\n// clear_path\n//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"1267.611647642926","left":"2830.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.10309904694903338":{"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 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 = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = 1\n mod.sort.checked = true\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 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 > 1) && (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 > 1) && (sort == false)) || (vecpath.length == 1))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1812.6116476429258","left":"3521.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.8622681794886853":{"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":"2034.9754468998394","left":"2326.7533689104926","filename":"modules/file/save","inputs":{},"outputs":{}},"0.992472539363189":{"definition":"//\n// set object\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 = 'set PCB defaults'\n//\n// initialization\n//\nvar init = function() {\n //\n add_output('mill 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.008'\n add_variable('max depth (in)','var02')\n mod.var02.value = '0.008'\n add_variable('offset number','var03')\n mod.var03.value = '4'\n //\n add_output('mill outline (1/32)')\n add_variable('tool diameter (in)','var10')\n mod.var10.value = '0.027559055118110236'\n add_variable('cut depth (in)','var11')\n mod.var11.value = '0.027559055118110236'\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 //\n }\n//\n// inputs\n//\nvar inputs = {}\n//\n// outputs\n//\nvar outputs = {\n settings:{type:'',\n event:function(vars){\n mods.output(mod,'settings',vars)\n }\n }\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n }\n//\n// local functions\n//\nfunction 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] = {span:span,variables:{}}\n mod.div.appendChild(btn)\n mod.setting = label\n mod.div.appendChild(document.createElement('br'))\n }\nfunction 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//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1187.611647642926","left":"2286.8139994703106","filename":"undefined","inputs":{},"outputs":{}},"0.8764917270856811":{"definition":"//\n// path to G-code\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2018\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// Updated: Neil Gershenfeld\n// Date: Oct 28 2020\n// Comments: added mm/s vs mm/min option\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 = '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.tool.value = '1'\n mod.coolantoff.checked = true\n mod.formatMm.checked = true\n mod.unitMinutes.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 speed: '))\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 // 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.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 // second or minute rate units \n //\n div.appendChild(document.createTextNode('rate units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitSeconds'\n input.checked = true\n div.appendChild(input)\n mod.unitSeconds = input\n div.appendChild(document.createTextNode('second'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitMinutes'\n div.appendChild(input)\n mod.unitMinutes = input\n div.appendChild(document.createTextNode('minute'))\n }\n//\n// local functions\n//\nfunction make_path() {\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 if (mod.unitMinutes.checked) {\n cut_speed *= 60\n plunge_speed *= 60\n }\n var spindle_speed = parseFloat(mod.spindlespeed.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 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 //str += \"G04 P1\\n\" // give spindle 1 second to spin up...........................comment out\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 str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\"\n str += \"G00X\"+x.toFixed(4)+\"Y\"+y.toFixed(4)+\"Z\"+jog_height.toFixed(4)+\"\\n\"\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 }\n }\n //\n // finish\n //\n str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\" // move up before stopping spindle\n //\n str += \"G1X0Y0Z0\\n\" // movetoXYZ0\n //\n str += \"M05\\n\" // spindle stop\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 // 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\n","top":"1566.892543840524","left":"2295.9482640838332","filename":"modules/path/formats/g-code","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.08127540142793499\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.08127540142793499\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.992472539363189\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"settings\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8764917270856811\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8764917270856811\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8622681794886853\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}"]} \ No newline at end of file diff --git a/docs/Instruction/images/proverXL/mods/mill_2D_PCB_svg.html b/docs/Instruction/images/proverXL/mods/mill_2D_PCB_svg.html new file mode 100644 index 0000000000000000000000000000000000000000..d873f5a36b13ef118d20953e7775a9b30605ba9d --- /dev/null +++ b/docs/Instruction/images/proverXL/mods/mill_2D_PCB_svg.html @@ -0,0 +1,1915 @@ +<html> +<head><meta charset='utf-8'> +<title>mods</title> +</head> +<body link='black' alink='black' vlink='black'> +<script> +// +// mods.js +// +// Neil Gershenfeld +// (c) Massachusetts Institute of Technology 2018 +// +// This work may be reproduced, modified, distributed, performed, and +// displayed for any purpose, but must acknowledge the mods +// project. Copyright is retained and must be preserved. The work is +// provided as is; no warranty is provided, and users accept all +// liability. +// +// closure +// +(function(){ +// +// globals +// +var mods = {} +mods.mod = {} +mods.globals = {} +mods.ui = {source:null, + progname:'', + padding:7, + bezier:100, + canvas:250, + rows:5, + cols:20, + link_color:'rgb(0,0,128)', + link_highlight:'rgb(255,0,0)', + header:50, + selected:{}, + mousedown:null, + menu:null, + top:null, + left:null, + xstart:null, + ystart:null, + xtrans:null, + ytrans:null + } +// +// UI +// +document.body.style.overflow = "hidden" +function mods_transform() { + var transform = document.body.style.transform + var m = new DOMMatrix(getComputedStyle(document.body).transform) + var s = m.m11 + var tx = m.m41/s + var ty = m.m42/s + var origin = document.body.style.transformOrigin + var pxx = origin.indexOf('px') + var ox = parseFloat(origin.slice(0,pxx)) + var pxy = origin.indexOf('px',pxx+2) + var oy = parseFloat(origin.slice(pxx+2,pxy)) + return({s:s,tx:tx,ty:ty,ox:ox,oy:oy}) + } +document.body.style.transform = 'scale(1) translate(0px,0px)' +document.body.style.transformOrigin = '0px 0px' +// +// scroll wheel event +// +window.addEventListener('wheel',function(evt) { + /* + (xw+tx-ox)*s+ox = xs + xw = ox-tx+(xs-ox)/s + (xw+tx0-ox0)*s+ox0 = (xw+tx1-ox1)*s+ox1 + (tx0-ox0)*s+ox0 = (tx1-ox1)*s+ox1 + tx0+(ox1-ox0)+(ox0-ox1)/s = tx1 + tx0+(ox1-ox0)*(1-1/s) = tx1 + */ + var el = document.elementFromPoint(evt.pageX,evt.pageY) + if ((el.tagName == "HTML") || (el.tagName == "BODY")) { + set_prompt('scroll to zoom') + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + if (evt.deltaY > 0) + var scale = t.s*1.1 + else + var scale = t.s*0.9 + var tx = t.tx+(evt.pageX-t.ox)*(1-1/t.s) + var ty = t.ty+(evt.pageY-t.oy)*(1-1/t.s) + document.body.style.transform = `scale(${scale}) translate(${tx}px,${ty}px)` + document.body.style.transformOrigin = `${evt.pageX}px ${evt.pageY}px` + } + }) +// +// body mouse events +// +window.addEventListener('mousedown',function(evt) { + // + // get element mouse is over + // + var el = document.elementFromPoint(evt.pageX,evt.pageY) + // + // check if on body + // + if ((el.tagName == "HTML") || (el.tagName == "BODY")) { + // + // remember button + // + mods.ui.mousedown = evt.button + if (mods.ui.mousedown == 0) { + set_prompt('left-drag to pan, right-drag to select') + if (mods.ui.menu != null) { + document.body.removeChild(mods.ui.menu) + mods.ui.menu = null + } + } + else if (mods.ui.mousedown == 2) { + set_prompt('menu; left-drag to pan, right-drag to select') + } + // + // remember position + // + var t = mods_transform() + mods.ui.xstart = evt.pageX + mods.ui.ystart = evt.pageY + mods.ui.xtrans = t.tx + mods.ui.ytrans = t.ty + } + }) +window.addEventListener('mousemove',function(evt) { + // + // mouse move + // + if (mods.ui.mousedown != null) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + if (mods.ui.mousedown == 0) { + // + // pan on left drag + // + xtrans = mods.ui.xtrans+(evt.pageX-mods.ui.xstart)/t.s + ytrans = mods.ui.ytrans+(evt.pageY-mods.ui.ystart)/t.s + document.body.style.transform = `scale(${t.s}) translate(${xtrans}px,${ytrans}px)` + } + else if (mods.ui.mousedown == 2) { + // + // select on right drag + // + var rect = document.getElementById('svgrect') + if (rect == undefined) { + // + // start dragging + // + if (mods.ui.menu != null) { + document.body.removeChild(mods.ui.menu) + mods.ui.menu = null + } + set_prompt('right-drag to select') + var t = mods_transform() + var rect = document.createElementNS('http://www.w3.org/2000/svg','rect') + rect.setAttribute('id','svgrect') + rect.setAttribute('x',t.ox-t.tx+(evt.pageX-t.ox)/t.s) + rect.setAttribute('y',t.oy-t.ty+(evt.pageY-t.oy)/t.s-mods.ui.header) + rect.setAttribute('width',0) + rect.setAttribute('height',0) + rect.setAttribute('fill','rgb(200,200,200)') + rect.setAttribute('stroke','none') + var svg = document.getElementById('svg') + svg.insertBefore(rect,svg.firstChild) + } + else { + // + // continue dragging + // + var rect = document.getElementById('svgrect') + var xp = t.ox-t.tx+(mods.ui.xstart-t.ox)/t.s + var yp = t.oy-t.ty+(mods.ui.ystart-t.oy)/t.s-mods.ui.header + var xw = t.ox-t.tx+(evt.pageX-t.ox)/t.s + var yw = t.oy-t.ty+(evt.pageY-t.oy)/t.s-mods.ui.header + if (xw < xp) { + rect.setAttribute('x',xw) + rect.setAttribute('width',xp-xw) + } + else + rect.setAttribute('width',xw-xp) + if (yw < yp) { + rect.setAttribute('y',yw) + rect.setAttribute('height',yp-yw) + } + else + rect.setAttribute('height',yw-yp) + } + } + } + }) +window.addEventListener('mouseup',function(evt) { + // + // mouse up + // + mods.ui.mousedown = null + // + // check for selection rectangle + // + var rect = document.getElementById('svgrect') + if (rect != null) { + // + // rectangle exists, selecting modules + // + var x = parseFloat(rect.getAttribute('x')) + var y = parseFloat(rect.getAttribute('y')) + var width = parseFloat(rect.getAttribute('width')) + var height = parseFloat(rect.getAttribute('height')) + svg.removeChild(rect) + var modules = document.getElementById('modules') + // + // loop to find selected modules + // + mods.ui.selected = {} + for (var module in modules.childNodes) { + var container = modules.childNodes[module] + var id = container.id + if (id != undefined) { + var name = container.firstChild + var left = parseFloat(container.dataset.left) + var top = parseFloat(container.dataset.top) + if ((x <= left) && (left <= x+width) && (y <= top) && (top <= y+height)) { + // + // module is in selection rectangle + // + name.style.fontWeight = "bold" + mods.ui.selected[id] = true + } + else { + // + // module is not in selection rectangle + // + name.style.fontWeight = "normal" + } + } + } + } + }) +// +// context menu +// +window.addEventListener('contextmenu',function(evt){ + evt.stopPropagation() + evt.preventDefault() + if (mods.ui.menu != null) { + document.body.removeChild(mods.ui.menu) + mods.ui.menu = null + } + var div = document.createElement('div') + make_menu(div) + add_menu(div,'modules',modules) + add_menu(div,'programs',programs) + add_menu(div,'edit',edit) + add_menu(div,'options',options) + document.body.appendChild(div) + function make_menu(div) { + mods.ui.menu = div + div.style.position = "absolute" + var t = mods_transform() + div.style.top = t.oy-t.ty+(evt.pageY-t.oy)/t.s + div.style.left = t.ox-t.tx+(evt.pageX-t.ox)/t.s + div.style.zIndex = 0 + div.style.cursor = 'default' + div.style.backgroundColor = "rgb(220,255,255)" + div.style.padding = 1.5*mods.ui.padding + div.style.textAlign = 'left' + div.style.border = '2px solid' + div.style.borderRadius = '10px' + } + function add_menu(div,text,click) { + var textdiv = document.createElement('div') + textdiv.appendChild(document.createTextNode(text)) + textdiv.appendChild(document.createElement('br')) + textdiv.addEventListener('mouseover',function(evt){ + evt.target.style.fontWeight = 'bold'}) + textdiv.addEventListener('mouseout',function(evt){ + evt.target.style.fontWeight = 'normal'}) + textdiv.addEventListener('mousedown',click) + textdiv.addEventListener('touchend',click) + div.appendChild(textdiv) + } + // + // modules menu + // + function modules(evt) { + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + var div = document.createElement('div') + make_menu(div) + set_prompt('modules') + // + // open server module + // + add_menu(div,'open server module',function(evt){ + function module_label(label) { + var div = document.createElement('div') + var i = document.createElement('i') + i.appendChild(document.createTextNode(label)) + div.appendChild(i) + div.appendChild(document.createElement('br')) + menu.appendChild(div) + } + function module_menu(label,module) { + var div = document.createElement('div') + div.appendChild( + document.createTextNode('\u00A0\u00A0\u00A0'+label)) + div.addEventListener('mouseover',function(evt){ + evt.target.style.fontWeight = 'bold'}) + div.addEventListener('mouseout',function(evt){ + evt.target.style.fontWeight = 'normal'}) + div.addEventListener('mousedown',function(evt){ + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var t = mods_transform() + mod_message_handler(module, + t.oy-t.ty+(evt.pageY-t.oy)/t.s, + t.ox-t.tx+(evt.pageX-t.ox)/t.s)}) + div.appendChild(document.createElement('br')) + menu.appendChild(div) + } + document.body.removeChild(evt.target.parentNode) + var menu = document.createElement('div') + make_menu(menu) + document.body.appendChild(menu) + menu.style.width = mods.ui.canvas + menu.style.height = mods.ui.canvas + menu.style.overflow = 'auto' + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + var str = req.response + eval(str) + } + } + req.open('GET','modules/index.js'+'?rnd='+Math.random()) + req.send() + }) + // + // open local module + // + add_menu(div,'open local module',function(evt){ + var t = mods_transform() + mods.ui.top = t.oy-t.ty+(evt.pageY-t.oy)/t.s + mods.ui.left = t.ox-t.tx+(evt.pageX-t.ox)/t.s + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var file = document.getElementById('mod_input') + file.value = null + file.click() + }) + // + // open remote module + // + add_menu(div,'open remote module',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + set_prompt('remotes not yet implemented') + }) + document.body.appendChild(div) + } + // + // programs menu + // + function programs(evt) { + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + var div = document.createElement('div') + make_menu(div) + set_prompt('programs') + // + // open local program + // + add_menu(div,'open local program',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var file = document.getElementById('prog_input') + file.value = null + file.click() + }) + // + // open server program + // + add_menu(div,'open server program',function(evt){ + function program_label(label) { + var div = document.createElement('div') + var i = document.createElement('i') + i.appendChild(document.createTextNode(label)) + div.appendChild(i) + div.appendChild(document.createElement('br')) + menu.appendChild(div) + } + function program_menu(label,program) { + var div = document.createElement('div') + div.appendChild( + document.createTextNode('\u00A0\u00A0\u00A0'+label)) + div.addEventListener('mouseover',function(evt){ + evt.target.style.fontWeight = 'bold'}) + div.addEventListener('mouseout',function(evt){ + evt.target.style.fontWeight = 'normal'}) + div.addEventListener('mousedown',function(evt){ + evt.preventDefault() + evt.stopPropagation() + if (location.port == 80) + var uri = 'http://'+location.hostname + +'?program='+program + else + var uri = 'http://'+location.hostname+':' + +location.port+'?program='+program + set_prompt('<a href='+uri+'>program link</a>') + prog_message_handler(program) + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + }) + div.appendChild(document.createElement('br')) + menu.appendChild(div) + } + document.body.removeChild(evt.target.parentNode) + var menu = document.createElement('div') + make_menu(menu) + document.body.appendChild(menu) + menu.style.width = mods.ui.canvas + menu.style.height = mods.ui.canvas + menu.style.overflow = 'auto' + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + var str = req.response + eval(str) + } + } + req.open('GET','programs/index.js'+'?rnd='+Math.random()) + req.send() + }) + // + // open remote program + // + add_menu(div,'open remote program',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + set_prompt('remotes not yet implemented') + }) + // + // save local program + // + add_menu(div,'save local program',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + save_program() + }) + // + // save local page + // + add_menu(div,'save local page',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + save_page() + }) + document.body.appendChild(div) + } + // + // edit menu + // + function edit(evt) { + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + var div = document.createElement('div') + make_menu(div) + set_prompt('edit') + // + // cut + // + add_menu(div,'cut',function(evt){ + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + if ((Object.keys(mods.ui.selected).length) == 0) { + set_prompt("nothing selected") + } + else { + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + delete_module(id) + mods.ui.selected = {} + } + } + }) + // + // copy + // + add_menu(div,'copy',function(evt){ + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + set_prompt('copy not yet implemented') + }) + // + // paste + // + add_menu(div,'paste',function(evt){ + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + set_prompt('paste not yet implemented') + }) + document.body.appendChild(div) + } + // + // options menu + // + function options(evt) { + evt.preventDefault() + evt.stopPropagation() + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var div = document.createElement('div') + make_menu(div) + set_prompt('options') + // + // view files + // + add_menu(div,'view files',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var win = window.open('files.html') + }) + document.body.appendChild(div) + // + // view project + // + add_menu(div,'view project',function(evt){ + document.body.removeChild(evt.target.parentNode) + mods.ui.menu = null + var win = window.open('https://gitlab.cba.mit.edu/pub/mods') + }) + document.body.appendChild(div) + } + }) +// +// prompt +// +document.body.appendChild(document.createTextNode(' ')) +var span = document.createElement('span') + span.setAttribute('id','logo') + span.style.display = 'inline-block' + span.style.verticalAlign = 'middle' + span.style.width = 20 + span.style.height = 20 + span.style.padding = mods.ui.padding + span.appendChild(logo(1)) + document.body.appendChild(span) +document.body.appendChild(document.createTextNode(' ')) +var span = document.createElement('span') + span.setAttribute('id','prompt') + span.style.display = 'inline-block' + span.style.verticalAlign = 'middle' + var innerspan = document.createElement('span') + span.appendChild(innerspan) + document.body.appendChild(span) +function logo(size) { + var x = 0 + var y = 2.8*size/3.8 + var svgNS = "http://www.w3.org/2000/svg" + var logo = document.createElementNS(svgNS,"svg") + logo.setAttributeNS("http://www.w3.org/2000/xmlns/", + "xmlns:xlink","http://www.w3.org/1999/xlink") + logo.setAttributeNS(null,'viewBox',"0 0 "+size+" "+size) + var new_rect = document.createElementNS(svgNS,"rect"); + new_rect.setAttribute("width",size/3.8) + new_rect.setAttribute("height",size/3.8) + new_rect.setAttribute("x",x) + new_rect.setAttribute("y",y) + new_rect.setAttribute("fill","blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS,"rect"); + new_rect.setAttribute("width",size/3.8) + new_rect.setAttribute("height",size/3.8) + new_rect.setAttribute("x",x+1.4*size/3.8) + new_rect.setAttribute("y",y) + new_rect.setAttribute("fill","blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS,"rect"); + new_rect.setAttribute("width",size/3.8) + new_rect.setAttribute("height",size/3.8) + new_rect.setAttribute("x",x+2.8*size/3.8) + new_rect.setAttribute("y",y) + new_rect.setAttribute("fill","blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS, "rect"); + new_rect.setAttribute("width",size/3.8) + new_rect.setAttribute("height",size/3.8) + new_rect.setAttribute("x",x) + new_rect.setAttribute("y",y-1.4*size/3.8) + new_rect.setAttribute("fill","blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS, "rect"); + new_rect.setAttribute("width", size / 3.8) + new_rect.setAttribute("height", size / 3.8) + new_rect.setAttribute("x", x + 2.8 * size / 3.8) + new_rect.setAttribute("y", y - 1.4 * size / 3.8) + new_rect.setAttribute("fill", "blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS, "rect"); + new_rect.setAttribute("width", size / 3.8) + new_rect.setAttribute("height", size / 3.8) + new_rect.setAttribute("x", x + 1.4 * size / 3.8) + new_rect.setAttribute("y", y - 2.8 * size / 3.8) + new_rect.setAttribute("fill", "blue") + logo.appendChild(new_rect) + var new_rect = document.createElementNS(svgNS, "rect"); + new_rect.setAttribute("width", size / 3.8) + new_rect.setAttribute("height", size / 3.8) + new_rect.setAttribute("x", x + 2.8 * size / 3.8) + new_rect.setAttribute("y", y - 2.8 * size / 3.8) + new_rect.setAttribute("fill", "blue") + logo.appendChild(new_rect) + var new_circ = document.createElementNS(svgNS, "circle"); + new_circ.setAttribute("r", size / (2 * 3.8)) + new_circ.setAttribute("cx", x + size / (2 * 3.8)) + new_circ.setAttribute("cy", y + size / (2 * 3.8) - 2.8 * size / 3.8) + new_circ.setAttribute("fill", "red") + logo.appendChild(new_circ) + var new_circ = document.createElementNS(svgNS, "circle"); + new_circ.setAttribute("r", size / (2 * 3.8)) + new_circ.setAttribute("cx", x + size / (2 * 3.8) + 1.4 * size / 3.8) + new_circ.setAttribute("cy", y + size / (2 * 3.8) - 1.4 * size / 3.8) + new_circ.setAttribute("fill", "red") + logo.appendChild(new_circ) + return logo + } +set_prompt('right click/two finger/long press for menu; scroll for zoom, drag for pan') +// +// SVG canvas for drawing +// +var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg") + svg.style.position = 'absolute' + svg.style.backgroundColor = 'rgb(255,255,255)' + svg.style.top = mods.ui.header + svg.style.left = 0 + svg.style.zIndex = 0 + svg.style.overflow = 'visible' + svg.setAttribute('width',40) + svg.setAttribute('height',40) + svg.setAttribute('id','svg') + svg.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink") + document.body.appendChild(svg) +// +// link container +// +var svg = document.getElementById('svg') +var g = document.createElementNS('http://www.w3.org/2000/svg','g') + g.setAttribute('id','links') + svg.appendChild(g) +// +// file reading controls +// +var file = document.createElement('input') + file.setAttribute('type','file') + file.setAttribute('id','mod_input') + file.style.position = 'absolute' + file.style.left = 0 + file.style.top = 0 + file.style.width = 0 + file.style.height = 0 + file.style.opacity = 0 + file.addEventListener('change',function() { + mod_read_handler() + }) + document.body.appendChild(file) +var file = document.createElement('input') + file.setAttribute('type','file') + file.setAttribute('id','prog_input') + file.style.position = 'absolute' + file.style.left = 0 + file.style.top = 0 + file.style.width = 0 + file.style.height = 0 + file.style.opacity = 0 + file.addEventListener('change',function() { + prog_read_handler() + }) + document.body.appendChild(file) +// +// module container +// +var div = document.createElement('div') + div.setAttribute('id','modules') + document.body.appendChild(div) +// +// check for program load query +// +if (location.search.length > 0) { + var args = location.search.slice(1).split('&') + for (var a in args) { + var arg = args[a].split('=') + if (arg[0] == 'program') + prog_message_handler(arg[1]) + } + } +// +// program routines +// +function prog_read_handler(event) { + var file = document.getElementById('prog_input') + var file_reader = new FileReader() + file_reader.onload = prog_load_handler + file_reader.readAsText(file.files[0]) + mods.ui.progname = file.files[0].name + } +function prog_message_handler(filename) { + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + prog = JSON.parse(req.response) + prog_load(prog) + } + } + var index = filename.lastIndexOf('/') + if (index != -1) { + mods.ui.progname = filename.slice(index+1) + } + else + mods.ui.progname = filename + // + // send request, with random query to prevent caching + // + req.open('GET',filename+'?rnd='+Math.random()) + req.send() + } +function prog_load_handler(event) { + prog = JSON.parse(event.target.result) + prog_load(prog) + } +function prog_load(prog) { + // + // load modules + // + for (var idnumber in prog.modules) { + var module = prog.modules[idnumber] + var str = module.definition + try { + eval('var args = '+str) + } + catch (err) { + console.log(err.message) + return + } + args.definition = str + args.id = idnumber + var t = mods_transform() + var xw = t.ox-t.tx+(mods.ui.xstart-t.ox)/t.s + var yw = t.oy-t.ty+(mods.ui.ystart-t.oy)/t.s-mods.ui.header + args.top = parseFloat(module.top)+yw + args.left = parseFloat(module.left)+xw + args.filename = module.filename + add_module(args) + } + // + // load links + // + for (var linkid in prog.links) { + var str = prog.links[linkid] + eval('var link = '+str) + eval('var linksrc = '+link.source) + eval('var linkdst = '+link.dest) + var src = document.getElementById( + JSON.stringify({id:linksrc.id,type:linksrc.type,name:linksrc.name})) + var dst = document.getElementById( + JSON.stringify({id:linkdst.id,type:linkdst.type,name:linkdst.name})) + add_link(src,dst) + } + } +function save_program() { + set_prompt('program name? ') + get_prompt(mods.ui.progname,function(filename){ + mods.ui.progname = filename + var prog = {modules:{},links:[]} + var modules = document.getElementById('modules') + // + // save modules + // + for (var c = 0; c < modules.childNodes.length; ++c) { + var module = modules.childNodes[c] + var idnumber = module.id + prog.modules[idnumber] = { + definition:update_module_definition( + idnumber,module.dataset.definition), + top:module.dataset.top, + left:module.dataset.left, + filename: module.dataset.filename, + inputs:{}, + outputs:{} + } + } + // + // save links + // + var svg = document.getElementById('svg') + var links = svg.getElementById('links') + for (var l = 0; l < links.childNodes.length; ++l) { + var link = links.childNodes[l] + var linkid = link.id + prog.links.push(linkid) + } + // + // download + // + var text = JSON.stringify(prog) + var a = document.createElement('a') + a.setAttribute('href','data:text/plain;charset=utf-8,'+ + encodeURIComponent(text)) + a.setAttribute('download',filename) + a.style.display = 'none' + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + }) + } +function save_page() { + set_prompt('page name? ') + get_prompt(mods.ui.progname+".html",function(filename){ + mods.ui.progname = filename + var prog = {modules:{},links:[]} + var modules = document.getElementById('modules') + // + // save modules + // + for (var c = 0; c < modules.childNodes.length; ++c) { + var module = modules.childNodes[c] + var idnumber = module.id + prog.modules[idnumber] = { + definition:update_module_definition( + idnumber,module.dataset.definition), + top:module.dataset.top, + left:module.dataset.left, + filename: module.dataset.filename, + inputs:{}, + outputs:{} + } + } + // + // save links + // + var svg = document.getElementById('svg') + var links = svg.getElementById('links') + for (var l = 0; l < links.childNodes.length; ++l) { + var link = links.childNodes[l] + var linkid = link.id + prog.links.push(linkid) + } + // + // read mods.js + // + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + // + // construct page + // + var str = req.response + var text ="<html>\n" + text += "<head><meta charset='utf-8'>\n" + text += "<title>mods</title>\n" + text += "</head>\n" + text += "<body link='black' alink='black' vlink='black'>\n" + text += "<"+"script"+">\n" + text += str + text += "var prog = JSON.parse(JSON.stringify("+JSON.stringify(prog)+"))\n" + text += "window.mods_prog_load(prog)\n" + text += "</"+"script"+">\n" + text += "</body>\n" + text += "</html>\n" + // + // download page + // + var a = document.createElement('a') + a.setAttribute('href','data:text/plain;charset=utf-8,'+ + encodeURIComponent(text)) + a.setAttribute('download',filename) + a.style.display = 'none' + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + } + } + // + // send request, with random query to prevent caching + // + req.open('GET','js/mods.js'+'?rnd='+Math.random()) + req.send() + }) + } +// +// add program load to window +// +window.mods_prog_load = function(prog) { + prog_load(prog) + } +// +// module routines +// +function mod_read_handler(event) { + var file = document.getElementById('mod_input') + var file_reader = new FileReader() + file_reader.onload = mod_load_handler + file_reader.readAsText(file.files[0]) + } +function mod_message_handler(filename,top,left) { + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + var str = req.response + try { + eval('var args = '+str) + } + catch (err) { + console.log(err.message) + return + } + args.definition = str + args.id = String(Math.random()) + args.top = top + args.left = left + args.filename = filename + add_module(args) + } + } + // + // send request, with random query to prevent caching + // + req.open('GET',filename+'?rnd='+Math.random()) + req.send() + } +function mod_load_handler(event) { + str = event.target.result + try { + eval('var args = '+str) + } + catch (err) { + console.log(err.message) + return + } + args.definition = str + args.id = String(Math.random()) + args.top = mods.ui.top + args.left = mods.ui.left + args.filename = "" + var div = add_module(args) + return(div) + } +function add_module(args) { + var idnumber = args.id + mods.mod[idnumber] = args.mod + var modules = document.getElementById('modules') + // + // container + // + var container = document.createElement('div') + container.setAttribute("id",idnumber) + container.style.position = "absolute" + container.style.top = args.top + container.style.left = args.left + container.dataset.top = args.top + container.dataset.left = args.left + container.dataset.filename = args.filename + container.dataset.name = args.name + container.style.zIndex = 0 + container.style.width = window.innerWidth + container.dataset.definition = args.definition + modules.appendChild(container) + // + // name + // + var divname = document.createElement('div') + divname.appendChild(document.createTextNode(args.name)) + divname.addEventListener('mouseover',name_over) + divname.addEventListener('mouseout',name_out) + divname.addEventListener('mousedown',name_mousedown) + divname.addEventListener('touchstart',name_touchdown) + divname.style.backgroundColor = "rgb(210,240,210)" + divname.style.padding = 1.5*mods.ui.padding + divname.style.position = "absolute" + divname.style.cursor = 'default' + divname.style.top = 0 + divname.style.left = 0 + divname.style.textAlign = 'center' + divname.style.border = '2px solid' + divname.style.borderRadius = '10px' + container.appendChild(divname) + // + // controls + // + var divctrl = document.createElement('div') + var editspan = document.createElement('span') + editspan.innerHTML = 'edit' + editspan.style.fontWeight = 'normal' + editspan.addEventListener('mouseover',function(event){ + set_prompt('click to edit') + editspan.style.fontWeight = 'bold'}) + editspan.addEventListener('mouseout',function(event){ + set_prompt('') + editspan.style.fontWeight = 'normal'}) + editspan.addEventListener('mousedown',edit_module) + divctrl.appendChild(editspan) + var delspan = document.createElement('span') + delspan.innerHTML = ' delete ' + delspan.addEventListener('mouseover',function(event){ + set_prompt('click to delete') + delspan.style.fontWeight = 'bold'}) + delspan.addEventListener('mouseout',function(event){ + set_prompt('') + delspan.style.fontWeight = 'normal'}) + delspan.addEventListener('mousedown',function(event){ + delete_module(event.target.parentNode.parentNode.id)}) + divctrl.appendChild(delspan) + divctrl.style.backgroundColor = "rgb(240,220,220)" + divctrl.style.padding = mods.ui.padding + divctrl.style.position = "absolute" + divctrl.style.cursor = 'default' + divctrl.style.top = divname.clientHeight + divctrl.style.left = 0 + divctrl.style.textAlign = 'center' + divctrl.style.border = '2px solid' + divctrl.style.borderRadius = '10px' + container.appendChild(divctrl) + divctrl.style.left = divname.clientWidth/2-divctrl.clientWidth/2 + // + // interface + // + var divint = document.createElement('div') + divint.style.backgroundColor = "rgb(240,240,240)" + divint.style.padding = mods.ui.padding + divint.style.position = "absolute" + divint.style.top = divname.clientHeight+divctrl.clientHeight + divint.style.textAlign = 'center' + divint.style.border = '2px solid' + divint.style.borderRadius = '10px' + divint.setAttribute('id',JSON.stringify({id:idnumber,type:'interface'})) + divint.dataset.id = idnumber + divint.dataset.divNameSize = divname.clientWidth + args.interface(divint) + container.appendChild(divint) + divint.style.left = divname.clientWidth/2-divint.clientWidth/2 + // + // inputs + // + var divin = document.createElement('div') + divin.setAttribute('id',JSON.stringify({id:idnumber,type:'inputs'})) + var b = document.createElement('b') + b.appendChild(document.createTextNode('inputs')) + divin.appendChild(b) + divin.style.backgroundColor = "rgb(240,240,210)" + divin.style.padding = mods.ui.padding + divin.style.paddingLeft = '2px' + divin.style.position= "absolute" + divin.style.top = divname.clientHeight+divctrl.clientHeight + divin.style.textAlign = 'left' + divin.style.border = '2px solid' + divin.style.borderRadius = '10px' + divin.setAttribute('id',JSON.stringify({id:idnumber,type:'inputs'})) + divin.style.cursor = 'default' + divin.addEventListener('mousedown',nothing) + divin.addEventListener('touchstart',nothing) + divin.addEventListener('mouseup',nothing) + divin.addEventListener('touchend',nothing) + for (var v in args.inputs) { + var div = document.createElement('div') + if (args.inputs[v].label != undefined) + div.innerHTML += args.inputs[v].label + else + div.innerHTML += v + if (args.inputs[v].type != '') + div.innerHTML += ' ('+args.inputs[v].type+')' + div.setAttribute('id',JSON.stringify({id:idnumber,type:'inputs',name:v})) + div.addEventListener('mouseover',input_over) + div.addEventListener('mouseout',input_out) + div.addEventListener('mousedown',input_mousedown) + div.addEventListener('touchstart',input_touchdown) + div.dataset.links = JSON.stringify([]) + div.dataset.name = v + divin.appendChild(div) + div.dataset.id = JSON.stringify( + {id:idnumber,type:'input',name:v, + rnd:Math.random()}) // randomize for unique events + window.addEventListener(div.dataset.id,args.inputs[v].event) + } + container.appendChild(divin) + if ((Object.keys(args.inputs).length) == 0) + divin.style.visibility = 'hidden' + divin.style.left = divname.clientWidth/2 + -divint.clientWidth/2-divin.clientWidth + for (var i = 1; i < divin.childNodes.length; ++i) { + divin.childNodes[i].dataset.dx = divin.offsetLeft + +divin.childNodes[i].offsetLeft + divin.childNodes[i].dataset.dy = divin.offsetTop + +divin.childNodes[i].offsetTop + +divin.childNodes[i].offsetHeight/2 + } + // + // outputs + // + var divout = document.createElement('div') + divout.setAttribute('id',JSON.stringify({id:idnumber,type:'outputs'})) + var b = document.createElement('b') + b.appendChild(document.createTextNode('outputs')) + divout.appendChild(b) + divout.style.backgroundColor = "rgb(240,240,210)" + divout.style.padding = mods.ui.padding + divout.style.paddingRight = '2px' + divout.style.position = "absolute" + divout.style.top = divname.clientHeight+divctrl.clientHeight + divout.style.textAlign = 'right' + divout.addEventListener('mousedown',nothing) + divout.style.border = '2px solid' + divout.style.borderRadius = '10px' + divout.setAttribute('id',JSON.stringify({id:idnumber,type:'outputs'})) + divout.style.cursor = 'default' + divout.addEventListener('touchstart',nothing) + divout.addEventListener('mouseup',nothing) + divout.addEventListener('touchend',nothing) + for (var v in args.outputs) { + var div = document.createElement('div') + if (args.outputs[v].label != undefined) + div.innerHTML += args.outputs[v].label + else + div.innerHTML += v + if (args.outputs[v].type != '') + div.innerHTML += ' ('+args.outputs[v].type+')' + div.setAttribute('id',JSON.stringify({id:idnumber,type:'outputs',name:v})) + div.addEventListener('mouseover',output_over) + div.addEventListener('mouseout',output_out) + div.addEventListener('mousedown',output_mousedown) + div.addEventListener('touchstart',output_touchdown) + div.dataset.links = JSON.stringify([]) + div.dataset.name = v + divout.appendChild(div) + div.dataset.id = JSON.stringify( + {id:idnumber,type:'output',name:v, + rnd:Math.random()}) // randomize for unique events + window.addEventListener(div.dataset.id,args.outputs[v].event) + } + container.appendChild(divout) + if ((Object.keys(args.outputs).length) == 0) + divout.style.visibility = 'hidden' + divout.style.left = divname.clientWidth/2 + +divint.clientWidth/2 + for (var i = 1; i < divout.childNodes.length; ++i) { + divout.childNodes[i].dataset.dx = divout.offsetLeft + +divout.childNodes[i].offsetLeft + +divout.childNodes[i].offsetWidth + divout.childNodes[i].dataset.dy = divout.offsetTop + +divout.childNodes[i].offsetTop + +divout.childNodes[i].offsetHeight/2 + } + // + // initialization + // + args.init() + // + // resize to contents + // + container.style.width = divint.clientWidth+divin.clientWidth+divout.clientWidth + mods.fit(divint) + // + // return container + // + return(container) + } +function delete_module(idnumber) { + // + // delete links + // + var ins = document.getElementById( + JSON.stringify({id:idnumber,type:'inputs'})) + var outs = document.getElementById( + JSON.stringify({id:idnumber,type:'outputs'})) + for (var i = 1; i < ins.childNodes.length; ++i) { + var links = JSON.parse(ins.childNodes[i].dataset.links) + for (var l in links) + delete_link(links[l]) + } + for (var i = 1; i < outs.childNodes.length; ++i) { + var links = JSON.parse(outs.childNodes[i].dataset.links) + for (var l in links) + delete_link(links[l]) + } + // + // delete container + // + var modules = document.getElementById('modules') + var container = document.getElementById(idnumber) + modules.removeChild(container) + // + // clear prompt + // + set_prompt('') + } +function update_module_definition(id) { + // + // get definition + // + var module = document.getElementById(id) + var def = module.dataset.definition + // + // check for mod + // + if (mods.mod[id] == undefined) + return def + // + // split definition + // + var lines = def.split('\n') + // + // find init function + // + var line = 0 + while (line < lines.length) { + if (lines[line].indexOf("var init") == 0) + break + line += 1 + } + // + // read initializations up to inputs function + // + while (line < lines.length) { + if (lines[line].indexOf(".value =") != -1) { + var start = 4+lines[line].indexOf("mod.") + var end = lines[line].indexOf(".value") + var key = lines[line].slice(start,end) + var value = mods.mod[id][key]['value'] + if (value.indexOf('\n') != -1) + value = value.replace(/\n/g,"\\n") + lines[line] = " mod."+key+".value = '"+value+"'" + } + else if (lines[line].indexOf(".checked =") != -1) { + var start = 4+lines[line].indexOf("mod.") + var end = lines[line].indexOf(".checked") + var key = lines[line].slice(start,end) + var value = mods.mod[id][key]['checked'] + lines[line] = " mod."+key+".checked = "+value + } + if (lines[line].indexOf("var inputs") == 0) + break + line += 1 + } + return(lines.join('\n')) + } +function edit_module(evt) { + var mod = evt.target.parentNode.parentNode + var idnumber = mod.id + var def = update_module_definition(idnumber) + // + // open edit window + // + var top = mod.dataset.top + var left = mod.dataset.left + var name = mod.dataset.name + var filename = mod.dataset.filename + var fontsize = 100 + var win = window.open('') + var file = document.createElement('input') + file.setAttribute('type','file') + file.setAttribute('id','edit_module_file') + file.style.position = 'absolute' + file.style.left = 0 + file.style.top = 0 + file.style.width = 0 + file.style.height = 0 + file.style.opacity = 0 + file.addEventListener('change',function() { + edit_module_read_handler() + }) + win.document.body.appendChild(file) + function edit_module_read_handler() { + var file = win.document.getElementById('edit_module_file') + var file_reader = new FileReader() + file_reader.onload = edit_module_load_handler + file_reader.readAsText(file.files[0]) + } + function edit_module_load_handler(event) { + str = event.target.result + var text = win.document.getElementById('edit_module_text') + text.value = str + update_module(idnumber) + win.close() + } + if (filename != "undefined" && filename != "") { + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('reload from server')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + var req = new XMLHttpRequest() + req.responseType = 'text' + req.onreadystatechange = function() { + if (req.readyState == XMLHttpRequest.DONE) { + text.value = req.response + update_module(idnumber) + } + } + req.open('GET',filename+'?rnd='+Math.random()) + req.send() + win.close() + }) + win.document.body.appendChild(btn) + } + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('load from file')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + var file = win.document.getElementById('edit_module_file') + file.value = null + file.click() + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('update and close')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + update_module(idnumber) + win.close() + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('update')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + update_module(idnumber) + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('close')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + win.close() + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('save')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + var a = document.createElement('a') + a.setAttribute('href','data:text/plain;charset=utf-8,'+ + encodeURIComponent(text.value)) + a.setAttribute('download',name) + a.style.display = 'none' + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('increase font')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + fontsize *= 1.2 + text.style.fontSize = fontsize+'%' + }) + win.document.body.appendChild(btn) + var btn = document.createElement('button') + btn.appendChild(document.createTextNode('decrease font')) + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.addEventListener('click',function(){ + fontsize /= 1.2 + text.style.fontSize = fontsize+'%' + }) + win.document.body.appendChild(btn) + win.document.body.appendChild(document.createElement('br')) + var text = document.createElement('textarea') + text.setAttribute('id','edit_module_text') + text.style.width = '100%' + text.style.height= '100%' + text.value = def + win.document.body.appendChild(text) + function reload_module(idnumber) { + } + function update_module(idnumber) { + // + // save links + // + var ins = document.getElementById( + JSON.stringify({id:idnumber,type:'inputs'})) + var inlinks = [] + for (var i = 1; i < ins.childNodes.length; ++i) { + var links = JSON.parse(ins.childNodes[i].dataset.links) + for (var l in links) + inlinks.push(links[l]) + } + var outs = document.getElementById( + JSON.stringify({id:idnumber,type:'outputs'})) + var outlinks = [] + for (var i = 1; i < outs.childNodes.length; ++i) { + var links = JSON.parse(outs.childNodes[i].dataset.links) + for (var l in links) + outlinks.push(links[l]) + } + // + // delete module + // + delete_module(idnumber) + // + // add module + // + var def = text.value + try { + eval('var args = '+def) + } + catch (err) { + console.log(err.message) + return + } + args.definition = def + args.id = idnumber + args.top = top + args.left = left + args.filename = filename + add_module(args) + // + // add links + // + for (var l in inlinks) { + eval('var link = '+inlinks[l]) + eval('var linksrc = '+link.source) + eval('var linkdst = '+link.dest) + var src = document.getElementById( + JSON.stringify( + {id:linksrc.id,type:linksrc.type,name:linksrc.name})) + var dst = document.getElementById( + JSON.stringify( + {id:linkdst.id,type:linkdst.type,name:linkdst.name})) + add_link(src,dst) + } + for (var l in outlinks) { + eval('var link = '+outlinks[l]) + eval('var linksrc = '+link.source) + eval('var linkdst = '+link.dest) + var src = document.getElementById( + JSON.stringify( + {id:linksrc.id,type:linksrc.type,name:linksrc.name})) + var dst = document.getElementById( + JSON.stringify( + {id:linkdst.id,type:linkdst.type,name:linkdst.name})) + add_link(src,dst) + } + } + } +// +// UI routines +// +function set_prompt(txt) { + var span = document.getElementById('prompt') + span.childNodes[0].innerHTML = ' '+txt + } +function get_prompt(txt,fn) { + var div = document.getElementById('prompt') + if (div.childNodes.length > 2) + // + // already getting a prompt + // + return + var text = document.createElement('input') + text.type = 'text' + text.size = 20 + text.value = txt + text.addEventListener('keydown',function(evt){ + if (evt.key == 'Enter') { + var div = document.getElementById('prompt') + div.removeChild(div.childNodes[1]) + div.removeChild(div.childNodes[1]) + set_prompt('') + fn(text.value) + } + }) + div.appendChild(text) + div.appendChild(document.createTextNode(' (enter)')) + text.focus() + } +function nothing(evt) { + evt.preventDefault() + evt.stopPropagation() + } +// +// link routines +// +mods.add_link = function(src,dst) { + add_link(src,dst) + } +function add_link(src,dst) { + // + // link order from out to in + // + if (src.id.indexOf('outputs') == -1) { + var tmp = src + src = dst + dst = tmp + } + // + // check if link exists + // + var id = JSON.stringify({source:src.id,dest:dst.id}) + var link = document.getElementById(id) + if (link != null) { + // + // yes, remove it and return + // + delete_link(id) + return + } + // + // no, add link + // + var links = JSON.parse(src.dataset.links) + links.push(id) + src.dataset.links = JSON.stringify(links) + var links = JSON.parse(dst.dataset.links) + links.push(id) + dst.dataset.links = JSON.stringify(links) + // + // draw link + // + xsrc = src.parentNode.parentNode.offsetLeft + +parseFloat(src.dataset.dx) + ysrc = src.parentNode.parentNode.offsetTop + +parseFloat(src.dataset.dy) + -mods.ui.header + xdst = dst.parentNode.parentNode.offsetLeft + +parseFloat(dst.dataset.dx) + ydst = dst.parentNode.parentNode.offsetTop + +parseFloat(dst.dataset.dy) + -mods.ui.header + var links = document.getElementById('links') + var path = document.createElementNS('http://www.w3.org/2000/svg','path') + path.setAttribute('id',id) + path.setAttribute('d','M'+xsrc+','+ysrc+' C'+(xsrc+mods.ui.bezier)+',' + +ysrc+' '+(xdst-mods.ui.bezier)+','+ydst+' '+xdst+','+ydst) + path.setAttribute('fill','none') + path.setAttribute('stroke','rgb(0,0,128)') + path.setAttribute('stroke-width',3) + links.appendChild(path) + /* + // + // don't trigger link + // + eval('var evtid = '+src.id) + evtid.type = 'output' + var evtstr = JSON.stringify(evtid) + var evt = new CustomEvent(evtstr) + window.dispatchEvent(evt) + */ + } +function delete_link(linkid) { + // + // delete a link + // + var links = document.getElementById('links') + links.removeChild(document.getElementById(linkid)) + link = JSON.parse(linkid) + src = document.getElementById(link.source) + dst = document.getElementById(link.dest) + var links = JSON.parse(src.dataset.links) + var index = links.indexOf(linkid) + links.splice(index,1) + src.dataset.links = JSON.stringify(links) + var links = JSON.parse(dst.dataset.links) + var index = links.indexOf(linkid) + links.splice(index,1) + dst.dataset.links = JSON.stringify(links) + } +function draw_links(idnumber,color) { + // + // draw a module's links + // + var ins = document.getElementById( + JSON.stringify({id:idnumber,type:'inputs'})) + var outs = document.getElementById( + JSON.stringify({id:idnumber,type:'outputs'})) + for (var i = 1; i < ins.childNodes.length; ++i) { + var links = JSON.parse(ins.childNodes[i].dataset.links) + for (var l in links) + draw_link(links[l],color) + } + for (var i = 1; i < outs.childNodes.length; ++i) { + var links = JSON.parse(outs.childNodes[i].dataset.links) + for (var l in links) + draw_link(links[l],color) + } + } +function draw_link(id,color) { + // + // draw a link + // + var link = JSON.parse(id) + src = document.getElementById(link.source) + dst = document.getElementById(link.dest) + var path = document.getElementById(id) + xsrc = src.parentNode.parentNode.offsetLeft + +parseFloat(src.dataset.dx) + ysrc = src.parentNode.parentNode.offsetTop + +parseFloat(src.dataset.dy) + -mods.ui.header + xdst = dst.parentNode.parentNode.offsetLeft + +parseFloat(dst.dataset.dx) + ydst = dst.parentNode.parentNode.offsetTop + +parseFloat(dst.dataset.dy) + -mods.ui.header + path.setAttribute('d','M'+xsrc+','+ysrc+' C'+(xsrc+mods.ui.bezier)+',' + +ysrc+' '+(xdst-mods.ui.bezier)+','+ydst+' '+xdst+','+ydst) + path.setAttribute('stroke',color) + } +// +// mods routines to be called from modules +// +mods.fit = function(div) { + // + // fit a module + // + div.style.left = div.dataset.divNameSize/2-div.clientWidth/2 + var divin = document.getElementById( + JSON.stringify( + {id:div.dataset.id,type:'inputs'})) + divin.style.left = div.dataset.divNameSize/2-div.clientWidth/2-divin.clientWidth + var divout = document.getElementById( + JSON.stringify( + {id:div.dataset.id,type:'outputs'})) + divout.style.left = div.dataset.divNameSize/2+div.clientWidth/2 + } +mods.output = function(mod,varname,val) { + // + // send module outputs + // + var div = mod.div + var key = JSON.parse(div.id) + var idnumber = key.id + var out = document.getElementById( + JSON.stringify( + {id:idnumber,type:'outputs',name:varname})) + var links = JSON.parse(out.dataset.links) + for (var l in links) { + var link = JSON.parse(links[l]) + var dest = JSON.parse(link.dest) + var divin = document.getElementById(JSON.stringify( + {id:dest.id,type:'inputs',name:dest.name})) + var evt = new CustomEvent(divin.dataset.id,{detail:val}) + window.dispatchEvent(evt) + } + } +// +// module mod-ification calls +// +mods.module_create = function(args) { + var event = {target:{result:args}} + var div = mod_load_handler(event) + return(div) + } +mods.module_delete = function(id) { + delete_module(id) + } +mods.module_id = function(div) { + return div.parentNode.id + } +mods.module_left = function(id) { + var module = document.getElementById(id) + return (parseInt(module.style.left)) + } +mods.module_move = function(id,dx,dy) { + var module = document.getElementById(id) + var top = parseInt(module.style.top) + module.style.top = top+dy + module.dataset.top = top+dy + var left = parseInt(module.style.left) + module.style.left = left+dx + module.dataset.left = left+dx + draw_links(id,mods.ui.link_color) + } +mods.module_inputs = function(id,index) { + var module = document.getElementById(id) + var inputs = document.getElementById( + JSON.stringify({id:id,type:'inputs'})) + console.log(inputs.childNodes[index]) + } +mods.module_outputs = function(id,index) { + var module = document.getElementById(id) + var outputs = document.getElementById( + JSON.stringify({id:id,type:'outputs'})) + console.log(outputs.childNodes[index]) + } +mods.module_position = function(id,x,y) { + var module = document.getElementById(id) + var top = parseInt(module.style.top) + module.style.top = y + module.dataset.top = y + var left = parseInt(module.style.left) + module.style.left = x + module.dataset.left = x + draw_links(id,mods.ui.link_color) + } +mods.module_top = function(id) { + var module = document.getElementById(id) + return (parseInt(module.style.top)) + } +mods.module_width = function(id) { + var module = document.getElementById(id) + return (parseInt(module.clientWidth)) + } +// +// input event handlers +// +function input_over(evt) { + evt.target.style.fontWeight = 'bold' + var links = JSON.parse(evt.target.dataset.links) + for (var l in links) + draw_link(links[l],mods.ui.link_highlight) + if (mods.ui.source == null) + set_prompt('click to link') + } +function input_out(evt) { + evt.target.style.fontWeight = 'normal' + var links = JSON.parse(evt.target.dataset.links) + for (var l in links) + draw_link(links[l],mods.ui.link_color) + if (mods.ui.source == null) + set_prompt('') + } +function input_mousedown(evt) { + if (mods.ui.source == null) { + mods.ui.source = evt.target + set_prompt('variable to link/unlink to?') + } + else { + add_link(mods.ui.source,evt.target) + set_prompt('') + mods.ui.source = null + } + } +function input_touchdown(evt) { + if (mods.ui.source == null) { + mods.ui.source = evt.target + set_prompt('variable to link/unlink to?') + } + else { + add_link(mods.ui.source,evt.target) + set_prompt('') + mods.ui.source = null + } + } +// +// output event handlers +// +function output_over(evt) { + evt.target.style.fontWeight = 'bold' + var links = JSON.parse(evt.target.dataset.links) + for (var l in links) + draw_link(links[l],mods.ui.link_highlight) + if (mods.ui.source == null) + set_prompt('click to link') + } +function output_out(evt) { + evt.target.style.fontWeight = 'normal' + var links = JSON.parse(evt.target.dataset.links) + for (var l in links) + draw_link(links[l],mods.ui.link_color) + if (mods.ui.source == null) + set_prompt('') + } +function output_mousedown(evt) { + if (mods.ui.source == null) { + mods.ui.source = evt.target + set_prompt('variable to link/unlink to?') + } + else { + add_link(mods.ui.source,evt.target) + set_prompt('') + mods.ui.source = null + } + } +function output_touchdown(evt) { + if (mods.ui.source == null) { + mods.ui.source = evt.target + set_prompt('variable to link/unlink to?') + } + else { + add_link(mods.ui.source,evt.target) + set_prompt('') + mods.ui.source = null + } + } +// +// name event handlers +// +function name_over(evt) { + evt.target.style.fontWeight = 'bold' + if (mods.ui.source == null) + set_prompt('click and drag to move') + } +function name_out(evt) { + evt.target.style.fontWeight = 'normal' + if (mods.ui.source == null) + set_prompt('') + } +function name_mousedown(evt) { + evt.preventDefault() + evt.stopPropagation() + var div = document.getElementById(evt.target.parentNode.id) + div.style.zIndex = 1 + mods.ui.xstart = evt.clientX + mods.ui.ystart = evt.clientY + mods.ui.selected[evt.target.parentNode.id] = true + window.addEventListener('mousemove',name_mousemove) + window.addEventListener('mouseup',name_mouseup) + } +function name_mousemove(evt) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + var dx = (evt.clientX-mods.ui.xstart)/t.s + var dy = (evt.clientY-mods.ui.ystart)/t.s + var newleft = parseFloat(div.dataset.left)+dx + var newtop = parseFloat(div.dataset.top)+dy + div.style.left = newleft+'px' + div.style.top = newtop+'px' + draw_links(id,mods.ui.link_color) + } + } +function name_mouseup(evt) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + div.style.zIndex = 0 + div.childNodes[0].style.fontWeight = 'normal' + var dx = (evt.clientX-mods.ui.xstart)/t.s + var dy = (evt.clientY-mods.ui.ystart)/t.s + div.dataset.left = parseFloat(div.dataset.left)+dx + div.dataset.top = parseFloat(div.dataset.top)+dy + window.removeEventListener('mousemove',name_mousemove) + window.removeEventListener('mouseup',name_mouseup) + } + mods.ui.selected = {} + } +function name_touchdown(evt) { + evt.preventDefault() + evt.stopPropagation() + var div = document.getElementById(evt.target.parentNode.id) + div.style.zIndex = 1 + mods.ui.xstart = evt.changedTouches[0].pageX + mods.ui.ystart = evt.changedTouches[0].pageY + mods.ui.selected[evt.target.parentNode.id] = true + window.addEventListener('touchmove',name_touchmove) + window.addEventListener('touchend',name_touchup) + } +function name_touchmove(evt) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + var dx = (evt.changedTouches[0].pageX-mods.ui.xstart)/t.s + var dy = (evt.changedTouches[0].pageY-mods.ui.ystart)/t.s + var newleft = parseFloat(div.dataset.left)+dx + var newtop = parseFloat(div.dataset.top)+dy + div.style.left = newleft+'px' + div.style.top = newtop+'px' + draw_links(id,mods.ui.link_color) + } + } +function name_touchup(evt) { + evt.preventDefault() + evt.stopPropagation() + var t = mods_transform() + for (var id in mods.ui.selected) { + var div = document.getElementById(id) + div.style.zIndex = 0 + var dx = (evt.changedTouches[0].pageX-mods.ui.xstart)/t.s + var dy = (evt.changedTouches[0].pageY-mods.ui.ystart)/t.s + div.dataset.left = parseFloat(div.dataset.left)+dx + div.dataset.top = parseFloat(div.dataset.top)+dy + window.removeEventListener('touchmove',name_touchmove) + window.removeEventListener('touchend',name_touchup) + } + mods.ui.selected = {} + } +})() +var prog = JSON.parse(JSON.stringify({"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"457.58350726962306","left":"3676.994408259124","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1074.583507269623","left":"4134.994408259125","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"944.5835072696229","left":"3703.994408259124","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 = ''\n }\n//\n// inputs\n//\nvar inputs = {\n distances:{type:'F32',\n event:function(evt){\n mod.distances = evt.detail\n var h = mod.distances.height\n var w = mod.distances.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.height = mod.distances.height \n ctx.canvas.width = mod.distances.width\n if (mod.offset.value != '')\n offset()\n }},\n offset:{type:'number',\n event:function(evt){\n mod.offset.value = evt.detail\n offset()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"557.583507269623","left":"4135.994408259125","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"590.583507269623","left":"3164.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.2892270043957246":{"definition":"//\n// view toolpath\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// todo:\n// erase and update new path\n// show depth info\n// show size\n// calculate camera far\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'view toolpath'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar 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//\nvar 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//\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('(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//\nfunction 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//\nfunction 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 geometry = new THREE.Geometry()\n geometry.vertices.push(start,stop)\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\t renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction 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\t scene = new THREE.Scene()\n\t mod.scene = scene\n\t var width = win.innerWidth\n\t var height = win.innerHeight\n\t var aspect = width/height\n\t var near = 0.1\n\t var far = 1000000\n\t camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n\t mod.camera = camera\n\t scene.add(camera)\n\t //\n\t // add renderer\n\t //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n\t renderer.setSize(width,height)\n\t 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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1294.583507269623","left":"2494.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.9557599338778935":{"definition":"//\n// mill raster 2D\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 = 'mill raster 2D'\n//\n// initialization\n//\nvar 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//\nvar inputs = {\n imageInfo:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.width\n ctx.canvas.height = mod.height\n }},\n path:{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 }\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:{type:'object',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n diameter:{type:'number',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{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:{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//\nvar 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//\nfunction 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 'offset number':\n mod.number.value = settings[s]\n break\n }\n }\n }\n//\n// clear_path\n//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"515.583507269623","left":"2517.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.10309904694903338":{"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 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 = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = 1\n mod.sort.checked = true\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 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 > 1) && (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 > 1) && (sort == false)) || (vecpath.length == 1))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1060.583507269623","left":"3208.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.992472539363189":{"definition":"//\n// set object\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 = 'set PCB defaults'\n//\n// initialization\n//\nvar init = function() {\n //\n add_output('mill 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.004'\n add_variable('max depth (in)','var02')\n mod.var02.value = '0.004'\n add_variable('offset number','var03')\n mod.var03.value = '4'\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.010'\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 //\n }\n//\n// inputs\n//\nvar inputs = {}\n//\n// outputs\n//\nvar outputs = {\n settings:{type:'',\n event:function(vars){\n mods.output(mod,'settings',vars)\n }\n }\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n }\n//\n// local functions\n//\nfunction 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] = {span:span,variables:{}}\n mod.div.appendChild(btn)\n mod.setting = label\n mod.div.appendChild(document.createElement('br'))\n }\nfunction 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//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"435.58350726962306","left":"1973.9944082591242","filename":"undefined","inputs":{},"outputs":{}},"0.8622681794886853":{"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":"1282.9473065265367","left":"2013.9337776993061","filename":"modules/file/save","inputs":{},"outputs":{}},"0.057765477464730264":{"definition":"//\n// read SVG\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'read SVG'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n SVG:{type:'string',\n event:function(evt) {\n svg_load_handler({target:{result:evt.detail}})\n }}}\n//\n// outputs\n//\nvar outputs = {\n SVG:{type:'string',\n event:function(){\n mods.output(mod,'SVG',mod.str)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // file input control\n //\n var file = document.createElement('input')\n file.setAttribute('type','file')\n file.setAttribute('id',div.id+'file_input')\n file.style.position = 'absolute'\n file.style.left = 0\n file.style.top = 0\n file.style.width = 0\n file.style.height = 0\n file.style.opacity = 0\n file.addEventListener('change',function() {\n svg_read_handler()\n })\n div.appendChild(file)\n mod.file = file\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // file select button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('select SVG file'))\n btn.addEventListener('click',function(){\n var file = document.getElementById(div.id+'file_input')\n file.value = null\n file.click()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // info div\n //\n var info = document.createElement('div')\n info.setAttribute('id',div.id+'info')\n var text = document.createTextNode('file:')\n info.appendChild(text)\n mod.name = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('width:')\n info.appendChild(text)\n mod.width = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('height:')\n info.appendChild(text)\n mod.height = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('units per inch:')\n info.appendChild(text)\n mod.units = text\n div.appendChild(info)\n }\n//\n// local functions\n//\n// read handler\n//\nfunction svg_read_handler(event) {\n //\n // read as text\n //\n var file_reader = new FileReader()\n file_reader.onload = svg_load_handler\n var input_file = mod.file.files[0]\n var file_name = input_file.name\n mod.name.nodeValue = \"file: \"+file_name\n file_reader.readAsText(input_file)\n }\n//\n// load handler\n//\nfunction svg_load_handler(event) {\n mod.str = event.target.result\n //\n // parse size\n //\n var i = mod.str.indexOf(\"width\")\n if (i == -1) {\n mod.width.nodeValue = \"width: not found\"\n mod.height.nodeValue = \"height: not found\"\n }\n else {\n var i1 = mod.str.indexOf(\"\\\"\",i+1)\n var i2 = mod.str.indexOf(\"\\\"\",i1+1)\n var width = mod.str.substring(i1+1,i2)\n i = mod.str.indexOf(\"height\")\n i1 = mod.str.indexOf(\"\\\"\",i+1)\n i2 = mod.str.indexOf(\"\\\"\",i1+1)\n var height = mod.str.substring(i1+1,i2)\n ih = mod.str.indexOf(\"height\")\n if (width.indexOf(\"px\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 90\n }\n else if (width.indexOf(\"mm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 25.4\n }\n else if (width.indexOf(\"cm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 2.54\n }\n else if (width.indexOf(\"in\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 1\n }\n else {\n var units = 90\n }\n mod.width.nodeValue = \"width: \"+width\n mod.height.nodeValue = \"height: \"+height\n mod.units.nodeValue = \"units per inch: \"+units\n }\n //\n // display\n //\n var img = new Image()\n var src = \"data:image/svg+xml;base64,\"+window.btoa(mod.str)\n img.setAttribute(\"src\",src)\n img.onload = function() {\n if (img.width > img.height) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-img.height/img.width)\n var w = mod.canvas.width\n var h = mod.canvas.width*img.height/img.width\n }\n else {\n var x0 = mod.canvas.width*.5*(1-img.width/img.height)\n var y0 = 0\n var w = mod.canvas.height*img.width/img.height\n var h = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(img,x0,y0,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = img.width\n ctx.canvas.height = img.height \n ctx.drawImage(img,0,0)\n outputs.SVG.event()\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"399.03488793999406","left":"1166.572396970906","filename":"modules/read/svg","inputs":{},"outputs":{}},"0.5022735462618486":{"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 = '1000'\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":"505.69493521948414","left":"1540.852199242571","filename":"modules/convert/svg/image","inputs":{},"outputs":{}},"0.9604471250218796":{"definition":"//\n// path to G-code\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2018\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// Updated: Neil Gershenfeld\n// Date: Oct 28 2020\n// Comments: added mm/s vs mm/min option\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 = '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.tool.value = '1'\n mod.coolantoff.checked = true\n mod.formatMm.checked = true\n mod.unitMinutes.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 speed: '))\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 // 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.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 // second or minute rate units \n //\n div.appendChild(document.createTextNode('rate units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitSeconds'\n input.checked = true\n div.appendChild(input)\n mod.unitSeconds = input\n div.appendChild(document.createTextNode('second'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitMinutes'\n div.appendChild(input)\n mod.unitMinutes = input\n div.appendChild(document.createTextNode('minute'))\n }\n//\n// local functions\n//\nfunction make_path() {\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 if (mod.unitMinutes.checked) {\n cut_speed *= 60\n plunge_speed *= 60\n }\n var spindle_speed = parseFloat(mod.spindlespeed.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 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 //str += \"G04 P1000\\n\" // give spindle 1 second to spin up................comment out\n //str += \"G04 P1\\n\" // give spindle 1 Millisecond to spin up..............comment out\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 str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\"\n str += \"G00X\"+x.toFixed(4)+\"Y\"+y.toFixed(4)+\"Z\"+jog_height.toFixed(4)+\"\\n\"\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 }\n }\n //\n // finish\n //\n str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\" // move up before stopping spindle\n str += \"M05\\n\" // spindle stop\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 // 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\n","top":"831.1683166499946","left":"1985.839479173399","filename":"modules/path/formats/g-code","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.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.992472539363189\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"settings\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5022735462618486\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5022735462618486\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.057765477464730264\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVG\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5022735462618486\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVG\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9604471250218796\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9604471250218796\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8622681794886853\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}"]})) +window.mods_prog_load(prog) +</script> +</body> +</html> diff --git a/docs/Instruction/images/proverXL/mods/mill_2D_PCB_svg.txt b/docs/Instruction/images/proverXL/mods/mill_2D_PCB_svg.txt new file mode 100644 index 0000000000000000000000000000000000000000..9cd2ee7fee28a107ee8bc657d1787dbb62eb6851 --- /dev/null +++ b/docs/Instruction/images/proverXL/mods/mill_2D_PCB_svg.txt @@ -0,0 +1 @@ +{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"390.58350726962306","left":"3347.994408259124","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1007.5835072696229","left":"3805.994408259124","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"877.5835072696229","left":"3374.994408259124","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 = ''\n }\n//\n// inputs\n//\nvar inputs = {\n distances:{type:'F32',\n event:function(evt){\n mod.distances = evt.detail\n var h = mod.distances.height\n var w = mod.distances.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.height = mod.distances.height \n ctx.canvas.width = mod.distances.width\n if (mod.offset.value != '')\n offset()\n }},\n offset:{type:'number',\n event:function(evt){\n mod.offset.value = evt.detail\n offset()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"490.58350726962306","left":"3806.994408259124","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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"523.583507269623","left":"2835.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.2892270043957246":{"definition":"//\n// view toolpath\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// todo:\n// erase and update new path\n// show depth info\n// show size\n// calculate camera far\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'view toolpath'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar 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//\nvar 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//\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('(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//\nfunction 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//\nfunction 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 geometry = new THREE.Geometry()\n geometry.vertices.push(start,stop)\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\t renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction 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\t scene = new THREE.Scene()\n\t mod.scene = scene\n\t var width = win.innerWidth\n\t var height = win.innerHeight\n\t var aspect = width/height\n\t var near = 0.1\n\t var far = 1000000\n\t camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n\t mod.camera = camera\n\t scene.add(camera)\n\t //\n\t // add renderer\n\t //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n\t renderer.setSize(width,height)\n\t 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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n\t }\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\t camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n\t renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1227.583507269623","left":"2165.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.9557599338778935":{"definition":"//\n// mill raster 2D\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 = 'mill raster 2D'\n//\n// initialization\n//\nvar 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//\nvar inputs = {\n imageInfo:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.width\n ctx.canvas.height = mod.height\n }},\n path:{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 }\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:{type:'object',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n diameter:{type:'number',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{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:{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//\nvar 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//\nfunction 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 'offset number':\n mod.number.value = settings[s]\n break\n }\n }\n }\n//\n// clear_path\n//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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 }\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//\nfunction 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//\nfunction 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//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"448.58350726962306","left":"2188.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.10309904694903338":{"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 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 = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = 1\n mod.sort.checked = true\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 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 > 1) && (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 > 1) && (sort == false)) || (vecpath.length == 1))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"993.5835072696229","left":"2879.994408259124","filename":"undefined","inputs":{},"outputs":{}},"0.992472539363189":{"definition":"//\n// set object\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 = 'set PCB defaults'\n//\n// initialization\n//\nvar init = function() {\n //\n add_output('mill 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.004'\n add_variable('max depth (in)','var02')\n mod.var02.value = '0.004'\n add_variable('offset number','var03')\n mod.var03.value = '4'\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.010'\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 //\n }\n//\n// inputs\n//\nvar inputs = {}\n//\n// outputs\n//\nvar outputs = {\n settings:{type:'',\n event:function(vars){\n mods.output(mod,'settings',vars)\n }\n }\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n }\n//\n// local functions\n//\nfunction 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] = {span:span,variables:{}}\n mod.div.appendChild(btn)\n mod.setting = label\n mod.div.appendChild(document.createElement('br'))\n }\nfunction 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//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"368.58350726962306","left":"1644.9944082591242","filename":"undefined","inputs":{},"outputs":{}},"0.8622681794886853":{"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":"1215.9473065265367","left":"1684.9337776993061","filename":"modules/file/save","inputs":{},"outputs":{}},"0.057765477464730264":{"definition":"//\n// read SVG\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'read SVG'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n SVG:{type:'string',\n event:function(evt) {\n svg_load_handler({target:{result:evt.detail}})\n }}}\n//\n// outputs\n//\nvar outputs = {\n SVG:{type:'string',\n event:function(){\n mods.output(mod,'SVG',mod.str)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // file input control\n //\n var file = document.createElement('input')\n file.setAttribute('type','file')\n file.setAttribute('id',div.id+'file_input')\n file.style.position = 'absolute'\n file.style.left = 0\n file.style.top = 0\n file.style.width = 0\n file.style.height = 0\n file.style.opacity = 0\n file.addEventListener('change',function() {\n svg_read_handler()\n })\n div.appendChild(file)\n mod.file = file\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // file select button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('select SVG file'))\n btn.addEventListener('click',function(){\n var file = document.getElementById(div.id+'file_input')\n file.value = null\n file.click()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // info div\n //\n var info = document.createElement('div')\n info.setAttribute('id',div.id+'info')\n var text = document.createTextNode('file:')\n info.appendChild(text)\n mod.name = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('width:')\n info.appendChild(text)\n mod.width = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('height:')\n info.appendChild(text)\n mod.height = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('units per inch:')\n info.appendChild(text)\n mod.units = text\n div.appendChild(info)\n }\n//\n// local functions\n//\n// read handler\n//\nfunction svg_read_handler(event) {\n //\n // read as text\n //\n var file_reader = new FileReader()\n file_reader.onload = svg_load_handler\n var input_file = mod.file.files[0]\n var file_name = input_file.name\n mod.name.nodeValue = \"file: \"+file_name\n file_reader.readAsText(input_file)\n }\n//\n// load handler\n//\nfunction svg_load_handler(event) {\n mod.str = event.target.result\n //\n // parse size\n //\n var i = mod.str.indexOf(\"width\")\n if (i == -1) {\n mod.width.nodeValue = \"width: not found\"\n mod.height.nodeValue = \"height: not found\"\n }\n else {\n var i1 = mod.str.indexOf(\"\\\"\",i+1)\n var i2 = mod.str.indexOf(\"\\\"\",i1+1)\n var width = mod.str.substring(i1+1,i2)\n i = mod.str.indexOf(\"height\")\n i1 = mod.str.indexOf(\"\\\"\",i+1)\n i2 = mod.str.indexOf(\"\\\"\",i1+1)\n var height = mod.str.substring(i1+1,i2)\n ih = mod.str.indexOf(\"height\")\n if (width.indexOf(\"px\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 90\n }\n else if (width.indexOf(\"mm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 25.4\n }\n else if (width.indexOf(\"cm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 2.54\n }\n else if (width.indexOf(\"in\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 1\n }\n else {\n var units = 90\n }\n mod.width.nodeValue = \"width: \"+width\n mod.height.nodeValue = \"height: \"+height\n mod.units.nodeValue = \"units per inch: \"+units\n }\n //\n // display\n //\n var img = new Image()\n var src = \"data:image/svg+xml;base64,\"+window.btoa(mod.str)\n img.setAttribute(\"src\",src)\n img.onload = function() {\n if (img.width > img.height) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-img.height/img.width)\n var w = mod.canvas.width\n var h = mod.canvas.width*img.height/img.width\n }\n else {\n var x0 = mod.canvas.width*.5*(1-img.width/img.height)\n var y0 = 0\n var w = mod.canvas.height*img.width/img.height\n var h = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(img,x0,y0,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = img.width\n ctx.canvas.height = img.height \n ctx.drawImage(img,0,0)\n outputs.SVG.event()\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"332.03488793999406","left":"837.5723969709061","filename":"modules/read/svg","inputs":{},"outputs":{}},"0.5022735462618486":{"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 = '1000'\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":"438.69493521948414","left":"1211.852199242571","filename":"modules/convert/svg/image","inputs":{},"outputs":{}},"0.9604471250218796":{"definition":"//\n// path to G-code\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2018\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// Updated: Neil Gershenfeld\n// Date: Oct 28 2020\n// Comments: added mm/s vs mm/min option\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 = '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.tool.value = '1'\n mod.coolantoff.checked = true\n mod.formatMm.checked = true\n mod.unitMinutes.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 speed: '))\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 // 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.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 // second or minute rate units \n //\n div.appendChild(document.createTextNode('rate units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitSeconds'\n input.checked = true\n div.appendChild(input)\n mod.unitSeconds = input\n div.appendChild(document.createTextNode('second'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitMinutes'\n div.appendChild(input)\n mod.unitMinutes = input\n div.appendChild(document.createTextNode('minute'))\n }\n//\n// local functions\n//\nfunction make_path() {\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 if (mod.unitMinutes.checked) {\n cut_speed *= 60\n plunge_speed *= 60\n }\n var spindle_speed = parseFloat(mod.spindlespeed.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 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 //str += \"G04 P1000\\n\" // give spindle 1 second to spin up................comment out\n //str += \"G04 P1\\n\" // give spindle 1 Millisecond to spin up..............comment out\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 str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\"\n str += \"G00X\"+x.toFixed(4)+\"Y\"+y.toFixed(4)+\"Z\"+jog_height.toFixed(4)+\"\\n\"\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 }\n }\n //\n // finish\n //\n str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\" // move up before stopping spindle\n str += \"M05\\n\" // spindle stop\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 // 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\n","top":"764.1683166499946","left":"1656.839479173399","filename":"modules/path/formats/g-code","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.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.10309904694903338\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.992472539363189\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"settings\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5022735462618486\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5022735462618486\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9557599338778935\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.057765477464730264\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVG\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5022735462618486\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVG\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.2892270043957246\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9604471250218796\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9604471250218796\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8622681794886853\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}"]} \ No newline at end of file diff --git a/docs/Instruction/images/proverXL/mods_save_local_program.png b/docs/Instruction/images/proverXL/mods_save_local_program.png new file mode 100644 index 0000000000000000000000000000000000000000..c23be267a8162ae659e0cec9c88d2860ec332880 Binary files /dev/null and b/docs/Instruction/images/proverXL/mods_save_local_program.png differ diff --git a/docs/Instruction/images/proverXL/node-path_to_G-code.png b/docs/Instruction/images/proverXL/node-path_to_G-code.png new file mode 100644 index 0000000000000000000000000000000000000000..b48d799f4870a5a6f7c3041d61a2032f8b9ad0f9 Binary files /dev/null and b/docs/Instruction/images/proverXL/node-path_to_G-code.png differ diff --git a/docs/Instruction/images/proverXL/tool_change.jpg b/docs/Instruction/images/proverXL/tool_change.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f30dd717b47daf66e1db5418e47873cd0e8309e0 Binary files /dev/null and b/docs/Instruction/images/proverXL/tool_change.jpg differ diff --git a/docs/Instruction/images/proverXL/tool_move_to_left_forword.jpg b/docs/Instruction/images/proverXL/tool_move_to_left_forword.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1322be3ecb778a3598510e1dd4724ff1310d9f8e Binary files /dev/null and b/docs/Instruction/images/proverXL/tool_move_to_left_forword.jpg differ diff --git a/docs/Instruction/images/proverXL/tool_z_touch.jpg b/docs/Instruction/images/proverXL/tool_z_touch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2c3c328c7a71d90cf8c884c57351b6fb82f4931d Binary files /dev/null and b/docs/Instruction/images/proverXL/tool_z_touch.jpg differ diff --git a/docs/Instruction/images/proverXL/work_origin.png b/docs/Instruction/images/proverXL/work_origin.png new file mode 100644 index 0000000000000000000000000000000000000000..2c9cd4722953e781d4650dab642851a14ff2ac14 Binary files /dev/null and b/docs/Instruction/images/proverXL/work_origin.png differ diff --git a/docs/Instruction/images/proverXL/work_origin_before.png b/docs/Instruction/images/proverXL/work_origin_before.png new file mode 100644 index 0000000000000000000000000000000000000000..d21e0dd88473d472bddb7c1747bbbf328a6279d0 Binary files /dev/null and b/docs/Instruction/images/proverXL/work_origin_before.png differ diff --git a/docs/Instruction/tips/genmitsu_prover_xl_setup.md b/docs/Instruction/tips/genmitsu_prover_xl_setup.md index 9bd98b0c72a656146ea916617a3a1ce7de38c551..290b1f3a3bdc7629e07c5d3debafb2d249cecb6d 100644 --- a/docs/Instruction/tips/genmitsu_prover_xl_setup.md +++ b/docs/Instruction/tips/genmitsu_prover_xl_setup.md @@ -31,6 +31,10 @@ Download: [SVG](../images/proverXL/30x30_40x40mm.svg) ### CAM: [Mods](https://mods.cba.mit.edu/) +!!! Attention + See [Adjust MODS' g-code for this machine](#Adjust MODS' g-code for this machine) first (below) + +   @@ -53,7 +57,8 @@ Download: [SVG image.nc](../images/proverXL/SVG image.nc) - [Installation](https://resources.sienci.com/view/gs-installation/)  -#### Set up + +#### Set up Software - Install [CH340 Driver](https://learn.sparkfun.com/tutorials/how-to-install-ch340-drivers/all#mac-osx) - Download setting file [gSender-settings-Genmitsu PROVerXL-4030.json](../images/proverXL/gSender-settings-Genmitsu PROVerXL-4030.json) - Gear icon(right top) -> Clock to open Setting @@ -67,6 +72,7 @@ Download: [SVG image.nc](../images/proverXL/SVG image.nc) - Connect to Machine -> Unrecognized devices -> /dev/tty.wchusbserial1440  +### Set up Machine - Click to Run Homing !!! Danger @@ -75,3 +81,216 @@ Download: [SVG image.nc](../images/proverXL/SVG image.nc)  {width=500} + + +- Jog Control + - Home (Machine origin) + - X: 0 (Right) + - Y: 0 (Back) + - X: 0 (Up) + - Jog + - X: "** X- **" to move Left + - Y: "** Y- **" to move Forword + - Z: "** Z- **" to move Down + - Keyboard short cut + - X: shift + right/left + - Y: shift + up/down + - Z: shift + pageup/pagedown + + + +- Tool setting + - Use 2 Wrenches + - No.13 : hold top + - No.17 : loose/tight Collet holder + + + + +- Move to Material + - Use Jog control to your material(left-down) + + + +- Z-touch + - Use paper to touch the tool on the paper surface + - move one more -0.1mm down to reach the material surface + + + +- Work ZERO + - Click **Zero All** to Work origin (X0,Y0,Z0) + + + +### Load File + +- Click **Load File** and open [SVG image.nc](../images/proverXL/SVG image.nc) + + + +### Start milling + +- Click **Start Job** to run + + +### Milled + +{width=400} +{width=400} + + + + +## Adjust MODS' g-code for this machine + +[mods](https://mods.cba.mit.edu/) + + + + Compare G-code form png and from SVG + +!!! note + PNG's ```G04 P1``` or SVG's ```G04 P1000``` need to be **commented out** for this machine, otherwise machine will stop on this line. + + +30x30_40x40mm_96dpi.png.nc |SVG image.nc |Meanig +- |--- |-- +% |% |% Signals start +G17 |G17 |G17 Select X-Y plane +G21 |G21 |G21 Metric units +G40 |G40 |G40 Cancel cutter compensation +G49 |G49 |G49 Cancel cutter +G54 |G54 |G54 Set Datum +G80 |G80 |G80 Cancel cycles +G90 |G90 |G90 Absolute programming +G94 |G94 |G94 Per minute feed +T1M06 |T1M06 |T1 Tool Number, M06 Tool Change +F150.0000 |F150.0000 |F Defines feed rate +S11000 |S11000 |S Defines speed +G00Z2.0000 |G00Z2.0000 |G00 Positioning at rapid travel Z2.0000 +M03 |M03 |M03 Spindle on clockwise +**G04 P1** |**G04 P1000** |G04 Dwell P[ms] +G00Z2.0000 |G00Z2.0000 | +G00X5.3263Y4.7937Z2.0000 |G00X5.0324Y4.8037Z2.0000 | +G01Z-0.7989 F150.0000 |G01Z-0.7117 F150.0000 | +F150.0000 |F150.0000 | +G01X5.3263Y5.0600Z-0.7989 |G01X5.0324Y4.8291Z-0.7117 | +G01X4.7937Y5.5926Z-0.7989 |G01X4.9816Y4.8291Z-0.7117 | +G01X4.7937Y34.6208Z-0.7989 |G01X4.8545Y4.9561Z-0.7117 | +G01X5.8589Y35.1534Z-0.7989 |G01X4.8037Y5.1086Z-0.7117 | +G01X34.6208Y35.1534Z-0.7989 |G01X4.8037Y34.9472Z-0.7117 | +G01X35.1534Y34.0882Z-0.7989 |G01X4.9561Y35.1251Z-0.7117 | +G01X35.1534Y5.3263Z-0.7989 |G01X5.1086Y35.1759Z-0.7117 | +G01X34.0882Y4.7937Z-0.7989 |G01X34.9472Y35.1759Z-0.7117 | +G01X5.3263Y4.7937Z-0.7989 |G01X35.1251Y35.0235Z-0.7117 | +G00Z2.0000 |G01X35.1759Y34.8710Z-0.7117 | +G1X0Y0Z0 |G01X35.1759Y5.0324Z-0.7117 | +M05 |G01X35.0235Y4.8545Z-0.7117 | +M30 |G01X34.8710Y4.8037Z-0.7117 | +% |G01X5.0324Y4.8037Z-0.7117 | + |G00Z2.0000 | + |M05 |M05 Spindle Stop + |M30 |M30 Program end, return to start + |% | + +Ref. [G-gode](https://gcodetutor.com/cnc-machine-training/cnc-g-codes.html), +[M-code](https://gcodetutor.com/gcode-tutorial/cnc-m-codes.html), +[T1M06](https://cnc-programming-tips.blogspot.com/2014/11/what-is-m06-m-code-tool-change-command.html), +[G04](https://en.wikipedia.org/wiki/G-code), +[G54](https://nc-program.s-projects.net/g-code/g54-59.html), +[%](https://en.wikipedia.org/wiki/G-code) + + + + +### Edit **path to G-code** module + +{width=300} + +Comment out ```G04 P1000``` + +``` +// +// local functions +// +function make_path() { + var dx = 25.4*mod.width/mod.dpi + var cut_speed = parseFloat(mod.cutspeed.value) + var plunge_speed = parseFloat(mod.plungespeed.value) + var jog_height = parseFloat(mod.jogheight.value) + var nx = mod.width + var scale = dx/(nx-1) + var in_mm_scale = 1 + if (mod.formatInch.checked) { + dx /= 25.4 + scale /= 25.4 + cut_speed /= 25.4 + plunge_speed /= 25.4 + jog_height /= 25.4 + } + if (mod.unitMinutes.checked) { + cut_speed *= 60 + plunge_speed *= 60 + } + var spindle_speed = parseFloat(mod.spindlespeed.value) + var tool = parseInt(mod.tool.value) + str = "%\n" // tape start + str += "G17\n" // xy plane + if (mod.formatInch.checked) + str += "G20\n" // inches + if (mod.formatMm.checked) + str += "G21\n" // mm + str += "G40\n" // cancel tool radius compensation + str += "G49\n" // cancel tool length compensation + str += "G54\n" // coordinate system 1 + str += "G80\n" // cancel canned cycles + str += "G90\n" // absolute coordinates + str += "G94\n" // feed/minute units + str += "T"+tool+"M06\n" // tool selection, tool change + str += "F"+cut_speed.toFixed(4)+"\n" // feed rate + str += "S"+spindle_speed+"\n" // spindle speed + if (mod.coolanton.checked) + str += "M08\n" // coolant on + str += "G00Z"+jog_height.toFixed(4)+"\n" // move up before starting spindle + str += "M03\n" // spindle on clockwise + //str += "G04 P1000\n" // give spindle 1 second to spin up................comment out + //str += "G04 P1\n" // give spindle 1 Millisecond to spin up..............comment out +``` + +### save local program + + + +## Download local program + +mill_2D_PCB|PNG |SVG +-- |-- |-- +html|[png.html](../images/proverXL/mods/mill_2D_PCB_png.html)|[svg.html](../images/proverXL/mods/mill_2D_PCB_svg.html) +txt|[png.txt](../images/proverXL/mods/mill_2D_PCB_png.txt)|[svg.txt](../images/proverXL/mods/mill_2D_PCB_svg.txt) + + + +### open local program +- html + - Double Click mill_2D_PCB_svg.html (work in local PC) +- text + - Open [mods](https://mods.cba.mit.edu/) (MIT server) + - left click -> program -> open local program -> mill_2D_PCB_svg.txt -> open + + + +## line Test + +### V-bit + + - Cutting Edge Diameter 0.2mm + - 30 Degrees + +Depth of Cut|Width of Cut +-- |-- +0.1 |0.25359 +0.2 |0.30718 +0.3 |0.36077 + +Ref. [calculator width-of-cut of a “V†shaped cutting tool](https://woodworkerb.com/home/pcb-isolation-routing/tool-width-calculator/)