diff --git a/files.html b/files.html index 716b240713126780a554429a673877baceed711e..9e7b86c26bbe930f10b03aaf41a93629e28ee221 100644 --- a/files.html +++ b/files.html @@ -19,7 +19,6 @@ <a href='./js/files.js'>files.js</a><br> <a href='./js/load.js'>load.js</a><br> <a href='./js/mods.js'>mods.js</a><br> -<i> node_modules</i><br> <a href='./js/printserver.js'>printserver.js</a><br> <a href='./js/serialserver.js'>serialserver.js</a><br> <i> three.js</i><br> @@ -134,6 +133,7 @@ <i> programs</i><br> <i> image</i><br> <a href='./programs/image/function'>function</a><br> + <a href='./programs/image/palette%20mask%20raster'>palette mask raster</a><br> <a href='./programs/image/palette%20mask%20vector'>palette mask vector</a><br> <a href='./programs/index.html'>index.html</a><br> <i> iterate</i><br> diff --git a/modules/image/raster mask b/modules/image/raster mask index 14803e72f452854d6d1b05992953bbdcc4ae0e21..dc26f1d76221c492f93be777a7cb06ca8f5557cb 100644 --- a/modules/image/raster mask +++ b/modules/image/raster mask @@ -25,7 +25,6 @@ var name = 'raster mask' // initialization // var init = function() { - mod.fill.checked = true } // // inputs @@ -49,7 +48,9 @@ var inputs = { }}, mask:{type:'RGBA', event:function(evt){ - make_mask(evt.detail) + var ctx = mod.convert.getContext("2d") + ctx.putImageData(evt.detail,0,0) + make_mask() }}} // // outputs @@ -96,16 +97,6 @@ var interface = function(div){ }) div.appendChild(btn) div.appendChild(document.createElement('br')) - // - // fill - // - div.appendChild(document.createTextNode('fill masks: ')) - var input = document.createElement('input') - input.type = 'checkbox' - input.id = mod.div.id+'fill' - div.appendChild(input) - mod.fill = input - div.appendChild(document.createElement('br')) } // // local functions @@ -113,11 +104,11 @@ var interface = function(div){ // // make_mask // -function make_mask(path) { +function make_mask() { // // save mask // - save_mask(path) + save_mask() // // check for next mask // @@ -139,7 +130,7 @@ function make_mask(path) { // // save_mask // -function save_mask(path) { +function save_mask(mask) { // // create SVG // @@ -231,12 +222,9 @@ function save_mask(path) { text.setAttribute('dy','.2') text.textContent = name svg.appendChild(text) - // // raster mask // - var ctx = mod.convert.getContext("2d") - ctx.putImageData(imgdata,0,0) var href = mod.convert.toDataURL() var img = document.createElementNS(svgNS,'image') img.setAttribute('id',mod.div.id+'svgimg') @@ -246,41 +234,6 @@ function save_mask(path) { img.setAttribute('height',imgheight) img.setAttributeNS('http://www.w3.org/1999/xlink','href',href) svg.appendChild(img) - - // - // vector mask - // - var g = document.createElementNS(svgNS,'g') - svg.appendChild(g) - for (var seg = 0; seg < path.length; ++seg) { - var points = '' - for (var pt = 0; pt < path[seg].length; ++ pt) { - var x = 1.5+imgwidth*path[seg][pt][0]/(mod.image.width-1) - var y = 1.5+imgheight*(1-path[seg][pt][1]/(mod.image.height-1)) - points += x+','+y+' ' - } - var x = 1.5+imgwidth*path[seg][0][0]/(mod.image.width-1) - var y = 1.5+imgheight*(1-path[seg][0][1]/(mod.image.height-1)) - points += x+','+y+' ' - if (mod.fill.checked == true) { - var polyline = document.createElementNS(svgNS,'polyline') - polyline.setAttribute('stroke','none') - polyline.setAttribute('fill','black') - polyline.setAttribute('fill-rule','evenodd') - polyline.setAttribute('points',points) - g.appendChild(polyline) - } - else { - var polyline = document.createElementNS(svgNS,'polyline') - polyline.setAttribute('stroke','black') - polyline.setAttribute('stroke-width','0.01') - polyline.setAttribute('stroke-linecap','round') - polyline.setAttribute('fill','none') - polyline.setAttribute('points',points) - g.appendChild(polyline) - } - } - // // file // diff --git a/programs/image/palette mask raster b/programs/image/palette mask raster new file mode 100644 index 0000000000000000000000000000000000000000..9590f9252524f007f40a840556d5365f9a1f1646 --- /dev/null +++ b/programs/image/palette mask raster @@ -0,0 +1 @@ +{"modules":{"0.5648025145147616":{"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 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 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":"102","left":"204","inputs":{},"outputs":{}},"0.3959513260329336":{"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 name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"97","left":"1630","inputs":{},"outputs":{}},"0.3603827322636355":{"definition":"//\n// image palette\n// todo: linear time palette search\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2017\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'image palette'\n//\n// initialization\n//\nvar init = function() {\n mod.palette.value = '[[255,255,255],\\n[0,0,0],\\n[255,0,0],\\n[0,255,0],\\n[0,0,255]]'\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 var ctx = mod.convert.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n show_palette()\n }\n },\n palette:{type:'text',\n event:function(evt){\n mod.palette.value = evt.detail\n show_palette()\n }\n }\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 palette:{type:'text',\n event:function(){\n mods.output(mod,'palette',mod.palette.value)\n }}}\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 conversion canvas\n //\n var canvas = document.createElement('canvas')\n mod.convert = canvas\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 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 // image \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('original')\n span.appendChild(text)\n span.style.fontWeight = 'normal'\n btn.appendChild(span)\n mod.originalspan = span\n btn.addEventListener('click',function(){\n mod.originalspan.style.fontWeight = 'bold'\n mod.palettespan.style.fontWeight = 'normal'\n show_original()\n })\n div.appendChild(btn)\n div.appendChild(document.createTextNode(' image '))\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('palette')\n span.appendChild(text)\n span.style.fontWeight = 'bold'\n btn.appendChild(span)\n mod.palettespan = span\n btn.addEventListener('click',function(){\n mod.originalspan.style.fontWeight = 'normal'\n mod.palettespan.style.fontWeight = 'bold'\n show_palette()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // palette\n //\n div.appendChild(document.createTextNode('palette'))\n div.appendChild(document.createElement('br'))\n var text = document.createElement('textarea')\n text.setAttribute('rows',mods.ui.rows)\n text.setAttribute('cols',mods.ui.cols)\n div.appendChild(text)\n mod.palette = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n// show_original\n//\nfunction show_original() {\n var h = mod.img.height\n var w = mod.img.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,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 }\n//\n// show palette\n//\nfunction show_palette() {\n var blob = new Blob(['('+palette_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.palette.event()\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 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 var palette = JSON.parse(mod.palette.value)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,palette:palette,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// palette worker\n// todo: sort palette\n//\nfunction palette_worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var palette = evt.data.palette\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,rc,gc,bc\n var cmin,dmin,d\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 dmin = Number.MAX_VALUE\n for (color = 0; color < palette.length; ++color) {\n rc = palette[color][0]\n gc = palette[color][1]\n bc = palette[color][2]\n d = Math.sqrt(\n (rc-r)*(rc-r)+\n (gc-g)*(gc-g)+\n (bc-b)*(bc-b))\n if (d < dmin) {\n dmin = d\n cmin = color\n }\n }\n buf[(h-1-row)*w*4+col*4+0] = palette[cmin][0]\n buf[(h-1-row)*w*4+col*4+1] = palette[cmin][1]\n buf[(h-1-row)*w*4+col*4+2] = palette[cmin][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":"235","left":"614","inputs":{},"outputs":{}},"0.2403743422209378":{"definition":"//\n// read text\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2017\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'read text'\n//\n// initialization\n//\nvar init = function() {\n mod.text.value = \"load palette\"\n }\n//\n// inputs\n//\nvar inputs = {\n text:{type:'',\n event:function(evt) {\n mod.text.value = evt.detail\n }}}\n//\n// outputs\n//\nvar outputs = {\n text:{type:'',\n event:function(){\n mods.output(mod,'text',mod.text.value)}}}\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 text_read_handler()\n })\n div.appendChild(file)\n mod.file = file\n //\n // text\n //\n var text = document.createElement('textarea')\n text.setAttribute('rows',mods.ui.rows)\n text.setAttribute('cols',mods.ui.cols)\n text.addEventListener('input',function() {\n outputs.text.event()\n })\n div.appendChild(text)\n mod.text = text\n div.appendChild(document.createElement('br'))\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 text 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//\n// local functions\n//\n// read handler\n//\nfunction text_read_handler(event) {\n //\n // read as text\n //\n var file_reader = new FileReader()\n file_reader.onload = text_load_handler\n var input_file = mod.file.files[0]\n file_reader.readAsText(input_file)\n }\n//\n// load handler\n//\nfunction text_load_handler(event) {\n mod.text.value = event.target.result\n outputs.text.event()\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":"681","left":"212","inputs":{},"outputs":{}},"0.10571271239124669":{"definition":"//\n// image color separation\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2017\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'color separation'\n//\n// initialization\n//\nvar init = function() {\n mod.input = null\n mod.color.value = '[255,0,0]'\n mod.fill.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 var ctx = mod.convert.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n show_separation()\n }\n },\n color:{type:'RGB',\n event:function(evt){\n mod.color.value = JSON.stringify(evt.detail)\n }\n }\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 conversion canvas\n //\n var canvas = document.createElement('canvas')\n mod.convert = canvas\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 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 // image \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('original')\n span.appendChild(text)\n span.style.fontWeight = 'normal'\n btn.appendChild(span)\n mod.originalspan = span\n btn.addEventListener('click',function(){\n mod.originalspan.style.fontWeight = 'bold'\n mod.palettespan.style.fontWeight = 'normal'\n show_original()\n })\n div.appendChild(btn)\n div.appendChild(document.createTextNode(' image '))\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('separation')\n span.appendChild(text)\n span.style.fontWeight = 'bold'\n btn.appendChild(span)\n mod.palettespan = span\n btn.addEventListener('click',function(){\n mod.originalspan.style.fontWeight = 'normal'\n mod.palettespan.style.fontWeight = 'bold'\n show_separation()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // color\n //\n div.appendChild(document.createTextNode('color: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 10\n div.appendChild(input)\n mod.color = input\n div.appendChild(document.createElement('br'))\n //\n // edges\n //\n div.appendChild(document.createTextNode('fill edges: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'fill'\n div.appendChild(input)\n mod.fill = input\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n// show_original\n//\nfunction show_original() {\n var h = mod.img.height\n var w = mod.img.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,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 }\n//\n// show separation\n//\nfunction show_separation() {\n var blob = new Blob(['('+separation_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 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 var color = JSON.parse(mod.color.value)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,color:color,\n fill:mod.fill.checked,buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// separation worker\n//\nfunction separation_worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var color = evt.data.color\n var fill = evt.data.fill\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,rc,gc,bc\n var cmin,dmin,d\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 rc = color[0]\n gc = color[1]\n bc = color[2]\n if ((rc == r) && (gc == g) && (bc == b)) {\n buf[(h-1-row)*w*4+col*4+0] = 255\n buf[(h-1-row)*w*4+col*4+1] = 255\n buf[(h-1-row)*w*4+col*4+2] = 255\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n buf[(h-1-row)*w*4+col*4+0] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n if (fill == true) {\n for (var row = 0; row < h; ++row) {\n col = 0\n buf[(h-1-row)*w*4+col*4+0] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\n buf[(h-1-row)*w*4+col*4+3] = 255\n col = w-1\n buf[(h-1-row)*w*4+col*4+0] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n for (var col = 0; col < w; ++col) {\n row = 0\n buf[(h-1-row)*w*4+col*4+0] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\n buf[(h-1-row)*w*4+col*4+3] = 255\n row = h-1\n buf[(h-1-row)*w*4+col*4+0] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\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":"349","left":"1131","inputs":{},"outputs":{}},"0.5509389792243394":{"definition":"//\n// raster mask\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2017\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'raster mask'\n//\n// initialization\n//\nvar init = function() {\n mod.fill.checked = true\n }\n//\n// inputs\n//\nvar inputs = {\n imageInfo:{type:'object',\n event:function(evt){\n mod.imageInfo = evt.detail\n }},\n image:{type:'RGBA',\n event:function(evt){\n mod.image = evt.detail\n mod.labelspan.style.fontWeight = 'bold' \n var ctx = mod.convert.getContext(\"2d\")\n ctx.canvas.width = mod.image.width\n ctx.canvas.height = mod.image.height \n }},\n palette:{type:'text',\n event:function(evt){\n mod.palette = JSON.parse(evt.detail)\n }},\n mask:{type:'RGBA',\n event:function(evt){\n var ctx = mod.convert.getContext(\"2d\")\n ctx.putImageData(evt.detail,0,0)\n make_mask()\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n mods.output(mod,'image',mod.image)\n }},\n color:{type:'RGB',\n event:function(evt){\n mods.output(mod,'color',evt)\n }},\n SVG:{type:'file',\n event:function(evt){\n mods.output(mod,'SVG',evt)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // off-screen conversion canvas\n //\n var canvas = document.createElement('canvas')\n mod.convert = canvas\n //\n // button\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 masks')\n mod.label = text\n span.appendChild(text)\n mod.labelspan = span\n btn.appendChild(span)\n btn.addEventListener('click',function(){\n mod.index = 0\n outputs.color.event(mod.palette[mod.index])\n outputs.image.event()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // fill\n //\n div.appendChild(document.createTextNode('fill masks: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'fill'\n div.appendChild(input)\n mod.fill = input\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n//\n// make_mask\n//\nfunction make_mask() {\n //\n // save mask\n //\n save_mask()\n //\n // check for next mask\n //\n if (mod.index < (mod.palette.length-1)) {\n //\n // yes, output\n //\n mod.index += 1\n outputs.color.event(mod.palette[mod.index])\n outputs.image.event()\n }\n else {\n //\n // no, done\n //\n mod.labelspan.style.fontWeight = 'normal' \n } \n }\n//\n// save_mask\n//\nfunction save_mask(mask) {\n //\n // create SVG\n //\n var imgwidth = mod.image.width/parseFloat(mod.imageInfo.dpi)\n var imgheight = mod.image.height/parseFloat(mod.imageInfo.dpi)\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',(3+imgwidth)+'in')\n svg.setAttribute('height',(3+imgheight)+'in')\n svg.style.backgroundColor = 'rgb(255,255,255)'\n svg.setAttribute('viewBox','0 0 '+(3+imgwidth)+' '+(3+imgheight))\n //\n // background\n //\n var rect = document.createElementNS(svgNS,'rect')\n rect.setAttribute('x','0')\n rect.setAttribute('y','0')\n rect.setAttribute('width',3+imgwidth)\n rect.setAttribute('height',3+imgheight)\n rect.setAttribute('stroke','none')\n rect.setAttribute('fill','white')\n svg.appendChild(rect)\n //\n // registration\n //\n var g = document.createElementNS(svgNS,'g')\n svg.appendChild(g)\n var polyline = document.createElementNS(svgNS,'polyline')\n polyline.setAttribute('stroke','red')\n polyline.setAttribute('stroke-width','0.01')\n polyline.setAttribute('stroke-linecap','round')\n polyline.setAttribute('fill','none')\n polyline.setAttribute('points','0.5,0.5 '+\n '0.5,'+(imgheight+2.5)+' '+\n (imgwidth+2.5)+','+(imgheight+2.5)+' '+\n (imgwidth+2.5)+',0.9 '+\n (imgwidth+2.1)+',0.5 '+\n '0.5,0.5')\n g.appendChild(polyline)\n var circle = document.createElementNS(svgNS,'circle')\n circle.setAttribute('cx','1')\n circle.setAttribute('cy','1')\n circle.setAttribute('r','0.125')\n circle.setAttribute('stroke','red')\n circle.setAttribute('stroke-width','0.01')\n circle.setAttribute('fill','none')\n g.appendChild(circle)\n var circle = document.createElementNS(svgNS,'circle')\n circle.setAttribute('cx','1')\n circle.setAttribute('cy',(2+imgheight))\n circle.setAttribute('r','0.125')\n circle.setAttribute('stroke','red')\n circle.setAttribute('stroke-width','0.01')\n circle.setAttribute('fill','none')\n g.appendChild(circle)\n var circle = document.createElementNS(svgNS,'circle')\n circle.setAttribute('cx',(2+imgwidth))\n circle.setAttribute('cy',(2+imgheight))\n circle.setAttribute('r','0.125')\n circle.setAttribute('stroke','red')\n circle.setAttribute('stroke-width','0.01')\n circle.setAttribute('fill','none')\n g.appendChild(circle)\n var circle = document.createElementNS(svgNS,'circle')\n circle.setAttribute('cx',(2+imgwidth))\n circle.setAttribute('cy','1')\n circle.setAttribute('r','0.125')\n circle.setAttribute('stroke','red')\n circle.setAttribute('stroke-width','0.01')\n circle.setAttribute('fill','none')\n g.appendChild(circle)\n //\n // name\n //\n var name = mod.imageInfo.name+'.'+mod.palette[mod.index][0]\n name += '.'+mod.palette[mod.index][1]\n name += '.'+mod.palette[mod.index][2]\n if (mod.palette[mod.index][3] != undefined)\n name += '.'+mod.palette[mod.index][3]\n var text = document.createElementNS(svgNS,'text')\n text.setAttribute('id',mod.div.id+'svgtext')\n text.setAttribute('x',(3+imgwidth)/2)\n text.setAttribute('y','1')\n text.setAttribute('fill','red')\n text.setAttribute('font-size','.5')\n text.setAttribute('text-anchor','middle')\n text.setAttribute('dy','.2')\n text.textContent = name\n svg.appendChild(text)\n //\n // raster mask\n //\n var href = mod.convert.toDataURL()\n var img = document.createElementNS(svgNS,'image')\n img.setAttribute('id',mod.div.id+'svgimg')\n img.setAttribute('x','1.5')\n img.setAttribute('y','1.5')\n img.setAttribute('width',imgwidth)\n img.setAttribute('height',imgheight)\n img.setAttributeNS('http://www.w3.org/1999/xlink','href',href)\n svg.appendChild(img)\n //\n // file\n //\n var obj = {}\n obj.type = 'file'\n obj.name = name+'.svg'\n var xml = new XMLSerializer().serializeToString(svg)\n obj.contents = xml\n outputs.SVG.event(obj)\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":"94","left":"1160","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.5648025145147616\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3603827322636355\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.2403743422209378\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"text\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3603827322636355\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"palette\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3603827322636355\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5509389792243394\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3603827322636355\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"palette\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5509389792243394\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"palette\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5648025145147616\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5509389792243394\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5509389792243394\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10571271239124669\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5509389792243394\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"color\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10571271239124669\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"color\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.10571271239124669\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5509389792243394\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mask\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5509389792243394\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVG\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3959513260329336\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}"]} \ No newline at end of file diff --git a/programs/index.html b/programs/index.html index 724e7497533806888b36156d73a5137da235ea66..934ea5fcbf22bf5fa8ab189923cdbe1fcdd08493 100644 --- a/programs/index.html +++ b/programs/index.html @@ -11,6 +11,7 @@ </script> <i>image</i><br> <a href="javascript:handler('programs/image/function')">function</a><br> + <a href="javascript:handler('programs/image/palette%20mask%20raster')">palette mask raster</a><br> <a href="javascript:handler('programs/image/palette%20mask%20vector')">palette mask vector</a><br> <i>iterate</i><br> <a href="javascript:handler('programs/iterate/z%20theta%20scan')">z theta scan</a><br>