From 9105e371e704f097f9ac64e85e19a0f8bcd7746b Mon Sep 17 00:00:00 2001
From: Neil Gershenfeld <gersh@cba.mit.edu>
Date: Thu, 11 Jan 2018 09:06:10 -0500
Subject: [PATCH] wip

---
 programs/image/motion detect | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/programs/image/motion detect b/programs/image/motion detect
index 819fd51..84f68e3 100644
--- a/programs/image/motion detect	
+++ b/programs/image/motion detect	
@@ -1 +1 @@
-{"modules":{"0.10092185293872713":{"definition":"//\n// convert rgba jpg\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 = 'convert RGBA to JPG'\n//\n// initialization\n//\nvar init = function() {\n   mod.name.value = \"file.jpg\"\n   mod.compress.value = .75\n   }\n//\n// inputs\n//\nvar inputs = {\n   image:{type:'RGBA',\n      event:function(evt){\n         var ctx = mod.img.getContext(\"2d\")\n         ctx.canvas.width = evt.detail.width\n         ctx.canvas.height = evt.detail.height \n         ctx.putImageData(evt.detail,0,0)\n         mod.pxtext.nodeValue = evt.detail.width+' x '+evt.detail.height+' px'\n         convert_image()\n         }},\n   imageInfo:{type:'object',\n      event:function(evt){\n         mod.name.value = evt.detail.name+'.jpg'\n         }}\n   }\n//\n// outputs\n//\nvar outputs = {\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   // 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   // info div\n   //\n   var info = document.createElement('div')\n      info.appendChild(document.createTextNode('file name: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.name = input\n      info.appendChild(document.createElement('br'))\n      info.appendChild(document.createTextNode('compression: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.compress = input\n      info.appendChild(document.createTextNode(' (0-1)'))\n      info.appendChild(document.createElement('br'))\n      var text = document.createTextNode('px: ')\n         info.appendChild(text)\n         mod.pxtext = text\n      div.appendChild(info)\n   }\n//\n// local functions\n//\nfunction convert_image() {\n   //\n   // preview\n   //\n   var h = mod.img.height\n   var w = mod.img.width\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   // convert and save\n   //\n   mod.img.toBlob(function(blob){\n      var url = URL.createObjectURL(blob)\n      var link = document.createElement('a')\n      link.download = mod.name.value\n      link.href = url\n      document.body.appendChild(link)\n      link.click()\n      document.body.removeChild(link)\n      URL.revokeObjectURL(url)\n      },'image/jpeg',parseFloat(mod.compress.value))\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":"142","left":"1098","inputs":{},"outputs":{}},"0.4997564076516918":{"definition":"//\n// video\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 = 'video'\n//\n// initialization\n//\nvar init = function() {\n   mod.width.value = 1280 \n   mod.height.value = 720\n   mod.flip.checked = false\n   start_video()\n   }\n//\n// inputs\n//\nvar inputs = {\n   capture:{type:'event',\n      event:function(evt){\n         capture_video()}}}\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   // capture 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('capture'))\n      btn.addEventListener('click',function() {\n         capture_video()\n         })\n      div.appendChild(btn)\n   div.appendChild(document.createTextNode(' '))\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   // width\n   //\n   div.appendChild(document.createTextNode('width: '))\n   var input = document.createElement('input')\n      input.type = 'text'\n      input.size = 6\n      input.addEventListener('change',function() {\n         update_video()\n         })\n      div.appendChild(input)\n      mod.width = input\n   div.appendChild(document.createElement('br'))\n   //\n   // height\n   //\n   div.appendChild(document.createTextNode(' height: '))\n   var input = document.createElement('input')\n      input.type = 'text'\n      input.size = 6\n      input.addEventListener('change',function() {\n         update_video()\n         })\n      div.appendChild(input)\n      mod.height = input\n   div.appendChild(document.createElement('br'))\n   //\n   // flip\n   //\n   div.appendChild(document.createTextNode('flip image: '))\n   var input = document.createElement('input')\n      input.type = 'checkbox'\n      div.appendChild(input)\n      mod.flip = input\n   div.appendChild(document.createElement('br'))\n   //\n   // video element\n   //\n   var video = document.createElement('video')\n      mod.video = video\n   }\n//\n// local functions\n//\nfunction start_video() {\n   var w = parseInt(mod.width.value)\n   var h = parseInt(mod.height.value)\n   var ctx = mod.img.getContext(\"2d\")\n   ctx.canvas.width = w\n   ctx.canvas.height = h\n   var constraints = {\n      audio:false,\n      video:{width:w,height:h}\n      }\n   navigator.mediaDevices.getUserMedia(constraints)\n      .then(function(stream) {\n         mod.video.srcObject = stream\n         mod.video.onloadedmetadata = function(e) {\n            mod.video.play()\n            }\n         })\n      .catch(function(err) {\n         console.log(err.name + \": \"+err.message)\n         })\n   }\nfunction update_video() {\n   var w = parseInt(mod.width.value)\n   var h = parseInt(mod.height.value)\n   mod.video.setAttribute('width',w)\n   mod.video.setAttribute('height',h)\n   var ctx = mod.img.getContext(\"2d\")\n   ctx.canvas.width = w\n   ctx.canvas.height = h\n   }\nfunction capture_video() {\n   var w = parseInt(mod.width.value)\n   var h = parseInt(mod.height.value)\n   var ctx = mod.img.getContext(\"2d\")\n   ctx.drawImage(mod.video,0,0,w,h)\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      outputs.image.event()\n      webworker.terminate()\n      })\n   var ctx = mod.img.getContext(\"2d\")\n   var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n   webworker.postMessage({\n      height:mod.img.height,width:mod.img.width,\n      checked:mod.flip.checked,\n      buffer:img.data.buffer},[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 checked = evt.data.checked\n      var buf = new Uint8ClampedArray(evt.data.buffer)\n      if (checked == true) {\n         var newbuf = new Uint8ClampedArray(buf.length)\n         for (var row = 0; row < h; ++row) {\n            for (var col = 0; col < w; ++col) {\n               newbuf[(h-1-row)*w*4+col*4+0] = \n                  buf[row*w*4+(w-1-col)*4+0] \n               newbuf[(h-1-row)*w*4+col*4+1] = \n                  buf[row*w*4+(w-1-col)*4+1]\n               newbuf[(h-1-row)*w*4+col*4+2] = \n                  buf[row*w*4+(w-1-col)*4+2]\n               newbuf[(h-1-row)*w*4+col*4+3] = \n                  buf[row*w*4+(w-1-col)*4+3]\n               }\n            }\n         self.postMessage({buffer:newbuf.buffer},[newbuf.buffer])\n         }\n      else\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":"302","left":"738","inputs":{},"outputs":{}},"0.30101921769726425":{"definition":"//\n// motion detect\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 = 'motion detect'\n//\n// initialization\n//\nvar init = function() {\n   //\n   // UI settings\n   //\n   mod.threshold.value = 0.025\n   mod.latency.value = 15\n   mod.delay.value = 1\n   mod.dpi = 100\n   mod.win = null\n   //\n   // trigger image after latency (for start-up)\n   //\n   setTimeout(outputs.trigger.event,\n      parseFloat(mod.latency.value)*1000)\n   }\n//\n// inputs\n//\nvar inputs = {\n   image:{type:'RGBA',\n      event:function(evt){\n         var ctx = mod.img.getContext(\"2d\")\n         var lastctx = mod.lastimg.getContext(\"2d\")\n         lastctx.canvas.width = ctx.canvas.width\n         lastctx.canvas.height = ctx.canvas.height\n         lastctx.drawImage(mod.img,0,0)\n         ctx.canvas.width = evt.detail.width\n         ctx.canvas.height = evt.detail.height \n         ctx.putImageData(evt.detail,0,0)\n         compare_images()\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(obj){\n         mods.output(mod,'imageInfo',obj)}},\n   trigger:{type:'event',\n      event:function(){\n         mods.output(mod,'trigger',null)}}}\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 last image canvas\n   //\n   var canvas = document.createElement('canvas')\n      mod.lastimg = canvas\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 last image'))\n      btn.addEventListener('click',function(){\n         mod.win = window.open('')\n         mod.win.document.title = 'motion detect last image'\n         mod.win.document.body.style.overflow = 'hidden'\n         mod.win.document.body.style.border = 0\n         mod.win.document.body.style.padding = 0\n         mod.win.document.body.style.margin = 0\n         mod.win.addEventListener('unload',function() {\n            mod.win = null\n            })\n         var canvas = document.createElement('canvas')\n            canvas.width = mod.img.width\n            canvas.height = mod.img.height\n            canvas.setAttribute('id',mod.div.id+'canvas')\n            mod.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   // info div\n   //\n   var info = document.createElement('div')\n      var text = document.createTextNode('relative change: ')\n         info.appendChild(text)\n         mod.changetext = text\n      info.appendChild(document.createElement('br'))\n      info.appendChild(document.createTextNode('threshold: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.threshold = input\n      info.appendChild(document.createTextNode(' (0-1)'))\n      info.appendChild(document.createElement('br'))\n      info.appendChild(document.createTextNode('latency: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.latency = input\n      info.appendChild(document.createTextNode(' (s)'))\n      div.appendChild(info)\n      info.appendChild(document.createElement('br'))\n      info.appendChild(document.createTextNode('delay: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.delay = input\n      info.appendChild(document.createTextNode(' (s)'))\n      div.appendChild(info)\n   }\n//\n// local functions\n//\nfunction open_window() {\n   mod.win = window.open('')\n   mod.win.document.title = 'motion detect last image'\n   mod.win.document.body.style.overflow = 'hidden'\n   mod.win.document.body.style.border = 0\n   mod.win.document.body.style.padding = 0\n   mod.win.document.body.style.margin = 0\n   var canvas = document.createElement('canvas')\n      canvas.width = mod.img.width\n      canvas.height = mod.img.height\n      canvas.setAttribute('id',mod.div.id+'canvas')\n      mod.win.document.body.appendChild(canvas)\n   }\nfunction compare_images() {\n   //\n   // create worker\n   //\n   var blob = new Blob(['('+worker.toString()+'())'])\n   var url = window.URL.createObjectURL(blob)\n   var webworker = new Worker(url)\n   //\n   // worker handler\n   //\n   webworker.addEventListener('message',function(evt) {\n      window.URL.revokeObjectURL(url)\n      mod.changetext.nodeValue = 'relative change: '+evt.data.change.toFixed(3)\n      mod.change = evt.data.change\n      //\n      // check if change > threshold\n      //\n      if (mod.change > parseFloat(mod.threshold.value)) {\n         //\n         // yes, output image\n         //\n         var obj = {}\n         var date = new Date()\n         var year = date.getFullYear()\n         var month = ('0'+(1+parseInt(date.getMonth()))).slice(-2)\n         var day = ('0'+date.getDate()).slice(-2)\n         var hour = ('0'+date.getHours()).slice(-2)\n         var minute = ('0'+date.getMinutes()).slice(-2)\n         var second = ('0'+date.getSeconds()).slice(-2)\n         var name = year+'-'+month+'-'+day+'-'+hour+'-'+minute+'-'+second\n         obj.name = name\n         obj.dpi = mod.dpi\n         obj.width = mod.img.width\n         obj.height = mod.img.height\n         outputs.imageInfo.event(obj)\n         outputs.image.event()\n         //\n         // trigger next image after latency\n         //\n         setTimeout(outputs.trigger.event,\n            parseFloat(mod.latency.value)*1000)\n         }\n      else {\n         //\n         // no, trigger next image after delay\n         //\n         setTimeout(outputs.trigger.event,\n            parseFloat(mod.delay.value)*1000)\n         }\n      //\n      // update canvas\n      //\n      var h = mod.img.height\n      var w = mod.img.width\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      // update view window\n      //\n      if (mod.win != null) {\n         var canvas = mod.win.document.getElementById(mod.div.id+'canvas')\n         canvas.width = mod.img.width\n         canvas.height = mod.img.height\n         var ctx = canvas.getContext(\"2d\")\n         ctx.clearRect(0,0,mod.img.width,mod.img.height)\n         ctx.drawImage(mod.img,0,0)\n         }\n      //\n      // terminate worker\n      //\n      webworker.terminate()\n      })\n   //\n   // call worker\n   //\n   var ctx = mod.img.getContext(\"2d\")\n   var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n   var ctx = mod.lastimg.getContext(\"2d\")\n   var lastimg = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n   var t = parseFloat(mod.threshold.value)\n   webworker.postMessage({\n      height:mod.img.height,width:mod.img.width,threshold:t,\n      buffer:img.data.buffer,lastbuffer:lastimg.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 lastbuf = new Uint8ClampedArray(evt.data.lastbuffer)\n      var change = 0\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            rl = lastbuf[(h-1-row)*w*4+col*4+0] \n            gl = lastbuf[(h-1-row)*w*4+col*4+1] \n            bl = lastbuf[(h-1-row)*w*4+col*4+2] \n            change += (Math.abs(r-rl)/255 \n               +Math.abs(g-gl)/255\n               +Math.abs(b-bl)/255)/3\n            }\n         }\n      change = change/(w*h)\n      self.postMessage({change:change})\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":"142","left":"241","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.30101921769726425\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10092185293872713\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.30101921769726425\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10092185293872713\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.30101921769726425\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"trigger\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4997564076516918\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"capture\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.4997564076516918\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.30101921769726425\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}"]}
\ No newline at end of file
+{"modules":{"0.10092185293872713":{"definition":"//\n// convert rgba jpg\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 = 'convert RGBA to JPG'\n//\n// initialization\n//\nvar init = function() {\n   mod.name.value = \"file.jpg\"\n   mod.compress.value = .75\n   }\n//\n// inputs\n//\nvar inputs = {\n   image:{type:'RGBA',\n      event:function(evt){\n         var ctx = mod.img.getContext(\"2d\")\n         ctx.canvas.width = evt.detail.width\n         ctx.canvas.height = evt.detail.height \n         ctx.putImageData(evt.detail,0,0)\n         mod.pxtext.nodeValue = evt.detail.width+' x '+evt.detail.height+' px'\n         convert_image()\n         }},\n   imageInfo:{type:'object',\n      event:function(evt){\n         mod.name.value = evt.detail.name+'.jpg'\n         }}\n   }\n//\n// outputs\n//\nvar outputs = {\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   // 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   // info div\n   //\n   var info = document.createElement('div')\n      info.appendChild(document.createTextNode('file name: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.name = input\n      info.appendChild(document.createElement('br'))\n      info.appendChild(document.createTextNode('compression: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.compress = input\n      info.appendChild(document.createTextNode(' (0-1)'))\n      info.appendChild(document.createElement('br'))\n      var text = document.createTextNode('px: ')\n         info.appendChild(text)\n         mod.pxtext = text\n      div.appendChild(info)\n   }\n//\n// local functions\n//\nfunction convert_image() {\n   //\n   // preview\n   //\n   var h = mod.img.height\n   var w = mod.img.width\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   // convert and save\n   //\n   mod.img.toBlob(function(blob){\n      var url = URL.createObjectURL(blob)\n      var link = document.createElement('a')\n      link.download = mod.name.value\n      link.href = url\n      document.body.appendChild(link)\n      link.click()\n      document.body.removeChild(link)\n      URL.revokeObjectURL(url)\n      },'image/jpeg',parseFloat(mod.compress.value))\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":"142","left":"1098","inputs":{},"outputs":{}},"0.4997564076516918":{"definition":"//\n// video\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 = 'video'\n//\n// initialization\n//\nvar init = function() {\n   mod.width.value = 1280 \n   mod.height.value = 720\n   mod.flip.checked = false\n   start_video()\n   }\n//\n// inputs\n//\nvar inputs = {\n   capture:{type:'event',\n      event:function(evt){\n         capture_video()}}}\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   // capture 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('capture'))\n      btn.addEventListener('click',function() {\n         capture_video()\n         })\n      div.appendChild(btn)\n   div.appendChild(document.createTextNode(' '))\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   // width\n   //\n   div.appendChild(document.createTextNode('width: '))\n   var input = document.createElement('input')\n      input.type = 'text'\n      input.size = 6\n      input.addEventListener('change',function() {\n         update_video()\n         })\n      div.appendChild(input)\n      mod.width = input\n   div.appendChild(document.createElement('br'))\n   //\n   // height\n   //\n   div.appendChild(document.createTextNode(' height: '))\n   var input = document.createElement('input')\n      input.type = 'text'\n      input.size = 6\n      input.addEventListener('change',function() {\n         update_video()\n         })\n      div.appendChild(input)\n      mod.height = input\n   div.appendChild(document.createElement('br'))\n   //\n   // flip\n   //\n   div.appendChild(document.createTextNode('flip image: '))\n   var input = document.createElement('input')\n      input.type = 'checkbox'\n      div.appendChild(input)\n      mod.flip = input\n   div.appendChild(document.createElement('br'))\n   //\n   // video element\n   //\n   var video = document.createElement('video')\n      mod.video = video\n   }\n//\n// local functions\n//\nfunction start_video() {\n   var w = parseInt(mod.width.value)\n   var h = parseInt(mod.height.value)\n   var ctx = mod.img.getContext(\"2d\")\n   ctx.canvas.width = w\n   ctx.canvas.height = h\n   var constraints = {\n      audio:false,\n      video:{width:w,height:h}\n      }\n   navigator.mediaDevices.getUserMedia(constraints)\n      .then(function(stream) {\n         mod.video.srcObject = stream\n         mod.video.onloadedmetadata = function(e) {\n            mod.video.play()\n            }\n         })\n      .catch(function(err) {\n         console.log(err.name + \": \"+err.message)\n         })\n   }\nfunction update_video() {\n   var w = parseInt(mod.width.value)\n   var h = parseInt(mod.height.value)\n   mod.video.setAttribute('width',w)\n   mod.video.setAttribute('height',h)\n   var ctx = mod.img.getContext(\"2d\")\n   ctx.canvas.width = w\n   ctx.canvas.height = h\n   }\nfunction capture_video() {\n   var w = parseInt(mod.width.value)\n   var h = parseInt(mod.height.value)\n   var ctx = mod.img.getContext(\"2d\")\n   ctx.drawImage(mod.video,0,0,w,h)\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      outputs.image.event()\n      webworker.terminate()\n      })\n   var ctx = mod.img.getContext(\"2d\")\n   var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n   webworker.postMessage({\n      height:mod.img.height,width:mod.img.width,\n      checked:mod.flip.checked,\n      buffer:img.data.buffer},[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 checked = evt.data.checked\n      var buf = new Uint8ClampedArray(evt.data.buffer)\n      if (checked == true) {\n         var newbuf = new Uint8ClampedArray(buf.length)\n         for (var row = 0; row < h; ++row) {\n            for (var col = 0; col < w; ++col) {\n               newbuf[(h-1-row)*w*4+col*4+0] = \n                  buf[row*w*4+(w-1-col)*4+0] \n               newbuf[(h-1-row)*w*4+col*4+1] = \n                  buf[row*w*4+(w-1-col)*4+1]\n               newbuf[(h-1-row)*w*4+col*4+2] = \n                  buf[row*w*4+(w-1-col)*4+2]\n               newbuf[(h-1-row)*w*4+col*4+3] = \n                  buf[row*w*4+(w-1-col)*4+3]\n               }\n            }\n         self.postMessage({buffer:newbuf.buffer},[newbuf.buffer])\n         }\n      else\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":"302","left":"738","inputs":{},"outputs":{}},"0.5045640640829611":{"definition":"//\n// motion detect\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 = 'motion detect'\n//\n// initialization\n//\nvar init = function() {\n   //\n   // UI settings\n   //\n   mod.threshold.value = 0.025\n   mod.latency.value = 15\n   mod.delay.value = 1\n   mod.dpi = 100\n   mod.win = null\n   //\n   // trigger image after latency (for start-up)\n   //\n   setTimeout(outputs.trigger.event,\n      parseFloat(mod.latency.value)*1000)\n   }\n//\n// inputs\n//\nvar inputs = {\n   image:{type:'RGBA',\n      event:function(evt){\n         var ctx = mod.img.getContext(\"2d\")\n         var lastctx = mod.lastimg.getContext(\"2d\")\n         lastctx.canvas.width = ctx.canvas.width\n         lastctx.canvas.height = ctx.canvas.height\n         lastctx.drawImage(mod.img,0,0)\n         ctx.canvas.width = evt.detail.width\n         ctx.canvas.height = evt.detail.height \n         ctx.putImageData(evt.detail,0,0)\n         compare_images()\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(obj){\n         mods.output(mod,'imageInfo',obj)}},\n   trigger:{type:'event',\n      event:function(){\n         mods.output(mod,'trigger',null)}}}\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 last image canvas\n   //\n   var canvas = document.createElement('canvas')\n      mod.lastimg = canvas\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 last image'))\n      btn.addEventListener('click',function(){\n         mod.win = window.open('')\n         mod.win.document.title = 'last image'\n         mod.win.document.body.style.overflow = 'hidden'\n         mod.win.document.body.style.border = 0\n         mod.win.document.body.style.padding = 0\n         mod.win.document.body.style.margin = 0\n         mod.win.addEventListener('unload',function() {\n            mod.win = null\n            })\n         var canvas = document.createElement('canvas')\n            canvas.width = mod.img.width\n            canvas.height = mod.img.height\n            canvas.setAttribute('id',mod.div.id+'canvas')\n            mod.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   // info div\n   //\n   var info = document.createElement('div')\n      var text = document.createTextNode('relative change: ')\n         info.appendChild(text)\n         mod.changetext = text\n      info.appendChild(document.createElement('br'))\n      info.appendChild(document.createTextNode('threshold: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.threshold = input\n      info.appendChild(document.createTextNode(' (0-1)'))\n      info.appendChild(document.createElement('br'))\n      info.appendChild(document.createTextNode('latency: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.latency = input\n      info.appendChild(document.createTextNode(' (s)'))\n      div.appendChild(info)\n      info.appendChild(document.createElement('br'))\n      info.appendChild(document.createTextNode('delay: '))\n      var input = document.createElement('input')\n         input.type = 'text'\n         input.size = 6\n         info.appendChild(input)\n         mod.delay = input\n      info.appendChild(document.createTextNode(' (s)'))\n      div.appendChild(info)\n   }\n//\n// local functions\n//\nfunction open_window() {\n   mod.win = window.open('')\n   mod.win.document.title = 'motion detect last image'\n   mod.win.document.body.style.overflow = 'hidden'\n   mod.win.document.body.style.border = 0\n   mod.win.document.body.style.padding = 0\n   mod.win.document.body.style.margin = 0\n   var canvas = document.createElement('canvas')\n      canvas.width = mod.img.width\n      canvas.height = mod.img.height\n      canvas.setAttribute('id',mod.div.id+'canvas')\n      mod.win.document.body.appendChild(canvas)\n   }\nfunction compare_images() {\n   //\n   // create worker\n   //\n   var blob = new Blob(['('+worker.toString()+'())'])\n   var url = window.URL.createObjectURL(blob)\n   var webworker = new Worker(url)\n   //\n   // worker handler\n   //\n   webworker.addEventListener('message',function(evt) {\n      window.URL.revokeObjectURL(url)\n      mod.changetext.nodeValue = 'relative change: '+evt.data.change.toFixed(3)\n      mod.change = evt.data.change\n      //\n      // check if change > threshold\n      //\n      if (mod.change > parseFloat(mod.threshold.value)) {\n         //\n         // yes, output image\n         //\n         var obj = {}\n         var date = new Date()\n         var year = date.getFullYear()\n         var month = ('0'+(1+parseInt(date.getMonth()))).slice(-2)\n         var day = ('0'+date.getDate()).slice(-2)\n         var hour = ('0'+date.getHours()).slice(-2)\n         var minute = ('0'+date.getMinutes()).slice(-2)\n         var second = ('0'+date.getSeconds()).slice(-2)\n         var name = year+'-'+month+'-'+day+'-'+hour+'-'+minute+'-'+second\n         obj.name = name\n         obj.dpi = mod.dpi\n         obj.width = mod.img.width\n         obj.height = mod.img.height\n         outputs.imageInfo.event(obj)\n         outputs.image.event()\n         //\n         // trigger next image after latency\n         //\n         setTimeout(outputs.trigger.event,\n            parseFloat(mod.latency.value)*1000)\n         }\n      else {\n         //\n         // no, trigger next image after delay\n         //\n         setTimeout(outputs.trigger.event,\n            parseFloat(mod.delay.value)*1000)\n         }\n      //\n      // update canvas\n      //\n      var h = mod.img.height\n      var w = mod.img.width\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      // update view window\n      //\n      if (mod.win != null) {\n         var canvas = mod.win.document.getElementById(mod.div.id+'canvas')\n         canvas.width = mod.img.width\n         canvas.height = mod.img.height\n         var ctx = canvas.getContext(\"2d\")\n         ctx.clearRect(0,0,mod.img.width,mod.img.height)\n         ctx.drawImage(mod.img,0,0)\n         }\n      //\n      // terminate worker\n      //\n      webworker.terminate()\n      })\n   //\n   // call worker\n   //\n   var ctx = mod.img.getContext(\"2d\")\n   var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n   var ctx = mod.lastimg.getContext(\"2d\")\n   var lastimg = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n   var t = parseFloat(mod.threshold.value)\n   webworker.postMessage({\n      height:mod.img.height,width:mod.img.width,threshold:t,\n      buffer:img.data.buffer,lastbuffer:lastimg.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 lastbuf = new Uint8ClampedArray(evt.data.lastbuffer)\n      var change = 0\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            rl = lastbuf[(h-1-row)*w*4+col*4+0] \n            gl = lastbuf[(h-1-row)*w*4+col*4+1] \n            bl = lastbuf[(h-1-row)*w*4+col*4+2] \n            change += (Math.abs(r-rl)/255 \n               +Math.abs(g-gl)/255\n               +Math.abs(b-bl)/255)/3\n            }\n         }\n      change = change/(w*h)\n      self.postMessage({change:change})\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":"140","left":"232","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.5045640640829611\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10092185293872713\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5045640640829611\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.10092185293872713\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5045640640829611\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"trigger\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4997564076516918\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"capture\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.4997564076516918\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5045640640829611\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}"]}
\ No newline at end of file
-- 
GitLab