diff --git a/docs/Instruction/images/matterport/mt_ex.mp4 b/docs/Instruction/images/matterport/mt_ex.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..878061522fa19983fc86c53b5cadedfc740d8bb8
Binary files /dev/null and b/docs/Instruction/images/matterport/mt_ex.mp4 differ
diff --git a/docs/Instruction/images/proverXL/line_test_1:64.jpg b/docs/Instruction/images/proverXL/line_test_1:64.jpg
deleted file mode 100644
index 2333661d54f82158a8908f864b24ed39e9b0faf4..0000000000000000000000000000000000000000
Binary files a/docs/Instruction/images/proverXL/line_test_1:64.jpg and /dev/null 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
index 1624982511f1e1b9c4ea1db3d482f732daeb8014..9f1acb641e4267726980a7ee01addc2ed9d1c988 100644
--- a/docs/Instruction/images/proverXL/mods/mill_2D_PCB_png.html
+++ b/docs/Instruction/images/proverXL/mods/mill_2D_PCB_png.html
@@ -4,1910 +4,1910 @@
 </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 = {}
-   }
-})()
+//
+// 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>
diff --git a/docs/Instruction/images/week02/fa2021-group-assignment-power-speed-ppi.svg b/docs/Instruction/images/week02/fa2021-group-assignment-power-speed-ppi.svg
index 78b625e0e2bb2165fee88b7fd4177bf91f213c60..89c35bbc48e36173cd2565510b177335a12ea620 100644
--- a/docs/Instruction/images/week02/fa2021-group-assignment-power-speed-ppi.svg
+++ b/docs/Instruction/images/week02/fa2021-group-assignment-power-speed-ppi.svg
@@ -1,110 +1,110 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Creator: CorelDRAW 2018 (64-Bit) -->
-<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="406.4mm" height="304.8mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
-viewBox="0 0 40640 30480"
- xmlns:xlink="http://www.w3.org/1999/xlink">
- <defs>
-  <font id="FontID0" horiz-adv-x="667" font-variant="normal" style="fill-rule:nonzero" font-style="normal" font-weight="400">
-	<font-face 
-		font-family="Arial">
-		<font-face-src>
-			<font-face-name name="Arial"/>
-		</font-face-src>
-	</font-face>
-   <missing-glyph><path d="M0 0z"/></missing-glyph>
-   <glyph unicode="0" horiz-adv-x="556" d="M41.9998 353.337c0,84.4957 8.66926,152.834 25.996,204.495 17.5039,51.8384 43.3345,91.665 77.669,119.503 34.1692,27.8267 77.3383,41.6691 129.33,41.6691 38.3384,0 71.9997,-7.67714 101.173,-23.1731 28.9959,-15.496 52.9959,-37.8306 71.669,-67.0037 18.6613,-28.9959 33.3306,-64.4997 43.9959,-106.322 10.8307,-41.8345 16.1692,-98.3382 16.1692,-169.169 0,-83.9997 -8.66926,-152.007 -25.8306,-203.668 -17.1732,-51.8384 -43.0038,-91.665 -77.3383,-119.669 -34.1692,-28.0038 -77.5036,-41.9998 -129.838,-41.9998 -68.8226,0 -123,24.496 -162.33,73.6651 -47.1612,59.9998 -70.6651,157.169 -70.6651,291.672zm89.9996 -0.342518c0,-117.826 13.6653,-196.157 41.1731,-234.826 27.496,-38.8345 61.4998,-58.1691 101.822,-58.1691 40.3345,0 74.3383,19.4999 101.834,58.3344 27.5078,38.9998 41.1731,117.165 41.1731,234.66 0,117.838 -13.6653,196.169 -41.1731,234.838 -27.3306,38.8345 -61.6651,58.1691 -102.826,58.1691 -40.3345,0 -72.6729,-17.1732 -96.8382,-51.3305 -30.1652,-43.4998 -45.1652,-124.169 -45.1652,-241.676z"/>
-   <glyph unicode="1" horiz-adv-x="556" d="M371.999 0l-88.0036 0 0 560.336c-21.1653,-20.3385 -48.9919,-40.4998 -83.3264,-60.673 -34.3345,-20.1613 -65.3383,-35.3266 -92.6689,-45.3305l0 85.0036c49.1691,22.996 92.1729,50.9998 128.999,83.9997 36.8384,32.8345 62.9998,64.6651 78.3304,95.6689l56.6691 0 0 -719.005z"/>
-   <glyph unicode="2" horiz-adv-x="556" d="M503.006 83.9997l0 -83.9997 -474.01 0c-0.661415,21.1653 2.83463,41.5038 10.3346,61.1691 12.1653,32.1613 31.6652,63.9919 58.4998,95.161 26.8345,31.1692 65.173,67.169 115.003,108 77.5036,63.8383 130.169,114.342 157.665,151.665 27.6731,37.3345 41.5038,72.6729 41.5038,106.004 0,34.8306 -12.5078,64.169 -37.4999,88.1689 -25.0038,23.8346 -57.673,35.8345 -97.8303,35.8345 -42.5077,0 -76.4997,-12.6732 -102,-37.9959 -25.5117,-25.1692 -38.3384,-60.1769 -38.6691,-105l-89.9996 8.99996c6.16533,67.3226 29.492,118.665 69.8265,153.992 40.4998,35.3384 94.665,53.0077 162.838,53.0077 68.669,0 123.165,-19.0039 163.169,-57.1769 40.1573,-38.1613 60.1651,-85.4997 60.1651,-141.826 0,-28.6652 -5.83462,-56.8344 -17.6692,-84.5075 -11.6692,-27.8267 -31.1692,-56.9998 -58.3344,-87.6611 -27.3306,-30.4959 -72.4958,-72.6611 -135.496,-126 -52.8305,-44.3384 -86.8343,-74.3383 -101.834,-90.165 -14.9999,-15.8385 -27.3306,-31.6652 -37.1691,-47.6691l351.506 0z"/>
-   <glyph unicode="3" horiz-adv-x="556" d="M41.9998 190.003l88.0036 12c10.1574,-50.0077 27.1652,-86.0075 51.3305,-108 24.1653,-22.0039 53.8344,-32.9999 88.9957,-32.9999 41.6691,0 76.8422,14.3267 105.165,42.992 28.3345,28.6652 42.5077,64.169 42.5077,106.5 0,40.3345 -13.1692,73.6769 -39.6731,100.004 -26.3267,26.1731 -59.8344,39.3306 -100.665,39.3306 -16.6653,0 -37.3345,-3.3307 -62.1612,-9.82673l9.82673 76.9958c5.83462,-0.661415 10.6653,-1.00393 14.1732,-1.00393 37.6652,0 71.3265,9.83854 101.492,29.3385 29.9999,19.4999 44.9998,49.4998 44.9998,89.9996 0,32.1731 -10.996,58.8305 -32.8227,79.9957 -21.8385,21.1771 -50.0077,31.677 -84.5075,31.677 -34.3345,0 -62.8344,-10.8425 -85.665,-32.5038 -22.8306,-21.6731 -37.4999,-54.1651 -43.9959,-97.4996l-88.0036 16.0039c10.8307,58.9958 35.1613,104.822 73.3344,137.326 37.9959,32.5038 85.4997,48.673 142.169,48.673 38.9998,0 74.9997,-8.33855 107.834,-25.1692 32.9999,-16.6653 58.1573,-39.5077 75.4958,-68.3383 17.5039,-28.9959 26.1613,-59.6691 26.1613,-92.1611 0,-30.8385 -8.32674,-58.8423 -24.8267,-84.165 -16.6653,-25.3345 -41.3384,-45.5077 -73.8304,-60.5076 42.3305,-9.66138 75.1651,-29.9999 98.6571,-60.8265 23.3385,-30.6731 35.0077,-69.3422 35.0077,-115.5 0,-62.5037 -22.8306,-115.677 -68.3383,-159.177 -45.6612,-43.4998 -103.334,-65.1612 -172.995,-65.1612 -62.8344,0 -115.003,18.8385 -156.507,56.3384 -41.6573,37.4999 -65.3265,85.9957 -71.1611,145.665z"/>
-   <glyph unicode="4" horiz-adv-x="556" d="M322.995 0l0 170.999 -310.995 0 0 80.9997 326.999 464.006 71.9997 0 0 -464.006 97.0036 0 0 -80.9997 -97.0036 0 0 -170.999 -88.0036 0zm0 251.999l0 322.168 -224.491 -322.168 224.491 0z"/>
-   <glyph unicode="5" horiz-adv-x="556" d="M41.9998 187.995l91.9957 7.00391c6.83856,-44.8345 22.6653,-78.4958 47.3384,-101.161 24.6613,-22.4999 54.8384,-33.8384 90.3304,-33.8384 42.8384,0 78.6729,16.0039 107.834,47.8345 28.9959,31.9959 43.4998,74.3383 43.4998,127.003 0,50.1612 -13.8307,89.6571 -41.6691,118.665 -27.8267,28.9959 -64.6651,43.4998 -110.492,43.4998 -28.4999,0 -53.9998,-6.50785 -76.6651,-19.4999 -22.4999,-13.0039 -40.1691,-29.8345 -53.173,-50.5037l-82.9957 12 68.9997 368.006 354.991 0 0 -83.9997 -284.822 0 -38.1731 -193.169c42.8384,30.1652 87.8382,45.1652 134.834,45.1652 62.3383,0 115.003,-21.6731 157.83,-65.0076 42.8384,-43.3227 64.3344,-98.9996 64.3344,-166.995 0,-64.8305 -18.8267,-120.838 -56.5037,-167.999 -45.8266,-58.0037 -108.496,-86.9997 -187.83,-86.9997 -64.9958,0 -118.003,18.3306 -159.165,54.8384 -41.1613,36.4959 -64.6651,84.8264 -70.4997,145.157z"/>
-   <glyph unicode="6" horiz-adv-x="556" d="M499.002 541.002l-86.9997 -7.00391c-7.83068,34.4999 -19.0039,59.6691 -33.5077,75.3304 -23.8227,25.1692 -53.3266,37.677 -88.3225,37.677 -28.1692,0 -53.173,-7.84249 -74.669,-23.5038 -27.6731,-20.1731 -49.3345,-49.4998 -65.5037,-88.1689 -16.0039,-38.8345 -24.3306,-94.0036 -25.0038,-165.495 21.1653,32.1613 47.173,56.1612 77.8343,71.6572 30.6731,15.6732 62.669,23.5038 96.3421,23.5038 58.6651,0 108.661,-21.6613 149.999,-64.8305 41.1613,-43.1691 61.8305,-98.8343 61.8305,-167.338 0,-44.9998 -9.67319,-86.669 -29.0078,-125.326 -19.4999,-38.6691 -45.9919,-68.173 -79.9957,-88.665 -33.8266,-20.5039 -72.3304,-30.8385 -115.334,-30.8385 -73.169,0 -133.003,26.9999 -179.326,80.8343 -46.1691,53.8344 -69.3422,142.665 -69.3422,266.338 0,138.33 25.6653,238.995 76.8422,301.83 44.6573,54.6612 104.834,82.0036 180.661,82.0036 56.3384,0 102.673,-15.8385 138.673,-47.3384 35.9999,-31.4999 57.6612,-74.9997 64.8305,-130.665zm-359.999 -308.999c0,-30.3306 6.49604,-59.3383 19.3346,-86.9997 12.8267,-27.6731 30.8267,-48.673 53.6573,-63.1651 23.0078,-14.5039 47.3384,-21.8385 73.169,-21.8385 37.4999,0 69.5076,14.9999 95.9996,44.9998 26.5038,29.9999 39.8384,70.6651 39.8384,122.173 0,49.4998 -13.1692,88.4997 -39.3306,117 -26.1731,28.4999 -59.5037,42.8266 -100.004,42.8266 -39.6731,0 -73.4997,-14.3267 -101.173,-42.8266 -27.6613,-28.4999 -41.492,-65.8344 -41.492,-112.169z"/>
-   <glyph unicode="7" horiz-adv-x="556" d="M46.9959 623.005l0 83.9997 463.002 0 0 -68.0076c-45.4959,-48.4959 -90.8264,-112.996 -135.496,-193.499 -44.8345,-80.5036 -79.3343,-163.334 -103.83,-248.503 -17.5039,-59.8344 -28.8424,-125.657 -33.6731,-196.995l-89.9996 0c1.00393,56.3384 12,124.5 33.1652,204.33 21.1653,80.0076 51.3305,157.003 90.8382,231.164 39.4959,74.1729 81.4957,136.677 126,187.511l-350.006 0z"/>
-   <glyph unicode="8" horiz-adv-x="556" d="M177.495 388.168c-36.4959,13.3346 -63.4958,32.5038 -81.165,57.1651 -17.4921,24.8385 -26.3267,54.4958 -26.3267,88.9957 0,52.1691 18.6613,95.8343 55.9958,131.338 37.4999,35.5038 87.165,53.3384 149.338,53.3384 62.3265,0 112.665,-18.1771 150.661,-54.5077 37.9959,-36.3306 56.9998,-80.4918 56.9998,-132.661 0,-33.1652 -8.66926,-62.173 -25.996,-86.669 -17.3385,-24.6731 -43.6652,-43.6652 -79.169,-56.9998 43.8305,-14.3385 76.9958,-37.3345 99.8382,-69.3304 22.8306,-31.8424 34.3345,-69.8383 34.3345,-114.165 0,-61.1691 -21.6731,-112.511 -64.8423,-154.169 -43.3345,-41.6691 -100.169,-62.5037 -170.669,-62.5037 -70.4997,0 -127.334,20.8346 -170.657,62.669 -43.1691,41.8345 -64.8423,93.9918 -64.8423,156.495 0,46.4998 11.8346,85.4997 35.5038,116.834 23.6692,31.4999 57.3305,52.8305 100.996,64.169zm-17.4921 149.338c0,-34.0038 10.8307,-61.6769 32.3266,-83.1729 21.6731,-21.496 50.0077,-32.3385 85.169,-32.3385 34.1692,0 61.8305,10.6771 83.3383,32.0077 21.496,21.3306 32.1613,47.5037 32.1613,78.4958 0,32.3385 -10.996,59.5037 -33.1652,81.5076 -22.0039,21.992 -49.8305,32.9999 -83.3383,32.9999 -33.8266,0 -61.6651,-10.8425 -83.6572,-32.3385 -21.8385,-21.496 -32.8345,-47.1612 -32.8345,-77.1611zm-29.0078 -331.003c0,-25.1692 5.83462,-49.3345 17.5039,-72.8383 11.6692,-23.3267 29.3385,-41.5038 52.9959,-54.3305 23.5038,-12.8385 48.8384,-19.3346 75.6729,-19.3346 42.3305,0 76.9958,13.4999 104.161,40.4998 27.1652,26.9999 40.6652,61.3344 40.6652,103.004 0,42.3305 -13.996,77.3265 -41.8227,105 -28.0038,27.6613 -63.3423,41.492 -105.838,41.492 -41.6691,0 -75.8383,-13.6653 -102.838,-40.9959 -26.9999,-27.3306 -40.4998,-61.4998 -40.4998,-102.496z"/>
-   <glyph unicode=":" horiz-adv-x="277" d="M89.9996 418.994l0 100.004 100.004 0 0 -100.004 -100.004 0zm0 -418.994l0 100.004 100.004 0 0 -100.004 -100.004 0z"/>
-   <glyph unicode="I" horiz-adv-x="277" d="M92.9996 0l0 716.005 94.9957 0 0 -716.005 -94.9957 0z"/>
-   <glyph unicode="P" horiz-adv-x="667" d="M76.9958 0l0 716.005 270.341 0c47.4919,0 83.8343,-2.33857 108.992,-6.83856 35.1731,-5.83462 64.6769,-16.996 88.4997,-33.3306 23.8346,-16.4999 43.0038,-39.5077 57.5076,-68.9997 14.4921,-29.5038 21.6613,-62.0076 21.6613,-97.4996 0,-60.673 -19.3346,-112.169 -58.1691,-154.169 -38.6573,-42.1652 -108.826,-63.1651 -210.165,-63.1651l-183.661 0 0 -292.003 -95.0075 0zm95.0075 376.002l184.995 0c61.3344,0 104.669,11.3267 130.499,34.1692 25.6653,22.8306 38.5038,54.8266 38.5038,96.3303 0,29.8345 -7.49997,55.4998 -22.6653,76.8304 -15.1653,21.3306 -35.1731,35.5038 -59.8344,42.3305 -16.0039,4.16927 -45.5077,6.34249 -88.3343,6.34249l-183.165 0 0 -256.003z"/>
-   <glyph unicode="S" horiz-adv-x="667" d="M46.0038 229.995l88.9957 8.00784c4.16927,-35.9999 13.996,-65.5037 29.5038,-88.665 15.496,-23.0078 39.4959,-41.6691 71.9997,-56.0077 32.492,-14.1614 69.1651,-21.3306 109.83,-21.3306 36.1652,0 67.9958,5.33856 95.6689,16.1692 27.6613,10.8307 48.1652,25.6653 61.6651,44.492 13.4999,18.8385 20.3385,39.3424 20.3385,61.3344 0,22.3346 -6.50785,41.8345 -19.4999,58.6769 -13.0039,16.6653 -34.5117,30.6613 -64.5116,42.1652 -19.1574,7.33462 -61.6651,18.9921 -127.499,34.8306 -65.8226,15.8267 -111.826,30.6613 -138.165,44.6691 -34.1692,17.9999 -59.4919,40.3345 -76.169,66.8265 -16.8306,26.5038 -25.1574,56.3384 -25.1574,89.1729 0,36.3306 10.3346,70.169 30.8267,101.492 20.5039,31.4999 50.3384,55.3344 89.8343,71.669 39.3306,16.3346 83.1729,24.5078 131.338,24.5078 52.9959,0 99.8264,-8.5039 140.326,-25.6771 40.4998,-16.996 71.669,-42.1652 93.5075,-75.4958 21.8267,-33.1652 33.4959,-70.8304 35.1613,-112.83l-90.9918 -7.00391c-4.8425,45.1652 -21.3424,79.3343 -49.5116,102.33 -28.1574,23.1731 -69.8265,34.677 -124.83,34.677 -57.3305,0 -99.165,-10.5 -125.326,-31.4999 -26.1731,-20.9999 -39.3424,-46.3345 -39.3424,-76.0036 0,-25.6653 9.34248,-46.8305 27.8385,-63.5076 18.3306,-16.4999 65.8344,-33.6613 142.83,-50.9998 77.0076,-17.4921 129.838,-32.6574 158.503,-45.6612 41.5038,-19.1692 72.3304,-43.3345 92.1611,-72.8383 19.8424,-29.3267 29.6692,-63.1651 29.6692,-101.492 0,-38.1731 -10.8307,-73.8422 -32.6692,-107.504 -21.8267,-33.6613 -52.9959,-59.8344 -93.8264,-78.4958 -40.8305,-18.6732 -86.669,-28.0038 -137.834,-28.0038 -64.6651,0 -118.83,9.49603 -162.495,28.3345 -43.8423,18.8267 -78.0115,47.1612 -102.838,85.169 -25.0038,37.8306 -37.9959,80.669 -39.3306,128.492z"/>
-   <glyph unicode="d" horiz-adv-x="556" d="M400.994 0l0 65.8344c-32.8227,-51.1652 -81.165,-76.8304 -144.992,-76.8304 -41.3384,0 -79.3343,11.3267 -113.834,34.1573 -34.6652,22.6771 -61.4998,54.5077 -80.669,95.1729 -19.0039,40.8305 -28.4999,87.6611 -28.4999,140.669 0,51.6612 8.66926,98.4917 25.8306,140.669 17.3385,41.9998 43.1691,74.3265 77.669,96.661 34.3345,22.4999 73.0036,33.6731 115.5,33.6731 31.3345,0 59.173,-6.6732 83.5036,-19.8424 24.3306,-13.1692 44.1612,-30.4959 59.4919,-51.6612l0 257.503 88.0036 0 0 -716.005 -82.0036 0zm-277.995 258.838c0,-66.3423 13.996,-115.842 42.1652,-148.677 28.0038,-32.8227 61.1691,-49.1573 99.3303,-49.1573 38.5038,0 71.1729,15.6614 98.1728,46.9959 26.8345,31.3345 40.3345,79.169 40.3345,143.503 0,70.6651 -13.6653,122.669 -41.1731,155.834 -27.3306,33.1652 -61.1572,49.6652 -101.326,49.6652 -39.1652,0 -71.8344,-15.8385 -98.1728,-47.6691 -26.1613,-31.8306 -39.3306,-82.0036 -39.3306,-150.495z"/>
-   <glyph unicode="e" horiz-adv-x="556" d="M419.998 167.999l91.0036 -12c-14.1732,-52.8305 -40.6652,-94.0036 -79.3343,-123.165 -38.6691,-29.1731 -88.0036,-43.8305 -148.169,-43.8305 -75.6611,0 -135.661,23.3267 -179.999,69.9919 -44.3384,46.4998 -66.4958,112.003 -66.4958,196.169 0,86.9997 22.3346,154.665 67.1572,202.665 44.8345,48.177 103.004,72.1769 174.507,72.1769 69.1651,0 125.669,-23.5038 169.499,-70.6769 43.8305,-47.1612 65.8344,-113.492 65.8344,-198.991 0,-5.17321 -0.165354,-13.0039 -0.496061,-23.3385l-386.502 0c3.3307,-56.8344 19.3346,-100.5 48.3305,-130.665 28.8306,-30.1652 64.9958,-45.3305 108.165,-45.3305 32.1731,0 59.6691,8.49209 82.3343,25.4999 22.8306,16.996 40.8305,44.1612 54.1651,81.4957zm-287.999 140.999l289.003 0c-3.83857,43.6652 -14.9999,76.3343 -33.1652,98.1728 -27.8385,33.8266 -64.169,50.8226 -108.673,50.8226 -40.3345,0 -74.1611,-13.4999 -101.669,-40.4998 -27.3306,-26.9999 -42.4959,-63.1651 -45.4959,-108.496z"/>
-   <glyph unicode="o" horiz-adv-x="556" d="M32.9999 259.499c0,95.8343 26.6692,166.83 80.1611,212.999 44.5038,38.3384 99.0114,57.5076 163.169,57.5076 71.1729,0 129.507,-23.3385 174.838,-70.0037 45.1652,-46.4998 67.8304,-111 67.8304,-193.169 0,-66.6612 -9.99209,-118.996 -29.9999,-157.169 -19.996,-38.1613 -49.1691,-67.8304 -87.4957,-88.9957 -38.1731,-21.1653 -80.0076,-31.6652 -125.173,-31.6652 -72.6611,0 -131.326,23.1613 -176.161,69.6611 -44.8345,46.4998 -67.169,113.503 -67.169,200.834zm89.9996 -0.165354c0,-66.3304 14.5039,-115.996 43.4998,-148.83 29.1731,-32.9999 65.669,-49.4998 109.83,-49.4998 43.6652,0 80.0076,16.4999 109.169,49.6652 28.9959,33.1652 43.4998,83.669 43.4998,151.499 0,64.0037 -14.6692,112.5 -43.8305,145.334 -29.1731,32.9999 -65.5037,49.4998 -108.838,49.4998 -44.1612,0 -80.6572,-16.3346 -109.83,-49.1691 -28.9959,-32.8345 -43.4998,-82.3343 -43.4998,-148.499z"/>
-   <glyph unicode="p" horiz-adv-x="556" d="M65.9997 -199.003l0 718.001 79.9957 0 0 -68.1611c18.8385,26.3267 40.1691,46.1573 64.0037,59.3265 23.6692,13.1692 52.4998,19.8424 86.3382,19.8424 44.3266,0 83.3264,-11.3385 117.165,-34.1692 33.8266,-22.6653 59.3265,-54.8384 76.6651,-96.165 17.1613,-41.5038 25.8306,-86.8343 25.8306,-136.334 0,-52.8423 -9.49603,-100.5 -28.4999,-143.007 -19.0039,-42.3305 -46.6652,-74.8344 -82.9957,-97.4996 -36.1652,-22.4999 -74.3383,-33.8266 -114.33,-33.8266 -29.3385,0 -55.6769,6.16533 -78.8383,18.496 -23.3385,12.3307 -42.3305,27.8385 -57.3305,46.6652l0 -253.168 -88.0036 0zm79.9957 455.337c0,-66.4958 13.4999,-115.665 40.3345,-147.495 26.9999,-31.8424 59.6691,-47.8345 98.0075,-47.8345 38.9998,0 72.3304,16.4999 99.9917,49.4998 27.8385,32.9999 41.6691,83.9997 41.6691,153.165 0,65.9997 -13.4999,115.334 -40.6652,148.169 -27.1652,32.8345 -59.5037,49.1573 -97.1689,49.1573 -37.3345,0 -70.3344,-17.4921 -98.9996,-52.3226 -28.8306,-35.0077 -43.1691,-85.6768 -43.1691,-152.338z"/>
-   <glyph unicode="r" horiz-adv-x="332" d="M64.9958 0l0 518.998 79.0036 0 0 -79.4997c20.1613,36.8384 38.8345,61.1691 55.9958,72.8383 17.1732,11.8346 35.9999,17.6692 56.5037,17.6692 29.6692,0 59.8344,-9.34248 90.4957,-27.8385l-31.3227 -81.165c-21.3424,12 -42.6731,17.9999 -64.0037,17.9999 -19.1692,0 -36.3306,-5.83462 -51.4959,-17.3385 -15.1771,-11.4921 -26.0078,-27.496 -32.5038,-47.9998 -9.83854,-31.1692 -14.6692,-65.3265 -14.6692,-102.496l0 -271.168 -88.0036 0z"/>
-   <glyph unicode="w" horiz-adv-x="722" d="M159.33 0l-156.826 518.998 91.9957 0 82.8304 -299.503 29.3385 -111.496c1.16929,5.50392 10.1693,41.1613 26.8345,106.996l82.169 304.003 90.165 0 78.165 -301.333 25.4999 -98.8343 29.8345 100.169 89.6689 299.999 85.4997 0 -162.838 -518.998 -91.0036 0 -82.3343 310.499 -20.8346 88.8304 -105 -399.329 -93.165 0z"/>
-  </font>
-  <style type="text/css">
-   <![CDATA[
-    @font-face { font-family:"Arial";font-variant:normal;font-style:normal;font-weight:normal;src:url("#FontID0") format(svg)}
-    .str4 {stroke:#FF6600;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .str2 {stroke:fuchsia;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .str0 {stroke:red;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .str3 {stroke:aqua;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .str1 {stroke:blue;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .fil0 {fill:none}
-    .fil1 {fill:black}
-    .fnt0 {font-weight:normal;font-size:846.67px;font-family:'Arial'}
-   ]]>
-  </style>
- </defs>
- <g id="レイヤ_x0020_1">
-  <metadata id="CorelCorpID_0Corel-Layer"/>
-  <rect class="fil0 str0" x="3000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="3000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="3000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="3000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="3000" y="15000" width="2000" height="2000"/>
-  <line class="fil0 str0" x1="-0" y1="0" x2="500" y2= "0" />
-  <text x="1058.24" y="7272.62"  class="fil1 fnt0">10</text>
-  <text x="1058.24" y="10264.61"  class="fil1 fnt0">20</text>
-  <text x="1084.7" y="13272.63"  class="fil1 fnt0">30</text>
-  <text x="1045.01" y="16259.4"  class="fil1 fnt0">40</text>
-  <text x="1568.56" y="4306.48"  class="fil1 fnt0">4</text>
-  <text x="1568.56" y="4306.48"  class="fil1 fnt0">4</text>
-  <g transform="matrix(2.64845E-14 -1 1 2.64845E-14 -14323.7 26102.3)">
-   <text x="20320" y="15240"  class="fil1 fnt0">Speed</text>
-  </g>
-  <text x="2934.81" y="990.69"  class="fil1 fnt0">Power</text>
-  <text x="3508.19" y="2299.3"  class="fil1 fnt0">40</text>
-  <rect class="fil0 str0" x="6000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="6000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="6000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="6000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="6000" y="15000" width="2000" height="2000"/>
-  <text x="6508.19" y="2299.3"  class="fil1 fnt0">50</text>
-  <rect class="fil0 str0" x="9000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="9000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="9000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="9000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="9000" y="15000" width="2000" height="2000"/>
-  <text x="9508.19" y="2299.3"  class="fil1 fnt0">60</text>
-  <rect class="fil0 str0" x="12000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="12000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="12000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="12000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="12000" y="15000" width="2000" height="2000"/>
-  <text x="12508.19" y="2299.3"  class="fil1 fnt0">70</text>
-  <rect class="fil0 str0" x="15000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="15000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="15000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="15000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="15000" y="15000" width="2000" height="2000"/>
-  <text x="15508.19" y="2299.3"  class="fil1 fnt0">80</text>
-  <text x="1519.73" y="18591.56"  class="fil1 fnt0">250PPI</text>
-  <rect class="fil0 str0" x="23000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="23000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="23000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="23000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="23000" y="15000" width="2000" height="2000"/>
-  <text x="20528.59" y="7316.82"  class="fil1 fnt0">300</text>
-  <text x="20528.59" y="10239.63"  class="fil1 fnt0">400</text>
-  <text x="20528.59" y="13202.97"  class="fil1 fnt0">500</text>
-  <text x="20528.59" y="16303.6"  class="fil1 fnt0">600</text>
-  <text x="20528.59" y="4350.68"  class="fil1 fnt0">250</text>
-  <g transform="matrix(2.64845E-14 -1 1 2.64845E-14 4760 24702.7)">
-   <text x="20320" y="15240"  class="fil1 fnt0">PPI</text>
-  </g>
-  <text x="21961.05" y="885.64"  class="fil1 fnt0">Power:</text>
-  <text x="21961.05" y="1831.51"  class="fil1 fnt0">Speed:</text>
- </g>
-</svg>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Creator: CorelDRAW 2018 (64-Bit) -->
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="406.4mm" height="304.8mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
+viewBox="0 0 40640 30480"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+  <font id="FontID0" horiz-adv-x="667" font-variant="normal" style="fill-rule:nonzero" font-style="normal" font-weight="400">
+	<font-face 
+		font-family="Arial">
+		<font-face-src>
+			<font-face-name name="Arial"/>
+		</font-face-src>
+	</font-face>
+   <missing-glyph><path d="M0 0z"/></missing-glyph>
+   <glyph unicode="0" horiz-adv-x="556" d="M41.9998 353.337c0,84.4957 8.66926,152.834 25.996,204.495 17.5039,51.8384 43.3345,91.665 77.669,119.503 34.1692,27.8267 77.3383,41.6691 129.33,41.6691 38.3384,0 71.9997,-7.67714 101.173,-23.1731 28.9959,-15.496 52.9959,-37.8306 71.669,-67.0037 18.6613,-28.9959 33.3306,-64.4997 43.9959,-106.322 10.8307,-41.8345 16.1692,-98.3382 16.1692,-169.169 0,-83.9997 -8.66926,-152.007 -25.8306,-203.668 -17.1732,-51.8384 -43.0038,-91.665 -77.3383,-119.669 -34.1692,-28.0038 -77.5036,-41.9998 -129.838,-41.9998 -68.8226,0 -123,24.496 -162.33,73.6651 -47.1612,59.9998 -70.6651,157.169 -70.6651,291.672zm89.9996 -0.342518c0,-117.826 13.6653,-196.157 41.1731,-234.826 27.496,-38.8345 61.4998,-58.1691 101.822,-58.1691 40.3345,0 74.3383,19.4999 101.834,58.3344 27.5078,38.9998 41.1731,117.165 41.1731,234.66 0,117.838 -13.6653,196.169 -41.1731,234.838 -27.3306,38.8345 -61.6651,58.1691 -102.826,58.1691 -40.3345,0 -72.6729,-17.1732 -96.8382,-51.3305 -30.1652,-43.4998 -45.1652,-124.169 -45.1652,-241.676z"/>
+   <glyph unicode="1" horiz-adv-x="556" d="M371.999 0l-88.0036 0 0 560.336c-21.1653,-20.3385 -48.9919,-40.4998 -83.3264,-60.673 -34.3345,-20.1613 -65.3383,-35.3266 -92.6689,-45.3305l0 85.0036c49.1691,22.996 92.1729,50.9998 128.999,83.9997 36.8384,32.8345 62.9998,64.6651 78.3304,95.6689l56.6691 0 0 -719.005z"/>
+   <glyph unicode="2" horiz-adv-x="556" d="M503.006 83.9997l0 -83.9997 -474.01 0c-0.661415,21.1653 2.83463,41.5038 10.3346,61.1691 12.1653,32.1613 31.6652,63.9919 58.4998,95.161 26.8345,31.1692 65.173,67.169 115.003,108 77.5036,63.8383 130.169,114.342 157.665,151.665 27.6731,37.3345 41.5038,72.6729 41.5038,106.004 0,34.8306 -12.5078,64.169 -37.4999,88.1689 -25.0038,23.8346 -57.673,35.8345 -97.8303,35.8345 -42.5077,0 -76.4997,-12.6732 -102,-37.9959 -25.5117,-25.1692 -38.3384,-60.1769 -38.6691,-105l-89.9996 8.99996c6.16533,67.3226 29.492,118.665 69.8265,153.992 40.4998,35.3384 94.665,53.0077 162.838,53.0077 68.669,0 123.165,-19.0039 163.169,-57.1769 40.1573,-38.1613 60.1651,-85.4997 60.1651,-141.826 0,-28.6652 -5.83462,-56.8344 -17.6692,-84.5075 -11.6692,-27.8267 -31.1692,-56.9998 -58.3344,-87.6611 -27.3306,-30.4959 -72.4958,-72.6611 -135.496,-126 -52.8305,-44.3384 -86.8343,-74.3383 -101.834,-90.165 -14.9999,-15.8385 -27.3306,-31.6652 -37.1691,-47.6691l351.506 0z"/>
+   <glyph unicode="3" horiz-adv-x="556" d="M41.9998 190.003l88.0036 12c10.1574,-50.0077 27.1652,-86.0075 51.3305,-108 24.1653,-22.0039 53.8344,-32.9999 88.9957,-32.9999 41.6691,0 76.8422,14.3267 105.165,42.992 28.3345,28.6652 42.5077,64.169 42.5077,106.5 0,40.3345 -13.1692,73.6769 -39.6731,100.004 -26.3267,26.1731 -59.8344,39.3306 -100.665,39.3306 -16.6653,0 -37.3345,-3.3307 -62.1612,-9.82673l9.82673 76.9958c5.83462,-0.661415 10.6653,-1.00393 14.1732,-1.00393 37.6652,0 71.3265,9.83854 101.492,29.3385 29.9999,19.4999 44.9998,49.4998 44.9998,89.9996 0,32.1731 -10.996,58.8305 -32.8227,79.9957 -21.8385,21.1771 -50.0077,31.677 -84.5075,31.677 -34.3345,0 -62.8344,-10.8425 -85.665,-32.5038 -22.8306,-21.6731 -37.4999,-54.1651 -43.9959,-97.4996l-88.0036 16.0039c10.8307,58.9958 35.1613,104.822 73.3344,137.326 37.9959,32.5038 85.4997,48.673 142.169,48.673 38.9998,0 74.9997,-8.33855 107.834,-25.1692 32.9999,-16.6653 58.1573,-39.5077 75.4958,-68.3383 17.5039,-28.9959 26.1613,-59.6691 26.1613,-92.1611 0,-30.8385 -8.32674,-58.8423 -24.8267,-84.165 -16.6653,-25.3345 -41.3384,-45.5077 -73.8304,-60.5076 42.3305,-9.66138 75.1651,-29.9999 98.6571,-60.8265 23.3385,-30.6731 35.0077,-69.3422 35.0077,-115.5 0,-62.5037 -22.8306,-115.677 -68.3383,-159.177 -45.6612,-43.4998 -103.334,-65.1612 -172.995,-65.1612 -62.8344,0 -115.003,18.8385 -156.507,56.3384 -41.6573,37.4999 -65.3265,85.9957 -71.1611,145.665z"/>
+   <glyph unicode="4" horiz-adv-x="556" d="M322.995 0l0 170.999 -310.995 0 0 80.9997 326.999 464.006 71.9997 0 0 -464.006 97.0036 0 0 -80.9997 -97.0036 0 0 -170.999 -88.0036 0zm0 251.999l0 322.168 -224.491 -322.168 224.491 0z"/>
+   <glyph unicode="5" horiz-adv-x="556" d="M41.9998 187.995l91.9957 7.00391c6.83856,-44.8345 22.6653,-78.4958 47.3384,-101.161 24.6613,-22.4999 54.8384,-33.8384 90.3304,-33.8384 42.8384,0 78.6729,16.0039 107.834,47.8345 28.9959,31.9959 43.4998,74.3383 43.4998,127.003 0,50.1612 -13.8307,89.6571 -41.6691,118.665 -27.8267,28.9959 -64.6651,43.4998 -110.492,43.4998 -28.4999,0 -53.9998,-6.50785 -76.6651,-19.4999 -22.4999,-13.0039 -40.1691,-29.8345 -53.173,-50.5037l-82.9957 12 68.9997 368.006 354.991 0 0 -83.9997 -284.822 0 -38.1731 -193.169c42.8384,30.1652 87.8382,45.1652 134.834,45.1652 62.3383,0 115.003,-21.6731 157.83,-65.0076 42.8384,-43.3227 64.3344,-98.9996 64.3344,-166.995 0,-64.8305 -18.8267,-120.838 -56.5037,-167.999 -45.8266,-58.0037 -108.496,-86.9997 -187.83,-86.9997 -64.9958,0 -118.003,18.3306 -159.165,54.8384 -41.1613,36.4959 -64.6651,84.8264 -70.4997,145.157z"/>
+   <glyph unicode="6" horiz-adv-x="556" d="M499.002 541.002l-86.9997 -7.00391c-7.83068,34.4999 -19.0039,59.6691 -33.5077,75.3304 -23.8227,25.1692 -53.3266,37.677 -88.3225,37.677 -28.1692,0 -53.173,-7.84249 -74.669,-23.5038 -27.6731,-20.1731 -49.3345,-49.4998 -65.5037,-88.1689 -16.0039,-38.8345 -24.3306,-94.0036 -25.0038,-165.495 21.1653,32.1613 47.173,56.1612 77.8343,71.6572 30.6731,15.6732 62.669,23.5038 96.3421,23.5038 58.6651,0 108.661,-21.6613 149.999,-64.8305 41.1613,-43.1691 61.8305,-98.8343 61.8305,-167.338 0,-44.9998 -9.67319,-86.669 -29.0078,-125.326 -19.4999,-38.6691 -45.9919,-68.173 -79.9957,-88.665 -33.8266,-20.5039 -72.3304,-30.8385 -115.334,-30.8385 -73.169,0 -133.003,26.9999 -179.326,80.8343 -46.1691,53.8344 -69.3422,142.665 -69.3422,266.338 0,138.33 25.6653,238.995 76.8422,301.83 44.6573,54.6612 104.834,82.0036 180.661,82.0036 56.3384,0 102.673,-15.8385 138.673,-47.3384 35.9999,-31.4999 57.6612,-74.9997 64.8305,-130.665zm-359.999 -308.999c0,-30.3306 6.49604,-59.3383 19.3346,-86.9997 12.8267,-27.6731 30.8267,-48.673 53.6573,-63.1651 23.0078,-14.5039 47.3384,-21.8385 73.169,-21.8385 37.4999,0 69.5076,14.9999 95.9996,44.9998 26.5038,29.9999 39.8384,70.6651 39.8384,122.173 0,49.4998 -13.1692,88.4997 -39.3306,117 -26.1731,28.4999 -59.5037,42.8266 -100.004,42.8266 -39.6731,0 -73.4997,-14.3267 -101.173,-42.8266 -27.6613,-28.4999 -41.492,-65.8344 -41.492,-112.169z"/>
+   <glyph unicode="7" horiz-adv-x="556" d="M46.9959 623.005l0 83.9997 463.002 0 0 -68.0076c-45.4959,-48.4959 -90.8264,-112.996 -135.496,-193.499 -44.8345,-80.5036 -79.3343,-163.334 -103.83,-248.503 -17.5039,-59.8344 -28.8424,-125.657 -33.6731,-196.995l-89.9996 0c1.00393,56.3384 12,124.5 33.1652,204.33 21.1653,80.0076 51.3305,157.003 90.8382,231.164 39.4959,74.1729 81.4957,136.677 126,187.511l-350.006 0z"/>
+   <glyph unicode="8" horiz-adv-x="556" d="M177.495 388.168c-36.4959,13.3346 -63.4958,32.5038 -81.165,57.1651 -17.4921,24.8385 -26.3267,54.4958 -26.3267,88.9957 0,52.1691 18.6613,95.8343 55.9958,131.338 37.4999,35.5038 87.165,53.3384 149.338,53.3384 62.3265,0 112.665,-18.1771 150.661,-54.5077 37.9959,-36.3306 56.9998,-80.4918 56.9998,-132.661 0,-33.1652 -8.66926,-62.173 -25.996,-86.669 -17.3385,-24.6731 -43.6652,-43.6652 -79.169,-56.9998 43.8305,-14.3385 76.9958,-37.3345 99.8382,-69.3304 22.8306,-31.8424 34.3345,-69.8383 34.3345,-114.165 0,-61.1691 -21.6731,-112.511 -64.8423,-154.169 -43.3345,-41.6691 -100.169,-62.5037 -170.669,-62.5037 -70.4997,0 -127.334,20.8346 -170.657,62.669 -43.1691,41.8345 -64.8423,93.9918 -64.8423,156.495 0,46.4998 11.8346,85.4997 35.5038,116.834 23.6692,31.4999 57.3305,52.8305 100.996,64.169zm-17.4921 149.338c0,-34.0038 10.8307,-61.6769 32.3266,-83.1729 21.6731,-21.496 50.0077,-32.3385 85.169,-32.3385 34.1692,0 61.8305,10.6771 83.3383,32.0077 21.496,21.3306 32.1613,47.5037 32.1613,78.4958 0,32.3385 -10.996,59.5037 -33.1652,81.5076 -22.0039,21.992 -49.8305,32.9999 -83.3383,32.9999 -33.8266,0 -61.6651,-10.8425 -83.6572,-32.3385 -21.8385,-21.496 -32.8345,-47.1612 -32.8345,-77.1611zm-29.0078 -331.003c0,-25.1692 5.83462,-49.3345 17.5039,-72.8383 11.6692,-23.3267 29.3385,-41.5038 52.9959,-54.3305 23.5038,-12.8385 48.8384,-19.3346 75.6729,-19.3346 42.3305,0 76.9958,13.4999 104.161,40.4998 27.1652,26.9999 40.6652,61.3344 40.6652,103.004 0,42.3305 -13.996,77.3265 -41.8227,105 -28.0038,27.6613 -63.3423,41.492 -105.838,41.492 -41.6691,0 -75.8383,-13.6653 -102.838,-40.9959 -26.9999,-27.3306 -40.4998,-61.4998 -40.4998,-102.496z"/>
+   <glyph unicode=":" horiz-adv-x="277" d="M89.9996 418.994l0 100.004 100.004 0 0 -100.004 -100.004 0zm0 -418.994l0 100.004 100.004 0 0 -100.004 -100.004 0z"/>
+   <glyph unicode="I" horiz-adv-x="277" d="M92.9996 0l0 716.005 94.9957 0 0 -716.005 -94.9957 0z"/>
+   <glyph unicode="P" horiz-adv-x="667" d="M76.9958 0l0 716.005 270.341 0c47.4919,0 83.8343,-2.33857 108.992,-6.83856 35.1731,-5.83462 64.6769,-16.996 88.4997,-33.3306 23.8346,-16.4999 43.0038,-39.5077 57.5076,-68.9997 14.4921,-29.5038 21.6613,-62.0076 21.6613,-97.4996 0,-60.673 -19.3346,-112.169 -58.1691,-154.169 -38.6573,-42.1652 -108.826,-63.1651 -210.165,-63.1651l-183.661 0 0 -292.003 -95.0075 0zm95.0075 376.002l184.995 0c61.3344,0 104.669,11.3267 130.499,34.1692 25.6653,22.8306 38.5038,54.8266 38.5038,96.3303 0,29.8345 -7.49997,55.4998 -22.6653,76.8304 -15.1653,21.3306 -35.1731,35.5038 -59.8344,42.3305 -16.0039,4.16927 -45.5077,6.34249 -88.3343,6.34249l-183.165 0 0 -256.003z"/>
+   <glyph unicode="S" horiz-adv-x="667" d="M46.0038 229.995l88.9957 8.00784c4.16927,-35.9999 13.996,-65.5037 29.5038,-88.665 15.496,-23.0078 39.4959,-41.6691 71.9997,-56.0077 32.492,-14.1614 69.1651,-21.3306 109.83,-21.3306 36.1652,0 67.9958,5.33856 95.6689,16.1692 27.6613,10.8307 48.1652,25.6653 61.6651,44.492 13.4999,18.8385 20.3385,39.3424 20.3385,61.3344 0,22.3346 -6.50785,41.8345 -19.4999,58.6769 -13.0039,16.6653 -34.5117,30.6613 -64.5116,42.1652 -19.1574,7.33462 -61.6651,18.9921 -127.499,34.8306 -65.8226,15.8267 -111.826,30.6613 -138.165,44.6691 -34.1692,17.9999 -59.4919,40.3345 -76.169,66.8265 -16.8306,26.5038 -25.1574,56.3384 -25.1574,89.1729 0,36.3306 10.3346,70.169 30.8267,101.492 20.5039,31.4999 50.3384,55.3344 89.8343,71.669 39.3306,16.3346 83.1729,24.5078 131.338,24.5078 52.9959,0 99.8264,-8.5039 140.326,-25.6771 40.4998,-16.996 71.669,-42.1652 93.5075,-75.4958 21.8267,-33.1652 33.4959,-70.8304 35.1613,-112.83l-90.9918 -7.00391c-4.8425,45.1652 -21.3424,79.3343 -49.5116,102.33 -28.1574,23.1731 -69.8265,34.677 -124.83,34.677 -57.3305,0 -99.165,-10.5 -125.326,-31.4999 -26.1731,-20.9999 -39.3424,-46.3345 -39.3424,-76.0036 0,-25.6653 9.34248,-46.8305 27.8385,-63.5076 18.3306,-16.4999 65.8344,-33.6613 142.83,-50.9998 77.0076,-17.4921 129.838,-32.6574 158.503,-45.6612 41.5038,-19.1692 72.3304,-43.3345 92.1611,-72.8383 19.8424,-29.3267 29.6692,-63.1651 29.6692,-101.492 0,-38.1731 -10.8307,-73.8422 -32.6692,-107.504 -21.8267,-33.6613 -52.9959,-59.8344 -93.8264,-78.4958 -40.8305,-18.6732 -86.669,-28.0038 -137.834,-28.0038 -64.6651,0 -118.83,9.49603 -162.495,28.3345 -43.8423,18.8267 -78.0115,47.1612 -102.838,85.169 -25.0038,37.8306 -37.9959,80.669 -39.3306,128.492z"/>
+   <glyph unicode="d" horiz-adv-x="556" d="M400.994 0l0 65.8344c-32.8227,-51.1652 -81.165,-76.8304 -144.992,-76.8304 -41.3384,0 -79.3343,11.3267 -113.834,34.1573 -34.6652,22.6771 -61.4998,54.5077 -80.669,95.1729 -19.0039,40.8305 -28.4999,87.6611 -28.4999,140.669 0,51.6612 8.66926,98.4917 25.8306,140.669 17.3385,41.9998 43.1691,74.3265 77.669,96.661 34.3345,22.4999 73.0036,33.6731 115.5,33.6731 31.3345,0 59.173,-6.6732 83.5036,-19.8424 24.3306,-13.1692 44.1612,-30.4959 59.4919,-51.6612l0 257.503 88.0036 0 0 -716.005 -82.0036 0zm-277.995 258.838c0,-66.3423 13.996,-115.842 42.1652,-148.677 28.0038,-32.8227 61.1691,-49.1573 99.3303,-49.1573 38.5038,0 71.1729,15.6614 98.1728,46.9959 26.8345,31.3345 40.3345,79.169 40.3345,143.503 0,70.6651 -13.6653,122.669 -41.1731,155.834 -27.3306,33.1652 -61.1572,49.6652 -101.326,49.6652 -39.1652,0 -71.8344,-15.8385 -98.1728,-47.6691 -26.1613,-31.8306 -39.3306,-82.0036 -39.3306,-150.495z"/>
+   <glyph unicode="e" horiz-adv-x="556" d="M419.998 167.999l91.0036 -12c-14.1732,-52.8305 -40.6652,-94.0036 -79.3343,-123.165 -38.6691,-29.1731 -88.0036,-43.8305 -148.169,-43.8305 -75.6611,0 -135.661,23.3267 -179.999,69.9919 -44.3384,46.4998 -66.4958,112.003 -66.4958,196.169 0,86.9997 22.3346,154.665 67.1572,202.665 44.8345,48.177 103.004,72.1769 174.507,72.1769 69.1651,0 125.669,-23.5038 169.499,-70.6769 43.8305,-47.1612 65.8344,-113.492 65.8344,-198.991 0,-5.17321 -0.165354,-13.0039 -0.496061,-23.3385l-386.502 0c3.3307,-56.8344 19.3346,-100.5 48.3305,-130.665 28.8306,-30.1652 64.9958,-45.3305 108.165,-45.3305 32.1731,0 59.6691,8.49209 82.3343,25.4999 22.8306,16.996 40.8305,44.1612 54.1651,81.4957zm-287.999 140.999l289.003 0c-3.83857,43.6652 -14.9999,76.3343 -33.1652,98.1728 -27.8385,33.8266 -64.169,50.8226 -108.673,50.8226 -40.3345,0 -74.1611,-13.4999 -101.669,-40.4998 -27.3306,-26.9999 -42.4959,-63.1651 -45.4959,-108.496z"/>
+   <glyph unicode="o" horiz-adv-x="556" d="M32.9999 259.499c0,95.8343 26.6692,166.83 80.1611,212.999 44.5038,38.3384 99.0114,57.5076 163.169,57.5076 71.1729,0 129.507,-23.3385 174.838,-70.0037 45.1652,-46.4998 67.8304,-111 67.8304,-193.169 0,-66.6612 -9.99209,-118.996 -29.9999,-157.169 -19.996,-38.1613 -49.1691,-67.8304 -87.4957,-88.9957 -38.1731,-21.1653 -80.0076,-31.6652 -125.173,-31.6652 -72.6611,0 -131.326,23.1613 -176.161,69.6611 -44.8345,46.4998 -67.169,113.503 -67.169,200.834zm89.9996 -0.165354c0,-66.3304 14.5039,-115.996 43.4998,-148.83 29.1731,-32.9999 65.669,-49.4998 109.83,-49.4998 43.6652,0 80.0076,16.4999 109.169,49.6652 28.9959,33.1652 43.4998,83.669 43.4998,151.499 0,64.0037 -14.6692,112.5 -43.8305,145.334 -29.1731,32.9999 -65.5037,49.4998 -108.838,49.4998 -44.1612,0 -80.6572,-16.3346 -109.83,-49.1691 -28.9959,-32.8345 -43.4998,-82.3343 -43.4998,-148.499z"/>
+   <glyph unicode="p" horiz-adv-x="556" d="M65.9997 -199.003l0 718.001 79.9957 0 0 -68.1611c18.8385,26.3267 40.1691,46.1573 64.0037,59.3265 23.6692,13.1692 52.4998,19.8424 86.3382,19.8424 44.3266,0 83.3264,-11.3385 117.165,-34.1692 33.8266,-22.6653 59.3265,-54.8384 76.6651,-96.165 17.1613,-41.5038 25.8306,-86.8343 25.8306,-136.334 0,-52.8423 -9.49603,-100.5 -28.4999,-143.007 -19.0039,-42.3305 -46.6652,-74.8344 -82.9957,-97.4996 -36.1652,-22.4999 -74.3383,-33.8266 -114.33,-33.8266 -29.3385,0 -55.6769,6.16533 -78.8383,18.496 -23.3385,12.3307 -42.3305,27.8385 -57.3305,46.6652l0 -253.168 -88.0036 0zm79.9957 455.337c0,-66.4958 13.4999,-115.665 40.3345,-147.495 26.9999,-31.8424 59.6691,-47.8345 98.0075,-47.8345 38.9998,0 72.3304,16.4999 99.9917,49.4998 27.8385,32.9999 41.6691,83.9997 41.6691,153.165 0,65.9997 -13.4999,115.334 -40.6652,148.169 -27.1652,32.8345 -59.5037,49.1573 -97.1689,49.1573 -37.3345,0 -70.3344,-17.4921 -98.9996,-52.3226 -28.8306,-35.0077 -43.1691,-85.6768 -43.1691,-152.338z"/>
+   <glyph unicode="r" horiz-adv-x="332" d="M64.9958 0l0 518.998 79.0036 0 0 -79.4997c20.1613,36.8384 38.8345,61.1691 55.9958,72.8383 17.1732,11.8346 35.9999,17.6692 56.5037,17.6692 29.6692,0 59.8344,-9.34248 90.4957,-27.8385l-31.3227 -81.165c-21.3424,12 -42.6731,17.9999 -64.0037,17.9999 -19.1692,0 -36.3306,-5.83462 -51.4959,-17.3385 -15.1771,-11.4921 -26.0078,-27.496 -32.5038,-47.9998 -9.83854,-31.1692 -14.6692,-65.3265 -14.6692,-102.496l0 -271.168 -88.0036 0z"/>
+   <glyph unicode="w" horiz-adv-x="722" d="M159.33 0l-156.826 518.998 91.9957 0 82.8304 -299.503 29.3385 -111.496c1.16929,5.50392 10.1693,41.1613 26.8345,106.996l82.169 304.003 90.165 0 78.165 -301.333 25.4999 -98.8343 29.8345 100.169 89.6689 299.999 85.4997 0 -162.838 -518.998 -91.0036 0 -82.3343 310.499 -20.8346 88.8304 -105 -399.329 -93.165 0z"/>
+  </font>
+  <style type="text/css">
+   <![CDATA[
+    @font-face { font-family:"Arial";font-variant:normal;font-style:normal;font-weight:normal;src:url("#FontID0") format(svg)}
+    .str4 {stroke:#FF6600;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .str2 {stroke:fuchsia;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .str0 {stroke:red;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .str3 {stroke:aqua;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .str1 {stroke:blue;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .fil0 {fill:none}
+    .fil1 {fill:black}
+    .fnt0 {font-weight:normal;font-size:846.67px;font-family:'Arial'}
+   ]]>
+  </style>
+ </defs>
+ <g id="レイヤ_x0020_1">
+  <metadata id="CorelCorpID_0Corel-Layer"/>
+  <rect class="fil0 str0" x="3000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="3000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="3000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="3000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="3000" y="15000" width="2000" height="2000"/>
+  <line class="fil0 str0" x1="-0" y1="0" x2="500" y2= "0" />
+  <text x="1058.24" y="7272.62"  class="fil1 fnt0">10</text>
+  <text x="1058.24" y="10264.61"  class="fil1 fnt0">20</text>
+  <text x="1084.7" y="13272.63"  class="fil1 fnt0">30</text>
+  <text x="1045.01" y="16259.4"  class="fil1 fnt0">40</text>
+  <text x="1568.56" y="4306.48"  class="fil1 fnt0">4</text>
+  <text x="1568.56" y="4306.48"  class="fil1 fnt0">4</text>
+  <g transform="matrix(2.64845E-14 -1 1 2.64845E-14 -14323.7 26102.3)">
+   <text x="20320" y="15240"  class="fil1 fnt0">Speed</text>
+  </g>
+  <text x="2934.81" y="990.69"  class="fil1 fnt0">Power</text>
+  <text x="3508.19" y="2299.3"  class="fil1 fnt0">40</text>
+  <rect class="fil0 str0" x="6000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="6000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="6000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="6000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="6000" y="15000" width="2000" height="2000"/>
+  <text x="6508.19" y="2299.3"  class="fil1 fnt0">50</text>
+  <rect class="fil0 str0" x="9000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="9000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="9000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="9000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="9000" y="15000" width="2000" height="2000"/>
+  <text x="9508.19" y="2299.3"  class="fil1 fnt0">60</text>
+  <rect class="fil0 str0" x="12000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="12000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="12000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="12000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="12000" y="15000" width="2000" height="2000"/>
+  <text x="12508.19" y="2299.3"  class="fil1 fnt0">70</text>
+  <rect class="fil0 str0" x="15000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="15000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="15000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="15000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="15000" y="15000" width="2000" height="2000"/>
+  <text x="15508.19" y="2299.3"  class="fil1 fnt0">80</text>
+  <text x="1519.73" y="18591.56"  class="fil1 fnt0">250PPI</text>
+  <rect class="fil0 str0" x="23000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="23000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="23000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="23000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="23000" y="15000" width="2000" height="2000"/>
+  <text x="20528.59" y="7316.82"  class="fil1 fnt0">300</text>
+  <text x="20528.59" y="10239.63"  class="fil1 fnt0">400</text>
+  <text x="20528.59" y="13202.97"  class="fil1 fnt0">500</text>
+  <text x="20528.59" y="16303.6"  class="fil1 fnt0">600</text>
+  <text x="20528.59" y="4350.68"  class="fil1 fnt0">250</text>
+  <g transform="matrix(2.64845E-14 -1 1 2.64845E-14 4760 24702.7)">
+   <text x="20320" y="15240"  class="fil1 fnt0">PPI</text>
+  </g>
+  <text x="21961.05" y="885.64"  class="fil1 fnt0">Power:</text>
+  <text x="21961.05" y="1831.51"  class="fil1 fnt0">Speed:</text>
+ </g>
+</svg>
diff --git a/docs/images/week03/laser_1.png b/docs/images/week03/laser_1.png
old mode 100755
new mode 100644
diff --git a/docs/images/week03/laser_2.png b/docs/images/week03/laser_2.png
old mode 100755
new mode 100644
diff --git a/docs/images/week03/laser_3.png b/docs/images/week03/laser_3.png
old mode 100755
new mode 100644
diff --git a/docs/images/week03/laser_4.png b/docs/images/week03/laser_4.png
old mode 100755
new mode 100644
diff --git a/docs/images/week03/psr.svg b/docs/images/week03/psr.svg
index 78b625e0e2bb2165fee88b7fd4177bf91f213c60..89c35bbc48e36173cd2565510b177335a12ea620 100644
--- a/docs/images/week03/psr.svg
+++ b/docs/images/week03/psr.svg
@@ -1,110 +1,110 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Creator: CorelDRAW 2018 (64-Bit) -->
-<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="406.4mm" height="304.8mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
-viewBox="0 0 40640 30480"
- xmlns:xlink="http://www.w3.org/1999/xlink">
- <defs>
-  <font id="FontID0" horiz-adv-x="667" font-variant="normal" style="fill-rule:nonzero" font-style="normal" font-weight="400">
-	<font-face 
-		font-family="Arial">
-		<font-face-src>
-			<font-face-name name="Arial"/>
-		</font-face-src>
-	</font-face>
-   <missing-glyph><path d="M0 0z"/></missing-glyph>
-   <glyph unicode="0" horiz-adv-x="556" d="M41.9998 353.337c0,84.4957 8.66926,152.834 25.996,204.495 17.5039,51.8384 43.3345,91.665 77.669,119.503 34.1692,27.8267 77.3383,41.6691 129.33,41.6691 38.3384,0 71.9997,-7.67714 101.173,-23.1731 28.9959,-15.496 52.9959,-37.8306 71.669,-67.0037 18.6613,-28.9959 33.3306,-64.4997 43.9959,-106.322 10.8307,-41.8345 16.1692,-98.3382 16.1692,-169.169 0,-83.9997 -8.66926,-152.007 -25.8306,-203.668 -17.1732,-51.8384 -43.0038,-91.665 -77.3383,-119.669 -34.1692,-28.0038 -77.5036,-41.9998 -129.838,-41.9998 -68.8226,0 -123,24.496 -162.33,73.6651 -47.1612,59.9998 -70.6651,157.169 -70.6651,291.672zm89.9996 -0.342518c0,-117.826 13.6653,-196.157 41.1731,-234.826 27.496,-38.8345 61.4998,-58.1691 101.822,-58.1691 40.3345,0 74.3383,19.4999 101.834,58.3344 27.5078,38.9998 41.1731,117.165 41.1731,234.66 0,117.838 -13.6653,196.169 -41.1731,234.838 -27.3306,38.8345 -61.6651,58.1691 -102.826,58.1691 -40.3345,0 -72.6729,-17.1732 -96.8382,-51.3305 -30.1652,-43.4998 -45.1652,-124.169 -45.1652,-241.676z"/>
-   <glyph unicode="1" horiz-adv-x="556" d="M371.999 0l-88.0036 0 0 560.336c-21.1653,-20.3385 -48.9919,-40.4998 -83.3264,-60.673 -34.3345,-20.1613 -65.3383,-35.3266 -92.6689,-45.3305l0 85.0036c49.1691,22.996 92.1729,50.9998 128.999,83.9997 36.8384,32.8345 62.9998,64.6651 78.3304,95.6689l56.6691 0 0 -719.005z"/>
-   <glyph unicode="2" horiz-adv-x="556" d="M503.006 83.9997l0 -83.9997 -474.01 0c-0.661415,21.1653 2.83463,41.5038 10.3346,61.1691 12.1653,32.1613 31.6652,63.9919 58.4998,95.161 26.8345,31.1692 65.173,67.169 115.003,108 77.5036,63.8383 130.169,114.342 157.665,151.665 27.6731,37.3345 41.5038,72.6729 41.5038,106.004 0,34.8306 -12.5078,64.169 -37.4999,88.1689 -25.0038,23.8346 -57.673,35.8345 -97.8303,35.8345 -42.5077,0 -76.4997,-12.6732 -102,-37.9959 -25.5117,-25.1692 -38.3384,-60.1769 -38.6691,-105l-89.9996 8.99996c6.16533,67.3226 29.492,118.665 69.8265,153.992 40.4998,35.3384 94.665,53.0077 162.838,53.0077 68.669,0 123.165,-19.0039 163.169,-57.1769 40.1573,-38.1613 60.1651,-85.4997 60.1651,-141.826 0,-28.6652 -5.83462,-56.8344 -17.6692,-84.5075 -11.6692,-27.8267 -31.1692,-56.9998 -58.3344,-87.6611 -27.3306,-30.4959 -72.4958,-72.6611 -135.496,-126 -52.8305,-44.3384 -86.8343,-74.3383 -101.834,-90.165 -14.9999,-15.8385 -27.3306,-31.6652 -37.1691,-47.6691l351.506 0z"/>
-   <glyph unicode="3" horiz-adv-x="556" d="M41.9998 190.003l88.0036 12c10.1574,-50.0077 27.1652,-86.0075 51.3305,-108 24.1653,-22.0039 53.8344,-32.9999 88.9957,-32.9999 41.6691,0 76.8422,14.3267 105.165,42.992 28.3345,28.6652 42.5077,64.169 42.5077,106.5 0,40.3345 -13.1692,73.6769 -39.6731,100.004 -26.3267,26.1731 -59.8344,39.3306 -100.665,39.3306 -16.6653,0 -37.3345,-3.3307 -62.1612,-9.82673l9.82673 76.9958c5.83462,-0.661415 10.6653,-1.00393 14.1732,-1.00393 37.6652,0 71.3265,9.83854 101.492,29.3385 29.9999,19.4999 44.9998,49.4998 44.9998,89.9996 0,32.1731 -10.996,58.8305 -32.8227,79.9957 -21.8385,21.1771 -50.0077,31.677 -84.5075,31.677 -34.3345,0 -62.8344,-10.8425 -85.665,-32.5038 -22.8306,-21.6731 -37.4999,-54.1651 -43.9959,-97.4996l-88.0036 16.0039c10.8307,58.9958 35.1613,104.822 73.3344,137.326 37.9959,32.5038 85.4997,48.673 142.169,48.673 38.9998,0 74.9997,-8.33855 107.834,-25.1692 32.9999,-16.6653 58.1573,-39.5077 75.4958,-68.3383 17.5039,-28.9959 26.1613,-59.6691 26.1613,-92.1611 0,-30.8385 -8.32674,-58.8423 -24.8267,-84.165 -16.6653,-25.3345 -41.3384,-45.5077 -73.8304,-60.5076 42.3305,-9.66138 75.1651,-29.9999 98.6571,-60.8265 23.3385,-30.6731 35.0077,-69.3422 35.0077,-115.5 0,-62.5037 -22.8306,-115.677 -68.3383,-159.177 -45.6612,-43.4998 -103.334,-65.1612 -172.995,-65.1612 -62.8344,0 -115.003,18.8385 -156.507,56.3384 -41.6573,37.4999 -65.3265,85.9957 -71.1611,145.665z"/>
-   <glyph unicode="4" horiz-adv-x="556" d="M322.995 0l0 170.999 -310.995 0 0 80.9997 326.999 464.006 71.9997 0 0 -464.006 97.0036 0 0 -80.9997 -97.0036 0 0 -170.999 -88.0036 0zm0 251.999l0 322.168 -224.491 -322.168 224.491 0z"/>
-   <glyph unicode="5" horiz-adv-x="556" d="M41.9998 187.995l91.9957 7.00391c6.83856,-44.8345 22.6653,-78.4958 47.3384,-101.161 24.6613,-22.4999 54.8384,-33.8384 90.3304,-33.8384 42.8384,0 78.6729,16.0039 107.834,47.8345 28.9959,31.9959 43.4998,74.3383 43.4998,127.003 0,50.1612 -13.8307,89.6571 -41.6691,118.665 -27.8267,28.9959 -64.6651,43.4998 -110.492,43.4998 -28.4999,0 -53.9998,-6.50785 -76.6651,-19.4999 -22.4999,-13.0039 -40.1691,-29.8345 -53.173,-50.5037l-82.9957 12 68.9997 368.006 354.991 0 0 -83.9997 -284.822 0 -38.1731 -193.169c42.8384,30.1652 87.8382,45.1652 134.834,45.1652 62.3383,0 115.003,-21.6731 157.83,-65.0076 42.8384,-43.3227 64.3344,-98.9996 64.3344,-166.995 0,-64.8305 -18.8267,-120.838 -56.5037,-167.999 -45.8266,-58.0037 -108.496,-86.9997 -187.83,-86.9997 -64.9958,0 -118.003,18.3306 -159.165,54.8384 -41.1613,36.4959 -64.6651,84.8264 -70.4997,145.157z"/>
-   <glyph unicode="6" horiz-adv-x="556" d="M499.002 541.002l-86.9997 -7.00391c-7.83068,34.4999 -19.0039,59.6691 -33.5077,75.3304 -23.8227,25.1692 -53.3266,37.677 -88.3225,37.677 -28.1692,0 -53.173,-7.84249 -74.669,-23.5038 -27.6731,-20.1731 -49.3345,-49.4998 -65.5037,-88.1689 -16.0039,-38.8345 -24.3306,-94.0036 -25.0038,-165.495 21.1653,32.1613 47.173,56.1612 77.8343,71.6572 30.6731,15.6732 62.669,23.5038 96.3421,23.5038 58.6651,0 108.661,-21.6613 149.999,-64.8305 41.1613,-43.1691 61.8305,-98.8343 61.8305,-167.338 0,-44.9998 -9.67319,-86.669 -29.0078,-125.326 -19.4999,-38.6691 -45.9919,-68.173 -79.9957,-88.665 -33.8266,-20.5039 -72.3304,-30.8385 -115.334,-30.8385 -73.169,0 -133.003,26.9999 -179.326,80.8343 -46.1691,53.8344 -69.3422,142.665 -69.3422,266.338 0,138.33 25.6653,238.995 76.8422,301.83 44.6573,54.6612 104.834,82.0036 180.661,82.0036 56.3384,0 102.673,-15.8385 138.673,-47.3384 35.9999,-31.4999 57.6612,-74.9997 64.8305,-130.665zm-359.999 -308.999c0,-30.3306 6.49604,-59.3383 19.3346,-86.9997 12.8267,-27.6731 30.8267,-48.673 53.6573,-63.1651 23.0078,-14.5039 47.3384,-21.8385 73.169,-21.8385 37.4999,0 69.5076,14.9999 95.9996,44.9998 26.5038,29.9999 39.8384,70.6651 39.8384,122.173 0,49.4998 -13.1692,88.4997 -39.3306,117 -26.1731,28.4999 -59.5037,42.8266 -100.004,42.8266 -39.6731,0 -73.4997,-14.3267 -101.173,-42.8266 -27.6613,-28.4999 -41.492,-65.8344 -41.492,-112.169z"/>
-   <glyph unicode="7" horiz-adv-x="556" d="M46.9959 623.005l0 83.9997 463.002 0 0 -68.0076c-45.4959,-48.4959 -90.8264,-112.996 -135.496,-193.499 -44.8345,-80.5036 -79.3343,-163.334 -103.83,-248.503 -17.5039,-59.8344 -28.8424,-125.657 -33.6731,-196.995l-89.9996 0c1.00393,56.3384 12,124.5 33.1652,204.33 21.1653,80.0076 51.3305,157.003 90.8382,231.164 39.4959,74.1729 81.4957,136.677 126,187.511l-350.006 0z"/>
-   <glyph unicode="8" horiz-adv-x="556" d="M177.495 388.168c-36.4959,13.3346 -63.4958,32.5038 -81.165,57.1651 -17.4921,24.8385 -26.3267,54.4958 -26.3267,88.9957 0,52.1691 18.6613,95.8343 55.9958,131.338 37.4999,35.5038 87.165,53.3384 149.338,53.3384 62.3265,0 112.665,-18.1771 150.661,-54.5077 37.9959,-36.3306 56.9998,-80.4918 56.9998,-132.661 0,-33.1652 -8.66926,-62.173 -25.996,-86.669 -17.3385,-24.6731 -43.6652,-43.6652 -79.169,-56.9998 43.8305,-14.3385 76.9958,-37.3345 99.8382,-69.3304 22.8306,-31.8424 34.3345,-69.8383 34.3345,-114.165 0,-61.1691 -21.6731,-112.511 -64.8423,-154.169 -43.3345,-41.6691 -100.169,-62.5037 -170.669,-62.5037 -70.4997,0 -127.334,20.8346 -170.657,62.669 -43.1691,41.8345 -64.8423,93.9918 -64.8423,156.495 0,46.4998 11.8346,85.4997 35.5038,116.834 23.6692,31.4999 57.3305,52.8305 100.996,64.169zm-17.4921 149.338c0,-34.0038 10.8307,-61.6769 32.3266,-83.1729 21.6731,-21.496 50.0077,-32.3385 85.169,-32.3385 34.1692,0 61.8305,10.6771 83.3383,32.0077 21.496,21.3306 32.1613,47.5037 32.1613,78.4958 0,32.3385 -10.996,59.5037 -33.1652,81.5076 -22.0039,21.992 -49.8305,32.9999 -83.3383,32.9999 -33.8266,0 -61.6651,-10.8425 -83.6572,-32.3385 -21.8385,-21.496 -32.8345,-47.1612 -32.8345,-77.1611zm-29.0078 -331.003c0,-25.1692 5.83462,-49.3345 17.5039,-72.8383 11.6692,-23.3267 29.3385,-41.5038 52.9959,-54.3305 23.5038,-12.8385 48.8384,-19.3346 75.6729,-19.3346 42.3305,0 76.9958,13.4999 104.161,40.4998 27.1652,26.9999 40.6652,61.3344 40.6652,103.004 0,42.3305 -13.996,77.3265 -41.8227,105 -28.0038,27.6613 -63.3423,41.492 -105.838,41.492 -41.6691,0 -75.8383,-13.6653 -102.838,-40.9959 -26.9999,-27.3306 -40.4998,-61.4998 -40.4998,-102.496z"/>
-   <glyph unicode=":" horiz-adv-x="277" d="M89.9996 418.994l0 100.004 100.004 0 0 -100.004 -100.004 0zm0 -418.994l0 100.004 100.004 0 0 -100.004 -100.004 0z"/>
-   <glyph unicode="I" horiz-adv-x="277" d="M92.9996 0l0 716.005 94.9957 0 0 -716.005 -94.9957 0z"/>
-   <glyph unicode="P" horiz-adv-x="667" d="M76.9958 0l0 716.005 270.341 0c47.4919,0 83.8343,-2.33857 108.992,-6.83856 35.1731,-5.83462 64.6769,-16.996 88.4997,-33.3306 23.8346,-16.4999 43.0038,-39.5077 57.5076,-68.9997 14.4921,-29.5038 21.6613,-62.0076 21.6613,-97.4996 0,-60.673 -19.3346,-112.169 -58.1691,-154.169 -38.6573,-42.1652 -108.826,-63.1651 -210.165,-63.1651l-183.661 0 0 -292.003 -95.0075 0zm95.0075 376.002l184.995 0c61.3344,0 104.669,11.3267 130.499,34.1692 25.6653,22.8306 38.5038,54.8266 38.5038,96.3303 0,29.8345 -7.49997,55.4998 -22.6653,76.8304 -15.1653,21.3306 -35.1731,35.5038 -59.8344,42.3305 -16.0039,4.16927 -45.5077,6.34249 -88.3343,6.34249l-183.165 0 0 -256.003z"/>
-   <glyph unicode="S" horiz-adv-x="667" d="M46.0038 229.995l88.9957 8.00784c4.16927,-35.9999 13.996,-65.5037 29.5038,-88.665 15.496,-23.0078 39.4959,-41.6691 71.9997,-56.0077 32.492,-14.1614 69.1651,-21.3306 109.83,-21.3306 36.1652,0 67.9958,5.33856 95.6689,16.1692 27.6613,10.8307 48.1652,25.6653 61.6651,44.492 13.4999,18.8385 20.3385,39.3424 20.3385,61.3344 0,22.3346 -6.50785,41.8345 -19.4999,58.6769 -13.0039,16.6653 -34.5117,30.6613 -64.5116,42.1652 -19.1574,7.33462 -61.6651,18.9921 -127.499,34.8306 -65.8226,15.8267 -111.826,30.6613 -138.165,44.6691 -34.1692,17.9999 -59.4919,40.3345 -76.169,66.8265 -16.8306,26.5038 -25.1574,56.3384 -25.1574,89.1729 0,36.3306 10.3346,70.169 30.8267,101.492 20.5039,31.4999 50.3384,55.3344 89.8343,71.669 39.3306,16.3346 83.1729,24.5078 131.338,24.5078 52.9959,0 99.8264,-8.5039 140.326,-25.6771 40.4998,-16.996 71.669,-42.1652 93.5075,-75.4958 21.8267,-33.1652 33.4959,-70.8304 35.1613,-112.83l-90.9918 -7.00391c-4.8425,45.1652 -21.3424,79.3343 -49.5116,102.33 -28.1574,23.1731 -69.8265,34.677 -124.83,34.677 -57.3305,0 -99.165,-10.5 -125.326,-31.4999 -26.1731,-20.9999 -39.3424,-46.3345 -39.3424,-76.0036 0,-25.6653 9.34248,-46.8305 27.8385,-63.5076 18.3306,-16.4999 65.8344,-33.6613 142.83,-50.9998 77.0076,-17.4921 129.838,-32.6574 158.503,-45.6612 41.5038,-19.1692 72.3304,-43.3345 92.1611,-72.8383 19.8424,-29.3267 29.6692,-63.1651 29.6692,-101.492 0,-38.1731 -10.8307,-73.8422 -32.6692,-107.504 -21.8267,-33.6613 -52.9959,-59.8344 -93.8264,-78.4958 -40.8305,-18.6732 -86.669,-28.0038 -137.834,-28.0038 -64.6651,0 -118.83,9.49603 -162.495,28.3345 -43.8423,18.8267 -78.0115,47.1612 -102.838,85.169 -25.0038,37.8306 -37.9959,80.669 -39.3306,128.492z"/>
-   <glyph unicode="d" horiz-adv-x="556" d="M400.994 0l0 65.8344c-32.8227,-51.1652 -81.165,-76.8304 -144.992,-76.8304 -41.3384,0 -79.3343,11.3267 -113.834,34.1573 -34.6652,22.6771 -61.4998,54.5077 -80.669,95.1729 -19.0039,40.8305 -28.4999,87.6611 -28.4999,140.669 0,51.6612 8.66926,98.4917 25.8306,140.669 17.3385,41.9998 43.1691,74.3265 77.669,96.661 34.3345,22.4999 73.0036,33.6731 115.5,33.6731 31.3345,0 59.173,-6.6732 83.5036,-19.8424 24.3306,-13.1692 44.1612,-30.4959 59.4919,-51.6612l0 257.503 88.0036 0 0 -716.005 -82.0036 0zm-277.995 258.838c0,-66.3423 13.996,-115.842 42.1652,-148.677 28.0038,-32.8227 61.1691,-49.1573 99.3303,-49.1573 38.5038,0 71.1729,15.6614 98.1728,46.9959 26.8345,31.3345 40.3345,79.169 40.3345,143.503 0,70.6651 -13.6653,122.669 -41.1731,155.834 -27.3306,33.1652 -61.1572,49.6652 -101.326,49.6652 -39.1652,0 -71.8344,-15.8385 -98.1728,-47.6691 -26.1613,-31.8306 -39.3306,-82.0036 -39.3306,-150.495z"/>
-   <glyph unicode="e" horiz-adv-x="556" d="M419.998 167.999l91.0036 -12c-14.1732,-52.8305 -40.6652,-94.0036 -79.3343,-123.165 -38.6691,-29.1731 -88.0036,-43.8305 -148.169,-43.8305 -75.6611,0 -135.661,23.3267 -179.999,69.9919 -44.3384,46.4998 -66.4958,112.003 -66.4958,196.169 0,86.9997 22.3346,154.665 67.1572,202.665 44.8345,48.177 103.004,72.1769 174.507,72.1769 69.1651,0 125.669,-23.5038 169.499,-70.6769 43.8305,-47.1612 65.8344,-113.492 65.8344,-198.991 0,-5.17321 -0.165354,-13.0039 -0.496061,-23.3385l-386.502 0c3.3307,-56.8344 19.3346,-100.5 48.3305,-130.665 28.8306,-30.1652 64.9958,-45.3305 108.165,-45.3305 32.1731,0 59.6691,8.49209 82.3343,25.4999 22.8306,16.996 40.8305,44.1612 54.1651,81.4957zm-287.999 140.999l289.003 0c-3.83857,43.6652 -14.9999,76.3343 -33.1652,98.1728 -27.8385,33.8266 -64.169,50.8226 -108.673,50.8226 -40.3345,0 -74.1611,-13.4999 -101.669,-40.4998 -27.3306,-26.9999 -42.4959,-63.1651 -45.4959,-108.496z"/>
-   <glyph unicode="o" horiz-adv-x="556" d="M32.9999 259.499c0,95.8343 26.6692,166.83 80.1611,212.999 44.5038,38.3384 99.0114,57.5076 163.169,57.5076 71.1729,0 129.507,-23.3385 174.838,-70.0037 45.1652,-46.4998 67.8304,-111 67.8304,-193.169 0,-66.6612 -9.99209,-118.996 -29.9999,-157.169 -19.996,-38.1613 -49.1691,-67.8304 -87.4957,-88.9957 -38.1731,-21.1653 -80.0076,-31.6652 -125.173,-31.6652 -72.6611,0 -131.326,23.1613 -176.161,69.6611 -44.8345,46.4998 -67.169,113.503 -67.169,200.834zm89.9996 -0.165354c0,-66.3304 14.5039,-115.996 43.4998,-148.83 29.1731,-32.9999 65.669,-49.4998 109.83,-49.4998 43.6652,0 80.0076,16.4999 109.169,49.6652 28.9959,33.1652 43.4998,83.669 43.4998,151.499 0,64.0037 -14.6692,112.5 -43.8305,145.334 -29.1731,32.9999 -65.5037,49.4998 -108.838,49.4998 -44.1612,0 -80.6572,-16.3346 -109.83,-49.1691 -28.9959,-32.8345 -43.4998,-82.3343 -43.4998,-148.499z"/>
-   <glyph unicode="p" horiz-adv-x="556" d="M65.9997 -199.003l0 718.001 79.9957 0 0 -68.1611c18.8385,26.3267 40.1691,46.1573 64.0037,59.3265 23.6692,13.1692 52.4998,19.8424 86.3382,19.8424 44.3266,0 83.3264,-11.3385 117.165,-34.1692 33.8266,-22.6653 59.3265,-54.8384 76.6651,-96.165 17.1613,-41.5038 25.8306,-86.8343 25.8306,-136.334 0,-52.8423 -9.49603,-100.5 -28.4999,-143.007 -19.0039,-42.3305 -46.6652,-74.8344 -82.9957,-97.4996 -36.1652,-22.4999 -74.3383,-33.8266 -114.33,-33.8266 -29.3385,0 -55.6769,6.16533 -78.8383,18.496 -23.3385,12.3307 -42.3305,27.8385 -57.3305,46.6652l0 -253.168 -88.0036 0zm79.9957 455.337c0,-66.4958 13.4999,-115.665 40.3345,-147.495 26.9999,-31.8424 59.6691,-47.8345 98.0075,-47.8345 38.9998,0 72.3304,16.4999 99.9917,49.4998 27.8385,32.9999 41.6691,83.9997 41.6691,153.165 0,65.9997 -13.4999,115.334 -40.6652,148.169 -27.1652,32.8345 -59.5037,49.1573 -97.1689,49.1573 -37.3345,0 -70.3344,-17.4921 -98.9996,-52.3226 -28.8306,-35.0077 -43.1691,-85.6768 -43.1691,-152.338z"/>
-   <glyph unicode="r" horiz-adv-x="332" d="M64.9958 0l0 518.998 79.0036 0 0 -79.4997c20.1613,36.8384 38.8345,61.1691 55.9958,72.8383 17.1732,11.8346 35.9999,17.6692 56.5037,17.6692 29.6692,0 59.8344,-9.34248 90.4957,-27.8385l-31.3227 -81.165c-21.3424,12 -42.6731,17.9999 -64.0037,17.9999 -19.1692,0 -36.3306,-5.83462 -51.4959,-17.3385 -15.1771,-11.4921 -26.0078,-27.496 -32.5038,-47.9998 -9.83854,-31.1692 -14.6692,-65.3265 -14.6692,-102.496l0 -271.168 -88.0036 0z"/>
-   <glyph unicode="w" horiz-adv-x="722" d="M159.33 0l-156.826 518.998 91.9957 0 82.8304 -299.503 29.3385 -111.496c1.16929,5.50392 10.1693,41.1613 26.8345,106.996l82.169 304.003 90.165 0 78.165 -301.333 25.4999 -98.8343 29.8345 100.169 89.6689 299.999 85.4997 0 -162.838 -518.998 -91.0036 0 -82.3343 310.499 -20.8346 88.8304 -105 -399.329 -93.165 0z"/>
-  </font>
-  <style type="text/css">
-   <![CDATA[
-    @font-face { font-family:"Arial";font-variant:normal;font-style:normal;font-weight:normal;src:url("#FontID0") format(svg)}
-    .str4 {stroke:#FF6600;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .str2 {stroke:fuchsia;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .str0 {stroke:red;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .str3 {stroke:aqua;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .str1 {stroke:blue;stroke-width:7.62;stroke-miterlimit:22.9256}
-    .fil0 {fill:none}
-    .fil1 {fill:black}
-    .fnt0 {font-weight:normal;font-size:846.67px;font-family:'Arial'}
-   ]]>
-  </style>
- </defs>
- <g id="レイヤ_x0020_1">
-  <metadata id="CorelCorpID_0Corel-Layer"/>
-  <rect class="fil0 str0" x="3000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="3000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="3000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="3000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="3000" y="15000" width="2000" height="2000"/>
-  <line class="fil0 str0" x1="-0" y1="0" x2="500" y2= "0" />
-  <text x="1058.24" y="7272.62"  class="fil1 fnt0">10</text>
-  <text x="1058.24" y="10264.61"  class="fil1 fnt0">20</text>
-  <text x="1084.7" y="13272.63"  class="fil1 fnt0">30</text>
-  <text x="1045.01" y="16259.4"  class="fil1 fnt0">40</text>
-  <text x="1568.56" y="4306.48"  class="fil1 fnt0">4</text>
-  <text x="1568.56" y="4306.48"  class="fil1 fnt0">4</text>
-  <g transform="matrix(2.64845E-14 -1 1 2.64845E-14 -14323.7 26102.3)">
-   <text x="20320" y="15240"  class="fil1 fnt0">Speed</text>
-  </g>
-  <text x="2934.81" y="990.69"  class="fil1 fnt0">Power</text>
-  <text x="3508.19" y="2299.3"  class="fil1 fnt0">40</text>
-  <rect class="fil0 str0" x="6000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="6000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="6000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="6000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="6000" y="15000" width="2000" height="2000"/>
-  <text x="6508.19" y="2299.3"  class="fil1 fnt0">50</text>
-  <rect class="fil0 str0" x="9000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="9000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="9000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="9000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="9000" y="15000" width="2000" height="2000"/>
-  <text x="9508.19" y="2299.3"  class="fil1 fnt0">60</text>
-  <rect class="fil0 str0" x="12000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="12000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="12000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="12000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="12000" y="15000" width="2000" height="2000"/>
-  <text x="12508.19" y="2299.3"  class="fil1 fnt0">70</text>
-  <rect class="fil0 str0" x="15000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="15000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="15000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="15000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="15000" y="15000" width="2000" height="2000"/>
-  <text x="15508.19" y="2299.3"  class="fil1 fnt0">80</text>
-  <text x="1519.73" y="18591.56"  class="fil1 fnt0">250PPI</text>
-  <rect class="fil0 str0" x="23000" y="3000" width="2000" height="2000"/>
-  <rect class="fil0 str1" x="23000" y="6000" width="2000" height="2000"/>
-  <rect class="fil0 str2" x="23000" y="9000" width="2000" height="2000"/>
-  <rect class="fil0 str3" x="23000" y="12000" width="2000" height="2000"/>
-  <rect class="fil0 str4" x="23000" y="15000" width="2000" height="2000"/>
-  <text x="20528.59" y="7316.82"  class="fil1 fnt0">300</text>
-  <text x="20528.59" y="10239.63"  class="fil1 fnt0">400</text>
-  <text x="20528.59" y="13202.97"  class="fil1 fnt0">500</text>
-  <text x="20528.59" y="16303.6"  class="fil1 fnt0">600</text>
-  <text x="20528.59" y="4350.68"  class="fil1 fnt0">250</text>
-  <g transform="matrix(2.64845E-14 -1 1 2.64845E-14 4760 24702.7)">
-   <text x="20320" y="15240"  class="fil1 fnt0">PPI</text>
-  </g>
-  <text x="21961.05" y="885.64"  class="fil1 fnt0">Power:</text>
-  <text x="21961.05" y="1831.51"  class="fil1 fnt0">Speed:</text>
- </g>
-</svg>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Creator: CorelDRAW 2018 (64-Bit) -->
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="406.4mm" height="304.8mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
+viewBox="0 0 40640 30480"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+  <font id="FontID0" horiz-adv-x="667" font-variant="normal" style="fill-rule:nonzero" font-style="normal" font-weight="400">
+	<font-face 
+		font-family="Arial">
+		<font-face-src>
+			<font-face-name name="Arial"/>
+		</font-face-src>
+	</font-face>
+   <missing-glyph><path d="M0 0z"/></missing-glyph>
+   <glyph unicode="0" horiz-adv-x="556" d="M41.9998 353.337c0,84.4957 8.66926,152.834 25.996,204.495 17.5039,51.8384 43.3345,91.665 77.669,119.503 34.1692,27.8267 77.3383,41.6691 129.33,41.6691 38.3384,0 71.9997,-7.67714 101.173,-23.1731 28.9959,-15.496 52.9959,-37.8306 71.669,-67.0037 18.6613,-28.9959 33.3306,-64.4997 43.9959,-106.322 10.8307,-41.8345 16.1692,-98.3382 16.1692,-169.169 0,-83.9997 -8.66926,-152.007 -25.8306,-203.668 -17.1732,-51.8384 -43.0038,-91.665 -77.3383,-119.669 -34.1692,-28.0038 -77.5036,-41.9998 -129.838,-41.9998 -68.8226,0 -123,24.496 -162.33,73.6651 -47.1612,59.9998 -70.6651,157.169 -70.6651,291.672zm89.9996 -0.342518c0,-117.826 13.6653,-196.157 41.1731,-234.826 27.496,-38.8345 61.4998,-58.1691 101.822,-58.1691 40.3345,0 74.3383,19.4999 101.834,58.3344 27.5078,38.9998 41.1731,117.165 41.1731,234.66 0,117.838 -13.6653,196.169 -41.1731,234.838 -27.3306,38.8345 -61.6651,58.1691 -102.826,58.1691 -40.3345,0 -72.6729,-17.1732 -96.8382,-51.3305 -30.1652,-43.4998 -45.1652,-124.169 -45.1652,-241.676z"/>
+   <glyph unicode="1" horiz-adv-x="556" d="M371.999 0l-88.0036 0 0 560.336c-21.1653,-20.3385 -48.9919,-40.4998 -83.3264,-60.673 -34.3345,-20.1613 -65.3383,-35.3266 -92.6689,-45.3305l0 85.0036c49.1691,22.996 92.1729,50.9998 128.999,83.9997 36.8384,32.8345 62.9998,64.6651 78.3304,95.6689l56.6691 0 0 -719.005z"/>
+   <glyph unicode="2" horiz-adv-x="556" d="M503.006 83.9997l0 -83.9997 -474.01 0c-0.661415,21.1653 2.83463,41.5038 10.3346,61.1691 12.1653,32.1613 31.6652,63.9919 58.4998,95.161 26.8345,31.1692 65.173,67.169 115.003,108 77.5036,63.8383 130.169,114.342 157.665,151.665 27.6731,37.3345 41.5038,72.6729 41.5038,106.004 0,34.8306 -12.5078,64.169 -37.4999,88.1689 -25.0038,23.8346 -57.673,35.8345 -97.8303,35.8345 -42.5077,0 -76.4997,-12.6732 -102,-37.9959 -25.5117,-25.1692 -38.3384,-60.1769 -38.6691,-105l-89.9996 8.99996c6.16533,67.3226 29.492,118.665 69.8265,153.992 40.4998,35.3384 94.665,53.0077 162.838,53.0077 68.669,0 123.165,-19.0039 163.169,-57.1769 40.1573,-38.1613 60.1651,-85.4997 60.1651,-141.826 0,-28.6652 -5.83462,-56.8344 -17.6692,-84.5075 -11.6692,-27.8267 -31.1692,-56.9998 -58.3344,-87.6611 -27.3306,-30.4959 -72.4958,-72.6611 -135.496,-126 -52.8305,-44.3384 -86.8343,-74.3383 -101.834,-90.165 -14.9999,-15.8385 -27.3306,-31.6652 -37.1691,-47.6691l351.506 0z"/>
+   <glyph unicode="3" horiz-adv-x="556" d="M41.9998 190.003l88.0036 12c10.1574,-50.0077 27.1652,-86.0075 51.3305,-108 24.1653,-22.0039 53.8344,-32.9999 88.9957,-32.9999 41.6691,0 76.8422,14.3267 105.165,42.992 28.3345,28.6652 42.5077,64.169 42.5077,106.5 0,40.3345 -13.1692,73.6769 -39.6731,100.004 -26.3267,26.1731 -59.8344,39.3306 -100.665,39.3306 -16.6653,0 -37.3345,-3.3307 -62.1612,-9.82673l9.82673 76.9958c5.83462,-0.661415 10.6653,-1.00393 14.1732,-1.00393 37.6652,0 71.3265,9.83854 101.492,29.3385 29.9999,19.4999 44.9998,49.4998 44.9998,89.9996 0,32.1731 -10.996,58.8305 -32.8227,79.9957 -21.8385,21.1771 -50.0077,31.677 -84.5075,31.677 -34.3345,0 -62.8344,-10.8425 -85.665,-32.5038 -22.8306,-21.6731 -37.4999,-54.1651 -43.9959,-97.4996l-88.0036 16.0039c10.8307,58.9958 35.1613,104.822 73.3344,137.326 37.9959,32.5038 85.4997,48.673 142.169,48.673 38.9998,0 74.9997,-8.33855 107.834,-25.1692 32.9999,-16.6653 58.1573,-39.5077 75.4958,-68.3383 17.5039,-28.9959 26.1613,-59.6691 26.1613,-92.1611 0,-30.8385 -8.32674,-58.8423 -24.8267,-84.165 -16.6653,-25.3345 -41.3384,-45.5077 -73.8304,-60.5076 42.3305,-9.66138 75.1651,-29.9999 98.6571,-60.8265 23.3385,-30.6731 35.0077,-69.3422 35.0077,-115.5 0,-62.5037 -22.8306,-115.677 -68.3383,-159.177 -45.6612,-43.4998 -103.334,-65.1612 -172.995,-65.1612 -62.8344,0 -115.003,18.8385 -156.507,56.3384 -41.6573,37.4999 -65.3265,85.9957 -71.1611,145.665z"/>
+   <glyph unicode="4" horiz-adv-x="556" d="M322.995 0l0 170.999 -310.995 0 0 80.9997 326.999 464.006 71.9997 0 0 -464.006 97.0036 0 0 -80.9997 -97.0036 0 0 -170.999 -88.0036 0zm0 251.999l0 322.168 -224.491 -322.168 224.491 0z"/>
+   <glyph unicode="5" horiz-adv-x="556" d="M41.9998 187.995l91.9957 7.00391c6.83856,-44.8345 22.6653,-78.4958 47.3384,-101.161 24.6613,-22.4999 54.8384,-33.8384 90.3304,-33.8384 42.8384,0 78.6729,16.0039 107.834,47.8345 28.9959,31.9959 43.4998,74.3383 43.4998,127.003 0,50.1612 -13.8307,89.6571 -41.6691,118.665 -27.8267,28.9959 -64.6651,43.4998 -110.492,43.4998 -28.4999,0 -53.9998,-6.50785 -76.6651,-19.4999 -22.4999,-13.0039 -40.1691,-29.8345 -53.173,-50.5037l-82.9957 12 68.9997 368.006 354.991 0 0 -83.9997 -284.822 0 -38.1731 -193.169c42.8384,30.1652 87.8382,45.1652 134.834,45.1652 62.3383,0 115.003,-21.6731 157.83,-65.0076 42.8384,-43.3227 64.3344,-98.9996 64.3344,-166.995 0,-64.8305 -18.8267,-120.838 -56.5037,-167.999 -45.8266,-58.0037 -108.496,-86.9997 -187.83,-86.9997 -64.9958,0 -118.003,18.3306 -159.165,54.8384 -41.1613,36.4959 -64.6651,84.8264 -70.4997,145.157z"/>
+   <glyph unicode="6" horiz-adv-x="556" d="M499.002 541.002l-86.9997 -7.00391c-7.83068,34.4999 -19.0039,59.6691 -33.5077,75.3304 -23.8227,25.1692 -53.3266,37.677 -88.3225,37.677 -28.1692,0 -53.173,-7.84249 -74.669,-23.5038 -27.6731,-20.1731 -49.3345,-49.4998 -65.5037,-88.1689 -16.0039,-38.8345 -24.3306,-94.0036 -25.0038,-165.495 21.1653,32.1613 47.173,56.1612 77.8343,71.6572 30.6731,15.6732 62.669,23.5038 96.3421,23.5038 58.6651,0 108.661,-21.6613 149.999,-64.8305 41.1613,-43.1691 61.8305,-98.8343 61.8305,-167.338 0,-44.9998 -9.67319,-86.669 -29.0078,-125.326 -19.4999,-38.6691 -45.9919,-68.173 -79.9957,-88.665 -33.8266,-20.5039 -72.3304,-30.8385 -115.334,-30.8385 -73.169,0 -133.003,26.9999 -179.326,80.8343 -46.1691,53.8344 -69.3422,142.665 -69.3422,266.338 0,138.33 25.6653,238.995 76.8422,301.83 44.6573,54.6612 104.834,82.0036 180.661,82.0036 56.3384,0 102.673,-15.8385 138.673,-47.3384 35.9999,-31.4999 57.6612,-74.9997 64.8305,-130.665zm-359.999 -308.999c0,-30.3306 6.49604,-59.3383 19.3346,-86.9997 12.8267,-27.6731 30.8267,-48.673 53.6573,-63.1651 23.0078,-14.5039 47.3384,-21.8385 73.169,-21.8385 37.4999,0 69.5076,14.9999 95.9996,44.9998 26.5038,29.9999 39.8384,70.6651 39.8384,122.173 0,49.4998 -13.1692,88.4997 -39.3306,117 -26.1731,28.4999 -59.5037,42.8266 -100.004,42.8266 -39.6731,0 -73.4997,-14.3267 -101.173,-42.8266 -27.6613,-28.4999 -41.492,-65.8344 -41.492,-112.169z"/>
+   <glyph unicode="7" horiz-adv-x="556" d="M46.9959 623.005l0 83.9997 463.002 0 0 -68.0076c-45.4959,-48.4959 -90.8264,-112.996 -135.496,-193.499 -44.8345,-80.5036 -79.3343,-163.334 -103.83,-248.503 -17.5039,-59.8344 -28.8424,-125.657 -33.6731,-196.995l-89.9996 0c1.00393,56.3384 12,124.5 33.1652,204.33 21.1653,80.0076 51.3305,157.003 90.8382,231.164 39.4959,74.1729 81.4957,136.677 126,187.511l-350.006 0z"/>
+   <glyph unicode="8" horiz-adv-x="556" d="M177.495 388.168c-36.4959,13.3346 -63.4958,32.5038 -81.165,57.1651 -17.4921,24.8385 -26.3267,54.4958 -26.3267,88.9957 0,52.1691 18.6613,95.8343 55.9958,131.338 37.4999,35.5038 87.165,53.3384 149.338,53.3384 62.3265,0 112.665,-18.1771 150.661,-54.5077 37.9959,-36.3306 56.9998,-80.4918 56.9998,-132.661 0,-33.1652 -8.66926,-62.173 -25.996,-86.669 -17.3385,-24.6731 -43.6652,-43.6652 -79.169,-56.9998 43.8305,-14.3385 76.9958,-37.3345 99.8382,-69.3304 22.8306,-31.8424 34.3345,-69.8383 34.3345,-114.165 0,-61.1691 -21.6731,-112.511 -64.8423,-154.169 -43.3345,-41.6691 -100.169,-62.5037 -170.669,-62.5037 -70.4997,0 -127.334,20.8346 -170.657,62.669 -43.1691,41.8345 -64.8423,93.9918 -64.8423,156.495 0,46.4998 11.8346,85.4997 35.5038,116.834 23.6692,31.4999 57.3305,52.8305 100.996,64.169zm-17.4921 149.338c0,-34.0038 10.8307,-61.6769 32.3266,-83.1729 21.6731,-21.496 50.0077,-32.3385 85.169,-32.3385 34.1692,0 61.8305,10.6771 83.3383,32.0077 21.496,21.3306 32.1613,47.5037 32.1613,78.4958 0,32.3385 -10.996,59.5037 -33.1652,81.5076 -22.0039,21.992 -49.8305,32.9999 -83.3383,32.9999 -33.8266,0 -61.6651,-10.8425 -83.6572,-32.3385 -21.8385,-21.496 -32.8345,-47.1612 -32.8345,-77.1611zm-29.0078 -331.003c0,-25.1692 5.83462,-49.3345 17.5039,-72.8383 11.6692,-23.3267 29.3385,-41.5038 52.9959,-54.3305 23.5038,-12.8385 48.8384,-19.3346 75.6729,-19.3346 42.3305,0 76.9958,13.4999 104.161,40.4998 27.1652,26.9999 40.6652,61.3344 40.6652,103.004 0,42.3305 -13.996,77.3265 -41.8227,105 -28.0038,27.6613 -63.3423,41.492 -105.838,41.492 -41.6691,0 -75.8383,-13.6653 -102.838,-40.9959 -26.9999,-27.3306 -40.4998,-61.4998 -40.4998,-102.496z"/>
+   <glyph unicode=":" horiz-adv-x="277" d="M89.9996 418.994l0 100.004 100.004 0 0 -100.004 -100.004 0zm0 -418.994l0 100.004 100.004 0 0 -100.004 -100.004 0z"/>
+   <glyph unicode="I" horiz-adv-x="277" d="M92.9996 0l0 716.005 94.9957 0 0 -716.005 -94.9957 0z"/>
+   <glyph unicode="P" horiz-adv-x="667" d="M76.9958 0l0 716.005 270.341 0c47.4919,0 83.8343,-2.33857 108.992,-6.83856 35.1731,-5.83462 64.6769,-16.996 88.4997,-33.3306 23.8346,-16.4999 43.0038,-39.5077 57.5076,-68.9997 14.4921,-29.5038 21.6613,-62.0076 21.6613,-97.4996 0,-60.673 -19.3346,-112.169 -58.1691,-154.169 -38.6573,-42.1652 -108.826,-63.1651 -210.165,-63.1651l-183.661 0 0 -292.003 -95.0075 0zm95.0075 376.002l184.995 0c61.3344,0 104.669,11.3267 130.499,34.1692 25.6653,22.8306 38.5038,54.8266 38.5038,96.3303 0,29.8345 -7.49997,55.4998 -22.6653,76.8304 -15.1653,21.3306 -35.1731,35.5038 -59.8344,42.3305 -16.0039,4.16927 -45.5077,6.34249 -88.3343,6.34249l-183.165 0 0 -256.003z"/>
+   <glyph unicode="S" horiz-adv-x="667" d="M46.0038 229.995l88.9957 8.00784c4.16927,-35.9999 13.996,-65.5037 29.5038,-88.665 15.496,-23.0078 39.4959,-41.6691 71.9997,-56.0077 32.492,-14.1614 69.1651,-21.3306 109.83,-21.3306 36.1652,0 67.9958,5.33856 95.6689,16.1692 27.6613,10.8307 48.1652,25.6653 61.6651,44.492 13.4999,18.8385 20.3385,39.3424 20.3385,61.3344 0,22.3346 -6.50785,41.8345 -19.4999,58.6769 -13.0039,16.6653 -34.5117,30.6613 -64.5116,42.1652 -19.1574,7.33462 -61.6651,18.9921 -127.499,34.8306 -65.8226,15.8267 -111.826,30.6613 -138.165,44.6691 -34.1692,17.9999 -59.4919,40.3345 -76.169,66.8265 -16.8306,26.5038 -25.1574,56.3384 -25.1574,89.1729 0,36.3306 10.3346,70.169 30.8267,101.492 20.5039,31.4999 50.3384,55.3344 89.8343,71.669 39.3306,16.3346 83.1729,24.5078 131.338,24.5078 52.9959,0 99.8264,-8.5039 140.326,-25.6771 40.4998,-16.996 71.669,-42.1652 93.5075,-75.4958 21.8267,-33.1652 33.4959,-70.8304 35.1613,-112.83l-90.9918 -7.00391c-4.8425,45.1652 -21.3424,79.3343 -49.5116,102.33 -28.1574,23.1731 -69.8265,34.677 -124.83,34.677 -57.3305,0 -99.165,-10.5 -125.326,-31.4999 -26.1731,-20.9999 -39.3424,-46.3345 -39.3424,-76.0036 0,-25.6653 9.34248,-46.8305 27.8385,-63.5076 18.3306,-16.4999 65.8344,-33.6613 142.83,-50.9998 77.0076,-17.4921 129.838,-32.6574 158.503,-45.6612 41.5038,-19.1692 72.3304,-43.3345 92.1611,-72.8383 19.8424,-29.3267 29.6692,-63.1651 29.6692,-101.492 0,-38.1731 -10.8307,-73.8422 -32.6692,-107.504 -21.8267,-33.6613 -52.9959,-59.8344 -93.8264,-78.4958 -40.8305,-18.6732 -86.669,-28.0038 -137.834,-28.0038 -64.6651,0 -118.83,9.49603 -162.495,28.3345 -43.8423,18.8267 -78.0115,47.1612 -102.838,85.169 -25.0038,37.8306 -37.9959,80.669 -39.3306,128.492z"/>
+   <glyph unicode="d" horiz-adv-x="556" d="M400.994 0l0 65.8344c-32.8227,-51.1652 -81.165,-76.8304 -144.992,-76.8304 -41.3384,0 -79.3343,11.3267 -113.834,34.1573 -34.6652,22.6771 -61.4998,54.5077 -80.669,95.1729 -19.0039,40.8305 -28.4999,87.6611 -28.4999,140.669 0,51.6612 8.66926,98.4917 25.8306,140.669 17.3385,41.9998 43.1691,74.3265 77.669,96.661 34.3345,22.4999 73.0036,33.6731 115.5,33.6731 31.3345,0 59.173,-6.6732 83.5036,-19.8424 24.3306,-13.1692 44.1612,-30.4959 59.4919,-51.6612l0 257.503 88.0036 0 0 -716.005 -82.0036 0zm-277.995 258.838c0,-66.3423 13.996,-115.842 42.1652,-148.677 28.0038,-32.8227 61.1691,-49.1573 99.3303,-49.1573 38.5038,0 71.1729,15.6614 98.1728,46.9959 26.8345,31.3345 40.3345,79.169 40.3345,143.503 0,70.6651 -13.6653,122.669 -41.1731,155.834 -27.3306,33.1652 -61.1572,49.6652 -101.326,49.6652 -39.1652,0 -71.8344,-15.8385 -98.1728,-47.6691 -26.1613,-31.8306 -39.3306,-82.0036 -39.3306,-150.495z"/>
+   <glyph unicode="e" horiz-adv-x="556" d="M419.998 167.999l91.0036 -12c-14.1732,-52.8305 -40.6652,-94.0036 -79.3343,-123.165 -38.6691,-29.1731 -88.0036,-43.8305 -148.169,-43.8305 -75.6611,0 -135.661,23.3267 -179.999,69.9919 -44.3384,46.4998 -66.4958,112.003 -66.4958,196.169 0,86.9997 22.3346,154.665 67.1572,202.665 44.8345,48.177 103.004,72.1769 174.507,72.1769 69.1651,0 125.669,-23.5038 169.499,-70.6769 43.8305,-47.1612 65.8344,-113.492 65.8344,-198.991 0,-5.17321 -0.165354,-13.0039 -0.496061,-23.3385l-386.502 0c3.3307,-56.8344 19.3346,-100.5 48.3305,-130.665 28.8306,-30.1652 64.9958,-45.3305 108.165,-45.3305 32.1731,0 59.6691,8.49209 82.3343,25.4999 22.8306,16.996 40.8305,44.1612 54.1651,81.4957zm-287.999 140.999l289.003 0c-3.83857,43.6652 -14.9999,76.3343 -33.1652,98.1728 -27.8385,33.8266 -64.169,50.8226 -108.673,50.8226 -40.3345,0 -74.1611,-13.4999 -101.669,-40.4998 -27.3306,-26.9999 -42.4959,-63.1651 -45.4959,-108.496z"/>
+   <glyph unicode="o" horiz-adv-x="556" d="M32.9999 259.499c0,95.8343 26.6692,166.83 80.1611,212.999 44.5038,38.3384 99.0114,57.5076 163.169,57.5076 71.1729,0 129.507,-23.3385 174.838,-70.0037 45.1652,-46.4998 67.8304,-111 67.8304,-193.169 0,-66.6612 -9.99209,-118.996 -29.9999,-157.169 -19.996,-38.1613 -49.1691,-67.8304 -87.4957,-88.9957 -38.1731,-21.1653 -80.0076,-31.6652 -125.173,-31.6652 -72.6611,0 -131.326,23.1613 -176.161,69.6611 -44.8345,46.4998 -67.169,113.503 -67.169,200.834zm89.9996 -0.165354c0,-66.3304 14.5039,-115.996 43.4998,-148.83 29.1731,-32.9999 65.669,-49.4998 109.83,-49.4998 43.6652,0 80.0076,16.4999 109.169,49.6652 28.9959,33.1652 43.4998,83.669 43.4998,151.499 0,64.0037 -14.6692,112.5 -43.8305,145.334 -29.1731,32.9999 -65.5037,49.4998 -108.838,49.4998 -44.1612,0 -80.6572,-16.3346 -109.83,-49.1691 -28.9959,-32.8345 -43.4998,-82.3343 -43.4998,-148.499z"/>
+   <glyph unicode="p" horiz-adv-x="556" d="M65.9997 -199.003l0 718.001 79.9957 0 0 -68.1611c18.8385,26.3267 40.1691,46.1573 64.0037,59.3265 23.6692,13.1692 52.4998,19.8424 86.3382,19.8424 44.3266,0 83.3264,-11.3385 117.165,-34.1692 33.8266,-22.6653 59.3265,-54.8384 76.6651,-96.165 17.1613,-41.5038 25.8306,-86.8343 25.8306,-136.334 0,-52.8423 -9.49603,-100.5 -28.4999,-143.007 -19.0039,-42.3305 -46.6652,-74.8344 -82.9957,-97.4996 -36.1652,-22.4999 -74.3383,-33.8266 -114.33,-33.8266 -29.3385,0 -55.6769,6.16533 -78.8383,18.496 -23.3385,12.3307 -42.3305,27.8385 -57.3305,46.6652l0 -253.168 -88.0036 0zm79.9957 455.337c0,-66.4958 13.4999,-115.665 40.3345,-147.495 26.9999,-31.8424 59.6691,-47.8345 98.0075,-47.8345 38.9998,0 72.3304,16.4999 99.9917,49.4998 27.8385,32.9999 41.6691,83.9997 41.6691,153.165 0,65.9997 -13.4999,115.334 -40.6652,148.169 -27.1652,32.8345 -59.5037,49.1573 -97.1689,49.1573 -37.3345,0 -70.3344,-17.4921 -98.9996,-52.3226 -28.8306,-35.0077 -43.1691,-85.6768 -43.1691,-152.338z"/>
+   <glyph unicode="r" horiz-adv-x="332" d="M64.9958 0l0 518.998 79.0036 0 0 -79.4997c20.1613,36.8384 38.8345,61.1691 55.9958,72.8383 17.1732,11.8346 35.9999,17.6692 56.5037,17.6692 29.6692,0 59.8344,-9.34248 90.4957,-27.8385l-31.3227 -81.165c-21.3424,12 -42.6731,17.9999 -64.0037,17.9999 -19.1692,0 -36.3306,-5.83462 -51.4959,-17.3385 -15.1771,-11.4921 -26.0078,-27.496 -32.5038,-47.9998 -9.83854,-31.1692 -14.6692,-65.3265 -14.6692,-102.496l0 -271.168 -88.0036 0z"/>
+   <glyph unicode="w" horiz-adv-x="722" d="M159.33 0l-156.826 518.998 91.9957 0 82.8304 -299.503 29.3385 -111.496c1.16929,5.50392 10.1693,41.1613 26.8345,106.996l82.169 304.003 90.165 0 78.165 -301.333 25.4999 -98.8343 29.8345 100.169 89.6689 299.999 85.4997 0 -162.838 -518.998 -91.0036 0 -82.3343 310.499 -20.8346 88.8304 -105 -399.329 -93.165 0z"/>
+  </font>
+  <style type="text/css">
+   <![CDATA[
+    @font-face { font-family:"Arial";font-variant:normal;font-style:normal;font-weight:normal;src:url("#FontID0") format(svg)}
+    .str4 {stroke:#FF6600;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .str2 {stroke:fuchsia;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .str0 {stroke:red;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .str3 {stroke:aqua;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .str1 {stroke:blue;stroke-width:7.62;stroke-miterlimit:22.9256}
+    .fil0 {fill:none}
+    .fil1 {fill:black}
+    .fnt0 {font-weight:normal;font-size:846.67px;font-family:'Arial'}
+   ]]>
+  </style>
+ </defs>
+ <g id="レイヤ_x0020_1">
+  <metadata id="CorelCorpID_0Corel-Layer"/>
+  <rect class="fil0 str0" x="3000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="3000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="3000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="3000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="3000" y="15000" width="2000" height="2000"/>
+  <line class="fil0 str0" x1="-0" y1="0" x2="500" y2= "0" />
+  <text x="1058.24" y="7272.62"  class="fil1 fnt0">10</text>
+  <text x="1058.24" y="10264.61"  class="fil1 fnt0">20</text>
+  <text x="1084.7" y="13272.63"  class="fil1 fnt0">30</text>
+  <text x="1045.01" y="16259.4"  class="fil1 fnt0">40</text>
+  <text x="1568.56" y="4306.48"  class="fil1 fnt0">4</text>
+  <text x="1568.56" y="4306.48"  class="fil1 fnt0">4</text>
+  <g transform="matrix(2.64845E-14 -1 1 2.64845E-14 -14323.7 26102.3)">
+   <text x="20320" y="15240"  class="fil1 fnt0">Speed</text>
+  </g>
+  <text x="2934.81" y="990.69"  class="fil1 fnt0">Power</text>
+  <text x="3508.19" y="2299.3"  class="fil1 fnt0">40</text>
+  <rect class="fil0 str0" x="6000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="6000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="6000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="6000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="6000" y="15000" width="2000" height="2000"/>
+  <text x="6508.19" y="2299.3"  class="fil1 fnt0">50</text>
+  <rect class="fil0 str0" x="9000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="9000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="9000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="9000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="9000" y="15000" width="2000" height="2000"/>
+  <text x="9508.19" y="2299.3"  class="fil1 fnt0">60</text>
+  <rect class="fil0 str0" x="12000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="12000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="12000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="12000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="12000" y="15000" width="2000" height="2000"/>
+  <text x="12508.19" y="2299.3"  class="fil1 fnt0">70</text>
+  <rect class="fil0 str0" x="15000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="15000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="15000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="15000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="15000" y="15000" width="2000" height="2000"/>
+  <text x="15508.19" y="2299.3"  class="fil1 fnt0">80</text>
+  <text x="1519.73" y="18591.56"  class="fil1 fnt0">250PPI</text>
+  <rect class="fil0 str0" x="23000" y="3000" width="2000" height="2000"/>
+  <rect class="fil0 str1" x="23000" y="6000" width="2000" height="2000"/>
+  <rect class="fil0 str2" x="23000" y="9000" width="2000" height="2000"/>
+  <rect class="fil0 str3" x="23000" y="12000" width="2000" height="2000"/>
+  <rect class="fil0 str4" x="23000" y="15000" width="2000" height="2000"/>
+  <text x="20528.59" y="7316.82"  class="fil1 fnt0">300</text>
+  <text x="20528.59" y="10239.63"  class="fil1 fnt0">400</text>
+  <text x="20528.59" y="13202.97"  class="fil1 fnt0">500</text>
+  <text x="20528.59" y="16303.6"  class="fil1 fnt0">600</text>
+  <text x="20528.59" y="4350.68"  class="fil1 fnt0">250</text>
+  <g transform="matrix(2.64845E-14 -1 1 2.64845E-14 4760 24702.7)">
+   <text x="20320" y="15240"  class="fil1 fnt0">PPI</text>
+  </g>
+  <text x="21961.05" y="885.64"  class="fil1 fnt0">Power:</text>
+  <text x="21961.05" y="1831.51"  class="fil1 fnt0">Speed:</text>
+ </g>
+</svg>