diff --git a/files.html b/files.html index c14da857ce260855dcb47d22a08066c5e7e98a2e..e392e389c0e2ced3922a352e8f01c63bbb20f9c2 100644 --- a/files.html +++ b/files.html @@ -10,7 +10,6 @@ </script> <i> .git</i><br> <a href='./.gitignore'>.gitignore</a><br> - <a href='./README.md'>README.md</a><br> <a href='./files.html'>files.html</a><br> <a href='./index.html'>index.html</a><br> <i> js</i><br> @@ -19,7 +18,6 @@ <a href='./js/files.js'>files.js</a><br> <a href='./js/mods.js'>mods.js</a><br> <a href='./js/modules.js'>modules.js</a><br> -<i> node_modules</i><br> <a href='./js/printserver.js'>printserver.js</a><br> <a href='./js/programs.js'>programs.js</a><br> <a href='./js/serialserver.js'>serialserver.js</a><br> @@ -27,6 +25,7 @@ <a href='./js/three.js/three.min.js'>three.min.js</a><br> <a href='./js/udpserver.js'>udpserver.js</a><br> <a href='./make'>make</a><br> + <a href='./make.bat'>make.bat</a><br> <i> modules</i><br> <i> character</i><br> <a href='./modules/character/convert'>convert</a><br> @@ -34,8 +33,11 @@ <a href='./modules/character/parse'>parse</a><br> <a href='./modules/character/variable'>variable</a><br> <i> connect</i><br> +<i> stl</i><br> + <a href='./modules/connect/stl/STL'>STL</a><br> <i> svg</i><br> <a href='./modules/connect/svg/ExtractFaces'>ExtractFaces</a><br> + <a href='./modules/connect/svg/Honeycomb'>Honeycomb</a><br> <a href='./modules/connect/svg/PCB'>PCB</a><br> <a href='./modules/connect/svg/SelectFace'>SelectFace</a><br> <i> convert</i><br> @@ -154,6 +156,8 @@ <a href='./modules/path/formats/g-code'>g-code</a><br> <a href='./modules/path/formats/svg'>svg</a><br> <i> machines</i><br> +<i> laser cutter</i><br> + <a href='./modules/path/machines/laser%20cutter/Epilog'>Epilog</a><br> <i> Roland</i><br> <i> milling</i><br> <a href='./modules/path/machines/Roland/milling/MDX-20'>MDX-20</a><br> @@ -161,8 +165,6 @@ <i> vinyl cutter</i><br> <a href='./modules/path/machines/Roland/vinyl%20cutter/GX-24'>GX-24</a><br> <a href='./modules/path/machines/ShopBot'>ShopBot</a><br> -<i> laser cutter</i><br> - <a href='./modules/path/machines/laser%20cutter/Epilog'>Epilog</a><br> <a href='./modules/path/view'>view</a><br> <i> processes</i><br> <i> cut</i><br> @@ -215,8 +217,11 @@ <a href='./programs/machines/Epilog/cut%20svg%20connect'>cut svg connect</a><br> <i> G-code</i><br> <a href='./programs/machines/G-code/mill%202.5D%20stl'>mill 2.5D stl</a><br> + <a href='./programs/machines/G-code/mill%202.5D%20stl%20connect'>mill 2.5D stl connect</a><br> <a href='./programs/machines/G-code/mill%202D%20png'>mill 2D png</a><br> <a href='./programs/machines/G-code/mill%202D%20svg'>mill 2D svg</a><br> +<i> RNDMC</i><br> + <a href='./programs/machines/RNDMC/honeycomb%20connect'>honeycomb connect</a><br> <i> Roland</i><br> <i> mill</i><br> <i> MDX-20</i><br> @@ -224,10 +229,11 @@ <a href='./programs/machines/Roland/mill/MDX-20/PCB%20png'>PCB png</a><br> <a href='./programs/machines/Roland/mill/MDX-20/PCB%20svg'>PCB svg</a><br> <i> SRM-20</i><br> + <a href='./programs/machines/Roland/mill/SRM-20/mill%202.5D%20stl'>mill 2.5D stl</a><br> + <a href='./programs/machines/Roland/mill/SRM-20/mill%202.5D%20stl%20connect'>mill 2.5D stl connect</a><br> <a href='./programs/machines/Roland/mill/SRM-20/PCB%20png'>PCB png</a><br> <a href='./programs/machines/Roland/mill/SRM-20/PCB%20svg'>PCB svg</a><br> <a href='./programs/machines/Roland/mill/SRM-20/PCB%20svg%20connect'>PCB svg connect</a><br> - <a href='./programs/machines/Roland/mill/SRM-20/mill%202.5D%20stl'>mill 2.5D stl</a><br> <i> vinyl cutter</i><br> <i> GX-24</i><br> <a href='./programs/machines/Roland/vinyl%20cutter/GX-24/cut%20png'>cut png</a><br> @@ -235,6 +241,7 @@ <a href='./programs/machines/Roland/vinyl%20cutter/GX-24/cut%20svg%20connect'>cut svg connect</a><br> <i> ShopBot</i><br> <a href='./programs/machines/ShopBot/mill%202.5D%20stl'>mill 2.5D stl</a><br> + <a href='./programs/machines/ShopBot/mill%202.5D%20stl%20connect'>mill 2.5D stl connect</a><br> <a href='./programs/machines/ShopBot/mill%202D%20png'>mill 2D png</a><br> <a href='./programs/machines/ShopBot/mill%202D%20png%20PCB'>mill 2D png PCB</a><br> <a href='./programs/machines/ShopBot/mill%202D%20stl'>mill 2D stl</a><br> @@ -253,25 +260,25 @@ <a href='./programs/processes/mill/raster/2D'>2D</a><br> <i> variable</i><br> <a href='./programs/variable/text%20variables'>text variables</a><br> + <a href='./README.md'>README.md</a><br> <i> scripts</i><br> <a href='./scripts/start%20mods%20server'>start mods server</a><br> <a href='./scripts/stop%20mods%20server'>stop mods server</a><br> <i> test</i><br> <a href='./test/3DBenchy.stl'>3DBenchy.stl</a><br> + <a href='./test/alien.png'>alien.png</a><br> <a href='./test/ATP.8E5.board.png'>ATP.8E5.board.png</a><br> <a href='./test/ATP.8E5.traces.png'>ATP.8E5.traces.png</a><br> + <a href='./test/cert.pem'>cert.pem</a><br> <a href='./test/David.png'>David.png</a><br> <a href='./test/David.small.png'>David.small.png</a><br> - <a href='./test/ML.jpg'>ML.jpg</a><br> - <a href='./test/ML.png'>ML.png</a><br> - <a href='./test/Suzanne.stl'>Suzanne.stl</a><br> - <a href='./test/alien.png'>alien.png</a><br> - <a href='./test/cert.pem'>cert.pem</a><br> <a href='./test/gradients.svg'>gradients.svg</a><br> <a href='./test/hmc-3dp3-01.stl'>hmc-3dp3-01.stl</a><br> <a href='./test/hsv.png'>hsv.png</a><br> <a href='./test/key.pem'>key.pem</a><br> <a href='./test/lines.png'>lines.png</a><br> + <a href='./test/ML.jpg'>ML.jpg</a><br> + <a href='./test/ML.png'>ML.png</a><br> <a href='./test/rhino.black.png'>rhino.black.png</a><br> <a href='./test/rhino.black.svg'>rhino.black.svg</a><br> <a href='./test/rhino.png'>rhino.png</a><br> @@ -286,6 +293,7 @@ <a href='./test/shapes.svg'>shapes.svg</a><br> <a href='./test/sunset.jpg'>sunset.jpg</a><br> <a href='./test/sunset.png'>sunset.png</a><br> + <a href='./test/Suzanne.stl'>Suzanne.stl</a><br> <a href='./test/tall.png'>tall.png</a><br> <a href='./test/test.png'>test.png</a><br> <a href='./test/transparent.png'>transparent.png</a><br> diff --git a/modules/connect/stl/STL b/modules/connect/stl/STL new file mode 100644 index 0000000000000000000000000000000000000000..682be0071acdf1614de966bedfc25a9a8c82e7a2 --- /dev/null +++ b/modules/connect/stl/STL @@ -0,0 +1,461 @@ +// +// STL module extracts stl from Tools/FabLab Connect command of SolidWorks products +// +// Shawn Liu @ Dassault Systemes SolidWorks Corporation +// (c) Massachusetts Institute of Technology 2019 +// +// 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(){ +// +// module globals +// +var mod = {} +// +// name +// +var name = 'STL connect' +// +// initialization +// +var init = function () { + mod.address = getParameterByName('swIP') || '127.0.0.1' + mod.port = getParameterByName('swPort') || '80' + mod.socket = 0 + mod.sag.value = '0.1' + mod.angle.value = '45' + socket_open() + } +// +// inputs +// +var inputs = { + } +// +// outputs +// +var outputs = { + mesh:{type:'STL', + event:function(buffer){ + mods.output(mod,'mesh',buffer)}} + } +// +// interface +// +var interface = function(div){ + mod.div = div + // + // canvas + // + var canvas = document.createElement('canvas') + canvas.width = mods.ui.canvas + canvas.height = mods.ui.canvas + canvas.style.backgroundColor = 'rgb(255,255,255)' + div.appendChild(canvas) + mod.canvas = canvas + div.appendChild(document.createElement('br')) + + div.appendChild(document.createTextNode('server:')) + div.appendChild(document.createElement('br')) + div.appendChild(document.createTextNode('address: ' + getParameterByName('swIP'))) + div.appendChild(document.createElement('br')) + div.appendChild(document.createTextNode('\u00a0\u00a0\u00a0\u00a0\u00a0port: ' + getParameterByName('swPort'))) + div.appendChild(document.createElement('br')) + div.appendChild(document.createTextNode('\u00a0\u00a0status: ')) + input = document.createElement('input') + input.type = 'text' + input.size = 12 + div.appendChild(input) + mod.status = input + div.appendChild(document.createElement('br')) + var btn = document.createElement('button') + btn.style.margin = 1 + btn.appendChild(document.createTextNode('open')) + btn.addEventListener('click', function () { + socket_open() + }) + div.appendChild(btn) + var btn = document.createElement('button') + btn.style.margin = 1 + btn.appendChild(document.createTextNode('close')) + btn.addEventListener('click', function () { + socket_close() + }) + div.appendChild(btn) + div.appendChild(document.createElement('br')) + div.appendChild(document.createTextNode('sag: ')) + input = document.createElement('input') + input.type = 'text' + input.size = 10 + div.appendChild(input) + mod.sag = input + div.appendChild(document.createTextNode('(mm)')) + div.appendChild(document.createElement('br')) + div.appendChild(document.createTextNode('angle: ')) + input = document.createElement('input') + input.type = 'text' + input.size = 10 + div.appendChild(input) + mod.angle = input + div.appendChild(document.createTextNode('(degree)')) + div.appendChild(document.createElement('br')) + var btn = document.createElement('button') + btn.style.margin = 1 + btn.appendChild(document.createTextNode('Extract STL')) + btn.addEventListener('click', function () { + extract_STL() + }) + div.appendChild(btn) + + // + // info + // + var info = document.createElement('div') + info.setAttribute('id', div.id + 'info') + var text = document.createTextNode('name: ') + info.appendChild(text) + mod.name = text + info.appendChild(document.createElement('br')) + var text = document.createTextNode('size: ') + info.appendChild(text) + mod.sizen = text + info.appendChild(document.createElement('br')) + var text = document.createTextNode('triangles: ') + info.appendChild(text) + mod.trianglesn = text + info.appendChild(document.createElement('br')) + var text = document.createTextNode('dx: ') + info.appendChild(text) + mod.dxn = text + info.appendChild(document.createElement('br')) + var text = document.createTextNode('dy: ') + info.appendChild(text) + mod.dyn = text + info.appendChild(document.createElement('br')) + var text = document.createTextNode('dz: ') + info.appendChild(text) + mod.dzn = text + div.appendChild(info) + } +// +// local functions +// + + // + // local functions + // + +function getParameterByName(name, url) { + if (!url) url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, " ")); +} + +function socket_open() { + var url = "ws://" + mod.address + ':' + mod.port + mod.socket = new WebSocket(url) + mod.socket.onopen = function (event) { + mod.status.value = "opened" + var connect = {} + connect.modCmd = 'connect' + connect.owner = getParameterByName('swOwner') + connect.id = getParameterByName('swID') + socket_send(JSON.stringify(connect)) + } + mod.socket.onerror = function (event) { + mod.status.value = "can not open" + } + mod.socket.onmessage = function (event) { + mod.status.value = "receive" + var swData = JSON.parse(event.data) + if (swData.swType === "AutoExtractSTL") { + var partName = swData.data.partName + if (partName.length > 25) + partName = partName.slice(0, 22) +'...' + mod.name.nodeValue = "name: " + partName + mod.str = swData.data.stl + stl_load_handler() + } + } + mod.socket.onclose = function (event) { + mod.status.value = "connection closed" + } +} +function socket_close() { + mod.socket.close() + mod.status.value = "closed" + mod.socket = 0 +} +function socket_send(msg) { + if (mod.socket != 0) { + mod.status.value = "send" + mod.socket.send(msg) + } + else { + mod.status.value = "can't send, not open" + } +} +function extract_STL() { + var modcmd = new Object; + modcmd.modCmd = "AutoExtractSTL"; + modcmd.sag = Number(mod.sag.value); // mm + modcmd.angle = Number(mod.angle.value); //degree + socket_send(JSON.stringify(modcmd)) +} + +function base64ToArrayBuffer(base64) { + var binary_string = window.atob(base64); + var len = binary_string.length; + var bytes = new Uint8Array(len); + for (var i = 0; i < len; i++) { + bytes[i] = binary_string.charCodeAt(i); + } + return bytes.buffer; +} + +// +// load handler +// +function stl_load_handler() { + // + // check for binary STL + // + var arraybuf = base64ToArrayBuffer(mod.str) + var endian = true + var view = new DataView(arraybuf) + var triangles = view.getUint32(80, endian) + var size = 80+4+triangles*(4*12+2) + if (size != view.byteLength) { + mod.sizen.nodeValue = 'error: not binary STL' + mod.trianglesn.nodeValue = '' + mod.dxn.nodeValue = '' + mod.dyn.nodeValue = '' + mod.dzn.nodeValue = '' + return + } + mod.sizen.nodeValue = 'size: '+size + mod.trianglesn.nodeValue = 'triangles: '+triangles + // + // find limits and draw + // + var blob = new Blob(['('+draw_limits_worker.toString()+'())']) + var url = window.URL.createObjectURL(blob) + var webworker = new Worker(url) + webworker.addEventListener('message',function(evt) { + // + // worker response + // + window.URL.revokeObjectURL(url) + // + // size + // + mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3) + mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3) + mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3) + // + // image + // + var image = evt.data.image + var height = mod.canvas.height + var width = mod.canvas.width + var buffer = new Uint8ClampedArray(evt.data.image) + var imgdata = new ImageData(buffer,width,height) + var ctx = mod.canvas.getContext("2d") + ctx.putImageData(imgdata,0,0) + // + // output + // + outputs.mesh.event(evt.data.mesh) + }) + var ctx = mod.canvas.getContext("2d") + ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height) + var img = ctx.getImageData(0, 0, mod.canvas.width, mod.canvas.height) + // + // call worker + // + webworker.postMessage({ + height:mod.canvas.height,width:mod.canvas.width, + image: img.data.buffer, mesh: arraybuf}, + [img.data.buffer, arraybuf]) + } +function draw_limits_worker() { + self.addEventListener('message',function(evt) { + // + // function to draw line + // + function line(x0,y0,x1,y1) { + var ix0 = Math.floor(xo+xw*(x0-xmin)/dx) + var iy0 = Math.floor(yo+yh*(ymax-y0)/dy) + var ix1 = Math.floor(xo+xw*(x1-xmin)/dx) + var iy1 = Math.floor(yo+yh*(ymax-y1)/dy) + var row,col + var idx = ix1-ix0 + var idy = iy1-iy0 + if (Math.abs(idy) > Math.abs(idx)) { + (idy > 0) ? + (row0=iy0,col0=ix0,row1=iy1,col1=ix1): + (row0=iy1,col0=ix1,row1=iy0,col1=ix0) + for (row = row0; row <= row1; ++row) { + col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0)) + image[row*width*4+col*4+0] = 0 + image[row*width*4+col*4+1] = 0 + image[row*width*4+col*4+2] = 0 + image[row*width*4+col*4+3] = 255 + } + } + else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) { + (idx > 0) ? + (row0=iy0,col0=ix0,row1=iy1,col1=ix1): + (row0=iy1,col0=ix1,row1=iy0,col1=ix0) + for (col = col0; col <= col1; ++col) { + row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0)) + image[row*width*4+col*4+0] = 0 + image[row*width*4+col*4+1] = 0 + image[row*width*4+col*4+2] = 0 + image[row*width*4+col*4+3] = 255 + } + } + else { + row = iy0 + col = ix0 + image[row*width*4+col*4+0] = 0 + image[row*width*4+col*4+1] = 0 + image[row*width*4+col*4+2] = 0 + image[row*width*4+col*4+3] = 255 + } + } + // + // get variables + // + var height = evt.data.height + var width = evt.data.width + var endian = true + var image = new Uint8ClampedArray(evt.data.image) + var view = new DataView(evt.data.mesh) + var triangles = view.getUint32(80,endian) + // + // find limits + // + var offset = 80+4 + var x0,x1,x2,y0,y1,y2,z0,z1,z2 + var xmin = Number.MAX_VALUE + var xmax = -Number.MAX_VALUE + var ymin = Number.MAX_VALUE + var ymax = -Number.MAX_VALUE + var zmin = Number.MAX_VALUE + var zmax = -Number.MAX_VALUE + for (var t = 0; t < triangles; ++t) { + offset += 3*4 + x0 = view.getFloat32(offset,endian) + offset += 4 + if (x0 > xmax) xmax = x0 + if (x0 < xmin) xmin = x0 + y0 = view.getFloat32(offset,endian) + offset += 4 + if (y0 > ymax) ymax = y0 + if (y0 < ymin) ymin = y0 + z0 = view.getFloat32(offset,endian) + offset += 4 + if (z0 > zmax) zmax = z0 + if (z0 < zmin) zmin = z0 + x1 = view.getFloat32(offset,endian) + offset += 4 + if (x1 > xmax) xmax = x1 + if (x1 < xmin) xmin = x1 + y1 = view.getFloat32(offset,endian) + offset += 4 + if (y1 > ymax) ymax = y1 + if (y1 < ymin) ymin = y1 + z1 = view.getFloat32(offset,endian) + offset += 4 + if (z1 > zmax) zmax = z1 + if (z1 < zmin) zmin = z1 + x2 = view.getFloat32(offset,endian) + offset += 4 + if (x2 > xmax) xmax = x2 + if (x2 < xmin) xmin = x2 + y2 = view.getFloat32(offset,endian) + offset += 4 + if (y2 > ymax) ymax = y2 + if (y2 < ymin) ymin = y2 + z2 = view.getFloat32(offset,endian) + offset += 4 + if (z2 > zmax) zmax = z2 + if (z2 < zmin) zmin = z2 + offset += 2 + } + var dx = xmax-xmin + var dy = ymax-ymin + var dz = zmax-zmin + // + // draw mesh + // + if (dx > dy) { + var xo = 0 + var yo = height*.5*(1-dy/dx) + var xw = width-1 + var yh = (width-1)*dy/dx + } + else { + var xo = width*.5*(1-dx/dy) + var yo = 0 + var xw = (height-1)*dx/dy + var yh = height-1 + } + offset = 80+4 + for (var t = 0; t < triangles; ++t) { + offset += 3*4 + x0 = view.getFloat32(offset,endian) + offset += 4 + y0 = view.getFloat32(offset,endian) + offset += 4 + z0 = view.getFloat32(offset,endian) + offset += 4 + x1 = view.getFloat32(offset,endian) + offset += 4 + y1 = view.getFloat32(offset,endian) + offset += 4 + z1 = view.getFloat32(offset,endian) + offset += 4 + x2 = view.getFloat32(offset,endian) + offset += 4 + y2 = view.getFloat32(offset,endian) + offset += 4 + z2 = view.getFloat32(offset,endian) + offset += 4 + offset += 2 + line(x0,y0,x1,y1) + line(x1,y1,x2,y2) + line(x2,y2,x0,y0) + } + // + // return results and close + // + self.postMessage({ + dx:dx,dy:dy,dz:dz, + image:evt.data.image,mesh:evt.data.mesh},[evt.data.image,evt.data.mesh]) + self.close() + }) + } +// +// return values +// +return ({ + mod:mod, + name:name, + init:init, + inputs:inputs, + outputs:outputs, + interface:interface + }) +}()) diff --git a/modules/connect/svg/Honeycomb b/modules/connect/svg/Honeycomb new file mode 100644 index 0000000000000000000000000000000000000000..53e52f4357d3c364e93bce082ec438f5ba1c4025 --- /dev/null +++ b/modules/connect/svg/Honeycomb @@ -0,0 +1,317 @@ +// +// Honeycomb module extracts flatten honeycomb sheet from Tools/FabLab Connect command of SolidWorks products +// +// Shawn Liu @ Dassault Systemes SolidWorks Corporation +// (c) Massachusetts Institute of Technology 2019 +// +// 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(){ +// +// module globals +// +var mod = {} +// +// name +// +var name = 'Honeycomb connect' +// +// initialization +// +var init = function() { + mod.address = getParameterByName('swIP') || '127.0.0.1' + mod.port = getParameterByName('swPort') || '80' + mod.socket = 0 + socket_open() + } +// +// inputs +// +var inputs = {} +// +// outputs +// +var outputs = { + SVG: { + type: 'string', + event: function () { + mods.output(mod, 'SVG', mod.str) + } + }, + file: { + type: 'object', + event: function (str) { + obj = {} + obj.name = mod.partName + ".svg" + obj.contents = str + mods.output(mod, 'file', obj) + } + } +} +// +// interface +// +var interface = function(div){ + mod.div = div + + // on-screen drawing canvas + // + var canvas = document.createElement('canvas') + canvas.width = mods.ui.canvas + canvas.height = mods.ui.canvas + canvas.style.backgroundColor = 'rgb(255,255,255)' + div.appendChild(canvas) + mod.canvas = canvas + div.appendChild(document.createElement('br')) + // + // off-screen image canvas + // + var canvas = document.createElement('canvas') + mod.img = canvas + + div.appendChild(document.createTextNode('server:')) + div.appendChild(document.createElement('br')) + div.appendChild(document.createTextNode('address: ' + getParameterByName('swIP'))) + div.appendChild(document.createElement('br')) + div.appendChild(document.createTextNode('\u00a0\u00a0\u00a0\u00a0\u00a0port: ' + getParameterByName('swPort'))) + div.appendChild(document.createElement('br')) + div.appendChild(document.createTextNode('\u00a0\u00a0status: ')) + input = document.createElement('input') + input.type = 'text' + input.size = 12 + div.appendChild(input) + mod.status = input + div.appendChild(document.createElement('br')) + var btn = document.createElement('button') + btn.style.margin = 1 + btn.appendChild(document.createTextNode('open')) + btn.addEventListener('click', function () { + socket_open() + }) + div.appendChild(btn) + var btn = document.createElement('button') + btn.style.margin = 1 + btn.appendChild(document.createTextNode('close')) + btn.addEventListener('click', function () { + socket_close() + }) + div.appendChild(btn) + div.appendChild(document.createElement('br')) + var btn = document.createElement('button') + btn.style.margin = 1 + btn.appendChild(document.createTextNode('Extract SVG')) + btn.addEventListener('click', function () { + extract_SVG() + }) + div.appendChild(btn) + div.appendChild(document.createElement('br')) + // + // view button + // + var btn = document.createElement('button') + btn.style.padding = mods.ui.padding + btn.style.margin = 1 + btn.appendChild(document.createTextNode('view')) + btn.addEventListener('click', function () { + var win = window.open('') + 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) + win.document.body.appendChild(document.createElement('br')) + var canvas = document.createElement('canvas') + canvas.width = mod.img.width + canvas.height = mod.img.height + win.document.body.appendChild(canvas) + var ctx = canvas.getContext("2d") + ctx.drawImage(mod.img, 0, 0) + }) + div.appendChild(btn) + div.appendChild(document.createElement('br')) + + // + // info div + // + var info = document.createElement('div') + info.setAttribute('id', div.id + 'info') + mod.name = document.createTextNode('name:') + info.appendChild(mod.name) + mod.thickness = document.createTextNode('thickness: ') + div.appendChild(mod.thickness) + info.appendChild(document.createElement('br')) + mod.width = document.createTextNode('width:') + info.appendChild(mod.width) + info.appendChild(document.createElement('br')) + mod.height = document.createTextNode('height:') + info.appendChild(mod.height) + div.appendChild(info) + + } +// +// local functions +// + +function getParameterByName(name, url) { + if (!url) url = window.location.href; + name = name.replace(/[\[\]]/g, "\\$&"); + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), + results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, " ")); +} + +function socket_open() { + var url = "ws://"+mod.address+':'+mod.port + mod.socket = new WebSocket(url) + mod.socket.onopen = function(event) { + mod.status.value = "opened" + var connect = {} + connect.modCmd = 'connect' + connect.owner = getParameterByName('swOwner') + connect.id = getParameterByName('swID') + socket_send(JSON.stringify(connect)) + } + mod.socket.onerror = function(event) { + mod.status.value = "can not open" + } + mod.socket.onmessage = function(event) { + mod.status.value = "receive" + var swData = JSON.parse(event.data); + if (swData.swType === "HoneycombFlattenSVG") { + mod.partName = swData.data.partName + var partName = swData.data.partName + if (partName.length > 25) + partName = partName.slice(0, 22) + '...' + mod.name.nodeValue = "name: " + partName + mod.thickness.nodeValue = "thickness: " + swData.data.thickness + ' (mm)'; + mod.str = swData.data.svg + svg_load_handler() + outputs.SVG.event(mod.str) + outputs.file.event(mod.str) + } + } + mod.socket.onclose = function (event) { + mod.status.value = "connection closed" + } + } +function socket_close() { + mod.socket.close() + mod.status.value = "closed" + mod.socket = 0 + } +function socket_send(msg) { + if (mod.socket != 0) { + mod.status.value = "send" + mod.socket.send(msg) + } + else { + mod.status.value = "can't send, not open" + } + } +function extract_SVG() { + var modcmd = new Object; + modcmd.modCmd = "AutoHoneycombFlatten"; + socket_send(JSON.stringify(modcmd)) + } + + // + // load handler + // +function svg_load_handler() { + // + // parse size + // + var i = mod.str.indexOf("width") + if (i == -1) { + mod.width.nodeValue = "width: not found" + mod.height.nodeValue = "height: not found" + } + else { + var i1 = mod.str.indexOf("\"", i + 1) + var i2 = mod.str.indexOf("\"", i1 + 1) + var width = mod.str.substring(i1 + 1, i2) + i = mod.str.indexOf("height") + i1 = mod.str.indexOf("\"", i + 1) + i2 = mod.str.indexOf("\"", i1 + 1) + var height = mod.str.substring(i1 + 1, i2) + ih = mod.str.indexOf("height") + if (width.indexOf("px") != -1) { + width = width.slice(0, -2) + height = height.slice(0, -2) + var units = 90 + } + else if (width.indexOf("mm") != -1) { + width = width.slice(0, -2) + height = height.slice(0, -2) + var units = 25.4 + } + else if (width.indexOf("cm") != -1) { + width = width.slice(0, -2) + height = height.slice(0, -2) + var units = 2.54 + } + else if (width.indexOf("in") != -1) { + width = width.slice(0, -2) + height = height.slice(0, -2) + var units = 1 + } + else { + var units = 90 + } + mod.width.nodeValue = "width: " + width / 3.543307 + ' (mm)'; + mod.height.nodeValue = "height: " + height / 3.543307 + ' (mm)'; + } + // + // display + // + var img = new Image() + var src = "data:image/svg+xml;base64," + window.btoa(mod.str) + img.setAttribute("src", src) + img.onload = function () { + if (img.width > img.height) { + var x0 = 0 + var y0 = mod.canvas.height * .5 * (1 - img.height / img.width) + var w = mod.canvas.width + var h = mod.canvas.width * img.height / img.width + } + else { + var x0 = mod.canvas.width * .5 * (1 - img.width / img.height) + var y0 = 0 + var w = mod.canvas.height * img.width / img.height + var h = mod.canvas.height + } + var ctx = mod.canvas.getContext("2d") + ctx.clearRect(0, 0, mod.canvas.width, mod.canvas.height) + ctx.drawImage(img, x0, y0, w, h) + var ctx = mod.img.getContext("2d") + ctx.canvas.width = img.width + ctx.canvas.height = img.height + ctx.drawImage(img, 0, 0) + outputs.SVG.event() + } +} + + +// +// return values +// +return ({ + mod:mod, + name:name, + init:init, + inputs:inputs, + outputs:outputs, + interface:interface + }) +}()) diff --git a/modules/convert/svg/array b/modules/convert/svg/array index 9d5c63b335827c4bb7997cc465fde97ac855c9f3..81110c7424974e20a059d44a80f1d995d0e2bd8e 100644 --- a/modules/convert/svg/array +++ b/modules/convert/svg/array @@ -16,7 +16,9 @@ // // module globals // -var mod = { +var mod = {} + +var modParam = { 'parameters':{ 'stock_width':50., 'stock_height':24., @@ -30,9 +32,10 @@ var name = 'nest SVG Array' // initialization // var init = function() { - Object.keys(mod.parameters).forEach( function(k){ - mod[k].value = mod.parameters[k]; //set default values - }); + mod.stock_width.value = modParam.parameters.stock_width; + mod.stock_height.value = modParam.parameters.stock_height; + mod.padding.value = modParam.parameters.padding; + } // // inputs @@ -75,7 +78,7 @@ var interface = function(div){ mod.bigview = document.createElementNS("http://www.w3.org/2000/svg", "svg"); //add parameter inputs - Object.keys(mod.parameters).forEach(function(p){ + Object.keys(modParam.parameters).forEach(function (p) { var textnode = document.createElement('span'); textnode.innerHTML = p+': '; textnode.style="display: inline-block; width: 80px; font-size: 12px;"; @@ -108,7 +111,7 @@ var interface = function(div){ //max text input_max.type='text'; input_max.size=2; - input_max.value = 2*mod.parameters[p]; //set initial maximum to twice default value + input_max.value = 2 * modParam.parameters[p]; //set initial maximum to twice default value input_max.addEventListener('blur',function(){ input_range.value = 100 * input_text.value / input_max.value; input_text.value = Math.min( input_text.value, input_max.value ); @@ -156,6 +159,7 @@ var interface = function(div){ // local functions function nest(sw_json){ + if (!sw_json) return //sw_json is text json exported from soliworks of the form [{partTitle:”Part-1”, thickness:20, count:2, svgArray:[svgf1, svgf2, …],{partTitle:”Part-2”, thickness:20, count:3, svgArray:[svgf3, svgf4, …] //stocksize is an array [width,height] of stock dimensions //we scale so the stock size takes up the %75 of screen diff --git a/modules/index.js b/modules/index.js index de206bcefec5f0905d64a60bf23743b7f898c5b9..ae2de366234a75e87e93b1671917e74fe9eb046c 100644 --- a/modules/index.js +++ b/modules/index.js @@ -4,8 +4,11 @@ module_menu(' in out','modules/character/in%20out') module_menu(' parse','modules/character/parse') module_menu(' variable','modules/character/variable') module_label('connect') +module_label(' stl') +module_menu(' STL','modules/connect/stl/STL') module_label(' svg') module_menu(' ExtractFaces','modules/connect/svg/ExtractFaces') +module_menu(' Honeycomb','modules/connect/svg/Honeycomb') module_menu(' PCB','modules/connect/svg/PCB') module_menu(' SelectFace','modules/connect/svg/SelectFace') module_label('convert') @@ -123,6 +126,8 @@ module_menu(' dxf','modules/path/formats/dxf') module_menu(' g-code','modules/path/formats/g-code') module_menu(' svg','modules/path/formats/svg') module_label(' machines') +module_label(' laser cutter') +module_menu(' Epilog','modules/path/machines/laser%20cutter/Epilog') module_label(' Roland') module_label(' milling') module_menu(' MDX-20','modules/path/machines/Roland/milling/MDX-20') @@ -130,8 +135,6 @@ module_menu(' SRM-20','modules/path/machines/Roland/milli module_label(' vinyl cutter') module_menu(' GX-24','modules/path/machines/Roland/vinyl%20cutter/GX-24') module_menu(' ShopBot','modules/path/machines/ShopBot') -module_label(' laser cutter') -module_menu(' Epilog','modules/path/machines/laser%20cutter/Epilog') module_menu(' view','modules/path/view') module_label('processes') module_label(' cut') diff --git a/programs/index.js b/programs/index.js index 1032953164bf709de2755e73a0a7bd087bd69fe9..ffc42ed36af41f212d80c71cb2b16f93df0b90d9 100644 --- a/programs/index.js +++ b/programs/index.js @@ -17,8 +17,11 @@ program_menu(' cut svg','programs/machines/Epilog/cut%20svg') program_menu(' cut svg connect','programs/machines/Epilog/cut%20svg%20connect') program_label(' G-code') program_menu(' mill 2.5D stl','programs/machines/G-code/mill%202.5D%20stl') +program_menu(' mill 2.5D stl connect','programs/machines/G-code/mill%202.5D%20stl%20connect') program_menu(' mill 2D png','programs/machines/G-code/mill%202D%20png') program_menu(' mill 2D svg','programs/machines/G-code/mill%202D%20svg') +program_label(' RNDMC') +program_menu(' honeycomb connect','programs/machines/RNDMC/honeycomb%20connect') program_label(' Roland') program_label(' mill') program_label(' MDX-20') @@ -26,10 +29,11 @@ program_menu(' PCB','programs/machines/Roland/mill/MDX-20 program_menu(' PCB png','programs/machines/Roland/mill/MDX-20/PCB%20png') program_menu(' PCB svg','programs/machines/Roland/mill/MDX-20/PCB%20svg') program_label(' SRM-20') +program_menu(' mill 2.5D stl','programs/machines/Roland/mill/SRM-20/mill%202.5D%20stl') +program_menu(' mill 2.5D stl connect','programs/machines/Roland/mill/SRM-20/mill%202.5D%20stl%20connect') program_menu(' PCB png','programs/machines/Roland/mill/SRM-20/PCB%20png') program_menu(' PCB svg','programs/machines/Roland/mill/SRM-20/PCB%20svg') program_menu(' PCB svg connect','programs/machines/Roland/mill/SRM-20/PCB%20svg%20connect') -program_menu(' mill 2.5D stl','programs/machines/Roland/mill/SRM-20/mill%202.5D%20stl') program_label(' vinyl cutter') program_label(' GX-24') program_menu(' cut png','programs/machines/Roland/vinyl%20cutter/GX-24/cut%20png') @@ -37,6 +41,7 @@ program_menu(' cut svg','programs/machines/Roland/vinyl%2 program_menu(' cut svg connect','programs/machines/Roland/vinyl%20cutter/GX-24/cut%20svg%20connect') program_label(' ShopBot') program_menu(' mill 2.5D stl','programs/machines/ShopBot/mill%202.5D%20stl') +program_menu(' mill 2.5D stl connect','programs/machines/ShopBot/mill%202.5D%20stl%20connect') program_menu(' mill 2D png','programs/machines/ShopBot/mill%202D%20png') program_menu(' mill 2D png PCB','programs/machines/ShopBot/mill%202D%20png%20PCB') program_menu(' mill 2D stl','programs/machines/ShopBot/mill%202D%20stl') diff --git a/programs/machines/G-code/mill 2.5D stl connect b/programs/machines/G-code/mill 2.5D stl connect new file mode 100644 index 0000000000000000000000000000000000000000..92c0c26d63a1f0da9c9575fb35766f4467312c5a --- /dev/null +++ b/programs/machines/G-code/mill 2.5D stl connect @@ -0,0 +1 @@ +{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"458.79857101582286","left":"2975.883638471559","inputs":{},"outputs":{}},"0.07944144280928633":{"definition":"//\n// edge detect\n// green = interior, blue = exterior, red = boundary\n// assumes input is thresholded\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'edge detect'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n edge_detect()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:interior, blue:exterior, red:boundary'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// edge detect\n//\nfunction edge_detect() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({worker:worker.toString(),\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var i00,i0m,i0p,im0,ip0,imm,imp,ipm,ipp,row,col\n //\n // find edges - interior\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != i0m) || (i00 != im0) || (i00 != imm)\n || (i00 != imp) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // left and right edges\n //\n for (row = 1; row < (h-1); ++row) {\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm) \n || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // top and bottom edges\n //\n for (col = 1; col < (w-1); ++col) {\n row = h-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != imm) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != ipm) \n || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // corners\n //\n row = 0\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"808.7985710158231","left":"3459.883638471559","inputs":{},"outputs":{}},"0.8903773266711255":{"definition":"//\n// orient edges\n// input is green:interior, blue:exterior, red:boundary\n// output is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'orient edges'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n var ctx = mod.display.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n orient_edges()\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // off-screen display canvas\n //\n var canvas = document.createElement('canvas')\n mod.display = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('red:north, dark red:south'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:east, dark green:west'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('blue:start, dark blue:stop'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.display,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// orient edges\n//\nfunction orient_edges() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n var disp = new Uint8ClampedArray(evt.data.display)\n var dispdata = new ImageData(disp,w,h)\n var ctx = mod.display.getContext(\"2d\")\n ctx.putImageData(dispdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var w = mod.canvas.width\n var h = mod.canvas.height\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,w,h)\n ctx.drawImage(mod.display,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var row,col\n var boundary = 0\n var interior = 1\n var exterior = 2\n var alpha = 3\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n //\n // orient body states\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && ((input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && ((input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n }\n }\n }\n //\n // orient edge states\n //\n for (col = 1; col < (w-1); ++col) {\n row = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n row = h-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n for (row = 1; row < (h-1); ++row) {\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n //\n // orient corner states (todo)\n //\n row = 0\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = 0\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n //\n // invert background for display\n //\n var display = new Uint8ClampedArray(h*w*4)\n var r,g,b,i\n for (row = 0; row < h; ++row) {\n for (col = 0; col < w; ++col) {\n r = output[(h-1-row)*w*4+col*4+0]\n g = output[(h-1-row)*w*4+col*4+1]\n b = output[(h-1-row)*w*4+col*4+2]\n i = r+g+b\n if (i != 0) { \n display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]\n display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]\n display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]\n display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]\n }\n else {\n display[(h-1-row)*w*4+col*4+0] = 255\n display[(h-1-row)*w*4+col*4+1] = 255\n display[(h-1-row)*w*4+col*4+2] = 255\n display[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // return output\n //\n self.postMessage({buffer:output.buffer,display:display.buffer},[output.buffer,display.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"902.7985710158231","left":"3013.883638471559","inputs":{},"outputs":{}},"0.6488303557466412":{"definition":"//\n// image threshold\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'image threshold'\n//\n// initialization\n//\nvar init = function() {\n mod.threshold.value = '0.5'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n threshold_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // threshold value\n //\n div.appendChild(document.createTextNode('threshold (0-1): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n threshold_image()\n })\n div.appendChild(input)\n mod.threshold = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// threshold image\n//\nfunction threshold_image() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var t = parseFloat(mod.threshold.value)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,threshold:t,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var t = evt.data.threshold\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,i\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n r = buf[(h-1-row)*w*4+col*4+0] \n g = buf[(h-1-row)*w*4+col*4+1] \n b = buf[(h-1-row)*w*4+col*4+2] \n a = buf[(h-1-row)*w*4+col*4+3] \n i = (r+g+b)/(3*255)\n if (a == 0)\n val = 255\n else if (i > t)\n var val = 255\n else\n var val = 0\n buf[(h-1-row)*w*4+col*4+0] = val\n buf[(h-1-row)*w*4+col*4+1] = val\n buf[(h-1-row)*w*4+col*4+2] = val\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"359.7985710158231","left":"2559.883638471559","inputs":{},"outputs":{}},"0.749132408760488":{"definition":"//\n// vectorize\n// input is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = '1'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n vectorize()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'array',\n event:function(){\n mods.output(mod,'path',mod.path)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br')) \n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // error value\n //\n div.appendChild(document.createTextNode('vector fit (pixels): '))\n //div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n vectorize()\n })\n div.appendChild(input)\n mod.error = input\n div.appendChild(document.createElement('br'))\n //\n // sort\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n input.checked = true\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// vectorize\n//\nfunction vectorize() {\n //\n // draw path\n //\n function draw_path(path) {\n window.URL.revokeObjectURL(url)\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute('viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment in path) {\n if (path[segment].length > 1) {\n if (xend != null) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = xend\n var y1 = yend\n var x2 = path[segment][0][0]\n var y2 = h-path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n svg.appendChild(g)\n }\n //\n // set up worker\n //\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n webworker.terminate()\n mod.path = evt.data.path\n draw_path(mod.path)\n outputs.path.event()\n })\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,sort:mod.sort.checked,\n error:parseFloat(mod.error.value),\n buffer:mod.input.data.buffer})\n }\n//\n// vectorize worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var sort = evt.data.sort\n var input = new Uint8ClampedArray(evt.data.buffer)\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n var path = []\n //\n // edge follower\n //\n function follow_edges(row,col) {\n if ((input[(h-1-row)*w*4+col*4+northsouth] != 0)\n || (input[(h-1-row)*w*4+col*4+eastwest] != 0)) {\n path[path.length] = [[col,row]]\n while (1) {\n if (input[(h-1-row)*w*4+col*4+northsouth] & north) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~north\n row += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+northsouth] & south) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~south\n row -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & east) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~east\n col += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & west) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~west\n col -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else\n break\n }\n }\n }\n //\n // follow boundary starts\n //\n for (var row = 1; row < (h-1); ++row) {\n col = 0\n follow_edges(row,col)\n col = w-1\n follow_edges(row,col)\n }\n for (var col = 1; col < (w-1); ++col) {\n row = 0\n follow_edges(row,col)\n row = h-1 \n follow_edges(row,col)\n }\n //\n // follow interior paths\n //\n for (var row = 1; row < (h-1); ++row) {\n for (var col = 1; col < (w-1); ++col) {\n follow_edges(row,col)\n }\n }\n //\n // vectorize path\n //\n var error = evt.data.error\n var vecpath = []\n for (var seg = 0; seg < path.length; ++seg) {\n var x0 = path[seg][0][0]\n var y0 = path[seg][0][1]\n vecpath[vecpath.length] = [[x0,y0]]\n var xsum = x0\n var ysum = y0\n var sum = 1\n for (var pt = 1; pt < path[seg].length; ++pt) {\n var xold = x\n var yold = y\n var x = path[seg][pt][0]\n var y = path[seg][pt][1]\n if (sum == 1) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n var xmean = xsum/sum\n var ymean = ysum/sum\n var dx = xmean-x0\n var dy = ymean-y0\n var d = Math.sqrt(dx*dx+dy*dy)\n var nx = dy/d\n var ny = -dx/d\n var l = Math.abs(nx*(x-x0)+ny*(y-y0))\n if (l < error) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [xold,yold]\n x0 = xold\n y0 = yold\n xsum = xold\n ysum = yold\n sum = 1\n }\n }\n if (pt == (path[seg].length-1)) {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [x,y]\n }\n }\n }\n //\n // sort path\n //\n if ((vecpath.length > 0) && (sort == true)) {\n var dmin = w*w+h*h\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][0]\n var d = x*x+y*y\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n var sortpath = [vecpath[segmin]]\n vecpath.splice(segmin,1)\n }\n while (vecpath.length > 0) {\n var dmin = w*w+h*h\n var x0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][0]\n var y0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][1]\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][1]\n var d = (x-x0)*(x-x0)+(y-y0)*(y-y0)\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n sortpath[sortpath.length] = vecpath[segmin]\n vecpath.splice(segmin,1)\n }\n }\n }\n else if ((vecpath.length > 0) && (sort == false))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"807.7985710158231","left":"2594.883638471559","inputs":{},"outputs":{}},"0.4793941661670936":{"definition":"//\n// save file\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'save file'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n file:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.contents = evt.detail.contents\n save_file()\n }}}\n//\n// outputs\n//\nvar outputs = {}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name:')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('size:')\n div.appendChild(text)\n mod.sizetext = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\nfunction save_file() {\n var a = document.createElement('a')\n a.setAttribute('href','data:text/plain;charset=utf-8,'+ \n encodeURIComponent(mod.contents))\n a.setAttribute('download',mod.name)\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n mod.nametext.nodeValue = 'name: '+mod.name\n mods.fit(mod.div)\n mod.sizetext.nodeValue = 'size: '+mod.contents.length\n mods.fit(mod.div)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1056.1053724684725","left":"1458.7079375500448","inputs":{},"outputs":{}},"0.3040697193095865":{"definition":"//\n// mesh rotate\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 = 'mesh rotate'\n//\n// initialization\n//\nvar init = function() {\n mod.rx.value = '0'\n mod.ry.value = '0'\n mod.rz.value = '0'\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = evt.detail\n rotate_mesh()}}}\n//\n// outputs\n//\nvar outputs = {\n mesh:{type:'STL',\n event:function(buffer){\n mods.output(mod,'mesh',buffer)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // 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 // rotation\n //\n div.appendChild(document.createTextNode('rotation (degrees):'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' x: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rx = input\n div.appendChild(document.createTextNode(' y: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.ry = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' z: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rz = input\n div.appendChild(document.createTextNode(' (enter)'))\n div.appendChild(document.createElement('br'))\n //\n // info\n //\n var text = document.createTextNode('dx:')\n div.appendChild(text)\n mod.dxn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dy:')\n div.appendChild(text)\n mod.dyn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dz:')\n div.appendChild(text)\n mod.dzn = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n// rotate mesh\n//\nfunction rotate_mesh() {\n //\n // check for binary STL\n //\n var endian = true\n var view = new DataView(mod.mesh)\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits, rotate, and draw\n //\n var blob = new Blob(['('+rotate_mesh_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n //\n // worker response\n //\n window.URL.revokeObjectURL(url)\n //\n // size\n //\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\n //\n // image\n //\n var image = evt.data.image\n var height = mod.canvas.height\n var width = mod.canvas.width\n var buffer = new Uint8ClampedArray(evt.data.image)\n var imgdata = new ImageData(buffer,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n //\n // mesh\n //\n mod.mesh = evt.data.mesh\n //\n // output\n //\n outputs.mesh.event(evt.data.rotate)\n })\n //\n // call worker\n //\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var img = ctx.getImageData(0,0,mod.canvas.width,mod.canvas.height)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n webworker.postMessage({\n height:mod.canvas.height,width:mod.canvas.width,\n rx:rx,ry:ry,rz:rz,\n image:img.data.buffer,mesh:mod.mesh},\n [img.data.buffer,mod.mesh])\n }\nfunction rotate_mesh_worker() {\n self.addEventListener('message',function(evt) {\n //\n // function to draw line\n //\n function line(x0,y0,x1,y1) {\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\n var row,col\n var idx = ix1-ix0\n var idy = iy1-iy0\n if (Math.abs(idy) > Math.abs(idx)) {\n (idy > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (row = row0; row <= row1; ++row) {\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\n (idx > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (col = col0; col <= col1; ++col) {\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else {\n row = iy0\n col = ix0\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n //return([x3,y3,z3])\n return({x:x3,y:y3,z:z3})\n }\n //\n // get variables\n //\n var height = evt.data.height\n var width = evt.data.width\n var rx = evt.data.rx\n var ry = evt.data.ry\n var rz = evt.data.rz\n var endian = true\n var image = new Uint8ClampedArray(evt.data.image)\n var view = new DataView(evt.data.mesh)\n var triangles = view.getUint32(80,endian)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0.x > xmax) xmax = p0.x\n if (p0.x < xmin) xmin = p0.x\n if (p0.y > ymax) ymax = p0.y\n if (p0.y < ymin) ymin = p0.y\n if (p0.z > zmax) zmax = p0.z\n if (p0.z < zmin) zmin = p0.z\n var p1 = rotate(x1,y1,z1)\n if (p1.x > xmax) xmax = p1.x\n if (p1.x < xmin) xmin = p1.x\n if (p1.y > ymax) ymax = p1.y\n if (p1.y < ymin) ymin = p1.y\n if (p1.z > zmax) zmax = p1.z\n if (p1.z < zmin) zmin = p1.z\n var p2 = rotate(x2,y2,z2)\n if (p2.x > xmax) xmax = p2.x\n if (p2.x < xmin) xmin = p2.x\n if (p2.y > ymax) ymax = p2.y\n if (p2.y < ymin) ymin = p2.y\n if (p2.z > zmax) zmax = p2.z\n if (p2.z < zmin) zmin = p2.z\n }\n var dx = xmax-xmin\n var dy = ymax-ymin\n var dz = zmax-zmin\n //\n // copy mesh\n //\n var newbuf = evt.data.mesh.slice(0)\n var newview = new DataView(newbuf)\n //\n // copy and draw mesh\n //\n if (dx > dy) {\n var xo = 0\n var yo = height*.5*(1-dy/dx)\n var xw = (width-1)\n var yh = (width-1)*dy/dx\n }\n else {\n var xo = width*.5*(1-dx/dy)\n var yo = 0\n var xw = (height-1)*dx/dy\n var yh = (height-1)\n }\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n line(p0.x,p0.y,p1.x,p1.y)\n line(p1.x,p1.y,p2.x,p2.y)\n line(p2.x,p2.y,p0.x,p0.y)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.z,endian)\n newoffset += 4\n newoffset += 2\n }\n //\n // return results and close\n //\n self.postMessage({\n dx:dx,dy:dy,dz:dz,\n image:evt.data.image,mesh:evt.data.mesh,rotate:newbuf},\n [evt.data.image,evt.data.mesh,newbuf])\n self.close()\n })\n }\nfunction old_rotate_mesh() {\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n return([x3,y3,z3])\n }\n //\n // get vars\n //\n var view = mod.mesh\n var endian = true\n var triangles = view.getUint32(80,endian)\n mod.triangles = triangles\n var size = 80+4+triangles*(4*12+2)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0[0] > xmax) xmax = p0[0]\n if (p0[0] < xmin) xmin = p0[0]\n if (p0[1] > ymax) ymax = p0[1]\n if (p0[1] < ymin) ymin = p0[1]\n if (p0[2] > zmax) zmax = p0[2]\n if (p0[2] < zmin) zmin = p0[2]\n var p1 = rotate(x1,y1,z1)\n if (p1[0] > xmax) xmax = p1[0]\n if (p1[0] < xmin) xmin = p1[0]\n if (p1[1] > ymax) ymax = p1[1]\n if (p1[1] < ymin) ymin = p1[1]\n if (p1[2] > zmax) zmax = p1[2]\n if (p1[2] < zmin) zmin = p1[2]\n var p2 = rotate(x2,y2,z2)\n if (p2[0] > xmax) xmax = p2[0]\n if (p2[0] < xmin) xmin = p2[0]\n if (p2[1] > ymax) ymax = p2[1]\n if (p2[1] < ymin) ymin = p2[1]\n if (p2[2] > zmax) zmax = p2[2]\n if (p2[2] < zmin) zmin = p2[2]\n }\n mod.dx = xmax-xmin\n mod.dy = ymax-ymin\n mod.dz = zmax-zmin\n mod.dxn.nodeValue = 'dx: '+mod.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+mod.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+mod.dz.toFixed(3)\n mod.xmin = xmin\n mod.ymin = ymin\n mod.zmin = zmin\n mod.xmax = xmax\n mod.ymax = ymax\n mod.zmax = zmax\n //\n // copy mesh\n //\n var buf = mod.mesh.buffer.slice(0)\n var newview = new DataView(buf)\n //\n // draw projection and save rotation\n //\n var ctx = mod.meshcanvas.getContext('2d')\n var w = mod.meshcanvas.width\n var h = mod.meshcanvas.height\n ctx.clearRect(0,0,w,h)\n var dx = mod.dx\n var dy = mod.dy\n if (dx > dy) {\n var xo = 0\n var yo = h*.5*(1-dy/dx)\n var xw = w\n var yh = w*dy/dx\n }\n else {\n var xo = w*.5*(1-dx/dy)\n var yo = 0\n var xw = h*dx/dy\n var yh = h\n }\n ctx.beginPath()\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n x0 = xo+xw*(p0[0]-xmin)/dx\n y0 = yo+yh*(ymax-p0[1])/dy\n x1 = xo+xw*(p1[0]-xmin)/dx\n y1 = yo+yh*(ymax-p1[1])/dy\n x2 = xo+xw*(p2[0]-xmin)/dx\n y2 = yo+yh*(ymax-p2[1])/dy\n ctx.moveTo(x0,y0)\n ctx.lineTo(x1,y1)\n ctx.lineTo(x2,y2)\n ctx.lineTo(x0,y0)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[2],endian)\n newoffset += 4\n newoffset += 2\n }\n ctx.stroke()\n //\n // generate output\n //\n outputs.mesh.event(buf)\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":"461.49148835504764","left":"988.5431668558716","inputs":{},"outputs":{}},"0.7667165137781767":{"definition":"//\n// offset\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2019\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'offset'\n//\n// initialization\n//\nvar init = function() {\n mod.offset.value = '25'\n mod.distances = ''\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 if ((mod.offset.value != '') && (mod.distances != ''))\n offset()\n else\n mod.distances = ''\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 // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"350.34902968661845","left":"3570.00249686672","inputs":{},"outputs":{}},"0.32304064019646705":{"definition":"//\n// mesh slice raster\n// \n// todo\n// include slice plane triangles\n// scale perturbation to resolution\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 = 'mesh slice raster'\n//\n// initialization\n//\nvar init = function() {\n mod.mmunits.value = '1'\n mod.inunits.value = '0.03937007874015748'\n mod.depth.value = '5'\n mod.width.value = '1000'\n mod.border.value = '0'\n mod.delta = 1e-6\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = new DataView(evt.detail)\n find_limits_slice()}},\n settings:{type:'',\n event:function(evt){\n for (var p in evt.detail)\n if (p == 'depthmm') {\n mod.depth.value = evt.detail[p]\n /parseFloat(mod.mmunits.value)\n }\n find_limits_slice()}}}\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 imageInfo:{type:'',\n event:function(){\n var obj = {}\n obj.name = \"mesh slice raster\"\n obj.width = mod.img.width\n obj.height = mod.img.height\n obj.dpi = mod.img.width/(mod.dx*parseFloat(mod.inunits.value))\n mods.output(mod,'imageInfo',obj)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen slice canvas\n //\n div.appendChild(document.createTextNode(' '))\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.slicecanvas = 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 // mesh units\n //\n div.appendChild(document.createTextNode('mesh units: (enter)'))\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('change',function(){\n mod.inunits.value = parseFloat(mod.mmunits.value)/25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.mmunits = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n mod.mmunits.value = parseFloat(mod.inunits.value)*25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.inunits = input\n //\n // mesh size\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mesh size:'))\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (units)')\n div.appendChild(text)\n mod.meshsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (mm)')\n div.appendChild(text)\n mod.mmsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (in)')\n div.appendChild(text)\n mod.insize = text\n //\n // slice depth\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice Z depth: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.depth = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice border \n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice border: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.border = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice width\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice width: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.width = input\n div.appendChild(document.createTextNode(' (pixels)'))\n //\n // view slice\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 slice'))\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// find limits then slice\n//\nfunction find_limits_slice() {\n var blob = new Blob(['('+limits_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 mod.triangles = evt.data.triangles\n mod.xmin = evt.data.xmin\n mod.xmax = evt.data.xmax\n mod.ymin = evt.data.ymin\n mod.ymax = evt.data.ymax\n mod.zmin = evt.data.zmin\n mod.zmax = evt.data.zmax\n mod.dx = mod.xmax-mod.xmin\n mod.dy = mod.ymax-mod.ymin\n mod.dz = mod.zmax-mod.zmin\n mod.meshsize.nodeValue = \n mod.dx.toFixed(3)+' x '+\n mod.dy.toFixed(3)+' x '+\n mod.dz.toFixed(3)+' (units)'\n var mm = parseFloat(mod.mmunits.value)\n mod.mmsize.nodeValue = \n (mod.dx*mm).toFixed(3)+' x '+\n (mod.dy*mm).toFixed(3)+' x '+\n (mod.dz*mm).toFixed(3)+' (mm)'\n var inches = parseFloat(mod.inunits.value)\n mod.insize.nodeValue = \n (mod.dx*inches).toFixed(3)+' x '+\n (mod.dy*inches).toFixed(3)+' x '+\n (mod.dz*inches).toFixed(3)+' (in)'\n mods.fit(mod.div)\n slice_mesh()\n })\n var border = parseFloat(mod.border.value)\n webworker.postMessage({\n mesh:mod.mesh,\n border:border,delta:mod.delta})\n }\nfunction limits_worker() {\n self.addEventListener('message',function(evt) {\n var view = evt.data.mesh\n var depth = evt.data.depth\n var border = evt.data.border\n var delta = evt.data.delta // perturb to remove degeneracies\n //\n // get vars\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n offset += 2\n if (x0 > xmax) xmax = x0\n if (x0 < xmin) xmin = x0\n if (y0 > ymax) ymax = y0\n if (y0 < ymin) ymin = y0\n if (z0 > zmax) zmax = z0\n if (z0 < zmin) zmin = z0\n if (x1 > xmax) xmax = x1\n if (x1 < xmin) xmin = x1\n if (y1 > ymax) ymax = y1\n if (y1 < ymin) ymin = y1\n if (z1 > zmax) zmax = z1\n if (z1 < zmin) zmin = z1\n if (x2 > xmax) xmax = x2\n if (x2 < xmin) xmin = x2\n if (y2 > ymax) ymax = y2\n if (y2 < ymin) ymin = y2\n if (z2 > zmax) zmax = z2\n if (z2 < zmin) zmin = z2\n }\n xmin -= border\n xmax += border\n ymin -= border\n ymax += border\n //\n // return\n //\n self.postMessage({triangles:triangles,\n xmin:xmin,xmax:xmax,ymin:ymin,ymax:ymax,\n zmin:zmin,zmax:zmax})\n self.close()\n })\n }\n//\n// slice mesh\n// \nfunction slice_mesh() {\n var blob = new Blob(['('+slice_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.slicecanvas.height*.5*(1-h/w)\n var wd = mod.slicecanvas.width\n var hd = mod.slicecanvas.width*h/w\n }\n else {\n var x0 = mod.slicecanvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.slicecanvas.height*w/h\n var hd = mod.slicecanvas.height\n }\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n outputs.image.event()\n outputs.imageInfo.event()\n })\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n var depth = parseFloat(mod.depth.value)\n mod.img.width = parseInt(mod.width.value)\n mod.img.height = Math.round(mod.img.width*mod.dy/mod.dx)\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.img.height,width:mod.img.width,depth:depth,\n imgbuffer:img.data.buffer,mesh:mod.mesh,\n xmin:mod.xmin,xmax:mod.xmax,\n ymin:mod.ymin,ymax:mod.ymax,\n zmin:mod.zmin,zmax:mod.zmax,\n delta:mod.delta},\n [img.data.buffer])\n }\nfunction slice_worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var depth = evt.data.depth\n var view = evt.data.mesh\n var delta = evt.data.delta // perturb to remove degeneracies\n var xmin = evt.data.xmin\n var xmax = evt.data.xmax\n var ymin = evt.data.ymin\n var ymax = evt.data.ymax\n var zmin = evt.data.zmin\n var zmax = evt.data.zmax\n var buf = new Uint8ClampedArray(evt.data.imgbuffer)\n //\n // get vars from buffer\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // initialize slice image\n //\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] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // find triangles crossing the slice\n //\n var segs = []\n offset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n //\n // assemble vertices\n //\n offset += 2\n var v = [[x0,y0,z0],[x1,y1,z1],[x2,y2,z2]]\n //\n // sort z\n //\n v.sort(function(a,b) {\n if (a[2] < b[2])\n return -1\n else if (a[2] > b[2])\n return 1\n else\n return 0\n })\n //\n // check for crossings\n //\n if ((v[0][2] < (zmax-depth)) && (v[2][2] > (zmax-depth))) {\n //\n // crossing found, check for side and save\n //\n if (v[1][2] < (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[2][0]+(v[1][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n var y1 = v[2][1]+(v[1][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n }\n else if (v[1][2] >= (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[1][0]+(v[0][0]-v[1][0])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n var y1 = v[1][1]+(v[0][1]-v[1][1])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n }\n if (y0 < y1)\n segs.push({x0:x0,y0:y0,x1:x1,y1:y1})\n else\n segs.push({x0:x1,y0:y1,x1:x0,y1:y0})\n }\n }\n //\n // fill interior\n //\n for (var row = 0; row < h; ++row) {\n var y = ymin+(ymax-ymin)*row/(h-1)\n rowsegs = segs.filter(p => ((p.y0 <= y) && (p.y1 >= y)))\n var xs = rowsegs.map(p =>\n (p.x0+(p.x1-p.x0)*(y-p.y0)/(p.y1-p.y0)))\n xs.sort((a,b) => (a-b))\n for (var col = 0; col < w; ++col) {\n var x = xmin+(xmax-xmin)*col/(w-1)\n var index = xs.findIndex((p) => (p >= x))\n if (index == -1)\n var i = 0\n else\n var i = 255*(index%2)\n buf[(h-1-row)*w*4+col*4+0] = i\n buf[(h-1-row)*w*4+col*4+1] = i\n buf[(h-1-row)*w*4+col*4+2] = i\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // output the slice\n //\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n self.close()\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"300.2962306652032","left":"1424.6209440086457","inputs":{},"outputs":{}},"0.4144526456371104":{"definition":"//\n// view path\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2019\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// 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 path'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n path:{type:'',\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 mods.fit(mod.div)\n outputs.path.event()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'',\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,'path',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 renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction open_view_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//\nfunction init_window() {\n //document.write('<script type=\"text/javascript\">'+arg+'</script>')\n //document.close()\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 mod.win.close()\n mod.win = undefined\n })\n mod.win.document.body.appendChild(btn)\n //\n // label text\n //\n var text = win.document.createTextNode(' left: pan, right: rotate, scroll: zoom')\n mod.win.document.body.appendChild(text)\n //\n // GL container\n //\n mod.win.document.body.appendChild(document.createElement('br')) \n container = mod.win.document.createElement('div')\n container.style.overflow = 'hidden'\n mod.win.document.body.appendChild(container)\n //\n // event handlers\n //\n container.addEventListener('contextmenu',context_menu)\n container.addEventListener('mousedown',mouse_down)\n container.addEventListener('mouseup',mouse_up)\n container.addEventListener('mousemove',mouse_move)\n container.addEventListener('wheel',mouse_wheel)\n //\n // add scene\n //\n scene = new THREE.Scene()\n mod.scene = scene\n var width = mod.win.innerWidth\n var height = mod.win.innerHeight\n var aspect = width/height\n var near = 0.1\n var far = 1000000\n camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n mod.camera = camera\n scene.add(camera)\n //\n // add renderer\n //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n renderer.setSize(width,height)\n container.appendChild(renderer.domElement)\n //\n // show the path if available\n //\n show_path()\n //\n // 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/mod.win.innerHeight\n -Math.cos(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n mod.y0 += \n Math.cos(mod.thetaz)*mod.height*dy/mod.win.innerHeight\n +Math.sin(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n else if (mod.button == 2) {\n mod.thetaxy += dy/mod.win.innerHeight\n mod.thetaz += dx/mod.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 if (Math.cos(mod.thetaxy) > 0)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n else\n camera.up = new THREE.Vector3(-Math.sin(mod.thetaz),-Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n //\n // mouse_wheel\n //\n function mouse_wheel(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dy = evt.deltaY/mod.win.innerHeight\n mod.r += mod.height*dy\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1200.2152674081028","left":"1978.345635960845","inputs":{},"outputs":{}},"0.9325875387173613":{"definition":"//\n// mill raster 2.5D\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2019\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'mill raster 2.5D'\n//\n// initialization\n//\nvar init = function() {\n mod.dia_in.value = '0.1'\n mod.dia_mm.value = '2.54'\n mod.cut_in.value = '0.1'\n mod.cut_mm.value = '2.54'\n mod.max_in.value = '1'\n mod.max_mm.value = '25.4'\n mod.number.value = '1'\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:'',\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:'',\n event:function(evt){\n if (mod.label.nodeValue == 'calculating') {\n //\n // calculation in progress, draw and accumulate\n //\n draw_path(evt.detail)\n accumulate_path(evt.detail)\n mod.offsetCount += 1\n if ((mod.offsetCount != parseInt(mod.number.value))\n && (evt.detail.length > 0)) {\n //\n // layer detail present and offset not complete\n //\n mod.offset += parseFloat(mod.stepover.value)\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else if (mod.depthmm < parseFloat(mod.max_mm.value)) {\n //\n // layer loop not complete\n //\n merge_layer()\n accumulate_toolpath()\n clear_layer()\n mod.depthmm += parseFloat(mod.cut_mm.value)\n if (mod.depthmm > parseFloat(mod.max_mm.value)) {\n mod.depthmm = parseFloat(mod.max_mm.value)\n }\n //\n // clear offset\n //\n outputs.offset.event('')\n //\n // set new depth\n //\n outputs.depth.event(mod.depthmm)\n //\n // set new offset\n //\n mod.offsetCount = 0\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else {\n //\n // done, finish and output\n //\n draw_path(mod.toolpath)\n draw_connections(mod.toolpath)\n mod.label.nodeValue = 'calculate'\n mod.labelspan.style.fontWeight = 'normal'\n outputs.toolpath.event()\n }\n }\n }\n },\n settings:{type:'',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n depth:{type:'',\n event:function(depth){\n mods.output(mod,'depth',{depthmm:depth})\n }\n },\n diameter:{type:'',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{type:'',\n event:function(size){\n mods.output(mod,'offset',size)\n }\n },\n toolpath:{type:'',\n event:function(){\n cmd = {}\n cmd.path = mod.toolpath\n cmd.name = mod.name\n cmd.dpi = mod.dpi\n cmd.width = mod.width\n cmd.height = mod.height\n cmd.depth = Math.round(parseFloat(mod.max_in.value)*mod.dpi)\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 outputs.offset.event('') // clear offset\n mod.depthmm = parseFloat(mod.cut_mm.value)\n outputs.depth.event(mod.depthmm) // set depth\n mod.toolpath = [] // start new toolpath\n clear_layer() // clear layer\n outputs.diameter.event()\n outputs.offset.event( // set offset\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\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_layer\n//\nfunction clear_layer() {\n mod.path = []\n mod.offset = 0.5\n mod.offsetCount = 0\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute(\n '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_layer\n//\nfunction merge_layer() {\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// accumulate_toolpath\n//\nfunction accumulate_toolpath() {\n for (var seg = 0; seg < mod.path.length; ++seg) {\n var newseg = []\n for (var pt = 0; pt < mod.path[seg].length; ++pt) {\n var idepth = -Math.round(mod.dpi*mod.depthmm/25.4)\n var point = mod.path[seg][pt].concat(idepth)\n newseg.splice(newseg.length,0,point)\n }\n mod.toolpath.push(newseg)\n }\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(\n '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(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n '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(path) {\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 < path.length; ++segment) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS(\n '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 = path[segment-1][path[segment-1].length-1][0]\n var y1 = h-path[segment-1][path[segment-1].length-1][1]-1\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(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n '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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"439.00932025295833","left":"1982.700932035969","inputs":{},"outputs":{}},"0.7335721197356367":{"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//\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 = '20'\n mod.plungespeed.value = '20'\n mod.jogspeed.value = '75'\n mod.jogheight.value = '5'\n mod.spindlespeed.value = '10000'\n mod.tool.value = '1'\n mod.coolanton.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 speed\n //\n div.appendChild(document.createTextNode('jog speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.jogspeed = 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\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\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_speed = parseFloat(mod.jogspeed.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_speed /= 25.4\n jog_height /= 25.4\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\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 += \"Z\"+jog_height.toFixed(4)+\"\\n\"\n str += \"G00X\"+x.toFixed(4)+\"Y\"+y.toFixed(4)+\"Z\"+jog_height.toFixed(4)+\"\\n\"\n //\n // move down\n //\n z = scale*mod.path[seg][0][2]\n str += \"G01Z\"+z.toFixed(4)+\" F\"+plunge_speed.toFixed(4)+\"\\n\"\n str += \"F\"+cut_speed.toFixed(4)+\"\\n\" //restore xy feed rate\n for (var pt = 1; pt < mod.path[seg].length; ++pt) {\n //\n // move to next point\n //\n x = scale*mod.path[seg][pt][0]\n y = scale*mod.path[seg][pt][1]\n z = scale*mod.path[seg][pt][2]\n str += \"G01X\"+x.toFixed(4)+\"Y\"+y.toFixed(4)+\"Z\"+z.toFixed(4)+\"\\n\"\n }\n }\n //\n // finish\n //\n str += \"G00Z\"+jog_height.toFixed(4)+\"\\n\" // move up before stopping spindle\n str += \"M05\\n\" // spindle stop\n if (mod.coolanton.checked)\n str += \"M09\\n\" // coolant off\n str += \"M30\\n\" // program end and reset\n str += \"%\\n\" // tape end\n //\n // output file\n //\n outputs.file.event(str)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"1054.8057495453727","left":"962.2547424183412","inputs":{},"outputs":{}},"0.9327347189729929":{"definition":"//\r\n// STL module extracts stl from Tools/FabLab Connect command of SolidWorks products\r\n// \r\n// Shawn Liu @ Dassault Systemes SolidWorks Corporation\r\n// (c) Massachusetts Institute of Technology 2019\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n//\r\n// name\r\n//\r\nvar name = 'STL connect'\r\n//\r\n// initialization\r\n//\r\nvar init = function () {\r\n mod.address = getParameterByName('swIP') || '127.0.0.1'\r\n mod.port = getParameterByName('swPort') || '80'\r\n mod.socket = 0\r\n mod.sag.value = '0.1'\n mod.angle.value = '45'\n socket_open()\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {\r\n }\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n mesh:{type:'STL',\r\n event:function(buffer){\r\n mods.output(mod,'mesh',buffer)}}\r\n }\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n //\r\n // canvas\r\n //\r\n var canvas = document.createElement('canvas')\r\n canvas.width = mods.ui.canvas\r\n canvas.height = mods.ui.canvas\r\n canvas.style.backgroundColor = 'rgb(255,255,255)'\r\n div.appendChild(canvas)\r\n mod.canvas = canvas\r\n div.appendChild(document.createElement('br'))\r\n\r\n div.appendChild(document.createTextNode('server:'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('address: ' + getParameterByName('swIP')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0port: ' + getParameterByName('swPort')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0status: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 12\r\n div.appendChild(input)\r\n mod.status = input\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('open'))\r\n btn.addEventListener('click', function () {\r\n socket_open()\r\n })\r\n div.appendChild(btn)\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.addEventListener('click', function () {\r\n socket_close()\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('sag: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.sag = input\r\n div.appendChild(document.createTextNode('(mm)'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('angle: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.angle = input\r\n div.appendChild(document.createTextNode('(degree)'))\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('Extract STL'))\r\n btn.addEventListener('click', function () {\r\n extract_STL()\r\n })\r\n div.appendChild(btn)\r\n\r\n //\r\n // info\r\n //\r\n var info = document.createElement('div')\r\n info.setAttribute('id', div.id + 'info')\r\n var text = document.createTextNode('name: ')\n info.appendChild(text)\n mod.name = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('size: ')\r\n info.appendChild(text)\r\n mod.sizen = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('triangles: ')\r\n info.appendChild(text)\r\n mod.trianglesn = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('dx: ')\r\n info.appendChild(text)\r\n mod.dxn = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('dy: ')\r\n info.appendChild(text)\r\n mod.dyn = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('dz: ')\r\n info.appendChild(text)\r\n mod.dzn = text\r\n div.appendChild(info)\r\n }\r\n//\r\n// local functions\r\n//\r\n\r\n //\r\n // local functions\r\n //\r\n\r\nfunction getParameterByName(name, url) {\r\n if (!url) url = window.location.href;\r\n name = name.replace(/[\\[\\]]/g, \"\\\\$&\");\r\n var regex = new RegExp(\"[?&]\" + name + \"(=([^&#]*)|&|#|$)\"),\r\n results = regex.exec(url);\r\n if (!results) return null;\r\n if (!results[2]) return '';\r\n return decodeURIComponent(results[2].replace(/\\+/g, \" \"));\r\n}\r\n\r\nfunction socket_open() {\r\n var url = \"ws://\" + mod.address + ':' + mod.port\r\n mod.socket = new WebSocket(url)\r\n mod.socket.onopen = function (event) {\r\n mod.status.value = \"opened\"\r\n var connect = {}\r\n connect.modCmd = 'connect'\r\n connect.owner = getParameterByName('swOwner')\r\n connect.id = getParameterByName('swID')\r\n socket_send(JSON.stringify(connect))\r\n }\r\n mod.socket.onerror = function (event) {\r\n mod.status.value = \"can not open\"\r\n }\r\n mod.socket.onmessage = function (event) {\r\n mod.status.value = \"receive\"\r\n var swData = JSON.parse(event.data)\r\n if (swData.swType === \"AutoExtractSTL\") {\r\n var partName = swData.data.partName\r\n if (partName.length > 25)\r\n partName = partName.slice(0, 22) +'...'\r\n mod.name.nodeValue = \"name: \" + partName\r\n mod.str = swData.data.stl\r\n stl_load_handler()\r\n }\r\n }\r\n mod.socket.onclose = function (event) {\r\n mod.status.value = \"connection closed\"\r\n }\r\n}\r\nfunction socket_close() {\r\n mod.socket.close()\r\n mod.status.value = \"closed\"\r\n mod.socket = 0\r\n}\r\nfunction socket_send(msg) {\r\n if (mod.socket != 0) {\r\n mod.status.value = \"send\"\r\n mod.socket.send(msg)\r\n }\r\n else {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n}\r\nfunction extract_STL() {\r\n var modcmd = new Object;\r\n modcmd.modCmd = \"AutoExtractSTL\";\r\n modcmd.sag = Number(mod.sag.value); // mm\r\n modcmd.angle = Number(mod.angle.value); //degree\r\n socket_send(JSON.stringify(modcmd))\r\n}\r\n\r\nfunction base64ToArrayBuffer(base64) {\r\n var binary_string = window.atob(base64);\r\n var len = binary_string.length;\r\n var bytes = new Uint8Array(len);\r\n for (var i = 0; i < len; i++) {\r\n bytes[i] = binary_string.charCodeAt(i);\r\n }\r\n return bytes.buffer;\r\n}\r\n\r\n//\r\n// load handler\r\n//\r\nfunction stl_load_handler() {\r\n //\r\n // check for binary STL\r\n //\r\n var arraybuf = base64ToArrayBuffer(mod.str)\r\n var endian = true\r\n var view = new DataView(arraybuf)\r\n var triangles = view.getUint32(80, endian)\r\n var size = 80+4+triangles*(4*12+2)\r\n if (size != view.byteLength) {\r\n mod.sizen.nodeValue = 'error: not binary STL'\r\n mod.trianglesn.nodeValue = ''\r\n mod.dxn.nodeValue = ''\r\n mod.dyn.nodeValue = ''\r\n mod.dzn.nodeValue = ''\r\n return\r\n }\r\n mod.sizen.nodeValue = 'size: '+size\r\n mod.trianglesn.nodeValue = 'triangles: '+triangles\r\n //\r\n // find limits and draw\r\n //\r\n var blob = new Blob(['('+draw_limits_worker.toString()+'())'])\r\n var url = window.URL.createObjectURL(blob)\r\n var webworker = new Worker(url)\r\n webworker.addEventListener('message',function(evt) {\r\n //\r\n // worker response\r\n //\r\n window.URL.revokeObjectURL(url)\r\n //\r\n // size\r\n //\r\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\r\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\r\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\r\n //\r\n // image\r\n //\r\n var image = evt.data.image\r\n var height = mod.canvas.height\r\n var width = mod.canvas.width\r\n var buffer = new Uint8ClampedArray(evt.data.image)\r\n var imgdata = new ImageData(buffer,width,height)\r\n var ctx = mod.canvas.getContext(\"2d\")\r\n ctx.putImageData(imgdata,0,0)\r\n //\r\n // output\r\n //\r\n outputs.mesh.event(evt.data.mesh)\r\n })\r\n var ctx = mod.canvas.getContext(\"2d\")\r\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\r\n var img = ctx.getImageData(0, 0, mod.canvas.width, mod.canvas.height)\r\n //\r\n // call worker\r\n //\r\n webworker.postMessage({\r\n height:mod.canvas.height,width:mod.canvas.width,\r\n image: img.data.buffer, mesh: arraybuf},\r\n [img.data.buffer, arraybuf])\r\n }\r\nfunction draw_limits_worker() {\r\n self.addEventListener('message',function(evt) {\r\n //\r\n // function to draw line\r\n //\r\n function line(x0,y0,x1,y1) {\r\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\r\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\r\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\r\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\r\n var row,col\r\n var idx = ix1-ix0\r\n var idy = iy1-iy0\r\n if (Math.abs(idy) > Math.abs(idx)) {\r\n (idy > 0) ?\r\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\r\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\r\n for (row = row0; row <= row1; ++row) {\r\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\r\n image[row*width*4+col*4+0] = 0\r\n image[row*width*4+col*4+1] = 0\r\n image[row*width*4+col*4+2] = 0\r\n image[row*width*4+col*4+3] = 255\r\n }\r\n }\r\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\r\n (idx > 0) ?\r\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\r\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\r\n for (col = col0; col <= col1; ++col) {\r\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\r\n image[row*width*4+col*4+0] = 0\r\n image[row*width*4+col*4+1] = 0\r\n image[row*width*4+col*4+2] = 0\r\n image[row*width*4+col*4+3] = 255\r\n }\r\n }\r\n else {\r\n row = iy0\r\n col = ix0\r\n image[row*width*4+col*4+0] = 0\r\n image[row*width*4+col*4+1] = 0\r\n image[row*width*4+col*4+2] = 0\r\n image[row*width*4+col*4+3] = 255\r\n }\r\n }\r\n //\r\n // get variables\r\n //\r\n var height = evt.data.height\r\n var width = evt.data.width\r\n var endian = true\r\n var image = new Uint8ClampedArray(evt.data.image)\r\n var view = new DataView(evt.data.mesh)\r\n var triangles = view.getUint32(80,endian)\r\n //\r\n // find limits\r\n //\r\n var offset = 80+4\r\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\r\n var xmin = Number.MAX_VALUE\r\n var xmax = -Number.MAX_VALUE\r\n var ymin = Number.MAX_VALUE\r\n var ymax = -Number.MAX_VALUE\r\n var zmin = Number.MAX_VALUE\r\n var zmax = -Number.MAX_VALUE\r\n for (var t = 0; t < triangles; ++t) {\r\n offset += 3*4\r\n x0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (x0 > xmax) xmax = x0\r\n if (x0 < xmin) xmin = x0\r\n y0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (y0 > ymax) ymax = y0\r\n if (y0 < ymin) ymin = y0\r\n z0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (z0 > zmax) zmax = z0\r\n if (z0 < zmin) zmin = z0\r\n x1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (x1 > xmax) xmax = x1\r\n if (x1 < xmin) xmin = x1\r\n y1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (y1 > ymax) ymax = y1\r\n if (y1 < ymin) ymin = y1\r\n z1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (z1 > zmax) zmax = z1\r\n if (z1 < zmin) zmin = z1\r\n x2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (x2 > xmax) xmax = x2\r\n if (x2 < xmin) xmin = x2\r\n y2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (y2 > ymax) ymax = y2\r\n if (y2 < ymin) ymin = y2\r\n z2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (z2 > zmax) zmax = z2\r\n if (z2 < zmin) zmin = z2\r\n offset += 2\r\n }\r\n var dx = xmax-xmin\r\n var dy = ymax-ymin\r\n var dz = zmax-zmin\r\n //\r\n // draw mesh\r\n //\r\n if (dx > dy) {\r\n var xo = 0\r\n var yo = height*.5*(1-dy/dx)\r\n var xw = width-1\r\n var yh = (width-1)*dy/dx\r\n }\r\n else {\r\n var xo = width*.5*(1-dx/dy)\r\n var yo = 0\r\n var xw = (height-1)*dx/dy\r\n var yh = height-1\r\n }\r\n offset = 80+4\r\n for (var t = 0; t < triangles; ++t) {\r\n offset += 3*4\r\n x0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n y0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n z0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n x1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n y1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n z1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n x2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n y2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n z2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n offset += 2\r\n line(x0,y0,x1,y1)\r\n line(x1,y1,x2,y2)\r\n line(x2,y2,x0,y0)\r\n }\r\n //\r\n // return results and close\r\n //\r\n self.postMessage({\r\n dx:dx,dy:dy,dz:dz,\r\n image:evt.data.image,mesh:evt.data.mesh},[evt.data.image,evt.data.mesh])\r\n self.close()\r\n })\r\n }\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n mod:mod,\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"239.08229921859862","left":"541.1152264935706","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7667165137781767\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.7667165137781767\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"depth\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7667165137781767\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4144526456371104\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7335721197356367\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.7335721197356367\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4793941661670936\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9327347189729929\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}"]} \ No newline at end of file diff --git a/programs/machines/RNDMC/honeycomb connect b/programs/machines/RNDMC/honeycomb connect new file mode 100644 index 0000000000000000000000000000000000000000..d7dbba86b932ff45382f26fa7e9c503d1c0392c9 --- /dev/null +++ b/programs/machines/RNDMC/honeycomb connect @@ -0,0 +1 @@ +{"modules":{"0.22479242263560417":{"definition":"//\r\n// RNDMC server module\r\n//\r\n// Neil Gershenfeld \r\n// (c) Massachusetts Institute of Technology 2017\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n//\r\n// name\r\n//\r\nvar name = 'WebSocket RNDMC'\r\n//\r\n// initialization\r\n//\r\nvar init = function() {\r\n mod.address.value = '127.0.0.1'\n mod.port.value = '1234'\n mod.socket = null\r\n socket_open()\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {\r\n path:{type:'array',\r\n event: function (evt) {\r\n mod.path = evt.detail\r\n mod.label.nodeValue = 'send path to RNDMC'\r\n console.log(JSON.stringify(mod.path))\r\n }}}\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n }\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n //\r\n // server\r\n //\r\n div.appendChild(document.createTextNode('address: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.address = input\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0port: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.port = input\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0status: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.status = input\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // open/close\r\n //\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('open'))\r\n btn.addEventListener('click',function() {\r\n socket_open()\r\n })\r\n div.appendChild(btn)\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.addEventListener('click',function() {\r\n socket_close()\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n var span = document.createElement('span')\r\n var text = document.createTextNode('waiting for file')\r\n mod.label = text\r\n span.appendChild(text)\r\n mod.labelspan = span\r\n btn.appendChild(span)\r\n btn.addEventListener('click', function () {\r\n if (mod.socket == null) {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n else if (mod.label.nodeValue == 'send path to RNDMC') {\r\n socket_send(JSON.stringify(mod.path))\r\n mod.label.nodeValue = 'cancel'\r\n }\r\n else if (mod.label.nodeValue == 'cancel') {\r\n socket_send('cancel')\r\n }\r\n })\r\n div.appendChild(btn)\r\n }\r\n//\r\n// local functions\r\n//\r\nfunction socket_open() {\r\n var url = \"ws://\"+mod.address.value+':'+mod.port.value\r\n mod.socket = new WebSocket(url)\r\n mod.socket.onopen = function(event) {\r\n mod.status.value = \"socket opened\"\r\n }\r\n mod.socket.onerror = function(event) {\r\n mod.status.value = \"can not open socket\"\r\n mod.socket = null\r\n }\r\n mod.socket.onmessage = function(event) {\r\n mod.status.value = event.data\r\n if ((event.data == 'done') || (event.data == 'cancel')\r\n || (event.data.slice(0,5) == 'error')) {\r\n mod.label.nodeValue = 'waiting for path'\r\n mod.labelspan.style.fontWeight = 'normal'\r\n }\r\n }\r\n }\r\nfunction socket_close() {\r\n if (mod.socket) {\r\n mod.socket.close()\r\n mod.status.value = \"socket closed\"\r\n mod.socket = null\r\n }\r\n }\r\nfunction socket_send(msg) {\r\n if (mod.socket != null) {\r\n mod.status.value = \"send\"\r\n mod.socket.send(msg)\r\n }\r\n else {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n }\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n mod:mod,\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"348.87777325850277","left":"755.4800755846891","inputs":{},"outputs":{}},"0.7272977112269394":{"definition":"//\r\n// convert honeycomb SVG\r\n//\r\n// Neil Gershenfeld \r\n// (c) Massachusetts Institute of Technology 2016\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function () {\r\n //\r\n // module globals\r\n //\r\n var mod = {}\r\n //\r\n // name\r\n //\r\n var name = 'convert honeycomb SVG'\r\n //\r\n // initialization\r\n //\r\n var init = function () {\r\n\r\n }\r\n //\r\n // inputs\r\n //\r\n var inputs = {\r\n SVG: {\r\n type: 'string',\r\n event: function (evt) {\r\n mod.svg = evt.detail\r\n mod.colorpaths = undefined;\r\n get_size()\r\n vectorize()\r\n }\r\n }\r\n }\r\n //\r\n // outputs\r\n //\r\n var outputs = {\r\n path:{type:'array',\r\n event: function () {\r\n mods.output(mod, 'path', mod.path)\r\n }}\r\n }\r\n //\r\n // interface\r\n //\r\n var interface = function (div) {\r\n mod.div = div\r\n //\r\n // on-screen SVG\r\n //\r\n var svgNS = \"http://www.w3.org/2000/svg\"\r\n var svg = document.createElementNS(svgNS, \"svg\")\r\n svg.setAttribute('id', mod.div.id + 'svg')\r\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\r\n \"xmlns:xlink\", \"http://www.w3.org/1999/xlink\")\r\n svg.setAttribute('width', mods.ui.canvas)\r\n svg.setAttribute('height', mods.ui.canvas)\r\n svg.style.backgroundColor = 'rgb(255,255,255)'\r\n var g = document.createElementNS(svgNS, 'g')\r\n g.setAttribute('id', mod.div.id + 'g')\r\n svg.appendChild(g)\r\n div.appendChild(svg)\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // off-screen image canvas\r\n //\r\n var canvas = document.createElement('canvas')\r\n mod.img = canvas\r\n //\r\n // colors\r\n //\r\n div.appendChild(document.createTextNode('select path:'))\r\n div.appendChild(document.createElement('br'))\r\n var select = document.createElement('select')\r\n select.setAttribute('style', 'width:150px');\r\n var el1 = document.createElement('option')\r\n el1.textContent = 'cut'\r\n el1.value = 'red'\r\n select.appendChild(el1)\r\n var el2 = document.createElement('option')\r\n el2.textContent = 'fold mountain'\r\n el2.value = 'green'\r\n select.appendChild(el2)\r\n var el3 = document.createElement('option')\r\n el3.textContent = 'fold valley'\r\n el3.value = 'blue'\r\n select.appendChild(el3)\r\n select.addEventListener(\"change\", function () {\r\n vectorize()\r\n });\r\n div.appendChild(select)\r\n mod.selectPaths = select\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // view button\r\n //\r\n var btn = document.createElement('button')\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('view'))\r\n btn.addEventListener('click', function () {\r\n var win = window.open('')\r\n var btn = document.createElement('button')\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n btn.addEventListener('click', function () {\r\n win.close()\r\n })\r\n win.document.body.appendChild(btn)\r\n win.document.body.appendChild(document.createElement('br'))\r\n var svg = document.getElementById(mod.div.id + 'svg')\r\n var clone = svg.cloneNode(true)\r\n clone.setAttribute('width', mod.width)\r\n clone.setAttribute('height', mod.height)\r\n win.document.body.appendChild(clone)\r\n })\r\n div.appendChild(btn)\r\n }\r\n //\r\n // local functions\r\n //\r\n // get size\r\n //\r\n function get_size() {\r\n var i = mod.svg.indexOf(\"width\")\r\n if (i == -1) {\r\n var width = 1\r\n var height = 1\r\n var units = 90\r\n }\r\n else {\r\n var i1 = mod.svg.indexOf(\"\\\"\", i + 1)\r\n var i2 = mod.svg.indexOf(\"\\\"\", i1 + 1)\r\n var width = mod.svg.substring(i1 + 1, i2)\r\n i = mod.svg.indexOf(\"height\")\r\n i1 = mod.svg.indexOf(\"\\\"\", i + 1)\r\n i2 = mod.svg.indexOf(\"\\\"\", i1 + 1)\r\n var height = mod.svg.substring(i1 + 1, i2)\r\n ih = mod.svg.indexOf(\"height\")\r\n if (width.indexOf(\"px\") != -1) {\r\n width = width.slice(0, -2)\r\n height = height.slice(0, -2)\r\n var units = 90\r\n }\r\n else if (width.indexOf(\"mm\") != -1) {\r\n width = width.slice(0, -2)\r\n height = height.slice(0, -2)\r\n var units = 25.4\r\n }\r\n else if (width.indexOf(\"cm\") != -1) {\r\n width = width.slice(0, -2)\r\n height = height.slice(0, -2)\r\n var units = 2.54\r\n }\r\n else if (width.indexOf(\"in\") != -1) {\r\n width = width.slice(0, -2)\r\n height = height.slice(0, -2)\r\n var units = 1\r\n }\r\n else {\r\n var units = 90\r\n }\r\n }\r\n mod.width = Math.round(parseFloat(width))\r\n mod.height = Math.round(parseFloat(height))\r\n mod.units = units\r\n }\r\n //\r\n // local functions\r\n //\r\n // vectorize\r\n //\r\n function vectorize() {\r\n\r\n svg_to_path();\r\n mod.path = mod.colorpaths[mod.selectPaths.value]\r\n draw_path(mod.path);\r\n outputs.path.event(mod.path)\r\n }\r\n //\r\n // draw path\r\n //\r\n function draw_path(path) {\r\n var svg = document.getElementById(mod.div.id + 'svg')\r\n svg.setAttribute('viewBox', \"0 0 \" + (mod.width *1.1- 1) + \" \" + (mod.height- 1))\r\n var g = document.getElementById(mod.div.id + 'g')\r\n svg.removeChild(g)\r\n var g = document.createElementNS('http://www.w3.org/2000/svg', 'g')\r\n g.setAttribute('id', mod.div.id + 'g')\r\n var h = mod.height\r\n var w = mod.width\r\n var xend = null\r\n var yend = null\r\n //\r\n // loop over segments\r\n //\r\n for (var segment in path) {\r\n if (path[segment].length > 1) {\r\n if (xend != null) {\r\n //\r\n // draw connection from previous segment\r\n //\r\n var line = document.createElementNS('http://www.w3.org/2000/svg', 'line')\r\n line.setAttribute('stroke', 'red')\r\n line.setAttribute('stroke-width', 1)\r\n line.setAttribute('stroke-linecap', 'round')\r\n var x1 = xend\r\n var y1 = yend\r\n var x2 = path[segment][0][0]\r\n var y2 = h - path[segment][0][1] - 1\r\n line.setAttribute('x1', x1)\r\n line.setAttribute('y1', y1)\r\n line.setAttribute('x2', x2)\r\n line.setAttribute('y2', y2)\r\n var dx = x2 - x1\r\n var dy = y2 - y1\r\n var d = Math.sqrt(dx * dx + dy * dy)\r\n if (d > 0) {\r\n nx = 6 * dx / d\r\n ny = 6 * dy / d\r\n var tx = 3 * dy / d\r\n var ty = -3 * dx / d\r\n g.appendChild(line)\r\n triangle = document.createElementNS('http://www.w3.org/2000/svg', 'polygon')\r\n triangle.setAttribute('points', x2 + ',' + y2 + ' ' + (x2 - nx + tx) + ',' + (y2 - ny + ty)\r\n + ' ' + (x2 - nx - tx) + ',' + (y2 - ny - ty))\r\n triangle.setAttribute('fill', 'red')\r\n g.appendChild(triangle)\r\n }\r\n }\r\n //\r\n // loop over points\r\n //\r\n for (var point = 1; point < path[segment].length; ++point) {\r\n var line = document.createElementNS('http://www.w3.org/2000/svg', 'line')\r\n line.setAttribute('stroke', 'black')\r\n line.setAttribute('stroke-width', 1)\r\n line.setAttribute('stroke-linecap', 'round')\r\n var x1 = path[segment][point - 1][0]\r\n var y1 = h - path[segment][point - 1][1] - 1\r\n var x2 = path[segment][point][0]\r\n var y2 = h - path[segment][point][1] - 1\r\n xend = x2\r\n yend = y2\r\n line.setAttribute('x1', x1)\r\n line.setAttribute('y1', y1)\r\n line.setAttribute('x2', x2)\r\n line.setAttribute('y2', y2)\r\n var dx = x2 - x1\r\n var dy = y2 - y1\r\n var d = Math.sqrt(dx * dx + dy * dy)\r\n if (d > 0) {\r\n nx = 6 * dx / d\r\n ny = 6 * dy / d\r\n var tx = 3 * dy / d\r\n var ty = -3 * dx / d\r\n g.appendChild(line)\r\n triangle = document.createElementNS('http://www.w3.org/2000/svg', 'polygon')\r\n triangle.setAttribute('points', x2 + ',' + y2 + ' ' + (x2 - nx + tx) + ',' + (y2 - ny + ty)\r\n + ' ' + (x2 - nx - tx) + ',' + (y2 - ny - ty))\r\n triangle.setAttribute('fill', 'black')\r\n g.appendChild(triangle)\r\n }\r\n }\r\n }\r\n }\r\n svg.appendChild(g)\r\n }\r\n //\r\n // convert honeycomb svg to path\r\n //\r\n function svg_to_path() {\r\n var svg = mod.svg\r\n if (mod.colorpaths) {\r\n return\r\n }\r\n var colorpaths = { 'red': [], 'blue': [], 'green': [] }\r\n var parser = new DOMParser();\r\n var doc = parser.parseFromString(svg, \"application/xml\");\r\n var nextElm = doc.children[0].firstElementChild;\r\n var colorpaths = { 'red': [], 'blue': [], 'green': [] };\r\n var valueToInt = function (val) {\r\n var mmToPixel = 3.7795275591;\r\n return Math.round(parseFloat(val) *mmToPixel)\r\n }\r\n var sameCoord = function (coord1, coord2) {\r\n return coord1[0] === coord2[0] && coord1[1] === coord2[1];\r\n }\r\n\r\n while (nextElm) {\r\n var color;\r\n var styles = nextElm.getAttribute('style').split(';');\r\n for (stylei in styles) {\r\n var style = styles[stylei].split(':')\r\n if (style[0] === 'stroke') {\r\n color = style[1];\r\n }\r\n }\r\n\r\n paths = colorpaths[color];\r\n if (nextElm.tagName === 'line') {\r\n var x1 = valueToInt(nextElm.getAttribute('x1'));\r\n var y1 = mod.height - valueToInt(nextElm.getAttribute('y1'));\r\n var x2 = valueToInt(nextElm.getAttribute('x2'));\r\n var y2 = mod.height - valueToInt(nextElm.getAttribute('y2'));\r\n var appended = false;\r\n for (pathi in paths) {\r\n var path = paths[pathi];\r\n if (sameCoord(path[path.length - 1], [x1, y1])) {\r\n path.push([x2, y2]);\r\n appended = true;\r\n }\r\n }\r\n if (!appended) {\r\n paths.push([[x1, y1], [x2, y2]])\r\n }\r\n }\r\n else if (nextElm.tagName === 'polyline') {\r\n var points = [];\r\n var pts = nextElm.getAttribute('points').split(' ');\r\n for (pti in pts) {\r\n var coords = pts[pti].split(',')\r\n points.push([valueToInt(coords[0]), mod.height - valueToInt(coords[1])]);\r\n }\r\n paths.push(points);\r\n }\r\n\r\n nextElm = nextElm.nextElementSibling;\r\n }\r\n \r\n mod.colorpaths = colorpaths;\r\n }\r\n //\r\n // return values\r\n //\r\n return ({\r\n mod: mod,\r\n name: name,\r\n init: init,\r\n inputs: inputs,\r\n outputs: outputs,\r\n interface: interface\r\n })\r\n}())\r\n","top":"185.78584481355912","left":"379.4665171150598","inputs":{},"outputs":{}},"0.29776381938511776":{"definition":"//\r\n// Honeycomb module extracts flatten honeycomb sheet from Tools/FabLab Connect command of SolidWorks products\r\n// \r\n// Shawn Liu @ Dassault Systemes SolidWorks Corporation\r\n// (c) Massachusetts Institute of Technology 2019\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n//\r\n// name\r\n//\r\nvar name = 'Honeycomb connect'\r\n//\r\n// initialization\r\n//\r\nvar init = function() {\r\n mod.address = getParameterByName('swIP') || '127.0.0.1'\r\n mod.port = getParameterByName('swPort') || '80'\r\n mod.socket = 0\r\n socket_open()\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {}\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n SVG: {\r\n type: 'string',\r\n event: function () {\r\n mods.output(mod, 'SVG', mod.str)\r\n }\r\n },\r\n file: {\r\n type: 'object',\r\n event: function (str) {\r\n obj = {}\r\n obj.name = mod.partName + \".svg\"\r\n obj.contents = str\r\n mods.output(mod, 'file', obj)\r\n }\r\n }\r\n}\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n\r\n // on-screen drawing canvas\r\n //\r\n var canvas = document.createElement('canvas')\r\n canvas.width = mods.ui.canvas\r\n canvas.height = mods.ui.canvas\r\n canvas.style.backgroundColor = 'rgb(255,255,255)'\r\n div.appendChild(canvas)\r\n mod.canvas = canvas\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // off-screen image canvas\r\n //\r\n var canvas = document.createElement('canvas')\r\n mod.img = canvas\r\n\r\n div.appendChild(document.createTextNode('server:'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('address: ' + getParameterByName('swIP')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0port: ' + getParameterByName('swPort')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0status: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 12\r\n div.appendChild(input)\r\n mod.status = input\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('open'))\r\n btn.addEventListener('click', function () {\r\n socket_open()\r\n })\r\n div.appendChild(btn)\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.addEventListener('click', function () {\r\n socket_close()\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('Extract SVG'))\r\n btn.addEventListener('click', function () {\r\n extract_SVG()\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // view button\r\n //\r\n var btn = document.createElement('button')\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('view'))\r\n btn.addEventListener('click', function () {\r\n var win = window.open('')\r\n var btn = document.createElement('button')\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n btn.addEventListener('click', function () {\r\n win.close()\r\n })\r\n win.document.body.appendChild(btn)\r\n win.document.body.appendChild(document.createElement('br'))\r\n var canvas = document.createElement('canvas')\r\n canvas.width = mod.img.width\r\n canvas.height = mod.img.height\r\n win.document.body.appendChild(canvas)\r\n var ctx = canvas.getContext(\"2d\")\r\n ctx.drawImage(mod.img, 0, 0)\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n\r\n //\r\n // info div\r\n //\r\n var info = document.createElement('div')\r\n info.setAttribute('id', div.id + 'info')\r\n mod.name = document.createTextNode('name:')\r\n info.appendChild(mod.name)\r\n mod.thickness = document.createTextNode('thickness: ')\r\n div.appendChild(mod.thickness)\r\n info.appendChild(document.createElement('br'))\r\n mod.width = document.createTextNode('width:')\r\n info.appendChild(mod.width)\r\n info.appendChild(document.createElement('br'))\r\n mod.height = document.createTextNode('height:')\r\n info.appendChild(mod.height)\r\n div.appendChild(info)\r\n\r\n }\r\n//\r\n// local functions\r\n//\r\n\r\nfunction getParameterByName(name, url) {\r\n if (!url) url = window.location.href;\r\n name = name.replace(/[\\[\\]]/g, \"\\\\$&\");\r\n var regex = new RegExp(\"[?&]\" + name + \"(=([^&#]*)|&|#|$)\"),\r\n results = regex.exec(url);\r\n if (!results) return null;\r\n if (!results[2]) return '';\r\n return decodeURIComponent(results[2].replace(/\\+/g, \" \"));\r\n}\r\n\r\nfunction socket_open() {\r\n var url = \"ws://\"+mod.address+':'+mod.port\r\n mod.socket = new WebSocket(url)\r\n mod.socket.onopen = function(event) {\r\n mod.status.value = \"opened\"\r\n var connect = {}\r\n connect.modCmd = 'connect'\r\n connect.owner = getParameterByName('swOwner')\r\n connect.id = getParameterByName('swID')\r\n socket_send(JSON.stringify(connect))\r\n }\r\n mod.socket.onerror = function(event) {\r\n mod.status.value = \"can not open\"\r\n }\r\n mod.socket.onmessage = function(event) {\r\n mod.status.value = \"receive\"\r\n var swData = JSON.parse(event.data);\r\n if (swData.swType === \"HoneycombFlattenSVG\") {\r\n mod.partName = swData.data.partName\r\n var partName = swData.data.partName\r\n if (partName.length > 25)\r\n partName = partName.slice(0, 22) + '...'\r\n mod.name.nodeValue = \"name: \" + partName\r\n mod.thickness.nodeValue = \"thickness: \" + swData.data.thickness + ' (mm)';\r\n mod.str = swData.data.svg\r\n svg_load_handler()\r\n outputs.SVG.event(mod.str)\r\n outputs.file.event(mod.str)\r\n }\r\n }\r\n mod.socket.onclose = function (event) {\r\n mod.status.value = \"connection closed\"\r\n }\r\n }\r\nfunction socket_close() {\r\n mod.socket.close()\r\n mod.status.value = \"closed\"\r\n mod.socket = 0\r\n }\r\nfunction socket_send(msg) {\r\n if (mod.socket != 0) {\r\n mod.status.value = \"send\"\r\n mod.socket.send(msg)\r\n }\r\n else {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n }\r\nfunction extract_SVG() {\r\n var modcmd = new Object;\r\n modcmd.modCmd = \"AutoHoneycombFlatten\";\r\n socket_send(JSON.stringify(modcmd))\r\n }\r\n\r\n //\r\n // load handler\r\n //\r\nfunction svg_load_handler() {\r\n //\r\n // parse size\r\n //\r\n var i = mod.str.indexOf(\"width\")\r\n if (i == -1) {\r\n mod.width.nodeValue = \"width: not found\"\r\n mod.height.nodeValue = \"height: not found\"\r\n }\r\n else {\r\n var i1 = mod.str.indexOf(\"\\\"\", i + 1)\r\n var i2 = mod.str.indexOf(\"\\\"\", i1 + 1)\r\n var width = mod.str.substring(i1 + 1, i2)\r\n i = mod.str.indexOf(\"height\")\r\n i1 = mod.str.indexOf(\"\\\"\", i + 1)\r\n i2 = mod.str.indexOf(\"\\\"\", i1 + 1)\r\n var height = mod.str.substring(i1 + 1, i2)\r\n ih = mod.str.indexOf(\"height\")\r\n if (width.indexOf(\"px\") != -1) {\r\n width = width.slice(0, -2)\r\n height = height.slice(0, -2)\r\n var units = 90\r\n }\r\n else if (width.indexOf(\"mm\") != -1) {\r\n width = width.slice(0, -2)\r\n height = height.slice(0, -2)\r\n var units = 25.4\r\n }\r\n else if (width.indexOf(\"cm\") != -1) {\r\n width = width.slice(0, -2)\r\n height = height.slice(0, -2)\r\n var units = 2.54\r\n }\r\n else if (width.indexOf(\"in\") != -1) {\r\n width = width.slice(0, -2)\r\n height = height.slice(0, -2)\r\n var units = 1\r\n }\r\n else {\r\n var units = 90\r\n }\r\n mod.width.nodeValue = \"width: \" + width / 3.543307 + ' (mm)';\r\n mod.height.nodeValue = \"height: \" + height / 3.543307 + ' (mm)';\r\n }\r\n //\r\n // display\r\n //\r\n var img = new Image()\r\n var src = \"data:image/svg+xml;base64,\" + window.btoa(mod.str)\r\n img.setAttribute(\"src\", src)\r\n img.onload = function () {\r\n if (img.width > img.height) {\r\n var x0 = 0\r\n var y0 = mod.canvas.height * .5 * (1 - img.height / img.width)\r\n var w = mod.canvas.width\r\n var h = mod.canvas.width * img.height / img.width\r\n }\r\n else {\r\n var x0 = mod.canvas.width * .5 * (1 - img.width / img.height)\r\n var y0 = 0\r\n var w = mod.canvas.height * img.width / img.height\r\n var h = mod.canvas.height\r\n }\r\n var ctx = mod.canvas.getContext(\"2d\")\r\n ctx.clearRect(0, 0, mod.canvas.width, mod.canvas.height)\r\n ctx.drawImage(img, x0, y0, w, h)\r\n var ctx = mod.img.getContext(\"2d\")\r\n ctx.canvas.width = img.width\r\n ctx.canvas.height = img.height\r\n ctx.drawImage(img, 0, 0)\r\n outputs.SVG.event()\r\n }\r\n}\r\n\r\n\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n mod:mod,\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"44.031708627888705","left":"-17.91909545661251","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.7272977112269394\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.22479242263560417\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.29776381938511776\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVG\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7272977112269394\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVG\\\"}\"}"]} \ No newline at end of file diff --git a/programs/machines/Roland/mill/SRM-20/mill 2.5D stl connect b/programs/machines/Roland/mill/SRM-20/mill 2.5D stl connect new file mode 100644 index 0000000000000000000000000000000000000000..769f2d214774507850cd1750e9bb26523581c8ad --- /dev/null +++ b/programs/machines/Roland/mill/SRM-20/mill 2.5D stl connect @@ -0,0 +1 @@ +{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"642.7985710158229","left":"3548.883638471559","inputs":{},"outputs":{}},"0.07944144280928633":{"definition":"//\n// edge detect\n// green = interior, blue = exterior, red = boundary\n// assumes input is thresholded\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'edge detect'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n edge_detect()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:interior, blue:exterior, red:boundary'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// edge detect\n//\nfunction edge_detect() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({worker:worker.toString(),\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var i00,i0m,i0p,im0,ip0,imm,imp,ipm,ipp,row,col\n //\n // find edges - interior\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != i0m) || (i00 != im0) || (i00 != imm)\n || (i00 != imp) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // left and right edges\n //\n for (row = 1; row < (h-1); ++row) {\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm) \n || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // top and bottom edges\n //\n for (col = 1; col < (w-1); ++col) {\n row = h-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != imm) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != ipm) \n || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // corners\n //\n row = 0\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"992.7985710158231","left":"4032.883638471559","inputs":{},"outputs":{}},"0.8903773266711255":{"definition":"//\n// orient edges\n// input is green:interior, blue:exterior, red:boundary\n// output is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'orient edges'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n var ctx = mod.display.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n orient_edges()\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // off-screen display canvas\n //\n var canvas = document.createElement('canvas')\n mod.display = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('red:north, dark red:south'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:east, dark green:west'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('blue:start, dark blue:stop'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.display,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// orient edges\n//\nfunction orient_edges() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n var disp = new Uint8ClampedArray(evt.data.display)\n var dispdata = new ImageData(disp,w,h)\n var ctx = mod.display.getContext(\"2d\")\n ctx.putImageData(dispdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var w = mod.canvas.width\n var h = mod.canvas.height\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,w,h)\n ctx.drawImage(mod.display,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var row,col\n var boundary = 0\n var interior = 1\n var exterior = 2\n var alpha = 3\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n //\n // orient body states\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && ((input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && ((input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n }\n }\n }\n //\n // orient edge states\n //\n for (col = 1; col < (w-1); ++col) {\n row = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n row = h-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n for (row = 1; row < (h-1); ++row) {\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n //\n // orient corner states (todo)\n //\n row = 0\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = 0\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n //\n // invert background for display\n //\n var display = new Uint8ClampedArray(h*w*4)\n var r,g,b,i\n for (row = 0; row < h; ++row) {\n for (col = 0; col < w; ++col) {\n r = output[(h-1-row)*w*4+col*4+0]\n g = output[(h-1-row)*w*4+col*4+1]\n b = output[(h-1-row)*w*4+col*4+2]\n i = r+g+b\n if (i != 0) { \n display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]\n display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]\n display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]\n display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]\n }\n else {\n display[(h-1-row)*w*4+col*4+0] = 255\n display[(h-1-row)*w*4+col*4+1] = 255\n display[(h-1-row)*w*4+col*4+2] = 255\n display[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // return output\n //\n self.postMessage({buffer:output.buffer,display:display.buffer},[output.buffer,display.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1086.7985710158232","left":"3586.883638471559","inputs":{},"outputs":{}},"0.6488303557466412":{"definition":"//\n// image threshold\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'image threshold'\n//\n// initialization\n//\nvar init = function() {\n mod.threshold.value = '0.5'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n threshold_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // threshold value\n //\n div.appendChild(document.createTextNode('threshold (0-1): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n threshold_image()\n })\n div.appendChild(input)\n mod.threshold = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// threshold image\n//\nfunction threshold_image() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var t = parseFloat(mod.threshold.value)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,threshold:t,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var t = evt.data.threshold\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,i\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n r = buf[(h-1-row)*w*4+col*4+0] \n g = buf[(h-1-row)*w*4+col*4+1] \n b = buf[(h-1-row)*w*4+col*4+2] \n a = buf[(h-1-row)*w*4+col*4+3] \n i = (r+g+b)/(3*255)\n if (a == 0)\n val = 255\n else if (i > t)\n var val = 255\n else\n var val = 0\n buf[(h-1-row)*w*4+col*4+0] = val\n buf[(h-1-row)*w*4+col*4+1] = val\n buf[(h-1-row)*w*4+col*4+2] = val\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"543.7985710158231","left":"3132.883638471559","inputs":{},"outputs":{}},"0.749132408760488":{"definition":"//\n// vectorize\n// input is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = '1'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n vectorize()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'array',\n event:function(){\n mods.output(mod,'path',mod.path)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br')) \n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // error value\n //\n div.appendChild(document.createTextNode('vector fit (pixels): '))\n //div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n vectorize()\n })\n div.appendChild(input)\n mod.error = input\n div.appendChild(document.createElement('br'))\n //\n // sort\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n input.checked = true\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// vectorize\n//\nfunction vectorize() {\n //\n // draw path\n //\n function draw_path(path) {\n window.URL.revokeObjectURL(url)\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute('viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment in path) {\n if (path[segment].length > 1) {\n if (xend != null) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = xend\n var y1 = yend\n var x2 = path[segment][0][0]\n var y2 = h-path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n svg.appendChild(g)\n }\n //\n // set up worker\n //\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n webworker.terminate()\n mod.path = evt.data.path\n draw_path(mod.path)\n outputs.path.event()\n })\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,sort:mod.sort.checked,\n error:parseFloat(mod.error.value),\n buffer:mod.input.data.buffer})\n }\n//\n// vectorize worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var sort = evt.data.sort\n var input = new Uint8ClampedArray(evt.data.buffer)\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n var path = []\n //\n // edge follower\n //\n function follow_edges(row,col) {\n if ((input[(h-1-row)*w*4+col*4+northsouth] != 0)\n || (input[(h-1-row)*w*4+col*4+eastwest] != 0)) {\n path[path.length] = [[col,row]]\n while (1) {\n if (input[(h-1-row)*w*4+col*4+northsouth] & north) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~north\n row += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+northsouth] & south) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~south\n row -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & east) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~east\n col += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & west) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~west\n col -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else\n break\n }\n }\n }\n //\n // follow boundary starts\n //\n for (var row = 1; row < (h-1); ++row) {\n col = 0\n follow_edges(row,col)\n col = w-1\n follow_edges(row,col)\n }\n for (var col = 1; col < (w-1); ++col) {\n row = 0\n follow_edges(row,col)\n row = h-1 \n follow_edges(row,col)\n }\n //\n // follow interior paths\n //\n for (var row = 1; row < (h-1); ++row) {\n for (var col = 1; col < (w-1); ++col) {\n follow_edges(row,col)\n }\n }\n //\n // vectorize path\n //\n var error = evt.data.error\n var vecpath = []\n for (var seg = 0; seg < path.length; ++seg) {\n var x0 = path[seg][0][0]\n var y0 = path[seg][0][1]\n vecpath[vecpath.length] = [[x0,y0]]\n var xsum = x0\n var ysum = y0\n var sum = 1\n for (var pt = 1; pt < path[seg].length; ++pt) {\n var xold = x\n var yold = y\n var x = path[seg][pt][0]\n var y = path[seg][pt][1]\n if (sum == 1) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n var xmean = xsum/sum\n var ymean = ysum/sum\n var dx = xmean-x0\n var dy = ymean-y0\n var d = Math.sqrt(dx*dx+dy*dy)\n var nx = dy/d\n var ny = -dx/d\n var l = Math.abs(nx*(x-x0)+ny*(y-y0))\n if (l < error) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [xold,yold]\n x0 = xold\n y0 = yold\n xsum = xold\n ysum = yold\n sum = 1\n }\n }\n if (pt == (path[seg].length-1)) {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [x,y]\n }\n }\n }\n //\n // sort path\n //\n if ((vecpath.length > 0) && (sort == true)) {\n var dmin = w*w+h*h\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][0]\n var d = x*x+y*y\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n var sortpath = [vecpath[segmin]]\n vecpath.splice(segmin,1)\n }\n while (vecpath.length > 0) {\n var dmin = w*w+h*h\n var x0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][0]\n var y0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][1]\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][1]\n var d = (x-x0)*(x-x0)+(y-y0)*(y-y0)\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n sortpath[sortpath.length] = vecpath[segmin]\n vecpath.splice(segmin,1)\n }\n }\n }\n else if ((vecpath.length > 0) && (sort == false))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"991.7985710158231","left":"3167.883638471559","inputs":{},"outputs":{}},"0.3040697193095865":{"definition":"//\n// mesh rotate\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 = 'mesh rotate'\n//\n// initialization\n//\nvar init = function() {\n mod.rx.value = '0'\n mod.ry.value = '0'\n mod.rz.value = '0'\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = evt.detail\n rotate_mesh()}}}\n//\n// outputs\n//\nvar outputs = {\n mesh:{type:'STL',\n event:function(buffer){\n mods.output(mod,'mesh',buffer)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // 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 // rotation\n //\n div.appendChild(document.createTextNode('rotation (degrees):'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' x: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rx = input\n div.appendChild(document.createTextNode(' y: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.ry = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' z: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rz = input\n div.appendChild(document.createTextNode(' (enter)'))\n div.appendChild(document.createElement('br'))\n //\n // info\n //\n var text = document.createTextNode('dx:')\n div.appendChild(text)\n mod.dxn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dy:')\n div.appendChild(text)\n mod.dyn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dz:')\n div.appendChild(text)\n mod.dzn = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n// rotate mesh\n//\nfunction rotate_mesh() {\n //\n // check for binary STL\n //\n var endian = true\n var view = new DataView(mod.mesh)\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits, rotate, and draw\n //\n var blob = new Blob(['('+rotate_mesh_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n //\n // worker response\n //\n window.URL.revokeObjectURL(url)\n //\n // size\n //\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\n //\n // image\n //\n var image = evt.data.image\n var height = mod.canvas.height\n var width = mod.canvas.width\n var buffer = new Uint8ClampedArray(evt.data.image)\n var imgdata = new ImageData(buffer,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n //\n // mesh\n //\n mod.mesh = evt.data.mesh\n //\n // output\n //\n outputs.mesh.event(evt.data.rotate)\n })\n //\n // call worker\n //\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var img = ctx.getImageData(0,0,mod.canvas.width,mod.canvas.height)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n webworker.postMessage({\n height:mod.canvas.height,width:mod.canvas.width,\n rx:rx,ry:ry,rz:rz,\n image:img.data.buffer,mesh:mod.mesh},\n [img.data.buffer,mod.mesh])\n }\nfunction rotate_mesh_worker() {\n self.addEventListener('message',function(evt) {\n //\n // function to draw line\n //\n function line(x0,y0,x1,y1) {\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\n var row,col\n var idx = ix1-ix0\n var idy = iy1-iy0\n if (Math.abs(idy) > Math.abs(idx)) {\n (idy > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (row = row0; row <= row1; ++row) {\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\n (idx > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (col = col0; col <= col1; ++col) {\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else {\n row = iy0\n col = ix0\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n //return([x3,y3,z3])\n return({x:x3,y:y3,z:z3})\n }\n //\n // get variables\n //\n var height = evt.data.height\n var width = evt.data.width\n var rx = evt.data.rx\n var ry = evt.data.ry\n var rz = evt.data.rz\n var endian = true\n var image = new Uint8ClampedArray(evt.data.image)\n var view = new DataView(evt.data.mesh)\n var triangles = view.getUint32(80,endian)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0.x > xmax) xmax = p0.x\n if (p0.x < xmin) xmin = p0.x\n if (p0.y > ymax) ymax = p0.y\n if (p0.y < ymin) ymin = p0.y\n if (p0.z > zmax) zmax = p0.z\n if (p0.z < zmin) zmin = p0.z\n var p1 = rotate(x1,y1,z1)\n if (p1.x > xmax) xmax = p1.x\n if (p1.x < xmin) xmin = p1.x\n if (p1.y > ymax) ymax = p1.y\n if (p1.y < ymin) ymin = p1.y\n if (p1.z > zmax) zmax = p1.z\n if (p1.z < zmin) zmin = p1.z\n var p2 = rotate(x2,y2,z2)\n if (p2.x > xmax) xmax = p2.x\n if (p2.x < xmin) xmin = p2.x\n if (p2.y > ymax) ymax = p2.y\n if (p2.y < ymin) ymin = p2.y\n if (p2.z > zmax) zmax = p2.z\n if (p2.z < zmin) zmin = p2.z\n }\n var dx = xmax-xmin\n var dy = ymax-ymin\n var dz = zmax-zmin\n //\n // copy mesh\n //\n var newbuf = evt.data.mesh.slice(0)\n var newview = new DataView(newbuf)\n //\n // copy and draw mesh\n //\n if (dx > dy) {\n var xo = 0\n var yo = height*.5*(1-dy/dx)\n var xw = (width-1)\n var yh = (width-1)*dy/dx\n }\n else {\n var xo = width*.5*(1-dx/dy)\n var yo = 0\n var xw = (height-1)*dx/dy\n var yh = (height-1)\n }\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n line(p0.x,p0.y,p1.x,p1.y)\n line(p1.x,p1.y,p2.x,p2.y)\n line(p2.x,p2.y,p0.x,p0.y)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.z,endian)\n newoffset += 4\n newoffset += 2\n }\n //\n // return results and close\n //\n self.postMessage({\n dx:dx,dy:dy,dz:dz,\n image:evt.data.image,mesh:evt.data.mesh,rotate:newbuf},\n [evt.data.image,evt.data.mesh,newbuf])\n self.close()\n })\n }\nfunction old_rotate_mesh() {\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n return([x3,y3,z3])\n }\n //\n // get vars\n //\n var view = mod.mesh\n var endian = true\n var triangles = view.getUint32(80,endian)\n mod.triangles = triangles\n var size = 80+4+triangles*(4*12+2)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0[0] > xmax) xmax = p0[0]\n if (p0[0] < xmin) xmin = p0[0]\n if (p0[1] > ymax) ymax = p0[1]\n if (p0[1] < ymin) ymin = p0[1]\n if (p0[2] > zmax) zmax = p0[2]\n if (p0[2] < zmin) zmin = p0[2]\n var p1 = rotate(x1,y1,z1)\n if (p1[0] > xmax) xmax = p1[0]\n if (p1[0] < xmin) xmin = p1[0]\n if (p1[1] > ymax) ymax = p1[1]\n if (p1[1] < ymin) ymin = p1[1]\n if (p1[2] > zmax) zmax = p1[2]\n if (p1[2] < zmin) zmin = p1[2]\n var p2 = rotate(x2,y2,z2)\n if (p2[0] > xmax) xmax = p2[0]\n if (p2[0] < xmin) xmin = p2[0]\n if (p2[1] > ymax) ymax = p2[1]\n if (p2[1] < ymin) ymin = p2[1]\n if (p2[2] > zmax) zmax = p2[2]\n if (p2[2] < zmin) zmin = p2[2]\n }\n mod.dx = xmax-xmin\n mod.dy = ymax-ymin\n mod.dz = zmax-zmin\n mod.dxn.nodeValue = 'dx: '+mod.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+mod.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+mod.dz.toFixed(3)\n mod.xmin = xmin\n mod.ymin = ymin\n mod.zmin = zmin\n mod.xmax = xmax\n mod.ymax = ymax\n mod.zmax = zmax\n //\n // copy mesh\n //\n var buf = mod.mesh.buffer.slice(0)\n var newview = new DataView(buf)\n //\n // draw projection and save rotation\n //\n var ctx = mod.meshcanvas.getContext('2d')\n var w = mod.meshcanvas.width\n var h = mod.meshcanvas.height\n ctx.clearRect(0,0,w,h)\n var dx = mod.dx\n var dy = mod.dy\n if (dx > dy) {\n var xo = 0\n var yo = h*.5*(1-dy/dx)\n var xw = w\n var yh = w*dy/dx\n }\n else {\n var xo = w*.5*(1-dx/dy)\n var yo = 0\n var xw = h*dx/dy\n var yh = h\n }\n ctx.beginPath()\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n x0 = xo+xw*(p0[0]-xmin)/dx\n y0 = yo+yh*(ymax-p0[1])/dy\n x1 = xo+xw*(p1[0]-xmin)/dx\n y1 = yo+yh*(ymax-p1[1])/dy\n x2 = xo+xw*(p2[0]-xmin)/dx\n y2 = yo+yh*(ymax-p2[1])/dy\n ctx.moveTo(x0,y0)\n ctx.lineTo(x1,y1)\n ctx.lineTo(x2,y2)\n ctx.lineTo(x0,y0)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[2],endian)\n newoffset += 4\n newoffset += 2\n }\n ctx.stroke()\n //\n // generate output\n //\n outputs.mesh.event(buf)\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":"645.4914883550476","left":"1561.5431668558717","inputs":{},"outputs":{}},"0.7667165137781767":{"definition":"//\n// offset\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2019\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'offset'\n//\n// initialization\n//\nvar init = function() {\n mod.offset.value = '19.394860006967658'\n mod.distances = ''\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 if ((mod.offset.value != '') && (mod.distances != ''))\n offset()\n else\n mod.distances = ''\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 // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"534.3490296866185","left":"4143.00249686672","inputs":{},"outputs":{}},"0.32304064019646705":{"definition":"//\n// mesh slice raster\n// \n// todo\n// include slice plane triangles\n// scale perturbation to resolution\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 = 'mesh slice raster'\n//\n// initialization\n//\nvar init = function() {\n mod.mmunits.value = '1'\n mod.inunits.value = '0.03937007874015748'\n mod.depth.value = '35'\n mod.width.value = '1000'\n mod.border.value = '10'\n mod.delta = 1e-6\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = new DataView(evt.detail)\n find_limits_slice()}},\n settings:{type:'',\n event:function(evt){\n for (var p in evt.detail)\n if (p == 'depthmm') {\n mod.depth.value = evt.detail[p]\n /parseFloat(mod.mmunits.value)\n }\n find_limits_slice()}}}\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 imageInfo:{type:'',\n event:function(){\n var obj = {}\n obj.name = \"mesh slice raster\"\n obj.width = mod.img.width\n obj.height = mod.img.height\n obj.dpi = mod.img.width/(mod.dx*parseFloat(mod.inunits.value))\n mods.output(mod,'imageInfo',obj)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen slice canvas\n //\n div.appendChild(document.createTextNode(' '))\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.slicecanvas = 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 // mesh units\n //\n div.appendChild(document.createTextNode('mesh units: (enter)'))\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('change',function(){\n mod.inunits.value = parseFloat(mod.mmunits.value)/25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.mmunits = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n mod.mmunits.value = parseFloat(mod.inunits.value)*25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.inunits = input\n //\n // mesh size\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mesh size:'))\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (units)')\n div.appendChild(text)\n mod.meshsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (mm)')\n div.appendChild(text)\n mod.mmsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (in)')\n div.appendChild(text)\n mod.insize = text\n //\n // slice depth\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice Z depth: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.depth = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice border \n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice border: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.border = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice width\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice width: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.width = input\n div.appendChild(document.createTextNode(' (pixels)'))\n //\n // view slice\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 slice'))\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// find limits then slice\n//\nfunction find_limits_slice() {\n var blob = new Blob(['('+limits_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 mod.triangles = evt.data.triangles\n mod.xmin = evt.data.xmin\n mod.xmax = evt.data.xmax\n mod.ymin = evt.data.ymin\n mod.ymax = evt.data.ymax\n mod.zmin = evt.data.zmin\n mod.zmax = evt.data.zmax\n mod.dx = mod.xmax-mod.xmin\n mod.dy = mod.ymax-mod.ymin\n mod.dz = mod.zmax-mod.zmin\n mod.meshsize.nodeValue = \n mod.dx.toFixed(3)+' x '+\n mod.dy.toFixed(3)+' x '+\n mod.dz.toFixed(3)+' (units)'\n var mm = parseFloat(mod.mmunits.value)\n mod.mmsize.nodeValue = \n (mod.dx*mm).toFixed(3)+' x '+\n (mod.dy*mm).toFixed(3)+' x '+\n (mod.dz*mm).toFixed(3)+' (mm)'\n var inches = parseFloat(mod.inunits.value)\n mod.insize.nodeValue = \n (mod.dx*inches).toFixed(3)+' x '+\n (mod.dy*inches).toFixed(3)+' x '+\n (mod.dz*inches).toFixed(3)+' (in)'\n mods.fit(mod.div)\n slice_mesh()\n })\n var border = parseFloat(mod.border.value)\n webworker.postMessage({\n mesh:mod.mesh,\n border:border,delta:mod.delta})\n }\nfunction limits_worker() {\n self.addEventListener('message',function(evt) {\n var view = evt.data.mesh\n var depth = evt.data.depth\n var border = evt.data.border\n var delta = evt.data.delta // perturb to remove degeneracies\n //\n // get vars\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n offset += 2\n if (x0 > xmax) xmax = x0\n if (x0 < xmin) xmin = x0\n if (y0 > ymax) ymax = y0\n if (y0 < ymin) ymin = y0\n if (z0 > zmax) zmax = z0\n if (z0 < zmin) zmin = z0\n if (x1 > xmax) xmax = x1\n if (x1 < xmin) xmin = x1\n if (y1 > ymax) ymax = y1\n if (y1 < ymin) ymin = y1\n if (z1 > zmax) zmax = z1\n if (z1 < zmin) zmin = z1\n if (x2 > xmax) xmax = x2\n if (x2 < xmin) xmin = x2\n if (y2 > ymax) ymax = y2\n if (y2 < ymin) ymin = y2\n if (z2 > zmax) zmax = z2\n if (z2 < zmin) zmin = z2\n }\n xmin -= border\n xmax += border\n ymin -= border\n ymax += border\n //\n // return\n //\n self.postMessage({triangles:triangles,\n xmin:xmin,xmax:xmax,ymin:ymin,ymax:ymax,\n zmin:zmin,zmax:zmax})\n self.close()\n })\n }\n//\n// slice mesh\n// \nfunction slice_mesh() {\n var blob = new Blob(['('+slice_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.slicecanvas.height*.5*(1-h/w)\n var wd = mod.slicecanvas.width\n var hd = mod.slicecanvas.width*h/w\n }\n else {\n var x0 = mod.slicecanvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.slicecanvas.height*w/h\n var hd = mod.slicecanvas.height\n }\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n outputs.image.event()\n outputs.imageInfo.event()\n })\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n var depth = parseFloat(mod.depth.value)\n mod.img.width = parseInt(mod.width.value)\n mod.img.height = Math.round(mod.img.width*mod.dy/mod.dx)\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.img.height,width:mod.img.width,depth:depth,\n imgbuffer:img.data.buffer,mesh:mod.mesh,\n xmin:mod.xmin,xmax:mod.xmax,\n ymin:mod.ymin,ymax:mod.ymax,\n zmin:mod.zmin,zmax:mod.zmax,\n delta:mod.delta},\n [img.data.buffer])\n }\nfunction slice_worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var depth = evt.data.depth\n var view = evt.data.mesh\n var delta = evt.data.delta // perturb to remove degeneracies\n var xmin = evt.data.xmin\n var xmax = evt.data.xmax\n var ymin = evt.data.ymin\n var ymax = evt.data.ymax\n var zmin = evt.data.zmin\n var zmax = evt.data.zmax\n var buf = new Uint8ClampedArray(evt.data.imgbuffer)\n //\n // get vars from buffer\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // initialize slice image\n //\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] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // find triangles crossing the slice\n //\n var segs = []\n offset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n //\n // assemble vertices\n //\n offset += 2\n var v = [[x0,y0,z0],[x1,y1,z1],[x2,y2,z2]]\n //\n // sort z\n //\n v.sort(function(a,b) {\n if (a[2] < b[2])\n return -1\n else if (a[2] > b[2])\n return 1\n else\n return 0\n })\n //\n // check for crossings\n //\n if ((v[0][2] < (zmax-depth)) && (v[2][2] > (zmax-depth))) {\n //\n // crossing found, check for side and save\n //\n if (v[1][2] < (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[2][0]+(v[1][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n var y1 = v[2][1]+(v[1][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n }\n else if (v[1][2] >= (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[1][0]+(v[0][0]-v[1][0])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n var y1 = v[1][1]+(v[0][1]-v[1][1])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n }\n if (y0 < y1)\n segs.push({x0:x0,y0:y0,x1:x1,y1:y1})\n else\n segs.push({x0:x1,y0:y1,x1:x0,y1:y0})\n }\n }\n //\n // fill interior\n //\n for (var row = 0; row < h; ++row) {\n var y = ymin+(ymax-ymin)*row/(h-1)\n rowsegs = segs.filter(p => ((p.y0 <= y) && (p.y1 >= y)))\n var xs = rowsegs.map(p =>\n (p.x0+(p.x1-p.x0)*(y-p.y0)/(p.y1-p.y0)))\n xs.sort((a,b) => (a-b))\n for (var col = 0; col < w; ++col) {\n var x = xmin+(xmax-xmin)*col/(w-1)\n var index = xs.findIndex((p) => (p >= x))\n if (index == -1)\n var i = 0\n else\n var i = 255*(index%2)\n buf[(h-1-row)*w*4+col*4+0] = i\n buf[(h-1-row)*w*4+col*4+1] = i\n buf[(h-1-row)*w*4+col*4+2] = i\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // output the slice\n //\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n self.close()\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"484.2962306652032","left":"1997.6209440086457","inputs":{},"outputs":{}},"0.4144526456371104":{"definition":"//\n// view path\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2019\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// 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 path'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n path:{type:'',\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 mods.fit(mod.div)\n outputs.path.event()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'',\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,'path',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 renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction open_view_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//\nfunction init_window() {\n //document.write('<script type=\"text/javascript\">'+arg+'</script>')\n //document.close()\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 mod.win.close()\n mod.win = undefined\n })\n mod.win.document.body.appendChild(btn)\n //\n // label text\n //\n var text = win.document.createTextNode(' left: pan, right: rotate, scroll: zoom')\n mod.win.document.body.appendChild(text)\n //\n // GL container\n //\n mod.win.document.body.appendChild(document.createElement('br')) \n container = mod.win.document.createElement('div')\n container.style.overflow = 'hidden'\n mod.win.document.body.appendChild(container)\n //\n // event handlers\n //\n container.addEventListener('contextmenu',context_menu)\n container.addEventListener('mousedown',mouse_down)\n container.addEventListener('mouseup',mouse_up)\n container.addEventListener('mousemove',mouse_move)\n container.addEventListener('wheel',mouse_wheel)\n //\n // add scene\n //\n scene = new THREE.Scene()\n mod.scene = scene\n var width = mod.win.innerWidth\n var height = mod.win.innerHeight\n var aspect = width/height\n var near = 0.1\n var far = 1000000\n camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n mod.camera = camera\n scene.add(camera)\n //\n // add renderer\n //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n renderer.setSize(width,height)\n container.appendChild(renderer.domElement)\n //\n // show the path if available\n //\n show_path()\n //\n // 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/mod.win.innerHeight\n -Math.cos(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n mod.y0 += \n Math.cos(mod.thetaz)*mod.height*dy/mod.win.innerHeight\n +Math.sin(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n else if (mod.button == 2) {\n mod.thetaxy += dy/mod.win.innerHeight\n mod.thetaz += dx/mod.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 if (Math.cos(mod.thetaxy) > 0)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n else\n camera.up = new THREE.Vector3(-Math.sin(mod.thetaz),-Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n //\n // mouse_wheel\n //\n function mouse_wheel(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dy = evt.deltaY/mod.win.innerHeight\n mod.r += mod.height*dy\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1406.5777561710643","left":"2836.3726137158815","inputs":{},"outputs":{}},"0.9325875387173613":{"definition":"//\n// mill raster 2.5D\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2019\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'mill raster 2.5D'\n//\n// initialization\n//\nvar init = function() {\n mod.dia_in.value = '0.1968503937007874'\n mod.dia_mm.value = '5'\n mod.cut_in.value = '0.3937007874015748'\n mod.cut_mm.value = '10'\n mod.max_in.value = '1.3779527559055118'\n mod.max_mm.value = '35'\n mod.number.value = '1'\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:'',\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:'',\n event:function(evt){\n if (mod.label.nodeValue == 'calculating') {\n //\n // calculation in progress, draw and accumulate\n //\n draw_path(evt.detail)\n accumulate_path(evt.detail)\n mod.offsetCount += 1\n if ((mod.offsetCount != parseInt(mod.number.value))\n && (evt.detail.length > 0)) {\n //\n // layer detail present and offset not complete\n //\n mod.offset += parseFloat(mod.stepover.value)\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else if (mod.depthmm < parseFloat(mod.max_mm.value)) {\n //\n // layer loop not complete\n //\n merge_layer()\n accumulate_toolpath()\n clear_layer()\n mod.depthmm += parseFloat(mod.cut_mm.value)\n if (mod.depthmm > parseFloat(mod.max_mm.value)) {\n mod.depthmm = parseFloat(mod.max_mm.value)\n }\n //\n // clear offset\n //\n outputs.offset.event('')\n //\n // set new depth\n //\n outputs.depth.event(mod.depthmm)\n //\n // set new offset\n //\n mod.offsetCount = 0\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else {\n //\n // done, finish and output\n //\n draw_path(mod.toolpath)\n draw_connections(mod.toolpath)\n mod.label.nodeValue = 'calculate'\n mod.labelspan.style.fontWeight = 'normal'\n outputs.toolpath.event()\n }\n }\n }\n },\n settings:{type:'',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n depth:{type:'',\n event:function(depth){\n mods.output(mod,'depth',{depthmm:depth})\n }\n },\n diameter:{type:'',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{type:'',\n event:function(size){\n mods.output(mod,'offset',size)\n }\n },\n toolpath:{type:'',\n event:function(){\n cmd = {}\n cmd.path = mod.toolpath\n cmd.name = mod.name\n cmd.dpi = mod.dpi\n cmd.width = mod.width\n cmd.height = mod.height\n cmd.depth = Math.round(parseFloat(mod.max_in.value)*mod.dpi)\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 outputs.offset.event('') // clear offset\n mod.depthmm = parseFloat(mod.cut_mm.value)\n outputs.depth.event(mod.depthmm) // set depth\n mod.toolpath = [] // start new toolpath\n clear_layer() // clear layer\n outputs.diameter.event()\n outputs.offset.event( // set offset\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\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_layer\n//\nfunction clear_layer() {\n mod.path = []\n mod.offset = 0.5\n mod.offsetCount = 0\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute(\n '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_layer\n//\nfunction merge_layer() {\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// accumulate_toolpath\n//\nfunction accumulate_toolpath() {\n for (var seg = 0; seg < mod.path.length; ++seg) {\n var newseg = []\n for (var pt = 0; pt < mod.path[seg].length; ++pt) {\n var idepth = -Math.round(mod.dpi*mod.depthmm/25.4)\n var point = mod.path[seg][pt].concat(idepth)\n newseg.splice(newseg.length,0,point)\n }\n mod.toolpath.push(newseg)\n }\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(\n '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(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n '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(path) {\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 < path.length; ++segment) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS(\n '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 = path[segment-1][path[segment-1].length-1][0]\n var y1 = h-path[segment-1][path[segment-1].length-1][1]-1\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(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n '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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"623.0093202529583","left":"2555.700932035969","inputs":{},"outputs":{}},"0.9327347189729929":{"definition":"//\r\n// STL module extracts stl from Tools/FabLab Connect command of SolidWorks products\r\n// \r\n// Shawn Liu @ Dassault Systemes SolidWorks Corporation\r\n// (c) Massachusetts Institute of Technology 2019\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n//\r\n// name\r\n//\r\nvar name = 'STL connect'\r\n//\r\n// initialization\r\n//\r\nvar init = function () {\r\n mod.address = getParameterByName('swIP') || '127.0.0.1'\r\n mod.port = getParameterByName('swPort') || '80'\r\n mod.socket = 0\r\n mod.sag.value = '0.1'\n mod.angle.value = '45'\n socket_open()\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {\r\n }\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n mesh:{type:'STL',\r\n event:function(buffer){\r\n mods.output(mod,'mesh',buffer)}}\r\n }\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n //\r\n // canvas\r\n //\r\n var canvas = document.createElement('canvas')\r\n canvas.width = mods.ui.canvas\r\n canvas.height = mods.ui.canvas\r\n canvas.style.backgroundColor = 'rgb(255,255,255)'\r\n div.appendChild(canvas)\r\n mod.canvas = canvas\r\n div.appendChild(document.createElement('br'))\r\n\r\n div.appendChild(document.createTextNode('server:'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('address: ' + getParameterByName('swIP')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0port: ' + getParameterByName('swPort')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0status: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 12\r\n div.appendChild(input)\r\n mod.status = input\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('open'))\r\n btn.addEventListener('click', function () {\r\n socket_open()\r\n })\r\n div.appendChild(btn)\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.addEventListener('click', function () {\r\n socket_close()\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('sag: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.sag = input\r\n div.appendChild(document.createTextNode('(mm)'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('angle: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.angle = input\r\n div.appendChild(document.createTextNode('(degree)'))\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('Extract STL'))\r\n btn.addEventListener('click', function () {\r\n extract_STL()\r\n })\r\n div.appendChild(btn)\r\n\r\n //\r\n // info\r\n //\r\n var info = document.createElement('div')\r\n info.setAttribute('id', div.id + 'info')\r\n var text = document.createTextNode('name: ')\n info.appendChild(text)\n mod.name = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('size: ')\r\n info.appendChild(text)\r\n mod.sizen = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('triangles: ')\r\n info.appendChild(text)\r\n mod.trianglesn = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('dx: ')\r\n info.appendChild(text)\r\n mod.dxn = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('dy: ')\r\n info.appendChild(text)\r\n mod.dyn = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('dz: ')\r\n info.appendChild(text)\r\n mod.dzn = text\r\n div.appendChild(info)\r\n }\r\n//\r\n// local functions\r\n//\r\n\r\n //\r\n // local functions\r\n //\r\n\r\nfunction getParameterByName(name, url) {\r\n if (!url) url = window.location.href;\r\n name = name.replace(/[\\[\\]]/g, \"\\\\$&\");\r\n var regex = new RegExp(\"[?&]\" + name + \"(=([^&#]*)|&|#|$)\"),\r\n results = regex.exec(url);\r\n if (!results) return null;\r\n if (!results[2]) return '';\r\n return decodeURIComponent(results[2].replace(/\\+/g, \" \"));\r\n}\r\n\r\nfunction socket_open() {\r\n var url = \"ws://\" + mod.address + ':' + mod.port\r\n mod.socket = new WebSocket(url)\r\n mod.socket.onopen = function (event) {\r\n mod.status.value = \"opened\"\r\n var connect = {}\r\n connect.modCmd = 'connect'\r\n connect.owner = getParameterByName('swOwner')\r\n connect.id = getParameterByName('swID')\r\n socket_send(JSON.stringify(connect))\r\n }\r\n mod.socket.onerror = function (event) {\r\n mod.status.value = \"can not open\"\r\n }\r\n mod.socket.onmessage = function (event) {\r\n mod.status.value = \"receive\"\r\n var swData = JSON.parse(event.data)\r\n if (swData.swType === \"AutoExtractSTL\") {\r\n var partName = swData.data.partName\r\n if (partName.length > 25)\r\n partName = partName.slice(0, 22) +'...'\r\n mod.name.nodeValue = \"name: \" + partName\r\n mod.str = swData.data.stl\r\n stl_load_handler()\r\n }\r\n }\r\n mod.socket.onclose = function (event) {\r\n mod.status.value = \"connection closed\"\r\n }\r\n}\r\nfunction socket_close() {\r\n mod.socket.close()\r\n mod.status.value = \"closed\"\r\n mod.socket = 0\r\n}\r\nfunction socket_send(msg) {\r\n if (mod.socket != 0) {\r\n mod.status.value = \"send\"\r\n mod.socket.send(msg)\r\n }\r\n else {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n}\r\nfunction extract_STL() {\r\n var modcmd = new Object;\r\n modcmd.modCmd = \"AutoExtractSTL\";\r\n modcmd.sag = Number(mod.sag.value); // mm\r\n modcmd.angle = Number(mod.angle.value); //degree\r\n socket_send(JSON.stringify(modcmd))\r\n}\r\n\r\nfunction base64ToArrayBuffer(base64) {\r\n var binary_string = window.atob(base64);\r\n var len = binary_string.length;\r\n var bytes = new Uint8Array(len);\r\n for (var i = 0; i < len; i++) {\r\n bytes[i] = binary_string.charCodeAt(i);\r\n }\r\n return bytes.buffer;\r\n}\r\n\r\n//\r\n// load handler\r\n//\r\nfunction stl_load_handler() {\r\n //\r\n // check for binary STL\r\n //\r\n var arraybuf = base64ToArrayBuffer(mod.str)\r\n var endian = true\r\n var view = new DataView(arraybuf)\r\n var triangles = view.getUint32(80, endian)\r\n var size = 80+4+triangles*(4*12+2)\r\n if (size != view.byteLength) {\r\n mod.sizen.nodeValue = 'error: not binary STL'\r\n mod.trianglesn.nodeValue = ''\r\n mod.dxn.nodeValue = ''\r\n mod.dyn.nodeValue = ''\r\n mod.dzn.nodeValue = ''\r\n return\r\n }\r\n mod.sizen.nodeValue = 'size: '+size\r\n mod.trianglesn.nodeValue = 'triangles: '+triangles\r\n //\r\n // find limits and draw\r\n //\r\n var blob = new Blob(['('+draw_limits_worker.toString()+'())'])\r\n var url = window.URL.createObjectURL(blob)\r\n var webworker = new Worker(url)\r\n webworker.addEventListener('message',function(evt) {\r\n //\r\n // worker response\r\n //\r\n window.URL.revokeObjectURL(url)\r\n //\r\n // size\r\n //\r\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\r\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\r\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\r\n //\r\n // image\r\n //\r\n var image = evt.data.image\r\n var height = mod.canvas.height\r\n var width = mod.canvas.width\r\n var buffer = new Uint8ClampedArray(evt.data.image)\r\n var imgdata = new ImageData(buffer,width,height)\r\n var ctx = mod.canvas.getContext(\"2d\")\r\n ctx.putImageData(imgdata,0,0)\r\n //\r\n // output\r\n //\r\n outputs.mesh.event(evt.data.mesh)\r\n })\r\n var ctx = mod.canvas.getContext(\"2d\")\r\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\r\n var img = ctx.getImageData(0, 0, mod.canvas.width, mod.canvas.height)\r\n //\r\n // call worker\r\n //\r\n webworker.postMessage({\r\n height:mod.canvas.height,width:mod.canvas.width,\r\n image: img.data.buffer, mesh: arraybuf},\r\n [img.data.buffer, arraybuf])\r\n }\r\nfunction draw_limits_worker() {\r\n self.addEventListener('message',function(evt) {\r\n //\r\n // function to draw line\r\n //\r\n function line(x0,y0,x1,y1) {\r\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\r\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\r\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\r\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\r\n var row,col\r\n var idx = ix1-ix0\r\n var idy = iy1-iy0\r\n if (Math.abs(idy) > Math.abs(idx)) {\r\n (idy > 0) ?\r\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\r\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\r\n for (row = row0; row <= row1; ++row) {\r\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\r\n image[row*width*4+col*4+0] = 0\r\n image[row*width*4+col*4+1] = 0\r\n image[row*width*4+col*4+2] = 0\r\n image[row*width*4+col*4+3] = 255\r\n }\r\n }\r\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\r\n (idx > 0) ?\r\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\r\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\r\n for (col = col0; col <= col1; ++col) {\r\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\r\n image[row*width*4+col*4+0] = 0\r\n image[row*width*4+col*4+1] = 0\r\n image[row*width*4+col*4+2] = 0\r\n image[row*width*4+col*4+3] = 255\r\n }\r\n }\r\n else {\r\n row = iy0\r\n col = ix0\r\n image[row*width*4+col*4+0] = 0\r\n image[row*width*4+col*4+1] = 0\r\n image[row*width*4+col*4+2] = 0\r\n image[row*width*4+col*4+3] = 255\r\n }\r\n }\r\n //\r\n // get variables\r\n //\r\n var height = evt.data.height\r\n var width = evt.data.width\r\n var endian = true\r\n var image = new Uint8ClampedArray(evt.data.image)\r\n var view = new DataView(evt.data.mesh)\r\n var triangles = view.getUint32(80,endian)\r\n //\r\n // find limits\r\n //\r\n var offset = 80+4\r\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\r\n var xmin = Number.MAX_VALUE\r\n var xmax = -Number.MAX_VALUE\r\n var ymin = Number.MAX_VALUE\r\n var ymax = -Number.MAX_VALUE\r\n var zmin = Number.MAX_VALUE\r\n var zmax = -Number.MAX_VALUE\r\n for (var t = 0; t < triangles; ++t) {\r\n offset += 3*4\r\n x0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (x0 > xmax) xmax = x0\r\n if (x0 < xmin) xmin = x0\r\n y0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (y0 > ymax) ymax = y0\r\n if (y0 < ymin) ymin = y0\r\n z0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (z0 > zmax) zmax = z0\r\n if (z0 < zmin) zmin = z0\r\n x1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (x1 > xmax) xmax = x1\r\n if (x1 < xmin) xmin = x1\r\n y1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (y1 > ymax) ymax = y1\r\n if (y1 < ymin) ymin = y1\r\n z1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (z1 > zmax) zmax = z1\r\n if (z1 < zmin) zmin = z1\r\n x2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (x2 > xmax) xmax = x2\r\n if (x2 < xmin) xmin = x2\r\n y2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (y2 > ymax) ymax = y2\r\n if (y2 < ymin) ymin = y2\r\n z2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (z2 > zmax) zmax = z2\r\n if (z2 < zmin) zmin = z2\r\n offset += 2\r\n }\r\n var dx = xmax-xmin\r\n var dy = ymax-ymin\r\n var dz = zmax-zmin\r\n //\r\n // draw mesh\r\n //\r\n if (dx > dy) {\r\n var xo = 0\r\n var yo = height*.5*(1-dy/dx)\r\n var xw = width-1\r\n var yh = (width-1)*dy/dx\r\n }\r\n else {\r\n var xo = width*.5*(1-dx/dy)\r\n var yo = 0\r\n var xw = (height-1)*dx/dy\r\n var yh = height-1\r\n }\r\n offset = 80+4\r\n for (var t = 0; t < triangles; ++t) {\r\n offset += 3*4\r\n x0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n y0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n z0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n x1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n y1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n z1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n x2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n y2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n z2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n offset += 2\r\n line(x0,y0,x1,y1)\r\n line(x1,y1,x2,y2)\r\n line(x2,y2,x0,y0)\r\n }\r\n //\r\n // return results and close\r\n //\r\n self.postMessage({\r\n dx:dx,dy:dy,dz:dz,\r\n image:evt.data.image,mesh:evt.data.mesh},[evt.data.image,evt.data.mesh])\r\n self.close()\r\n })\r\n }\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n mod:mod,\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"423.0822992185986","left":"1114.1152264935706","inputs":{},"outputs":{}},"0.14129137871043707":{"definition":"//\r\n// Roland SRM-20 milling machine\r\n//\r\n// Neil Gershenfeld\r\n// (c) Massachusetts Institute of Technology 2016\r\n//\r\n// This work may be reproduced, modified, distributed, performed, and\r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is\r\n// provided as is; no warranty is provided, and users accept all\r\n// liability.\r\n//\r\n// closure\r\n//\r\n/*\r\nG-codes:\r\nG00X10.0\r\nG90 (absolute positioning)\r\nG21 (mm units)\r\n#.0 numbers\r\nG00 (positioning rapid move)\r\nG01 (linear motion)\r\nM03 (start spindle)\r\nM05 (stop spindle)\r\nF (feed rate mm/min, 6-1800)\r\n203.2 (X) x 152.4 (Y) x 60.5 (Z) mm \r\n*/\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n//\r\n// name\r\n//\r\nvar name = 'Roland SRM-20 milling machine'\r\n//\r\n// initialization\r\n//\r\nvar init = function() {\r\n mod.units = 100.0\r\n mod.speed.value = 4\r\n mod.ox.value = 10\r\n mod.oy.value = 10\r\n mod.oz.value = 10\r\n mod.jz.value = 2\r\n mod.hx.value = 0\r\n mod.hy.value = 152.4\r\n mod.hz.value = 60.5\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {\r\n path:{type:'',\r\n event:function(evt){\r\n mod.name = evt.detail.name\r\n mod.path = evt.detail.path\r\n mod.dpi = evt.detail.dpi\r\n mod.width = evt.detail.width\r\n mod.height = evt.detail.height\r\n make_path()\r\n }}}\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n file:{type:'',\r\n event:function(obj){\r\n mods.output(mod,'file',obj)\r\n }}}\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n //\r\n // speed\r\n //\r\n div.appendChild(document.createTextNode('speed: '))\r\n var input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 6\r\n div.appendChild(input)\r\n mod.speed = input\r\n div.appendChild(document.createTextNode(' (mm/s)'))\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // origin\r\n //\r\n div.appendChild(document.createTextNode('origin:'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('x: '))\r\n var input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 6\r\n div.appendChild(input)\r\n mod.ox = input\r\n div.appendChild(document.createTextNode(' (mm)'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode(' y: '))\r\n var input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 6\r\n div.appendChild(input)\r\n mod.oy = input\r\n div.appendChild(document.createTextNode(' (mm)'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('z: '))\r\n var input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 6\r\n div.appendChild(input)\r\n mod.oz = input\r\n div.appendChild(document.createTextNode(' (mm)'))\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n var span = document.createElement('span')\r\n var text = document.createTextNode('move to origin')\r\n span.appendChild(text)\r\n btn.appendChild(span)\r\n btn.addEventListener('click',function(){\r\n var x0 = mod.units*parseFloat(mod.ox.value);\r\n var y0 = mod.units*parseFloat(mod.oy.value);\r\n var z0 = mod.units*parseFloat(mod.oz.value);\r\n var zjog = z0+mod.units*parseFloat(mod.jz.value);\r\n //\r\n // G-code version\r\n //\r\n /*\r\n str = '%\\n' // data start\r\n str += 'G90\\n' // absolute units\r\n str += 'G21\\n' // mm units\r\n str += 'G00Z30.0\\n' // move z\r\n str += 'M02\\n' // end of program\r\n */\r\n //\r\n // RML version\r\n //\r\n var str = \"PA;PA;VS10;!VZ10;!PZ0,\"+zjog+\";PU\"+x0+\",\"+y0+\";Z\"+x0+\",\"+y0+\",\"+z0+\";!MC0;\"+\"\\u0004\"\r\n //\r\n // send command\r\n //\r\n var obj = {}\r\n obj.type = 'command'\r\n obj.name = mod.name+'.rml'\r\n obj.contents = str\r\n outputs.file.event(obj)\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // jog\r\n //\r\n div.appendChild(document.createTextNode('jog height:'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('z: '))\r\n var input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 6\r\n div.appendChild(input)\r\n mod.jz = input\r\n div.appendChild(document.createTextNode(' (mm)'))\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // home\r\n //\r\n div.appendChild(document.createTextNode('home:'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('x: '))\r\n var input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 6\r\n div.appendChild(input)\r\n mod.hx = input\r\n div.appendChild(document.createTextNode(' (mm)'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode(' y: '))\r\n var input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 6\r\n div.appendChild(input)\r\n mod.hy = input\r\n div.appendChild(document.createTextNode(' (mm)'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('z: '))\r\n var input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 6\r\n div.appendChild(input)\r\n mod.hz = input\r\n div.appendChild(document.createTextNode(' (mm)'))\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n var span = document.createElement('span')\r\n var text = document.createTextNode('move to home')\r\n span.appendChild(text)\r\n btn.appendChild(span)\r\n btn.addEventListener('click',function(){\r\n var xhome = mod.units*parseFloat(mod.hx.value);\r\n var yhome = mod.units*parseFloat(mod.hy.value);\r\n var zhome = mod.units*parseFloat(mod.hz.value);\r\n //\r\n // G-code version\r\n //\r\n /*\r\n str = '%\\n' // data start\r\n str += 'G90\\n' // absolute units\r\n str += 'G21\\n' // mm units\r\n str += 'G00Z50.0\\n' // move z\r\n str += 'M02\\n' // end of program\r\n */\r\n //\r\n // RML version\r\n //\r\n var str = \"PA;PA;!PZ0,\"+zhome+\";PU\"+xhome+\",\"+yhome+\";!MC0;\"+\"\\u0004\"\r\n //\r\n // send command\r\n //\r\n var obj = {}\r\n obj.type = 'command'\r\n obj.name = mod.name+'.rml'\r\n obj.contents = str\r\n outputs.file.event(obj)\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n }\r\n//\r\n// local functions\r\n//\r\nfunction make_path() {\r\n var dx = 25.4*mod.width/mod.dpi\r\n var nx = mod.width\r\n var speed = parseFloat(mod.speed.value)\r\n var jog = parseFloat(mod.oz.value)+parseFloat(mod.jz.value)\r\n var ijog = Math.floor(mod.units*jog)\r\n var scale = mod.units*dx/(nx-1)\r\n var x0 = parseFloat(mod.ox.value)\r\n var y0 = parseFloat(mod.oy.value)\r\n var z0 = parseFloat(mod.oz.value)\r\n var xoffset = mod.units*x0\r\n var yoffset = mod.units*y0\r\n var zoffset = mod.units*z0\r\n var str = \"PA;PA;\" // plot absolute\r\n str += \"VS\"+speed+\";!VZ\"+speed+\";\"\r\n str += \"!PZ\"+0+\",\"+ijog+\";\" // set jog\r\n str += \"!MC1;\\n\" // turn motor on\r\n //\r\n // follow segments\r\n //\r\n for (var seg = 0; seg < mod.path.length; ++seg) {\r\n //\r\n // move up to starting point\r\n //\r\n x = xoffset+scale*mod.path[seg][0][0]\r\n y = yoffset+scale*mod.path[seg][0][1]\r\n str += \"PU\"+x.toFixed(0)+\",\"+y.toFixed(0)+\";\\n\"\r\n //\r\n // move down\r\n //\r\n z = zoffset+scale*mod.path[seg][0][2]\r\n str += \"Z\"+x.toFixed(0)+\",\"+y.toFixed(0)+\",\"+z.toFixed(0)+\";\\n\"\r\n for (var pt = 1; pt < mod.path[seg].length; ++pt) {\r\n //\r\n // move to next point\r\n //\r\n x = xoffset+scale*mod.path[seg][pt][0]\r\n y = yoffset+scale*mod.path[seg][pt][1]\r\n z = zoffset+scale*mod.path[seg][pt][2]\r\n str += \"Z\"+x.toFixed(0)+\",\"+y.toFixed(0)+\",\"+z.toFixed(0)+\";\\n\"\r\n }\r\n //\r\n // move up\r\n //\r\n str += \"PU\"+x.toFixed(0)+\",\"+y.toFixed(0)+\";\\n\"\r\n }\r\n //\r\n // turn off motor and move back\r\n //\r\n var xhome = mod.units*parseFloat(mod.hx.value)\r\n var yhome = mod.units*parseFloat(mod.hy.value)\r\n var zhome = mod.units*parseFloat(mod.hz.value)\r\n str += \"PA;PA;!PZ0,\"+zhome+\";PU\"+xhome+\",\"+yhome+\";!MC0;\"\r\n //\r\n // output string\r\n //\r\n var obj = {}\r\n obj.type = 'file'\r\n obj.name = mod.name+'.rml'\r\n obj.contents = str\r\n outputs.file.event(obj)\r\n }\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"1216.7552078317794","left":"1704.718114805777","inputs":{},"outputs":{}},"0.7206126800940682":{"definition":"//\r\n// print server module\r\n//\r\n// Neil Gershenfeld \r\n// (c) Massachusetts Institute of Technology 2017\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n//\r\n// name\r\n//\r\nvar name = 'WebSocket print'\r\n//\r\n// initialization\r\n//\r\nvar init = function() {\r\n mod.address.value = '127.0.0.1'\n mod.port.value = '1234'\n mod.printers.value = 'Printer not found'\n mod.socket = null\r\n socket_open()\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {\r\n file:{type:'',\r\n event:function(evt){\r\n if (evt.detail.type == 'file') {\r\n mod.job = evt.detail\r\n mod.label.nodeValue = 'send file to printer'\r\n mod.labelspan.style.fontWeight = 'bold'\r\n }\r\n else if (evt.detail.type == 'command') {\r\n mod.job = evt.detail\r\n mod.job.printer = mod.printers.value\r\n socket_send(JSON.stringify(mod.job))\r\n }\r\n }}}\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n }\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n //\r\n // server\r\n //\r\n var a = document.createElement('a')\r\n a.href = './js/printserver.js'\r\n a.innerHTML = 'printserver:'\r\n a.target = '_blank'\r\n div.appendChild(a)\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('address: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.address = input\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0port: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.port = input\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0status: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.status = input\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // open/close\r\n //\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('open'))\r\n btn.addEventListener('click',function() {\r\n socket_open()\r\n })\r\n div.appendChild(btn)\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.addEventListener('click',function() {\r\n socket_close()\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n //\r\n // printer\r\n //\r\n div.appendChild(document.createTextNode('printer:'))\r\n div.appendChild(document.createElement('br'))\r\n var select = document.createElement('select')\r\n select.setAttribute('style', 'width:150px');\r\n var el = document.createElement('option')\r\n el.textContent = 'Printer not found'\r\n el.value = 'Printer not found'\r\n select.appendChild(el)\r\n div.appendChild(select)\r\n mod.printers = select \r\n div.appendChild(document.createElement('br')) \r\n var btn = document.createElement('button')\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n var span = document.createElement('span')\r\n var text = document.createTextNode('waiting for file')\r\n mod.label = text\r\n span.appendChild(text)\r\n mod.labelspan = span\r\n btn.appendChild(span)\r\n btn.addEventListener('click',function(){\r\n if (mod.socket == null) {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n else if (mod.label.nodeValue == 'send file to printer') {\r\n mod.job.printer = mod.printers.value\r\n socket_send(JSON.stringify(mod.job))\r\n mod.label.nodeValue = 'cancel'\r\n }\r\n else if (mod.label.nodeValue == 'cancel') {\r\n socket_send('cancel')\r\n }\r\n })\r\n div.appendChild(btn)\r\n }\r\n//\r\n// local functions\r\n//\r\nfunction socket_open() {\r\n var url = \"ws://\"+mod.address.value+':'+mod.port.value\r\n mod.socket = new WebSocket(url)\r\n mod.socket.onopen = function(event) {\r\n mod.status.value = \"socket opened\"\r\n }\r\n mod.socket.onerror = function(event) {\r\n mod.status.value = \"can not open socket\"\r\n mod.socket = null\r\n }\r\n mod.socket.onmessage = function(event) {\r\n mod.status.value = event.data\r\n if ((event.data == 'done') || (event.data == 'cancel')\r\n || (event.data.slice(0,5) == 'error')) {\r\n mod.label.nodeValue = 'waiting for file'\r\n mod.labelspan.style.fontWeight = 'normal'\r\n }\r\n else if (event.data[0] === '{'){\r\n var printerInfo = JSON.parse(event.data)\r\n var printerList = printerInfo['printerList']\r\n if (printerList) {\r\n while (mod.printers.hasChildNodes()) {\r\n mod.printers.removeChild(mod.printers.lastChild);\r\n }\r\n\r\n for(var i = 0; i < printerList.length; i++){\r\n var printer = printerList[i];\r\n var el = document.createElement('option')\r\n el.textContent = printer\r\n el.value = printer\r\n mod.printers.appendChild(el)\r\n }\r\n var defaultPrinter = printerInfo['default']\r\n if(defaultPrinter && defaultPrinter.length>0){\r\n mod.printers.value = defaultPrinter\r\n }\r\n }\r\n }\r\n }\r\n }\r\nfunction socket_close() {\r\n mod.socket.close()\r\n mod.status.value = \"socket closed\"\r\n mod.socket = null\r\n }\r\nfunction socket_send(msg) {\r\n if (mod.socket != null) {\r\n mod.status.value = \"send\"\r\n mod.socket.send(msg)\r\n }\r\n else {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n }\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n mod:mod,\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"1351.066486212147","left":"2188.8434424353395","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7667165137781767\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.7667165137781767\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"depth\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7667165137781767\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9327347189729929\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.14129137871043707\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.14129137871043707\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7206126800940682\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4144526456371104\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}"]} \ No newline at end of file diff --git a/programs/machines/ShopBot/mill 2.5D stl connect b/programs/machines/ShopBot/mill 2.5D stl connect new file mode 100644 index 0000000000000000000000000000000000000000..218b217c3fa2e3812df5f93d3cdd55f650c18ed9 --- /dev/null +++ b/programs/machines/ShopBot/mill 2.5D stl connect @@ -0,0 +1 @@ +{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"563.8000042768857","left":"3159.386325836052","inputs":{},"outputs":{}},"0.07944144280928633":{"definition":"//\n// edge detect\n// green = interior, blue = exterior, red = boundary\n// assumes input is thresholded\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'edge detect'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n edge_detect()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:interior, blue:exterior, red:boundary'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// edge detect\n//\nfunction edge_detect() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({worker:worker.toString(),\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var i00,i0m,i0p,im0,ip0,imm,imp,ipm,ipp,row,col\n //\n // find edges - interior\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != i0m) || (i00 != im0) || (i00 != imm)\n || (i00 != imp) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // left and right edges\n //\n for (row = 1; row < (h-1); ++row) {\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm) \n || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // top and bottom edges\n //\n for (col = 1; col < (w-1); ++col) {\n row = h-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != imm) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != ipm) \n || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // corners\n //\n row = 0\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"913.8000042768859","left":"3643.386325836052","inputs":{},"outputs":{}},"0.8903773266711255":{"definition":"//\n// orient edges\n// input is green:interior, blue:exterior, red:boundary\n// output is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'orient edges'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n var ctx = mod.display.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n orient_edges()\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // off-screen display canvas\n //\n var canvas = document.createElement('canvas')\n mod.display = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('red:north, dark red:south'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:east, dark green:west'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('blue:start, dark blue:stop'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.display,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// orient edges\n//\nfunction orient_edges() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n var disp = new Uint8ClampedArray(evt.data.display)\n var dispdata = new ImageData(disp,w,h)\n var ctx = mod.display.getContext(\"2d\")\n ctx.putImageData(dispdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var w = mod.canvas.width\n var h = mod.canvas.height\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,w,h)\n ctx.drawImage(mod.display,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var row,col\n var boundary = 0\n var interior = 1\n var exterior = 2\n var alpha = 3\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n //\n // orient body states\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && ((input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && ((input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n }\n }\n }\n //\n // orient edge states\n //\n for (col = 1; col < (w-1); ++col) {\n row = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n row = h-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n for (row = 1; row < (h-1); ++row) {\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n //\n // orient corner states (todo)\n //\n row = 0\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = 0\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n //\n // invert background for display\n //\n var display = new Uint8ClampedArray(h*w*4)\n var r,g,b,i\n for (row = 0; row < h; ++row) {\n for (col = 0; col < w; ++col) {\n r = output[(h-1-row)*w*4+col*4+0]\n g = output[(h-1-row)*w*4+col*4+1]\n b = output[(h-1-row)*w*4+col*4+2]\n i = r+g+b\n if (i != 0) { \n display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]\n display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]\n display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]\n display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]\n }\n else {\n display[(h-1-row)*w*4+col*4+0] = 255\n display[(h-1-row)*w*4+col*4+1] = 255\n display[(h-1-row)*w*4+col*4+2] = 255\n display[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // return output\n //\n self.postMessage({buffer:output.buffer,display:display.buffer},[output.buffer,display.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1007.8000042768859","left":"3197.386325836052","inputs":{},"outputs":{}},"0.6488303557466412":{"definition":"//\n// image threshold\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'image threshold'\n//\n// initialization\n//\nvar init = function() {\n mod.threshold.value = '0.5'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n threshold_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // threshold value\n //\n div.appendChild(document.createTextNode('threshold (0-1): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n threshold_image()\n })\n div.appendChild(input)\n mod.threshold = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// threshold image\n//\nfunction threshold_image() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var t = parseFloat(mod.threshold.value)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,threshold:t,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var t = evt.data.threshold\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,i\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n r = buf[(h-1-row)*w*4+col*4+0] \n g = buf[(h-1-row)*w*4+col*4+1] \n b = buf[(h-1-row)*w*4+col*4+2] \n a = buf[(h-1-row)*w*4+col*4+3] \n i = (r+g+b)/(3*255)\n if (a == 0)\n val = 255\n else if (i > t)\n var val = 255\n else\n var val = 0\n buf[(h-1-row)*w*4+col*4+0] = val\n buf[(h-1-row)*w*4+col*4+1] = val\n buf[(h-1-row)*w*4+col*4+2] = val\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"464.800004276886","left":"2743.386325836052","inputs":{},"outputs":{}},"0.749132408760488":{"definition":"//\n// vectorize\n// input is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = '1'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n vectorize()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'array',\n event:function(){\n mods.output(mod,'path',mod.path)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br')) \n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // error value\n //\n div.appendChild(document.createTextNode('vector fit (pixels): '))\n //div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n vectorize()\n })\n div.appendChild(input)\n mod.error = input\n div.appendChild(document.createElement('br'))\n //\n // sort\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n input.checked = true\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// vectorize\n//\nfunction vectorize() {\n //\n // draw path\n //\n function draw_path(path) {\n window.URL.revokeObjectURL(url)\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute('viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment in path) {\n if (path[segment].length > 1) {\n if (xend != null) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = xend\n var y1 = yend\n var x2 = path[segment][0][0]\n var y2 = h-path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n svg.appendChild(g)\n }\n //\n // set up worker\n //\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n webworker.terminate()\n mod.path = evt.data.path\n draw_path(mod.path)\n outputs.path.event()\n })\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,sort:mod.sort.checked,\n error:parseFloat(mod.error.value),\n buffer:mod.input.data.buffer})\n }\n//\n// vectorize worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var sort = evt.data.sort\n var input = new Uint8ClampedArray(evt.data.buffer)\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n var path = []\n //\n // edge follower\n //\n function follow_edges(row,col) {\n if ((input[(h-1-row)*w*4+col*4+northsouth] != 0)\n || (input[(h-1-row)*w*4+col*4+eastwest] != 0)) {\n path[path.length] = [[col,row]]\n while (1) {\n if (input[(h-1-row)*w*4+col*4+northsouth] & north) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~north\n row += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+northsouth] & south) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~south\n row -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & east) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~east\n col += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & west) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~west\n col -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else\n break\n }\n }\n }\n //\n // follow boundary starts\n //\n for (var row = 1; row < (h-1); ++row) {\n col = 0\n follow_edges(row,col)\n col = w-1\n follow_edges(row,col)\n }\n for (var col = 1; col < (w-1); ++col) {\n row = 0\n follow_edges(row,col)\n row = h-1 \n follow_edges(row,col)\n }\n //\n // follow interior paths\n //\n for (var row = 1; row < (h-1); ++row) {\n for (var col = 1; col < (w-1); ++col) {\n follow_edges(row,col)\n }\n }\n //\n // vectorize path\n //\n var error = evt.data.error\n var vecpath = []\n for (var seg = 0; seg < path.length; ++seg) {\n var x0 = path[seg][0][0]\n var y0 = path[seg][0][1]\n vecpath[vecpath.length] = [[x0,y0]]\n var xsum = x0\n var ysum = y0\n var sum = 1\n for (var pt = 1; pt < path[seg].length; ++pt) {\n var xold = x\n var yold = y\n var x = path[seg][pt][0]\n var y = path[seg][pt][1]\n if (sum == 1) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n var xmean = xsum/sum\n var ymean = ysum/sum\n var dx = xmean-x0\n var dy = ymean-y0\n var d = Math.sqrt(dx*dx+dy*dy)\n var nx = dy/d\n var ny = -dx/d\n var l = Math.abs(nx*(x-x0)+ny*(y-y0))\n if (l < error) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [xold,yold]\n x0 = xold\n y0 = yold\n xsum = xold\n ysum = yold\n sum = 1\n }\n }\n if (pt == (path[seg].length-1)) {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [x,y]\n }\n }\n }\n //\n // sort path\n //\n if ((vecpath.length > 0) && (sort == true)) {\n var dmin = w*w+h*h\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][0]\n var d = x*x+y*y\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n var sortpath = [vecpath[segmin]]\n vecpath.splice(segmin,1)\n }\n while (vecpath.length > 0) {\n var dmin = w*w+h*h\n var x0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][0]\n var y0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][1]\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][1]\n var d = (x-x0)*(x-x0)+(y-y0)*(y-y0)\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n sortpath[sortpath.length] = vecpath[segmin]\n vecpath.splice(segmin,1)\n }\n }\n }\n else if ((vecpath.length > 0) && (sort == false))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"912.8000042768859","left":"2778.386325836052","inputs":{},"outputs":{}},"0.4793941661670936":{"definition":"//\n// save file\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'save file'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n file:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.contents = evt.detail.contents\n save_file()\n }}}\n//\n// outputs\n//\nvar outputs = {}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name:')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('size:')\n div.appendChild(text)\n mod.sizetext = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\nfunction save_file() {\n var a = document.createElement('a')\n a.setAttribute('href','data:text/plain;charset=utf-8,'+ \n encodeURIComponent(mod.contents))\n a.setAttribute('download',mod.name)\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n mod.nametext.nodeValue = 'name: '+mod.name\n mods.fit(mod.div)\n mod.sizetext.nodeValue = 'size: '+mod.contents.length\n mods.fit(mod.div)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1404.0054355376824","left":"1620.6631658186536","inputs":{},"outputs":{}},"0.7562574507163453":{"definition":"//\n// ShopBot\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\n\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'ShopBot'\n//\n// initialization\n//\nvar init = function() {\n mod.cutspeed.value = '20'\n mod.plungespeed.value = '20'\n mod.jogspeed.value = '75'\n mod.jogheight.value = '5'\n mod.spindlespeed.value = '10000'\n mod.unitsin.checked = true\n }\n//\n// inputs\n//\nvar inputs = {\n toolpath:{type:'object',\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:'object',\n event:function(str){\n obj = {}\n obj.name = mod.name+\".sbp\"\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 speed\n //\n div.appendChild(document.createTextNode('jog speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.jogspeed = 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 // file units\n //\n div.appendChild(document.createTextNode('file units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsin'\n div.appendChild(input)\n mod.unitsin = input\n div.appendChild(document.createTextNode('in'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsmm'\n div.appendChild(input)\n mod.unitsmm = input\n div.appendChild(document.createTextNode('mm'))\n }\n//\n// local functions\n//\nfunction make_path() {\n if (mod.unitsin.checked)\n var units = 1\n else\n var units = 25.4\n var dx = units*mod.width/mod.dpi\n var nx = mod.width\n var cut_speed = units*parseFloat(mod.cutspeed.value)/25.4\n var plunge_speed = units*parseFloat(mod.plungespeed.value)/25.4\n var jog_speed = units*parseFloat(mod.jogspeed.value)/25.4\n var jog_height = units*parseFloat(mod.jogheight.value)/25.4\n var spindle_speed = parseFloat(mod.spindlespeed.value)\n var scale = dx/(nx-1)\n str = \"SA\\r\\n\" // set to absolute distances\n str += \"TR,\"+spindle_speed+\",1\\r\\n\" // set spindle speed\n str += \"SO,1,1\\r\\n\" // set output number 1 to on\n str += \"pause 2\\r\\n\" // let spindle come up to speed\n str += \"MS,\"+cut_speed.toFixed(4)+\",\"+plunge_speed.toFixed(4)+\"\\r\\n\" // set xy,z speed\n str += \"JS,\"+jog_speed.toFixed(4)+\",\"+jog_speed.toFixed(4)+\"\\r\\n\" // set jog xy,z speed\n str += \"JZ,\"+jog_height.toFixed(4)+\"\\r\\n\" // move up\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 += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\n\"\n str += \"J2,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\"\\r\\n\"\n //\n // move down\n //\n z = scale*mod.path[seg][0][2]\n str += \"MZ,\"+z.toFixed(4)+\"\\r\\n\"\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 += \"M3,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\",\"+z.toFixed(4)+\"\\r\\n\"\n }\n }\n //\n // output file\n //\n str += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\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":"1073.2516012544538","left":"1643.895753383433","inputs":{},"outputs":{}},"0.3040697193095865":{"definition":"//\n// mesh rotate\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 = 'mesh rotate'\n//\n// initialization\n//\nvar init = function() {\n mod.rx.value = '0'\n mod.ry.value = '0'\n mod.rz.value = '0'\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = evt.detail\n rotate_mesh()}}}\n//\n// outputs\n//\nvar outputs = {\n mesh:{type:'STL',\n event:function(buffer){\n mods.output(mod,'mesh',buffer)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // 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 // rotation\n //\n div.appendChild(document.createTextNode('rotation (degrees):'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' x: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rx = input\n div.appendChild(document.createTextNode(' y: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.ry = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' z: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rz = input\n div.appendChild(document.createTextNode(' (enter)'))\n div.appendChild(document.createElement('br'))\n //\n // info\n //\n var text = document.createTextNode('dx:')\n div.appendChild(text)\n mod.dxn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dy:')\n div.appendChild(text)\n mod.dyn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dz:')\n div.appendChild(text)\n mod.dzn = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n// rotate mesh\n//\nfunction rotate_mesh() {\n //\n // check for binary STL\n //\n var endian = true\n var view = new DataView(mod.mesh)\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits, rotate, and draw\n //\n var blob = new Blob(['('+rotate_mesh_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n //\n // worker response\n //\n window.URL.revokeObjectURL(url)\n //\n // size\n //\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\n //\n // image\n //\n var image = evt.data.image\n var height = mod.canvas.height\n var width = mod.canvas.width\n var buffer = new Uint8ClampedArray(evt.data.image)\n var imgdata = new ImageData(buffer,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n //\n // mesh\n //\n mod.mesh = evt.data.mesh\n //\n // output\n //\n outputs.mesh.event(evt.data.rotate)\n })\n //\n // call worker\n //\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var img = ctx.getImageData(0,0,mod.canvas.width,mod.canvas.height)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n webworker.postMessage({\n height:mod.canvas.height,width:mod.canvas.width,\n rx:rx,ry:ry,rz:rz,\n image:img.data.buffer,mesh:mod.mesh},\n [img.data.buffer,mod.mesh])\n }\nfunction rotate_mesh_worker() {\n self.addEventListener('message',function(evt) {\n //\n // function to draw line\n //\n function line(x0,y0,x1,y1) {\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\n var row,col\n var idx = ix1-ix0\n var idy = iy1-iy0\n if (Math.abs(idy) > Math.abs(idx)) {\n (idy > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (row = row0; row <= row1; ++row) {\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\n (idx > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (col = col0; col <= col1; ++col) {\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else {\n row = iy0\n col = ix0\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n //return([x3,y3,z3])\n return({x:x3,y:y3,z:z3})\n }\n //\n // get variables\n //\n var height = evt.data.height\n var width = evt.data.width\n var rx = evt.data.rx\n var ry = evt.data.ry\n var rz = evt.data.rz\n var endian = true\n var image = new Uint8ClampedArray(evt.data.image)\n var view = new DataView(evt.data.mesh)\n var triangles = view.getUint32(80,endian)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0.x > xmax) xmax = p0.x\n if (p0.x < xmin) xmin = p0.x\n if (p0.y > ymax) ymax = p0.y\n if (p0.y < ymin) ymin = p0.y\n if (p0.z > zmax) zmax = p0.z\n if (p0.z < zmin) zmin = p0.z\n var p1 = rotate(x1,y1,z1)\n if (p1.x > xmax) xmax = p1.x\n if (p1.x < xmin) xmin = p1.x\n if (p1.y > ymax) ymax = p1.y\n if (p1.y < ymin) ymin = p1.y\n if (p1.z > zmax) zmax = p1.z\n if (p1.z < zmin) zmin = p1.z\n var p2 = rotate(x2,y2,z2)\n if (p2.x > xmax) xmax = p2.x\n if (p2.x < xmin) xmin = p2.x\n if (p2.y > ymax) ymax = p2.y\n if (p2.y < ymin) ymin = p2.y\n if (p2.z > zmax) zmax = p2.z\n if (p2.z < zmin) zmin = p2.z\n }\n var dx = xmax-xmin\n var dy = ymax-ymin\n var dz = zmax-zmin\n //\n // copy mesh\n //\n var newbuf = evt.data.mesh.slice(0)\n var newview = new DataView(newbuf)\n //\n // copy and draw mesh\n //\n if (dx > dy) {\n var xo = 0\n var yo = height*.5*(1-dy/dx)\n var xw = (width-1)\n var yh = (width-1)*dy/dx\n }\n else {\n var xo = width*.5*(1-dx/dy)\n var yo = 0\n var xw = (height-1)*dx/dy\n var yh = (height-1)\n }\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n line(p0.x,p0.y,p1.x,p1.y)\n line(p1.x,p1.y,p2.x,p2.y)\n line(p2.x,p2.y,p0.x,p0.y)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.z,endian)\n newoffset += 4\n newoffset += 2\n }\n //\n // return results and close\n //\n self.postMessage({\n dx:dx,dy:dy,dz:dz,\n image:evt.data.image,mesh:evt.data.mesh,rotate:newbuf},\n [evt.data.image,evt.data.mesh,newbuf])\n self.close()\n })\n }\nfunction old_rotate_mesh() {\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n return([x3,y3,z3])\n }\n //\n // get vars\n //\n var view = mod.mesh\n var endian = true\n var triangles = view.getUint32(80,endian)\n mod.triangles = triangles\n var size = 80+4+triangles*(4*12+2)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0[0] > xmax) xmax = p0[0]\n if (p0[0] < xmin) xmin = p0[0]\n if (p0[1] > ymax) ymax = p0[1]\n if (p0[1] < ymin) ymin = p0[1]\n if (p0[2] > zmax) zmax = p0[2]\n if (p0[2] < zmin) zmin = p0[2]\n var p1 = rotate(x1,y1,z1)\n if (p1[0] > xmax) xmax = p1[0]\n if (p1[0] < xmin) xmin = p1[0]\n if (p1[1] > ymax) ymax = p1[1]\n if (p1[1] < ymin) ymin = p1[1]\n if (p1[2] > zmax) zmax = p1[2]\n if (p1[2] < zmin) zmin = p1[2]\n var p2 = rotate(x2,y2,z2)\n if (p2[0] > xmax) xmax = p2[0]\n if (p2[0] < xmin) xmin = p2[0]\n if (p2[1] > ymax) ymax = p2[1]\n if (p2[1] < ymin) ymin = p2[1]\n if (p2[2] > zmax) zmax = p2[2]\n if (p2[2] < zmin) zmin = p2[2]\n }\n mod.dx = xmax-xmin\n mod.dy = ymax-ymin\n mod.dz = zmax-zmin\n mod.dxn.nodeValue = 'dx: '+mod.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+mod.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+mod.dz.toFixed(3)\n mod.xmin = xmin\n mod.ymin = ymin\n mod.zmin = zmin\n mod.xmax = xmax\n mod.ymax = ymax\n mod.zmax = zmax\n //\n // copy mesh\n //\n var buf = mod.mesh.buffer.slice(0)\n var newview = new DataView(buf)\n //\n // draw projection and save rotation\n //\n var ctx = mod.meshcanvas.getContext('2d')\n var w = mod.meshcanvas.width\n var h = mod.meshcanvas.height\n ctx.clearRect(0,0,w,h)\n var dx = mod.dx\n var dy = mod.dy\n if (dx > dy) {\n var xo = 0\n var yo = h*.5*(1-dy/dx)\n var xw = w\n var yh = w*dy/dx\n }\n else {\n var xo = w*.5*(1-dx/dy)\n var yo = 0\n var xw = h*dx/dy\n var yh = h\n }\n ctx.beginPath()\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n x0 = xo+xw*(p0[0]-xmin)/dx\n y0 = yo+yh*(ymax-p0[1])/dy\n x1 = xo+xw*(p1[0]-xmin)/dx\n y1 = yo+yh*(ymax-p1[1])/dy\n x2 = xo+xw*(p2[0]-xmin)/dx\n y2 = yo+yh*(ymax-p2[1])/dy\n ctx.moveTo(x0,y0)\n ctx.lineTo(x1,y1)\n ctx.lineTo(x2,y2)\n ctx.lineTo(x0,y0)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[2],endian)\n newoffset += 4\n newoffset += 2\n }\n ctx.stroke()\n //\n // generate output\n //\n outputs.mesh.event(buf)\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":"566.4929216161105","left":"1172.0458542203646","inputs":{},"outputs":{}},"0.7667165137781767":{"definition":"//\n// offset\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2019\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'offset'\n//\n// initialization\n//\nvar init = function() {\n mod.offset.value = '25'\n mod.distances = ''\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 if ((mod.offset.value != '') && (mod.distances != ''))\n offset()\n else\n mod.distances = ''\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 // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"455.3504629476813","left":"3753.5051842312128","inputs":{},"outputs":{}},"0.32304064019646705":{"definition":"//\n// mesh slice raster\n// \n// todo\n// include slice plane triangles\n// scale perturbation to resolution\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 = 'mesh slice raster'\n//\n// initialization\n//\nvar init = function() {\n mod.mmunits.value = '1'\n mod.inunits.value = '0.03937007874015748'\n mod.depth.value = '5'\n mod.width.value = '1000'\n mod.border.value = '0'\n mod.delta = 1e-6\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = new DataView(evt.detail)\n find_limits_slice()}},\n settings:{type:'',\n event:function(evt){\n for (var p in evt.detail)\n if (p == 'depthmm') {\n mod.depth.value = evt.detail[p]\n /parseFloat(mod.mmunits.value)\n }\n find_limits_slice()}}}\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 imageInfo:{type:'',\n event:function(){\n var obj = {}\n obj.name = \"mesh slice raster\"\n obj.width = mod.img.width\n obj.height = mod.img.height\n obj.dpi = mod.img.width/(mod.dx*parseFloat(mod.inunits.value))\n mods.output(mod,'imageInfo',obj)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen slice canvas\n //\n div.appendChild(document.createTextNode(' '))\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.slicecanvas = 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 // mesh units\n //\n div.appendChild(document.createTextNode('mesh units: (enter)'))\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('change',function(){\n mod.inunits.value = parseFloat(mod.mmunits.value)/25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.mmunits = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n mod.mmunits.value = parseFloat(mod.inunits.value)*25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.inunits = input\n //\n // mesh size\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mesh size:'))\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (units)')\n div.appendChild(text)\n mod.meshsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (mm)')\n div.appendChild(text)\n mod.mmsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (in)')\n div.appendChild(text)\n mod.insize = text\n //\n // slice depth\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice Z depth: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.depth = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice border \n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice border: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.border = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice width\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice width: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.width = input\n div.appendChild(document.createTextNode(' (pixels)'))\n //\n // view slice\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 slice'))\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// find limits then slice\n//\nfunction find_limits_slice() {\n var blob = new Blob(['('+limits_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 mod.triangles = evt.data.triangles\n mod.xmin = evt.data.xmin\n mod.xmax = evt.data.xmax\n mod.ymin = evt.data.ymin\n mod.ymax = evt.data.ymax\n mod.zmin = evt.data.zmin\n mod.zmax = evt.data.zmax\n mod.dx = mod.xmax-mod.xmin\n mod.dy = mod.ymax-mod.ymin\n mod.dz = mod.zmax-mod.zmin\n mod.meshsize.nodeValue = \n mod.dx.toFixed(3)+' x '+\n mod.dy.toFixed(3)+' x '+\n mod.dz.toFixed(3)+' (units)'\n var mm = parseFloat(mod.mmunits.value)\n mod.mmsize.nodeValue = \n (mod.dx*mm).toFixed(3)+' x '+\n (mod.dy*mm).toFixed(3)+' x '+\n (mod.dz*mm).toFixed(3)+' (mm)'\n var inches = parseFloat(mod.inunits.value)\n mod.insize.nodeValue = \n (mod.dx*inches).toFixed(3)+' x '+\n (mod.dy*inches).toFixed(3)+' x '+\n (mod.dz*inches).toFixed(3)+' (in)'\n mods.fit(mod.div)\n slice_mesh()\n })\n var border = parseFloat(mod.border.value)\n webworker.postMessage({\n mesh:mod.mesh,\n border:border,delta:mod.delta})\n }\nfunction limits_worker() {\n self.addEventListener('message',function(evt) {\n var view = evt.data.mesh\n var depth = evt.data.depth\n var border = evt.data.border\n var delta = evt.data.delta // perturb to remove degeneracies\n //\n // get vars\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n offset += 2\n if (x0 > xmax) xmax = x0\n if (x0 < xmin) xmin = x0\n if (y0 > ymax) ymax = y0\n if (y0 < ymin) ymin = y0\n if (z0 > zmax) zmax = z0\n if (z0 < zmin) zmin = z0\n if (x1 > xmax) xmax = x1\n if (x1 < xmin) xmin = x1\n if (y1 > ymax) ymax = y1\n if (y1 < ymin) ymin = y1\n if (z1 > zmax) zmax = z1\n if (z1 < zmin) zmin = z1\n if (x2 > xmax) xmax = x2\n if (x2 < xmin) xmin = x2\n if (y2 > ymax) ymax = y2\n if (y2 < ymin) ymin = y2\n if (z2 > zmax) zmax = z2\n if (z2 < zmin) zmin = z2\n }\n xmin -= border\n xmax += border\n ymin -= border\n ymax += border\n //\n // return\n //\n self.postMessage({triangles:triangles,\n xmin:xmin,xmax:xmax,ymin:ymin,ymax:ymax,\n zmin:zmin,zmax:zmax})\n self.close()\n })\n }\n//\n// slice mesh\n// \nfunction slice_mesh() {\n var blob = new Blob(['('+slice_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.slicecanvas.height*.5*(1-h/w)\n var wd = mod.slicecanvas.width\n var hd = mod.slicecanvas.width*h/w\n }\n else {\n var x0 = mod.slicecanvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.slicecanvas.height*w/h\n var hd = mod.slicecanvas.height\n }\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n outputs.image.event()\n outputs.imageInfo.event()\n })\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n var depth = parseFloat(mod.depth.value)\n mod.img.width = parseInt(mod.width.value)\n mod.img.height = Math.round(mod.img.width*mod.dy/mod.dx)\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.img.height,width:mod.img.width,depth:depth,\n imgbuffer:img.data.buffer,mesh:mod.mesh,\n xmin:mod.xmin,xmax:mod.xmax,\n ymin:mod.ymin,ymax:mod.ymax,\n zmin:mod.zmin,zmax:mod.zmax,\n delta:mod.delta},\n [img.data.buffer])\n }\nfunction slice_worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var depth = evt.data.depth\n var view = evt.data.mesh\n var delta = evt.data.delta // perturb to remove degeneracies\n var xmin = evt.data.xmin\n var xmax = evt.data.xmax\n var ymin = evt.data.ymin\n var ymax = evt.data.ymax\n var zmin = evt.data.zmin\n var zmax = evt.data.zmax\n var buf = new Uint8ClampedArray(evt.data.imgbuffer)\n //\n // get vars from buffer\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // initialize slice image\n //\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] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // find triangles crossing the slice\n //\n var segs = []\n offset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n //\n // assemble vertices\n //\n offset += 2\n var v = [[x0,y0,z0],[x1,y1,z1],[x2,y2,z2]]\n //\n // sort z\n //\n v.sort(function(a,b) {\n if (a[2] < b[2])\n return -1\n else if (a[2] > b[2])\n return 1\n else\n return 0\n })\n //\n // check for crossings\n //\n if ((v[0][2] < (zmax-depth)) && (v[2][2] > (zmax-depth))) {\n //\n // crossing found, check for side and save\n //\n if (v[1][2] < (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[2][0]+(v[1][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n var y1 = v[2][1]+(v[1][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n }\n else if (v[1][2] >= (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[1][0]+(v[0][0]-v[1][0])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n var y1 = v[1][1]+(v[0][1]-v[1][1])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n }\n if (y0 < y1)\n segs.push({x0:x0,y0:y0,x1:x1,y1:y1})\n else\n segs.push({x0:x1,y0:y1,x1:x0,y1:y0})\n }\n }\n //\n // fill interior\n //\n for (var row = 0; row < h; ++row) {\n var y = ymin+(ymax-ymin)*row/(h-1)\n rowsegs = segs.filter(p => ((p.y0 <= y) && (p.y1 >= y)))\n var xs = rowsegs.map(p =>\n (p.x0+(p.x1-p.x0)*(y-p.y0)/(p.y1-p.y0)))\n xs.sort((a,b) => (a-b))\n for (var col = 0; col < w; ++col) {\n var x = xmin+(xmax-xmin)*col/(w-1)\n var index = xs.findIndex((p) => (p >= x))\n if (index == -1)\n var i = 0\n else\n var i = 255*(index%2)\n buf[(h-1-row)*w*4+col*4+0] = i\n buf[(h-1-row)*w*4+col*4+1] = i\n buf[(h-1-row)*w*4+col*4+2] = i\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // output the slice\n //\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n self.close()\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"405.29766392626607","left":"1608.1236313731386","inputs":{},"outputs":{}},"0.4144526456371104":{"definition":"//\n// view path\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2019\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// 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 path'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n path:{type:'',\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 mods.fit(mod.div)\n outputs.path.event()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'',\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,'path',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 renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction open_view_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//\nfunction init_window() {\n //document.write('<script type=\"text/javascript\">'+arg+'</script>')\n //document.close()\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 mod.win.close()\n mod.win = undefined\n })\n mod.win.document.body.appendChild(btn)\n //\n // label text\n //\n var text = win.document.createTextNode(' left: pan, right: rotate, scroll: zoom')\n mod.win.document.body.appendChild(text)\n //\n // GL container\n //\n mod.win.document.body.appendChild(document.createElement('br')) \n container = mod.win.document.createElement('div')\n container.style.overflow = 'hidden'\n mod.win.document.body.appendChild(container)\n //\n // event handlers\n //\n container.addEventListener('contextmenu',context_menu)\n container.addEventListener('mousedown',mouse_down)\n container.addEventListener('mouseup',mouse_up)\n container.addEventListener('mousemove',mouse_move)\n container.addEventListener('wheel',mouse_wheel)\n //\n // add scene\n //\n scene = new THREE.Scene()\n mod.scene = scene\n var width = mod.win.innerWidth\n var height = mod.win.innerHeight\n var aspect = width/height\n var near = 0.1\n var far = 1000000\n camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n mod.camera = camera\n scene.add(camera)\n //\n // add renderer\n //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n renderer.setSize(width,height)\n container.appendChild(renderer.domElement)\n //\n // show the path if available\n //\n show_path()\n //\n // 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/mod.win.innerHeight\n -Math.cos(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n mod.y0 += \n Math.cos(mod.thetaz)*mod.height*dy/mod.win.innerHeight\n +Math.sin(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n else if (mod.button == 2) {\n mod.thetaxy += dy/mod.win.innerHeight\n mod.thetaz += dx/mod.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 if (Math.cos(mod.thetaxy) > 0)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n else\n camera.up = new THREE.Vector3(-Math.sin(mod.thetaz),-Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n //\n // mouse_wheel\n //\n function mouse_wheel(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dy = evt.deltaY/mod.win.innerHeight\n mod.r += mod.height*dy\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1305.2167006691657","left":"2161.848323325338","inputs":{},"outputs":{}},"0.9325875387173613":{"definition":"//\n// mill raster 2.5D\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2019\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'mill raster 2.5D'\n//\n// initialization\n//\nvar init = function() {\n mod.dia_in.value = '0.1'\n mod.dia_mm.value = '2.54'\n mod.cut_in.value = '0.1'\n mod.cut_mm.value = '2.54'\n mod.max_in.value = '1'\n mod.max_mm.value = '25.4'\n mod.number.value = '1'\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:'',\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:'',\n event:function(evt){\n if (mod.label.nodeValue == 'calculating') {\n //\n // calculation in progress, draw and accumulate\n //\n draw_path(evt.detail)\n accumulate_path(evt.detail)\n mod.offsetCount += 1\n if ((mod.offsetCount != parseInt(mod.number.value))\n && (evt.detail.length > 0)) {\n //\n // layer detail present and offset not complete\n //\n mod.offset += parseFloat(mod.stepover.value)\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else if (mod.depthmm < parseFloat(mod.max_mm.value)) {\n //\n // layer loop not complete\n //\n merge_layer()\n accumulate_toolpath()\n clear_layer()\n mod.depthmm += parseFloat(mod.cut_mm.value)\n if (mod.depthmm > parseFloat(mod.max_mm.value)) {\n mod.depthmm = parseFloat(mod.max_mm.value)\n }\n //\n // clear offset\n //\n outputs.offset.event('')\n //\n // set new depth\n //\n outputs.depth.event(mod.depthmm)\n //\n // set new offset\n //\n mod.offsetCount = 0\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else {\n //\n // done, finish and output\n //\n draw_path(mod.toolpath)\n draw_connections(mod.toolpath)\n mod.label.nodeValue = 'calculate'\n mod.labelspan.style.fontWeight = 'normal'\n outputs.toolpath.event()\n }\n }\n }\n },\n settings:{type:'',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n depth:{type:'',\n event:function(depth){\n mods.output(mod,'depth',{depthmm:depth})\n }\n },\n diameter:{type:'',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{type:'',\n event:function(size){\n mods.output(mod,'offset',size)\n }\n },\n toolpath:{type:'',\n event:function(){\n cmd = {}\n cmd.path = mod.toolpath\n cmd.name = mod.name\n cmd.dpi = mod.dpi\n cmd.width = mod.width\n cmd.height = mod.height\n cmd.depth = Math.round(parseFloat(mod.max_in.value)*mod.dpi)\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 outputs.offset.event('') // clear offset\n mod.depthmm = parseFloat(mod.cut_mm.value)\n outputs.depth.event(mod.depthmm) // set depth\n mod.toolpath = [] // start new toolpath\n clear_layer() // clear layer\n outputs.diameter.event()\n outputs.offset.event( // set offset\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\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_layer\n//\nfunction clear_layer() {\n mod.path = []\n mod.offset = 0.5\n mod.offsetCount = 0\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute(\n '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_layer\n//\nfunction merge_layer() {\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// accumulate_toolpath\n//\nfunction accumulate_toolpath() {\n for (var seg = 0; seg < mod.path.length; ++seg) {\n var newseg = []\n for (var pt = 0; pt < mod.path[seg].length; ++pt) {\n var idepth = -Math.round(mod.dpi*mod.depthmm/25.4)\n var point = mod.path[seg][pt].concat(idepth)\n newseg.splice(newseg.length,0,point)\n }\n mod.toolpath.push(newseg)\n }\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(\n '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(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n '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(path) {\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 < path.length; ++segment) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS(\n '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 = path[segment-1][path[segment-1].length-1][0]\n var y1 = h-path[segment-1][path[segment-1].length-1][1]-1\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(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n '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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"544.0107535140212","left":"2166.203619400462","inputs":{},"outputs":{}},"0.40901053044748015":{"definition":"//\r\n// STL module extracts stl from Tools/FabLab Connect command of SolidWorks products\r\n// \r\n// Shawn Liu @ Dassault Systemes SolidWorks Corporation\r\n// (c) Massachusetts Institute of Technology 2019\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n//\r\n// name\r\n//\r\nvar name = 'STL connect'\r\n//\r\n// initialization\r\n//\r\nvar init = function () {\r\n mod.address = getParameterByName('swIP') || '127.0.0.1'\r\n mod.port = getParameterByName('swPort') || '80'\r\n mod.socket = 0\r\n mod.sag.value = '0.1'\n mod.angle.value = '45'\n socket_open()\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {\r\n }\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n mesh:{type:'STL',\r\n event:function(buffer){\r\n mods.output(mod,'mesh',buffer)}}\r\n }\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n //\r\n // canvas\r\n //\r\n var canvas = document.createElement('canvas')\r\n canvas.width = mods.ui.canvas\r\n canvas.height = mods.ui.canvas\r\n canvas.style.backgroundColor = 'rgb(255,255,255)'\r\n div.appendChild(canvas)\r\n mod.canvas = canvas\r\n div.appendChild(document.createElement('br'))\r\n\r\n div.appendChild(document.createTextNode('server:'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('address: ' + getParameterByName('swIP')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0port: ' + getParameterByName('swPort')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0status: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 12\r\n div.appendChild(input)\r\n mod.status = input\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('open'))\r\n btn.addEventListener('click', function () {\r\n socket_open()\r\n })\r\n div.appendChild(btn)\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.addEventListener('click', function () {\r\n socket_close()\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('sag: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.sag = input\r\n div.appendChild(document.createTextNode('(mm)'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('angle: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.angle = input\r\n div.appendChild(document.createTextNode('(degree)'))\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('Extract STL'))\r\n btn.addEventListener('click', function () {\r\n extract_STL()\r\n })\r\n div.appendChild(btn)\r\n\r\n //\r\n // info\r\n //\r\n var info = document.createElement('div')\r\n info.setAttribute('id', div.id + 'info')\r\n var text = document.createTextNode('name: ')\n info.appendChild(text)\n mod.name = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('size: ')\r\n info.appendChild(text)\r\n mod.sizen = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('triangles: ')\r\n info.appendChild(text)\r\n mod.trianglesn = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('dx: ')\r\n info.appendChild(text)\r\n mod.dxn = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('dy: ')\r\n info.appendChild(text)\r\n mod.dyn = text\r\n info.appendChild(document.createElement('br'))\r\n var text = document.createTextNode('dz: ')\r\n info.appendChild(text)\r\n mod.dzn = text\r\n div.appendChild(info)\r\n }\r\n//\r\n// local functions\r\n//\r\n\r\n //\r\n // local functions\r\n //\r\n\r\nfunction getParameterByName(name, url) {\r\n if (!url) url = window.location.href;\r\n name = name.replace(/[\\[\\]]/g, \"\\\\$&\");\r\n var regex = new RegExp(\"[?&]\" + name + \"(=([^&#]*)|&|#|$)\"),\r\n results = regex.exec(url);\r\n if (!results) return null;\r\n if (!results[2]) return '';\r\n return decodeURIComponent(results[2].replace(/\\+/g, \" \"));\r\n}\r\n\r\nfunction socket_open() {\r\n var url = \"ws://\" + mod.address + ':' + mod.port\r\n mod.socket = new WebSocket(url)\r\n mod.socket.onopen = function (event) {\r\n mod.status.value = \"opened\"\r\n var connect = {}\r\n connect.modCmd = 'connect'\r\n connect.owner = getParameterByName('swOwner')\r\n connect.id = getParameterByName('swID')\r\n socket_send(JSON.stringify(connect))\r\n }\r\n mod.socket.onerror = function (event) {\r\n mod.status.value = \"can not open\"\r\n }\r\n mod.socket.onmessage = function (event) {\r\n mod.status.value = \"receive\"\r\n var swData = JSON.parse(event.data)\r\n if (swData.swType === \"AutoExtractSTL\") {\r\n var partName = swData.data.partName\r\n if (partName.length > 25)\r\n partName = partName.slice(0, 22) +'...'\r\n mod.name.nodeValue = \"name: \" + partName\r\n mod.str = swData.data.stl\r\n stl_load_handler()\r\n }\r\n }\r\n mod.socket.onclose = function (event) {\r\n mod.status.value = \"connection closed\"\r\n }\r\n}\r\nfunction socket_close() {\r\n mod.socket.close()\r\n mod.status.value = \"closed\"\r\n mod.socket = 0\r\n}\r\nfunction socket_send(msg) {\r\n if (mod.socket != 0) {\r\n mod.status.value = \"send\"\r\n mod.socket.send(msg)\r\n }\r\n else {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n}\r\nfunction extract_STL() {\r\n var modcmd = new Object;\r\n modcmd.modCmd = \"AutoExtractSTL\";\r\n modcmd.sag = Number(mod.sag.value); // mm\r\n modcmd.angle = Number(mod.angle.value); //degree\r\n socket_send(JSON.stringify(modcmd))\r\n}\r\n\r\nfunction base64ToArrayBuffer(base64) {\r\n var binary_string = window.atob(base64);\r\n var len = binary_string.length;\r\n var bytes = new Uint8Array(len);\r\n for (var i = 0; i < len; i++) {\r\n bytes[i] = binary_string.charCodeAt(i);\r\n }\r\n return bytes.buffer;\r\n}\r\n\r\n//\r\n// load handler\r\n//\r\nfunction stl_load_handler() {\r\n //\r\n // check for binary STL\r\n //\r\n var arraybuf = base64ToArrayBuffer(mod.str)\r\n var endian = true\r\n var view = new DataView(arraybuf)\r\n var triangles = view.getUint32(80, endian)\r\n var size = 80+4+triangles*(4*12+2)\r\n if (size != view.byteLength) {\r\n mod.sizen.nodeValue = 'error: not binary STL'\r\n mod.trianglesn.nodeValue = ''\r\n mod.dxn.nodeValue = ''\r\n mod.dyn.nodeValue = ''\r\n mod.dzn.nodeValue = ''\r\n return\r\n }\r\n mod.sizen.nodeValue = 'size: '+size\r\n mod.trianglesn.nodeValue = 'triangles: '+triangles\r\n //\r\n // find limits and draw\r\n //\r\n var blob = new Blob(['('+draw_limits_worker.toString()+'())'])\r\n var url = window.URL.createObjectURL(blob)\r\n var webworker = new Worker(url)\r\n webworker.addEventListener('message',function(evt) {\r\n //\r\n // worker response\r\n //\r\n window.URL.revokeObjectURL(url)\r\n //\r\n // size\r\n //\r\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\r\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\r\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\r\n //\r\n // image\r\n //\r\n var image = evt.data.image\r\n var height = mod.canvas.height\r\n var width = mod.canvas.width\r\n var buffer = new Uint8ClampedArray(evt.data.image)\r\n var imgdata = new ImageData(buffer,width,height)\r\n var ctx = mod.canvas.getContext(\"2d\")\r\n ctx.putImageData(imgdata,0,0)\r\n //\r\n // output\r\n //\r\n outputs.mesh.event(evt.data.mesh)\r\n })\r\n var ctx = mod.canvas.getContext(\"2d\")\r\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\r\n var img = ctx.getImageData(0, 0, mod.canvas.width, mod.canvas.height)\r\n //\r\n // call worker\r\n //\r\n webworker.postMessage({\r\n height:mod.canvas.height,width:mod.canvas.width,\r\n image: img.data.buffer, mesh: arraybuf},\r\n [img.data.buffer, arraybuf])\r\n }\r\nfunction draw_limits_worker() {\r\n self.addEventListener('message',function(evt) {\r\n //\r\n // function to draw line\r\n //\r\n function line(x0,y0,x1,y1) {\r\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\r\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\r\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\r\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\r\n var row,col\r\n var idx = ix1-ix0\r\n var idy = iy1-iy0\r\n if (Math.abs(idy) > Math.abs(idx)) {\r\n (idy > 0) ?\r\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\r\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\r\n for (row = row0; row <= row1; ++row) {\r\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\r\n image[row*width*4+col*4+0] = 0\r\n image[row*width*4+col*4+1] = 0\r\n image[row*width*4+col*4+2] = 0\r\n image[row*width*4+col*4+3] = 255\r\n }\r\n }\r\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\r\n (idx > 0) ?\r\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\r\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\r\n for (col = col0; col <= col1; ++col) {\r\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\r\n image[row*width*4+col*4+0] = 0\r\n image[row*width*4+col*4+1] = 0\r\n image[row*width*4+col*4+2] = 0\r\n image[row*width*4+col*4+3] = 255\r\n }\r\n }\r\n else {\r\n row = iy0\r\n col = ix0\r\n image[row*width*4+col*4+0] = 0\r\n image[row*width*4+col*4+1] = 0\r\n image[row*width*4+col*4+2] = 0\r\n image[row*width*4+col*4+3] = 255\r\n }\r\n }\r\n //\r\n // get variables\r\n //\r\n var height = evt.data.height\r\n var width = evt.data.width\r\n var endian = true\r\n var image = new Uint8ClampedArray(evt.data.image)\r\n var view = new DataView(evt.data.mesh)\r\n var triangles = view.getUint32(80,endian)\r\n //\r\n // find limits\r\n //\r\n var offset = 80+4\r\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\r\n var xmin = Number.MAX_VALUE\r\n var xmax = -Number.MAX_VALUE\r\n var ymin = Number.MAX_VALUE\r\n var ymax = -Number.MAX_VALUE\r\n var zmin = Number.MAX_VALUE\r\n var zmax = -Number.MAX_VALUE\r\n for (var t = 0; t < triangles; ++t) {\r\n offset += 3*4\r\n x0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (x0 > xmax) xmax = x0\r\n if (x0 < xmin) xmin = x0\r\n y0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (y0 > ymax) ymax = y0\r\n if (y0 < ymin) ymin = y0\r\n z0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (z0 > zmax) zmax = z0\r\n if (z0 < zmin) zmin = z0\r\n x1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (x1 > xmax) xmax = x1\r\n if (x1 < xmin) xmin = x1\r\n y1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (y1 > ymax) ymax = y1\r\n if (y1 < ymin) ymin = y1\r\n z1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (z1 > zmax) zmax = z1\r\n if (z1 < zmin) zmin = z1\r\n x2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (x2 > xmax) xmax = x2\r\n if (x2 < xmin) xmin = x2\r\n y2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (y2 > ymax) ymax = y2\r\n if (y2 < ymin) ymin = y2\r\n z2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n if (z2 > zmax) zmax = z2\r\n if (z2 < zmin) zmin = z2\r\n offset += 2\r\n }\r\n var dx = xmax-xmin\r\n var dy = ymax-ymin\r\n var dz = zmax-zmin\r\n //\r\n // draw mesh\r\n //\r\n if (dx > dy) {\r\n var xo = 0\r\n var yo = height*.5*(1-dy/dx)\r\n var xw = width-1\r\n var yh = (width-1)*dy/dx\r\n }\r\n else {\r\n var xo = width*.5*(1-dx/dy)\r\n var yo = 0\r\n var xw = (height-1)*dx/dy\r\n var yh = height-1\r\n }\r\n offset = 80+4\r\n for (var t = 0; t < triangles; ++t) {\r\n offset += 3*4\r\n x0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n y0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n z0 = view.getFloat32(offset,endian)\r\n offset += 4\r\n x1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n y1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n z1 = view.getFloat32(offset,endian)\r\n offset += 4\r\n x2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n y2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n z2 = view.getFloat32(offset,endian)\r\n offset += 4\r\n offset += 2\r\n line(x0,y0,x1,y1)\r\n line(x1,y1,x2,y2)\r\n line(x2,y2,x0,y0)\r\n }\r\n //\r\n // return results and close\r\n //\r\n self.postMessage({\r\n dx:dx,dy:dy,dz:dz,\r\n image:evt.data.image,mesh:evt.data.mesh},[evt.data.image,evt.data.mesh])\r\n self.close()\r\n })\r\n }\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n mod:mod,\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"406","left":"746","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.7562574507163453\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4793941661670936\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7667165137781767\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.7667165137781767\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.4144526456371104\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7562574507163453\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"depth\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32304064019646705\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.7667165137781767\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.9325875387173613\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4144526456371104\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.40901053044748015\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}"]} \ No newline at end of file diff --git a/programs/machines/ShopBot/mill 2D svg connect b/programs/machines/ShopBot/mill 2D svg connect index 2e0b5a092ca9d96d0cbfe69b97e9887a2e698c2c..f508cb6078938fc5c651567368d6bbe8167e5d08 100644 --- a/programs/machines/ShopBot/mill 2D svg connect +++ b/programs/machines/ShopBot/mill 2D svg connect @@ -1 +1 @@ -{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"168","left":"1908","inputs":{},"outputs":{}},"0.07944144280928633":{"definition":"//\n// edge detect\n// green = interior, blue = exterior, red = boundary\n// assumes input is thresholded\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'edge detect'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n edge_detect()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:interior, blue:exterior, red:boundary'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// edge detect\n//\nfunction edge_detect() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({worker:worker.toString(),\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var i00,i0m,i0p,im0,ip0,imm,imp,ipm,ipp,row,col\n //\n // find edges - interior\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != i0m) || (i00 != im0) || (i00 != imm)\n || (i00 != imp) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // left and right edges\n //\n for (row = 1; row < (h-1); ++row) {\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm) \n || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // top and bottom edges\n //\n for (col = 1; col < (w-1); ++col) {\n row = h-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != imm) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != ipm) \n || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // corners\n //\n row = 0\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"928","left":"2117","inputs":{},"outputs":{}},"0.8903773266711255":{"definition":"//\n// orient edges\n// input is green:interior, blue:exterior, red:boundary\n// output is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'orient edges'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n var ctx = mod.display.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n orient_edges()\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // off-screen display canvas\n //\n var canvas = document.createElement('canvas')\n mod.display = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('red:north, dark red:south'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:east, dark green:west'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('blue:start, dark blue:stop'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.display,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// orient edges\n//\nfunction orient_edges() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n var disp = new Uint8ClampedArray(evt.data.display)\n var dispdata = new ImageData(disp,w,h)\n var ctx = mod.display.getContext(\"2d\")\n ctx.putImageData(dispdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var w = mod.canvas.width\n var h = mod.canvas.height\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,w,h)\n ctx.drawImage(mod.display,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var row,col\n var boundary = 0\n var interior = 1\n var exterior = 2\n var alpha = 3\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n //\n // orient body states\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && ((input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && ((input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n }\n }\n }\n //\n // orient edge states\n //\n for (col = 1; col < (w-1); ++col) {\n row = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n row = h-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n for (row = 1; row < (h-1); ++row) {\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n //\n // orient corner states (todo)\n //\n row = 0\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = 0\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n //\n // invert background for display\n //\n var display = new Uint8ClampedArray(h*w*4)\n var r,g,b,i\n for (row = 0; row < h; ++row) {\n for (col = 0; col < w; ++col) {\n r = output[(h-1-row)*w*4+col*4+0]\n g = output[(h-1-row)*w*4+col*4+1]\n b = output[(h-1-row)*w*4+col*4+2]\n i = r+g+b\n if (i != 0) { \n display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]\n display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]\n display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]\n display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]\n }\n else {\n display[(h-1-row)*w*4+col*4+0] = 255\n display[(h-1-row)*w*4+col*4+1] = 255\n display[(h-1-row)*w*4+col*4+2] = 255\n display[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // return output\n //\n self.postMessage({buffer:output.buffer,display:display.buffer},[output.buffer,display.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"831","left":"2600","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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"462","left":"2986","inputs":{},"outputs":{}},"0.6488303557466412":{"definition":"//\n// image threshold\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'image threshold'\n//\n// initialization\n//\nvar init = function() {\n mod.threshold.value = 0.5\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n threshold_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // threshold value\n //\n div.appendChild(document.createTextNode('threshold (0-1): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n threshold_image()\n })\n div.appendChild(input)\n mod.threshold = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// threshold image\n//\nfunction threshold_image() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var t = parseFloat(mod.threshold.value)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,threshold:t,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var t = evt.data.threshold\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,i\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n r = buf[(h-1-row)*w*4+col*4+0] \n g = buf[(h-1-row)*w*4+col*4+1] \n b = buf[(h-1-row)*w*4+col*4+2] \n a = buf[(h-1-row)*w*4+col*4+3] \n i = (r+g+b)/(3*255)\n if (a == 0)\n val = 255\n else if (i > t)\n var val = 255\n else\n var val = 0\n buf[(h-1-row)*w*4+col*4+0] = val\n buf[(h-1-row)*w*4+col*4+1] = val\n buf[(h-1-row)*w*4+col*4+2] = val\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"12","left":"1494","inputs":{},"outputs":{}},"0.4793941661670936":{"definition":"//\n// save file\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'save file'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n file:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.contents = evt.detail.contents\n save_file()\n }}}\n//\n// outputs\n//\nvar outputs = {}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name:')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('size:')\n div.appendChild(text)\n mod.sizetext = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\nfunction save_file() {\n var a = document.createElement('a')\n a.setAttribute('href','data:text/plain;charset=utf-8,'+ \n encodeURIComponent(mod.contents))\n a.setAttribute('download',mod.name)\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n mod.nametext.nodeValue = 'name: '+mod.name\n mods.fit(mod.div)\n mod.sizetext.nodeValue = 'size: '+mod.contents.length\n mods.fit(mod.div)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1225","left":"1239","inputs":{},"outputs":{}},"0.6248369051648597":{"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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"817","left":"752","inputs":{},"outputs":{}},"0.23780413326993044":{"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 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 = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"32","left":"2912","inputs":{},"outputs":{}},"0.5857417886002868":{"definition":"//\n// convert SVG image\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 = 'convert SVG image'\n//\n// initialization\n//\nvar init = function() {\n mod.dpi.value = 100\n }\n//\n// inputs\n//\nvar inputs = {\n SVG:{type:'string',\n event:function(evt){\n mod.svg = evt.detail\n get_size()\n draw_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}},\n imageInfo:{type:'object',\n event:function(){\n var obj = {}\n obj.name = \"SVG image\"\n obj.dpi = parseFloat(mod.dpi.value)\n obj.width = mod.img.width\n obj.height = mod.img.height\n mods.output(mod,'imageInfo',obj)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n //\n // dpi\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('dpi: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n draw_image()\n })\n div.appendChild(input)\n mod.dpi = input\n div.appendChild(document.createTextNode(' (enter)'))\n //\n // units\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('units: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n draw_image()\n })\n div.appendChild(input)\n mod.unitstext = input\n div.appendChild(document.createTextNode(' (enter)'))\n //\n // size\n //\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('image size:')\n div.appendChild(text)\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(pixels)')\n div.appendChild(text)\n mod.pixels = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(inches)')\n div.appendChild(text)\n mod.inches = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(mm)')\n div.appendChild(text)\n mod.mm = text\n }\n//\n// local functions\n//\n// get size\n//\nfunction get_size() {\n var i = mod.svg.indexOf(\"width\")\n if (i == -1) {\n var width = 1\n var height = 1\n var units = 90\n }\n else {\n var i1 = mod.svg.indexOf(\"\\\"\",i+1)\n var i2 = mod.svg.indexOf(\"\\\"\",i1+1)\n var width = mod.svg.substring(i1+1,i2)\n i = mod.svg.indexOf(\"height\")\n i1 = mod.svg.indexOf(\"\\\"\",i+1)\n i2 = mod.svg.indexOf(\"\\\"\",i1+1)\n var height = mod.svg.substring(i1+1,i2)\n ih = mod.svg.indexOf(\"height\")\n if (width.indexOf(\"px\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 90\n }\n else if (width.indexOf(\"mm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 25.4\n }\n else if (width.indexOf(\"cm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 2.54\n }\n else if (width.indexOf(\"in\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 1\n }\n else {\n var units = 90\n }\n }\n mod.width = parseFloat(width)\n mod.height = parseFloat(height)\n mod.units = units\n mod.unitstext.value = units\n }\n//\n// draw image\n//\nfunction draw_image() {\n var dpi = parseFloat(mod.dpi.value)\n var units = parseFloat(mod.unitstext.value)\n var width = parseInt(dpi*mod.width/units)\n var height = parseInt(dpi*mod.height/units)\n mod.pixels.nodeValue = width+' x '+height+\" (pixels)\"\n mod.inches.nodeValue = (width/dpi).toFixed(3)+' x '+(height/dpi).toFixed(3)+\" (inches)\"\n mod.mm.nodeValue = (25.4*width/dpi).toFixed(3)+' x '+(25.4*height/dpi).toFixed(3)+\" (mm)\"\n var src = \"data:image/svg+xml;base64,\"+window.btoa(mod.svg)\n var img = new Image()\n img.setAttribute(\"src\",src)\n img.onload = function() {\n if (width > height) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-height/width)\n var w = mod.canvas.width\n var h = mod.canvas.width*height/width\n }\n else {\n var x0 = mod.canvas.width*.5*(1-width/height)\n var y0 = 0\n var w = mod.canvas.height*width/height\n var h = mod.canvas.height\n }\n mod.img.width = width\n mod.img.height = height\n var ctx = mod.img.getContext(\"2d\")\n ctx.clearRect(0,0,width,height)\n ctx.drawImage(img,0,0,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,w,h)\n outputs.image.event()\n outputs.imageInfo.event()\n }\n }\n//\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":"64","left":"701","inputs":{},"outputs":{}},"0.749132408760488":{"definition":"//\n// vectorize\n// input is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = 1\n mod.sort.checked = false\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n vectorize()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'array',\n event:function(){\n mods.output(mod,'path',mod.path)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br')) \n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // error value\n //\n div.appendChild(document.createTextNode('vector fit (pixels): '))\n //div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n vectorize()\n })\n div.appendChild(input)\n mod.error = input\n div.appendChild(document.createElement('br'))\n //\n // sort\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n input.checked = true\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// vectorize\n//\nfunction vectorize() {\n //\n // draw path\n //\n function draw_path(path) {\n window.URL.revokeObjectURL(url)\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute('viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment in path) {\n if (path[segment].length > 1) {\n if (xend != null) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = xend\n var y1 = yend\n var x2 = path[segment][0][0]\n var y2 = h-path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n svg.appendChild(g)\n }\n //\n // set up worker\n //\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n webworker.terminate()\n mod.path = evt.data.path\n draw_path(mod.path)\n outputs.path.event()\n })\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,sort:mod.sort.checked,\n error:parseFloat(mod.error.value),\n buffer:mod.input.data.buffer})\n }\n//\n// vectorize worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var sort = evt.data.sort\n var input = new Uint8ClampedArray(evt.data.buffer)\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n var path = []\n //\n // edge follower\n //\n function follow_edges(row,col) {\n if ((input[(h-1-row)*w*4+col*4+northsouth] != 0)\n || (input[(h-1-row)*w*4+col*4+eastwest] != 0)) {\n path[path.length] = [[col,row]]\n while (1) {\n if (input[(h-1-row)*w*4+col*4+northsouth] & north) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~north\n row += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+northsouth] & south) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~south\n row -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & east) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~east\n col += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & west) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~west\n col -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else\n break\n }\n }\n }\n //\n // follow boundary starts\n //\n for (var row = 1; row < (h-1); ++row) {\n col = 0\n follow_edges(row,col)\n col = w-1\n follow_edges(row,col)\n }\n for (var col = 1; col < (w-1); ++col) {\n row = 0\n follow_edges(row,col)\n row = h-1 \n follow_edges(row,col)\n }\n //\n // follow interior paths\n //\n for (var row = 1; row < (h-1); ++row) {\n for (var col = 1; col < (w-1); ++col) {\n follow_edges(row,col)\n }\n }\n //\n // vectorize path\n //\n var error = evt.data.error\n var vecpath = []\n for (var seg = 0; seg < path.length; ++seg) {\n var x0 = path[seg][0][0]\n var y0 = path[seg][0][1]\n vecpath[vecpath.length] = [[x0,y0]]\n var xsum = x0\n var ysum = y0\n var sum = 1\n for (var pt = 1; pt < path[seg].length; ++pt) {\n var xold = x\n var yold = y\n var x = path[seg][pt][0]\n var y = path[seg][pt][1]\n if (sum == 1) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n var xmean = xsum/sum\n var ymean = ysum/sum\n var dx = xmean-x0\n var dy = ymean-y0\n var d = Math.sqrt(dx*dx+dy*dy)\n var nx = dy/d\n var ny = -dx/d\n var l = Math.abs(nx*(x-x0)+ny*(y-y0))\n if (l < error) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [xold,yold]\n x0 = xold\n y0 = yold\n xsum = xold\n ysum = yold\n sum = 1\n }\n }\n if (pt == (path[seg].length-1)) {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [x,y]\n }\n }\n }\n //\n // sort path\n //\n if ((vecpath.length > 0) && (sort == true)) {\n var dmin = w*w+h*h\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][0]\n var d = x*x+y*y\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n var sortpath = [vecpath[segmin]]\n vecpath.splice(segmin,1)\n }\n while (vecpath.length > 0) {\n var dmin = w*w+h*h\n var x0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][0]\n var y0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][1]\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][1]\n var d = (x-x0)*(x-x0)+(y-y0)*(y-y0)\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n sortpath[sortpath.length] = vecpath[segmin]\n vecpath.splice(segmin,1)\n }\n }\n }\n else if ((vecpath.length > 0) && (sort == false))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"587","left":"1739","inputs":{},"outputs":{}},"0.32734870523599846":{"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.125\n mod.dia_mm.value = 25.4*parseFloat(mod.dia_in.value)\n mod.cut_in.value = 0.1\n mod.cut_mm.value = 25.4*parseFloat(mod.cut_in.value)\n mod.max_in.value = 0.1\n mod.max_mm.value = 25.4*parseFloat(mod.max_in.value)\n mod.number.value = 1\n mod.stepover.value = 0.5\n mod.merge.value = 1\n mod.reverse.checked = true\n mod.sort.checked = false\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 }\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//\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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"402","left":"1241","inputs":{},"outputs":{}},"0.49036025089153756":{"definition":"//\n// Automatic dogbones for preprocessing images to be machined\n//\n// Sam Calisch\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 = 'dogbone'\n//\n// initialization\n//\nvar init = function() {\n mod.diameter.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.diameter.value != '')\n dogbone()\n }},\n diameter:{type:'number',\n event:function(evt){\n mod.diameter.value = evt.detail\n if (mod.distances != undefined)\n dogbone()\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // diameter value\n //\n div.appendChild(document.createTextNode('diameter (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n dogbone()\n })\n div.appendChild(input)\n mod.diameter = 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// dogbone\n//\nfunction dogbone() {\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 diameter = parseFloat(mod.diameter.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n diameter:diameter,buffer:mod.distances.buffer})\n }\n//\n// dogbone worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var diameter = evt.data.diameter\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] <= 0) {\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\n //pick out ridge points at the right distance\n var distance_value = (diameter/2.) * (Math.sqrt(2)/2.);\n var distance_tol = 1; //Math.sqrt(2)/2. ;\n var r = Math.round(diameter/2);\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n var max_ud = input[ (h-1-row)*w+col ] >= Math.max(input[ (h-1-row)*w+col-1], input[ (h-1-row)*w+col+1]); //up down\n var max_lr = input[ (h-1-row)*w+col ] >= Math.max(input[ (h-1-row-1)*w+col], input[ (h-1-row+1)*w+col]); //left right\n var max_ru = input[ (h-1-row)*w+col ] >= Math.max(input[ (h-1-row-1)*w+col+1], input[ (h-1-row+1)*w+col-1]); //right up\n var max_rd = input[ (h-1-row)*w+col ] >= Math.max(input[ (h-1-row-1)*w+col-1], input[ (h-1-row+1)*w+col+1]); //right up\n //if we are local max in at least two directions\n if( (max_ud+max_lr+max_ru+max_rd) >= 2 && Math.abs(input[ (h-1-row)*w+col]-distance_value) <= distance_tol ) {\n for(var cx=-r; cx<=r; ++cx){\n var yx = Math.ceil(Math.sqrt(r*r-cx*cx));\n for(var cy=-yx; cy<=yx; ++cy){\n output[(h-1-(row+cx))*w*4+(col+cy)*4+0] = 0;\n output[(h-1-(row+cx))*w*4+(col+cy)*4+1] = 0;\n output[(h-1-(row+cx))*w*4+(col+cy)*4+2] = 0;\n output[(h-1-(row+cx))*w*4+(col+cy)*4+3] = 255;\n }\n }\n\n } \n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"38","left":"2414","inputs":{},"outputs":{}},"0.8617147326718335":{"definition":"//\n// ShopBot\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\n\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'ShopBot'\n//\n// initialization\n//\nvar init = function() {\n mod.cutspeed.value = 20\n mod.plungespeed.value = 20\n mod.jogspeed.value = 75\n mod.jogheight.value = 5\n mod.spindlespeed.value = 10000\n mod.unitsin.checked = true \n }\n//\n// inputs\n//\nvar inputs = {\n toolpath:{type:'object',\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:'object',\n event:function(str){\n obj = {}\n obj.name = mod.name+\".sbp\"\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 speed\n //\n div.appendChild(document.createTextNode('jog speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.jogspeed = 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 // file units\n //\n div.appendChild(document.createTextNode('file units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsin'\n div.appendChild(input)\n mod.unitsin = input\n div.appendChild(document.createTextNode('in'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsmm'\n div.appendChild(input)\n mod.unitsmm = input\n div.appendChild(document.createTextNode('mm'))\n }\n//\n// local functions\n//\nfunction make_path() {\n if (mod.unitsin.checked)\n var units = 1\n else\n var units = 25.4\n var dx = units*mod.width/mod.dpi\n var nx = mod.width\n var cut_speed = units*parseFloat(mod.cutspeed.value)/25.4\n var plunge_speed = units*parseFloat(mod.plungespeed.value)/25.4\n var jog_speed = units*parseFloat(mod.jogspeed.value)/25.4\n var jog_height = units*parseFloat(mod.jogheight.value)/25.4\n var spindle_speed = parseFloat(mod.spindlespeed.value)\n var scale = dx/(nx-1)\n str = \"SA\\r\\n\" // set to absolute distances\n str += \"TR,\"+spindle_speed+\",1\\r\\n\" // set spindle speed\n str += \"SO,1,1\\r\\n\" // set output number 1 to on\n str += \"pause 2\\r\\n\" // let spindle come up to speed\n str += \"MS,\"+cut_speed.toFixed(4)+\",\"+plunge_speed.toFixed(4)+\"\\r\\n\" // set xy,z speed\n str += \"JS,\"+jog_speed.toFixed(4)+\",\"+jog_speed.toFixed(4)+\"\\r\\n\" // set jog xy,z speed\n str += \"JZ,\"+jog_height.toFixed(4)+\"\\r\\n\" // move up\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 += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\n\"\n str += \"J2,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\"\\r\\n\"\n //\n // move down\n //\n z = scale*mod.path[seg][0][2]\n str += \"MZ,\"+z.toFixed(4)+\"\\r\\n\"\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 += \"M3,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\",\"+z.toFixed(4)+\"\\r\\n\"\n }\n }\n //\n // output file\n //\n str += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\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":"1103","left":"783","inputs":{},"outputs":{}},"0.07230598353953022":{"definition":"//\n// nest multiple SVGs into a single SVG\n//\n// Sam Calisch\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 'parameters':{\n 'stock_width':40.,\n 'stock_height':40.,\n 'padding':.25} //algorithm parameters, and default values\n}\n//\n// name\n//\nvar name = 'nest SVG Array'\n//\n// initialization\n//\nvar init = function() {\n Object.keys(mod.parameters).forEach( function(k){\n mod[k].value = mod.parameters[k]; //set default values\n });\n }\n//\n// inputs\n//\nvar inputs = {\n SVGArray:{type:'object',\n event:function(evt) {\n mod.svg_array = evt.detail;\n nest(mod.svg_array);\n }}}\n//\n// outputs\n//\nvar outputs = {\n SVG:{type:'string',\n event:function(){\n var str = new XMLSerializer().serializeToString(mod.bigview);\n mods.output(mod,'SVG',str)}},\n // TODO: make another output for parts that don't fit\n //Leftovers:{type:'object',\n // event:function(){\n // mods.output(mod,'Leftovers',mod.leftovers)}}\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n\n // on-screen drawing canvas\n var smallview = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");//document.createElement('canvas');\n smallview.setAttribute('width',mods.ui.canvas);\n smallview.setAttribute('height',mods.ui.canvas);\n smallview.setAttribute('preserveAspectRatio','xMinYMin meet');\n div.appendChild(smallview);\n mod.smallview = smallview;\n div.appendChild(document.createElement('br'));\n\n // off-screen image canvas\n mod.bigview = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\"); \n\n //add parameter inputs\n Object.keys(mod.parameters).forEach(function(p){\n var textnode = document.createElement('span');\n textnode.innerHTML = p+': ';\n textnode.style=\"display: inline-block; width: 80px; font-size: 12px;\";\n div.appendChild(textnode);\n var input_text = document.createElement('input');\n var input_max = document.createElement('input');\n var input_range = document.createElement('input') //add slider\n\n //value text\n input_text.type='text';\n input_text.size=3;\n mod[p] = input_text; //set initial minimum slider value\n div.appendChild(input_text);\n input_text.addEventListener('blur',function(){\n input_range.value = (100 * input_text.value / input_max.value);\n nest(mod.svg_array);\n });\n\n //slider\n input_range.type = 'range'; \n input_range.min = 0; input_range.max = 100;\n input_range.value = 50; \n input_range.style = '-webkit-appearance: none; width: 80px; height: 0px; border: none; margin-top: -4px; margin-left:2px;';\n input_range.addEventListener('input',function(){\n input_text.value = input_max.value * input_range.value/100.0;\n nest(mod.svg_array);\n });\n div.appendChild(input_range);\n\n //max text\n input_max.type='text';\n input_max.size=2;\n input_max.value = 2*mod.parameters[p]; //set initial maximum to twice default value\n input_max.addEventListener('blur',function(){\n input_range.value = 100 * input_text.value / input_max.value;\n input_text.value = Math.min( input_text.value, input_max.value ); \n nest(mod.svg_array);\n });\n div.appendChild(input_max);\n\n div.appendChild(document.createElement('br'));\n });\n\n // view button\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 mod.bigview.setAttribute('width', mod.stock_width.value+\"in\");\n mod.bigview.setAttribute('height', mod.stock_height.value+\"in\" );\n mod.bigview.setAttribute('preserveAspectRatio','xMinYMin meet');\n win.document.body.appendChild(mod.bigview);\n })\n div.appendChild(btn)\n div.appendChild(document.createTextNode(' draw grid?'));\n var draw_grid = document.createElement('input')\n mod.draw_grid = draw_grid;\n draw_grid.type = 'checkbox'\n draw_grid.name = mod.div.id+'grid'\n draw_grid.id = mod.div.id+'grid'\n draw_grid.checked = false\n draw_grid.addEventListener('change',function(){nest(mod.svg_array);});\n div.appendChild(draw_grid)\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n\nfunction nest(sw_json){\n //sw_json is text json exported from soliworks of the form [{partTitle:”Part-1”, thickness:20, count:2, svgArray:[svgf1, svgf2, …],{partTitle:”Part-2”, thickness:20, count:3, svgArray:[svgf3, svgf4, …]\n //stocksize is an array [width,height] of stock dimensions\n //we scale so the stock size takes up the %75 of screen\n //padding is an amount to leave between each piece\n var unitScale = sw_json[0].unit === \"mm\"?1000:1;\n var stocksize = [mod.stock_width.value/39.3*unitScale, mod.stock_height.value/39.3*unitScale]; //convert to meters\n //TODO: handle units more gracefully!\n var padding = mod.padding.value/39.3*unitScale;\n var draw_grid = false;\n\n //make sure first dimension is longer\n //if (stocksize[1] > stocksize[0]) stocksize = [stocksize[1],stocksize[0]];\n\n //fit stock width to page\n var scale = mod.smallview.width/stocksize[0]; \n\n var partName = sw_json[0]['partName'];\n var thickness = sw_json[0]['thickness'];\n var count = sw_json[0]['count'];\n var svgs = [];\n //deal with multiple parts from SW\n for(var i=0; i<sw_json.length; ++i){\n svgs = svgs.concat(sw_json[i].svgArray);\n }\n\n //create SVG for output\n var svgNS = \"http://www.w3.org/2000/svg\";\n //var nested = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n mod.smallview.innerHTML=''; //delete previous children\n \n //mod.smallview.setAttribute('width',(stocksize[0])*scale );\n //mod.smallview.setAttribute('height',(stocksize[1])*scale );\n mod.smallview.setAttribute('viewBox', \"0 0 \"+stocksize[0]+\" \"+stocksize[1]);\n\n //draw stock outline\n var stock = document.createElementNS(svgNS,'rect');\n stock.setAttribute('x', 0); stock.setAttribute('y', 0);\n stock.setAttribute('width', stocksize[0]); stock.setAttribute('height', stocksize[1]);\n stock.setAttribute('style', 'fill:rgb(0,0,0);')\n //stock.setAttribute('class','annotation'); //label for removal on output\n mod.smallview.appendChild(stock);\n\n var gs = [] //container for the g elements\n svgs.forEach(function(svg){\n var g = document.createElementNS(svgNS,'g');\n g.innerHTML = svg;\n var svg_tree = g.firstChild;\n var vb = svg_tree.getAttribute('viewBox').split(\" \").map(parseFloat);\n svg_tree.setAttribute('viewBox',(vb[0]-.5*padding)+\" \"+(vb[1]-.5*padding)+\" \"+(vb[2]+.5*padding)+\" \"+(vb[3]+.5*padding));\n svg_tree.setAttribute('width', vb[2]+padding); svg_tree.setAttribute('height', vb[3]+padding);\n g.setAttribute('w',vb[2]+padding); //use a foreign tag to bring width and height info along as we tranform \n g.setAttribute('h',vb[3]+padding); \n mod.smallview.appendChild(g);\n gs.push(g);\n });\n\n //orient so long axis is horizontal. if this is not the case, rotate\n gs.forEach(function(g){\n w = g.getAttribute('w'); h = g.getAttribute('h');\n if (h>w){\n g.setAttribute('transform','translate(0,'+w+') rotate(-90)' );\n g.setAttribute('w',h); g.setAttribute('h',w);\n } else{\n g.setAttribute('transform','' );\n }\n });\n\n //then sort by long dimension in descending order\n gs = gs.sort( function(g1,g2){ return g2.getAttribute('w') - g1.getAttribute('w')} );\n\n\n //then place first and calculate the left over rectangles\n function fill_rect(b1,b2,remaining_shapes){\n if (mod.draw_grid.checked){\n node = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\n node.setAttribute('x',b1[0]); node.setAttribute('y',b1[1]);\n node.setAttribute('width',b2[0]-b1[0]);\n node.setAttribute('height',b2[1]-b1[1]);\n node.setAttribute('class','annotation'); //label for removal on output\n node.setAttribute('style','fill:none;stroke-width:.001;stroke:rgb(0,0,255)');\n mod.smallview.appendChild(node);\n }\n\n\n //fill a rectangle defined by point b1 to point b2 with the first element from remaining_shapes that fits\n var dx = b2[0]-b1[0]; \n var dy = b2[1]-b1[1];\n for(i=0; i<remaining_shapes.length; i++){\n var gi = remaining_shapes[i];\n var w = parseFloat(gi.getAttribute('w'));\n var h = parseFloat(gi.getAttribute('h'));\n if (w <= dx+.000001 && h <= dy+.00001){ //successfully placed shape i\n remaining_shapes.splice(i,1); //remove shape i from remaining shapes\n gi.setAttribute('transform', 'translate('+(b1[0])+','+(b1[1])+') '+gi.getAttribute('transform')); //use base point as transform\n fill_rect([b1[0],b1[1]+h],[b1[0]+w,b2[1]],remaining_shapes); //prioritize filling lower rectangle\n fill_rect([b1[0]+w,b1[1]],b2,remaining_shapes); //then fill right rectangle\n break; //break out of for loop\n }\n }\n\n }\n fill_rect([.5*padding,.5*padding],[stocksize[0]-.5*padding,stocksize[1]-.5*padding],gs);\n \n //HACK: don't show parts that don't fit\n gs.forEach(function(g){\n g.innerHTML = ''; //delete!\n }); \n\n\n //create bigview svg from smallview svg\n mod.bigview.setAttribute( 'viewBox', mod.smallview.getAttribute('viewBox') );\n mod.bigview.innerHTML = mod.smallview.innerHTML;\n mod.bigview.setAttribute('width', mod.stock_width.value+\"in\");\n mod.bigview.setAttribute('height', mod.stock_height.value+\"in\" );\n\n //output events\n //mod.leftovers = [];\n outputs.SVG.event()\n\n\n /*\n\n //highlight parts that didn't fit\n gs.forEach(function(g){\n var svg = g.firstChild;\n var style = svg.firstChild.getAttribute('style');\n svg.firstChild.setAttribute('style',style+'stroke-width:'+.002*stocksize[0]+'; stroke:rgb(255,0,0)')\n });\n //nest parts that don't fit, note: this could just be done on another page.\n if (gs.length > 0){\n fill_rect([.5*padding,.5*padding],[stocksize[0]-.5*padding,stocksize[1]-.5*padding],gs);\n }\n */\n}\n\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"650","left":"335","inputs":{},"outputs":{}},"0.10390878290633299":{"definition":"//\n// string variables\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 'sw_array':[{\"partName\":\"rwanda-stool.SLDPRT\",\"unit\":\"meter\",\"thickness\":0.01905,\"count\":1,\"svgArray\":[\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"1127.67015523439\\\" height=\\\"989.826899907394\\\" viewBox=\\\"0 0 0.281917538808599 0.247456724976849\\\" style=\\\"background:black\\\"><path d=\\\" M 0.280917538808598,0.0359877248472174 A 0.35,0.35 0 0,0 0.161236292032361,0.243281724976848 L 0.155737030718329,0.246456724976848 L 0.150237769404297,0.246456724976848 L 0.150237769404298,0.182920175324464 L 0.149983769404299,0.182920175324464 L 0.149983769404299,0.118489816608022 L 0.130933769404299,0.118489816608022 L 0.130933769404299,0.182920175324464 L 0.130679769404298,0.182920175324464 L 0.130679769404297,0.246456724976849 L 0.125180508090266,0.246456724976849 L 0.119681246776236,0.243281724976848 A 0.35,0.35 0 0,0 0,0.0359877248472167 L 2.22044604925031E-16,0.0296377248472162 L 0.00274963065701597,0.024875224847216 L 0.0577738967247917,0.0566434996734087 L 0.0675528967247919,0.0397057748261927 L 0.012528630657016,0.00793749999999993 L 0.0152782613140317,0.0031749999999999 L 0.0207775226280628,0 A 0.35,0.35 0 0,0 0.260140016180536,2.5951463200613E-15 L 0.265639277494567,0.00317500000000245 L 0.268388908151583,0.00793750000000276 L 0.213364642083807,0.0397057748261942 L 0.223143642083807,0.0566434996734103 L 0.278167908151583,0.0248752248472187 L 0.280917538808599,0.0296377248472181 L 0.280917538808598,0.0359877248472174\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.281417538808599 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"1324.8\\\" height=\\\"1324.8\\\" viewBox=\\\"0 0 0.3312 0.3312\\\" style=\\\"background:black\\\"><path d=\\\" M 0.208704299650783,0.151216862423607 L 0.198925299650783,0.134279137576391 L 0.181987574803567,0.14405813757639 L 0.191766574803567,0.160995862423607 L 0.208704299650783,0.151216862423607 M 0.257757710571941,0.122895862423607 L 0.274695435419157,0.113116862423607 L 0.264916435419157,0.0961791375763911 L 0.247978710571941,0.105958137576391 L 0.257757710571941,0.122895862423607 M 0.23192086753497,0.115229137576391 L 0.214983142687754,0.125008137576391 L 0.224762142687754,0.141945862423607 L 0.24169986753497,0.132166862423607 L 0.23192086753497,0.115229137576391 M 0.0822212894280657,0.10595813757639 L 0.0652835645808497,0.0961791375763897 L 0.0555045645808495,0.113116862423606 L 0.0724422894280656,0.122895862423606 L 0.0822212894280657,0.10595813757639 M 0.115216857312253,0.12500813757639 L 0.0982791324650367,0.11522913757639 L 0.0885001324650366,0.132166862423606 L 0.105437857312253,0.141945862423606 L 0.115216857312253,0.12500813757639 M 0.14821242519644,0.14405813757639 L 0.131274700349224,0.13427913757639 L 0.121495700349224,0.151216862423606 L 0.13843342519644,0.160995862423606 L 0.14821242519644,0.14405813757639 M 0.155321000000003,0.190245999999998 L 0.155321000000003,0.209803999999998 L 0.174879000000003,0.209803999999998 L 0.174879000000003,0.190245999999998 L 0.155321000000003,0.190245999999998 M 0.155321000000003,0.228345999999998 L 0.155321000000003,0.247903999999998 L 0.174879000000003,0.247903999999998 L 0.174879000000003,0.228345999999998 L 0.155321000000003,0.228345999999998 M 0.155321000000003,0.266445999999998 L 0.155321000000003,0.286003999999998 L 0.174879000000003,0.286003999999998 L 0.174879000000003,0.266445999999998 L 0.155321000000003,0.266445999999998 M 0.3302,0.1651 A 0.1651,0.1651 0 1,1 0,0.1651 A 0.1651,0.1651 0 1,1 0.3302,0.1651\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.3307 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"1098.95817229398\\\" height=\\\"952.261593287967\\\" viewBox=\\\"0 0 0.274739543073496 0.238065398321992\\\" style=\\\"background:black\\\"><path d=\\\" M 0.273739543073496,0.220134601678009 L 0.263964543073497,0.237065398321992 A 0.45,0.45 0 0,0 0.00977499999999865,0.237065398321992 L 0,0.220134601678008 A 0.45,0.45 0 0,0 0.127094771536749,2.77555756156289E-17 L 0.146644771536747,0 A 0.45,0.45 0 0,0 0.273739543073496,0.220134601678009 M 0.197751436852561,0.198841862423608 L 0.220188423013809,0.211795862423607 L 0.229967423013809,0.194858137576391 L 0.207530436852561,0.181904137576391 L 0.197751436852561,0.198841862423608 M 0.127090771536748,0.0505460000000001 L 0.127090771536748,0.0764540000000001 L 0.146648771536748,0.0764540000000001 L 0.146648771536748,0.0505460000000001 L 0.127090771536748,0.0505460000000001 M 0.127090771536748,0.101346 L 0.127090771536748,0.127254 L 0.146648771536748,0.127254 L 0.146648771536748,0.101346 L 0.127090771536748,0.101346 M 0.0535511200596879,0.211795862423609 L 0.0759881062209351,0.198841862423608 L 0.0662091062209351,0.181904137576392 L 0.0437721200596878,0.194858137576393 L 0.0535511200596879,0.211795862423609 M 0.0975452105719373,0.186395862423608 L 0.119982196733185,0.173441862423608 L 0.110203196733184,0.156504137576392 L 0.0877662105719372,0.169458137576392 L 0.0975452105719373,0.186395862423608 M 0.185973332501559,0.169458137576392 L 0.163536346340311,0.156504137576392 L 0.153757346340312,0.173441862423608 L 0.176194332501559,0.186395862423608 L 0.185973332501559,0.169458137576392\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.274239543073496 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"715.2\\\" height=\\\"2442.4\\\" viewBox=\\\"0 0 0.1788 0.6106\\\" style=\\\"background:black\\\"><path d=\\\" M 0.0644303587164418,0.3302 L 0,0.3302 L 3.81639164714898E-17,0.01905 L 0.0254,0.01905 L 0.0254,0.038354 L 0.0508,0.038354 L 0.0508,0.01905 L 0.0762,0.01905 L 0.0762,0.038354 L 0.127,0.038354 L 0.127,0 L 0.17145,0 A 0.00635,0.00635 0 0,1 0.1778,0.00635 L 0.1778,0.038354 A 4,4 0 0,0 0.10795,0.590296000000001 L 0.0952500000000001,0.590296 L 0.09525,0.6096 L 0.0762,0.6096 L 0.0762,0.590296 L 0.05715,0.590296 L 0.05715,0.6096 L 0.0381,0.6096 L 0.0381,0.590296 L 0.01905,0.590296 L 0.01905,0.6096 L 0,0.6096 L 0,0.34925 L 0.0644303587164418,0.34925 L 0.0644303587164418,0.3302 M 0.0381,0.545846 A 0.00634999999999998,0.00634999999999998 0 0,0 0.0444500000000001,0.552196 L 0.066152723772689,0.552196 A 0.00635000000000026,0.00635000000000026 0 0,0 0.0724886137147865,0.546269082310705 A 4.0381,4.0381 0 0,1 0.0855169733792192,0.394361959562247 A 0.00635000000000038,0.00635000000000038 0 0,0 0.0792015708158069,0.38735 L 0.0444500000000001,0.38735 A 0.00635000000000001,0.00635000000000001 0 0,0 0.0381,0.3937 L 0.0381,0.545846 M 0.0381,0.0828040000000001 L 0.0381,0.28575 A 0.00635000000000002,0.00635000000000002 0 0,0 0.0444500000000001,0.2921 L 0.0919853797681748,0.2921 A 0.00634999999999996,0.00634999999999996 0 0,0 0.0982807110766947,0.286581446760754 A 4.0381,4.0381 0 0,1 0.130298417149589,0.0839540826939461 A 0.00635000000000012,0.00635000000000012 0 0,0 0.12405343437963,0.076454 L 0.0444500000000001,0.076454 A 0.00634999999999999,0.00634999999999999 0 0,0 0.0381,0.0828040000000001\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.1783 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"715.2\\\" height=\\\"2442.4\\\" viewBox=\\\"0 0 0.1788 0.6106\\\" style=\\\"background:black\\\"><path d=\\\" M 0.0644303587164418,0.3302 L 0,0.3302 L 3.81639164714898E-17,0.01905 L 0.0254,0.01905 L 0.0254,0.038354 L 0.0508,0.038354 L 0.0508,0.01905 L 0.0762000000000001,0.01905 L 0.0762000000000001,0.038354 L 0.127,0.038354 L 0.127,0 L 0.17145,0 A 0.00635,0.00635 0 0,1 0.1778,0.00635 L 0.1778,0.038354 A 4,4 0 0,0 0.10795,0.590296000000001 L 0.0952500000000001,0.590296 L 0.09525,0.6096 L 0.0762,0.6096 L 0.0762,0.590296 L 0.05715,0.590296 L 0.05715,0.6096 L 0.0381,0.6096 L 0.0381,0.590296 L 0.01905,0.590296 L 0.01905,0.6096 L 0,0.6096 L 0,0.34925 L 0.0644303587164418,0.34925 L 0.0644303587164418,0.3302 M 0.0381,0.545846 A 0.00634999999999998,0.00634999999999998 0 0,0 0.0444500000000001,0.552196 L 0.066152723772689,0.552196 A 0.00635000000000026,0.00635000000000026 0 0,0 0.0724886137147865,0.546269082310705 A 4.0381,4.0381 0 0,1 0.0855169733792192,0.394361959562247 A 0.00635000000000038,0.00635000000000038 0 0,0 0.0792015708158069,0.38735 L 0.0444500000000001,0.38735 A 0.00635000000000001,0.00635000000000001 0 0,0 0.0381,0.3937 L 0.0381,0.545846 M 0.0381,0.0828040000000001 L 0.0381,0.28575 A 0.00635000000000002,0.00635000000000002 0 0,0 0.0444500000000001,0.2921 L 0.0919853797681748,0.2921 A 0.00634999999999996,0.00634999999999996 0 0,0 0.0982807110766947,0.286581446760754 A 4.0381,4.0381 0 0,1 0.130298417149589,0.0839540826939461 A 0.00635000000000012,0.00635000000000012 0 0,0 0.12405343437963,0.076454 L 0.0444500000000001,0.076454 A 0.00634999999999999,0.00634999999999999 0 0,0 0.0381,0.0828040000000001\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.1783 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"715.2\\\" height=\\\"2442.4\\\" viewBox=\\\"0 0 0.1788 0.6106\\\" style=\\\"background:black\\\"><path d=\\\" M 0.113369641283558,0.2794 L 0.1778,0.2794 L 0.1778,0.59055 L 0.1524,0.59055 L 0.1524,0.571246 L 0.127,0.571246 L 0.127,0.59055 L 0.1016,0.59055 L 0.1016,0.571246 L 0.0508,0.571246 L 0.0508,0.6096 L 0.00635000000000002,0.6096 A 0.00635,0.00635 0 0,1 0,0.60325 L 0,0.571246 A 4,4 0 0,0 0.0698499999999999,0.0193039999999987 L 0.08255,0.019304 L 0.0825500000000001,1.11022302462516E-16 L 0.1016,0 L 0.1016,0.019304 L 0.12065,0.0193039999999999 L 0.12065,1.11022302462516E-16 L 0.1397,1.11022302462516E-16 L 0.1397,0.0193039999999999 L 0.15875,0.019304 L 0.15875,0 L 0.1778,1.11022302462516E-16 L 0.1778,0.26035 L 0.113369641283558,0.26035 L 0.113369641283558,0.2794 M 0.1397,0.0637540000000001 A 0.00634999999999998,0.00634999999999998 0 0,0 0.13335,0.057404 L 0.111647276227311,0.0574039999999997 A 0.00635000000000026,0.00635000000000026 0 0,0 0.105311386285214,0.0633309176892949 A 4.0381,4.0381 0 0,1 0.0922830266207809,0.215238040437753 A 0.00635000000000038,0.00635000000000038 0 0,0 0.0985984291841932,0.22225 L 0.13335,0.22225 A 0.00635000000000001,0.00635000000000001 0 0,0 0.1397,0.2159 L 0.1397,0.0637540000000001 M 0.1397,0.526796 L 0.1397,0.32385 A 0.00635000000000002,0.00635000000000002 0 0,0 0.13335,0.3175 L 0.0858146202318253,0.3175 A 0.00634999999999996,0.00634999999999996 0 0,0 0.0795192889233054,0.323018553239246 A 4.0381,4.0381 0 0,1 0.047501582850411,0.525645917306054 A 0.00635000000000012,0.00635000000000012 0 0,0 0.0537465656203701,0.533146 L 0.13335,0.533146 A 0.00634999999999999,0.00634999999999999 0 0,0 0.1397,0.526796\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.1783 0.0005), scale(-1, 1)\\\" /></svg>\"]}]\n}\n//\n// name\n//\nvar name = 'sw output'\n//\n// initialization\n//\nvar init = function() { }\n//\n// inputs\n//\nvar inputs = {\n }\n//\n// outputs\n//\nvar outputs = {\n sw_array:{type:'object',\n event:function(){\n mods.output(mod,'sw_array',mod.sw_array )}}\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n\n var btn = document.createElement('button');\n btn.style.padding = mods.ui.padding;\n btn.style.margin = 1;\n btn.appendChild(document.createTextNode('send'));\n btn.addEventListener('click',function(){\n outputs.sw_array.event();\n });\n div.appendChild(btn);\n\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n;\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"469","left":"22","inputs":{},"outputs":{}},"0.14150626327820492":{"definition":"//\r\n// ExtractFaces module extracts top faces of the same thickness from Tools/FabLab Connect command of SolidWorks products\r\n// \r\n// Shawn Liu @ Dassault Systemes SolidWorks Corporation\r\n// (c) Massachusetts Institute of Technology 2016\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n//\r\n// name\r\n//\r\nvar name = 'ExtractFaces connect'\r\n//\r\n// initialization\r\n//\r\nvar init = function() {\r\n mod.address = getParameterByName('swIP') || '127.0.0.1'\r\n mod.port = getParameterByName('swPort') || '80'\r\n mod.socket = 0\r\n mod.thickness.value = 0.75\r\n socket_open()\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {}\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n SVGArray:{type:'object',\r\n event:function(data){\r\n mods.output(mod, 'SVGArray', JSON.parse(data))\r\n }\r\n }\r\n}\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n div.appendChild(document.createTextNode('server:'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('address: ' + getParameterByName('swIP')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0port: ' + getParameterByName('swPort')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0status: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 12\r\n div.appendChild(input)\r\n mod.status = input\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('open'))\r\n btn.addEventListener('click', function () {\r\n socket_open()\r\n })\r\n div.appendChild(btn)\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.addEventListener('click', function () {\r\n socket_close()\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('thickness: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.thickness = input\r\n div.appendChild(document.createTextNode('(inch)'))\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('Extract SVGs'))\r\n btn.addEventListener('click',function() {\r\n extract_SVGs()\r\n })\r\n div.appendChild(btn)\r\n }\r\n//\r\n// local functions\r\n//\r\n\r\nfunction getParameterByName(name, url) {\r\n if (!url) url = window.location.href;\r\n name = name.replace(/[\\[\\]]/g, \"\\\\$&\");\r\n var regex = new RegExp(\"[?&]\" + name + \"(=([^&#]*)|&|#|$)\"),\r\n results = regex.exec(url);\r\n if (!results) return null;\r\n if (!results[2]) return '';\r\n return decodeURIComponent(results[2].replace(/\\+/g, \" \"));\r\n}\r\n\r\nfunction socket_open() {\r\n var url = \"ws://\"+mod.address+':'+mod.port\r\n mod.socket = new WebSocket(url)\r\n mod.socket.onopen = function(event) {\r\n mod.status.value = \"opened\"\r\n var connect = {}\r\n connect.modCmd = 'connect'\r\n connect.owner = getParameterByName('swOwner')\r\n connect.id = getParameterByName('swID')\r\n socket_send(JSON.stringify(connect))\r\n }\r\n mod.socket.onerror = function(event) {\r\n mod.status.value = \"can not open\"\r\n }\r\n mod.socket.onmessage = function(event) {\r\n mod.status.value = \"receive\"\r\n var swData = JSON.parse(event.data);\r\n if (swData.swType === \"FaceSVGArray\") {\r\n outputs.SVGArray.event(JSON.stringify(swData.data))\r\n }\r\n }\r\n mod.socket.onclose = function (event) {\r\n mod.status.value = \"connection closed\"\r\n }\r\n }\r\nfunction socket_close() {\r\n mod.socket.close()\r\n mod.status.value = \"closed\"\r\n mod.socket = 0\r\n }\r\nfunction socket_send(msg) {\r\n if (mod.socket != 0) {\r\n mod.status.value = \"send\"\r\n mod.socket.send(msg)\r\n }\r\n else {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n }\r\nfunction extract_SVGs() {\r\n var modcmd = new Object;\r\n modcmd.modCmd = \"AutoExtractFaces\";\r\n modcmd.thickness = Number(mod.thickness.value * 25.4); // inch to mm\r\n socket_send(JSON.stringify(modcmd))\r\n }\r\n\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n mod:mod,\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"96","left":"192","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.23780413326993044\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5857417886002868\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5857417886002868\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6248369051648597\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.49036025089153756\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.23780413326993044\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.49036025089153756\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"diameter\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.49036025089153756\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"diameter\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6248369051648597\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8617147326718335\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8617147326718335\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4793941661670936\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.07230598353953022\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVG\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5857417886002868\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVG\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.14150626327820492\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVGArray\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07230598353953022\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVGArray\\\"}\"}"]} +{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"340","left":"2454","inputs":{},"outputs":{}},"0.07944144280928633":{"definition":"//\n// edge detect\n// green = interior, blue = exterior, red = boundary\n// assumes input is thresholded\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'edge detect'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n edge_detect()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:interior, blue:exterior, red:boundary'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// edge detect\n//\nfunction edge_detect() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({worker:worker.toString(),\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var i00,i0m,i0p,im0,ip0,imm,imp,ipm,ipp,row,col\n //\n // find edges - interior\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != i0m) || (i00 != im0) || (i00 != imm)\n || (i00 != imp) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // left and right edges\n //\n for (row = 1; row < (h-1); ++row) {\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm) \n || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // top and bottom edges\n //\n for (col = 1; col < (w-1); ++col) {\n row = h-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != imm) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != ipm) \n || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // corners\n //\n row = 0\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1100","left":"2663","inputs":{},"outputs":{}},"0.8903773266711255":{"definition":"//\n// orient edges\n// input is green:interior, blue:exterior, red:boundary\n// output is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'orient edges'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n var ctx = mod.display.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n orient_edges()\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // off-screen display canvas\n //\n var canvas = document.createElement('canvas')\n mod.display = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('red:north, dark red:south'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:east, dark green:west'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('blue:start, dark blue:stop'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.display,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// orient edges\n//\nfunction orient_edges() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n var disp = new Uint8ClampedArray(evt.data.display)\n var dispdata = new ImageData(disp,w,h)\n var ctx = mod.display.getContext(\"2d\")\n ctx.putImageData(dispdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var w = mod.canvas.width\n var h = mod.canvas.height\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,w,h)\n ctx.drawImage(mod.display,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var row,col\n var boundary = 0\n var interior = 1\n var exterior = 2\n var alpha = 3\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n //\n // orient body states\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && ((input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && ((input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n }\n }\n }\n //\n // orient edge states\n //\n for (col = 1; col < (w-1); ++col) {\n row = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n row = h-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n for (row = 1; row < (h-1); ++row) {\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n //\n // orient corner states (todo)\n //\n row = 0\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = 0\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n //\n // invert background for display\n //\n var display = new Uint8ClampedArray(h*w*4)\n var r,g,b,i\n for (row = 0; row < h; ++row) {\n for (col = 0; col < w; ++col) {\n r = output[(h-1-row)*w*4+col*4+0]\n g = output[(h-1-row)*w*4+col*4+1]\n b = output[(h-1-row)*w*4+col*4+2]\n i = r+g+b\n if (i != 0) { \n display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]\n display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]\n display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]\n display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]\n }\n else {\n display[(h-1-row)*w*4+col*4+0] = 255\n display[(h-1-row)*w*4+col*4+1] = 255\n display[(h-1-row)*w*4+col*4+2] = 255\n display[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // return output\n //\n self.postMessage({buffer:output.buffer,display:display.buffer},[output.buffer,display.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1003","left":"3146","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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"634","left":"3532","inputs":{},"outputs":{}},"0.6488303557466412":{"definition":"//\n// image threshold\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'image threshold'\n//\n// initialization\n//\nvar init = function() {\n mod.threshold.value = '0.5'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n threshold_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // threshold value\n //\n div.appendChild(document.createTextNode('threshold (0-1): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n threshold_image()\n })\n div.appendChild(input)\n mod.threshold = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// threshold image\n//\nfunction threshold_image() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var t = parseFloat(mod.threshold.value)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,threshold:t,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var t = evt.data.threshold\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,i\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n r = buf[(h-1-row)*w*4+col*4+0] \n g = buf[(h-1-row)*w*4+col*4+1] \n b = buf[(h-1-row)*w*4+col*4+2] \n a = buf[(h-1-row)*w*4+col*4+3] \n i = (r+g+b)/(3*255)\n if (a == 0)\n val = 255\n else if (i > t)\n var val = 255\n else\n var val = 0\n buf[(h-1-row)*w*4+col*4+0] = val\n buf[(h-1-row)*w*4+col*4+1] = val\n buf[(h-1-row)*w*4+col*4+2] = val\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"184","left":"2040","inputs":{},"outputs":{}},"0.4793941661670936":{"definition":"//\n// save file\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'save file'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n file:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.contents = evt.detail.contents\n save_file()\n }}}\n//\n// outputs\n//\nvar outputs = {}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name:')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('size:')\n div.appendChild(text)\n mod.sizetext = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\nfunction save_file() {\n var a = document.createElement('a')\n a.setAttribute('href','data:text/plain;charset=utf-8,'+ \n encodeURIComponent(mod.contents))\n a.setAttribute('download',mod.name)\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n mod.nametext.nodeValue = 'name: '+mod.name\n mods.fit(mod.div)\n mod.sizetext.nodeValue = 'size: '+mod.contents.length\n mods.fit(mod.div)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1397","left":"1785","inputs":{},"outputs":{}},"0.6248369051648597":{"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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"989","left":"1298","inputs":{},"outputs":{}},"0.23780413326993044":{"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 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 = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"204","left":"3458","inputs":{},"outputs":{}},"0.5857417886002868":{"definition":"//\n// convert SVG image\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 = 'convert SVG image'\n//\n// initialization\n//\nvar init = function() {\n mod.dpi.value = '100'\n }\n//\n// inputs\n//\nvar inputs = {\n SVG:{type:'string',\n event:function(evt){\n mod.svg = evt.detail\n get_size()\n draw_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}},\n imageInfo:{type:'object',\n event:function(){\n var obj = {}\n obj.name = \"SVG image\"\n obj.dpi = parseFloat(mod.dpi.value)\n obj.width = mod.img.width\n obj.height = mod.img.height\n mods.output(mod,'imageInfo',obj)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n //\n // dpi\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('dpi: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n draw_image()\n })\n div.appendChild(input)\n mod.dpi = input\n div.appendChild(document.createTextNode(' (enter)'))\n //\n // units\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('units: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n draw_image()\n })\n div.appendChild(input)\n mod.unitstext = input\n div.appendChild(document.createTextNode(' (enter)'))\n //\n // size\n //\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('image size:')\n div.appendChild(text)\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(pixels)')\n div.appendChild(text)\n mod.pixels = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(inches)')\n div.appendChild(text)\n mod.inches = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(mm)')\n div.appendChild(text)\n mod.mm = text\n }\n//\n// local functions\n//\n// get size\n//\nfunction get_size() {\n var i = mod.svg.indexOf(\"width\")\n if (i == -1) {\n var width = 1\n var height = 1\n var units = 90\n }\n else {\n var i1 = mod.svg.indexOf(\"\\\"\",i+1)\n var i2 = mod.svg.indexOf(\"\\\"\",i1+1)\n var width = mod.svg.substring(i1+1,i2)\n i = mod.svg.indexOf(\"height\")\n i1 = mod.svg.indexOf(\"\\\"\",i+1)\n i2 = mod.svg.indexOf(\"\\\"\",i1+1)\n var height = mod.svg.substring(i1+1,i2)\n ih = mod.svg.indexOf(\"height\")\n if (width.indexOf(\"px\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 90\n }\n else if (width.indexOf(\"mm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 25.4\n }\n else if (width.indexOf(\"cm\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 2.54\n }\n else if (width.indexOf(\"in\") != -1) {\n width = width.slice(0,-2)\n height = height.slice(0,-2)\n var units = 1\n }\n else {\n var units = 90\n }\n }\n mod.width = parseFloat(width)\n mod.height = parseFloat(height)\n mod.units = units\n mod.unitstext.value = units\n }\n//\n// draw image\n//\nfunction draw_image() {\n var dpi = parseFloat(mod.dpi.value)\n var units = parseFloat(mod.unitstext.value)\n var width = parseInt(dpi*mod.width/units)\n var height = parseInt(dpi*mod.height/units)\n mod.pixels.nodeValue = width+' x '+height+\" (pixels)\"\n mod.inches.nodeValue = (width/dpi).toFixed(3)+' x '+(height/dpi).toFixed(3)+\" (inches)\"\n mod.mm.nodeValue = (25.4*width/dpi).toFixed(3)+' x '+(25.4*height/dpi).toFixed(3)+\" (mm)\"\n var src = \"data:image/svg+xml;base64,\"+window.btoa(mod.svg)\n var img = new Image()\n img.setAttribute(\"src\",src)\n img.onload = function() {\n if (width > height) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-height/width)\n var w = mod.canvas.width\n var h = mod.canvas.width*height/width\n }\n else {\n var x0 = mod.canvas.width*.5*(1-width/height)\n var y0 = 0\n var w = mod.canvas.height*width/height\n var h = mod.canvas.height\n }\n mod.img.width = width\n mod.img.height = height\n var ctx = mod.img.getContext(\"2d\")\n ctx.clearRect(0,0,width,height)\n ctx.drawImage(img,0,0,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,w,h)\n outputs.image.event()\n outputs.imageInfo.event()\n }\n }\n//\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":"236","left":"1247","inputs":{},"outputs":{}},"0.749132408760488":{"definition":"//\n// vectorize\n// input is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = '1'\n mod.sort.checked = false\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n vectorize()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'array',\n event:function(){\n mods.output(mod,'path',mod.path)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br')) \n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // error value\n //\n div.appendChild(document.createTextNode('vector fit (pixels): '))\n //div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n vectorize()\n })\n div.appendChild(input)\n mod.error = input\n div.appendChild(document.createElement('br'))\n //\n // sort\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n input.checked = true\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// vectorize\n//\nfunction vectorize() {\n //\n // draw path\n //\n function draw_path(path) {\n window.URL.revokeObjectURL(url)\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute('viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment in path) {\n if (path[segment].length > 1) {\n if (xend != null) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = xend\n var y1 = yend\n var x2 = path[segment][0][0]\n var y2 = h-path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n svg.appendChild(g)\n }\n //\n // set up worker\n //\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n webworker.terminate()\n mod.path = evt.data.path\n draw_path(mod.path)\n outputs.path.event()\n })\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,sort:mod.sort.checked,\n error:parseFloat(mod.error.value),\n buffer:mod.input.data.buffer})\n }\n//\n// vectorize worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var sort = evt.data.sort\n var input = new Uint8ClampedArray(evt.data.buffer)\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n var path = []\n //\n // edge follower\n //\n function follow_edges(row,col) {\n if ((input[(h-1-row)*w*4+col*4+northsouth] != 0)\n || (input[(h-1-row)*w*4+col*4+eastwest] != 0)) {\n path[path.length] = [[col,row]]\n while (1) {\n if (input[(h-1-row)*w*4+col*4+northsouth] & north) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~north\n row += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+northsouth] & south) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~south\n row -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & east) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~east\n col += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & west) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~west\n col -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else\n break\n }\n }\n }\n //\n // follow boundary starts\n //\n for (var row = 1; row < (h-1); ++row) {\n col = 0\n follow_edges(row,col)\n col = w-1\n follow_edges(row,col)\n }\n for (var col = 1; col < (w-1); ++col) {\n row = 0\n follow_edges(row,col)\n row = h-1 \n follow_edges(row,col)\n }\n //\n // follow interior paths\n //\n for (var row = 1; row < (h-1); ++row) {\n for (var col = 1; col < (w-1); ++col) {\n follow_edges(row,col)\n }\n }\n //\n // vectorize path\n //\n var error = evt.data.error\n var vecpath = []\n for (var seg = 0; seg < path.length; ++seg) {\n var x0 = path[seg][0][0]\n var y0 = path[seg][0][1]\n vecpath[vecpath.length] = [[x0,y0]]\n var xsum = x0\n var ysum = y0\n var sum = 1\n for (var pt = 1; pt < path[seg].length; ++pt) {\n var xold = x\n var yold = y\n var x = path[seg][pt][0]\n var y = path[seg][pt][1]\n if (sum == 1) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n var xmean = xsum/sum\n var ymean = ysum/sum\n var dx = xmean-x0\n var dy = ymean-y0\n var d = Math.sqrt(dx*dx+dy*dy)\n var nx = dy/d\n var ny = -dx/d\n var l = Math.abs(nx*(x-x0)+ny*(y-y0))\n if (l < error) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [xold,yold]\n x0 = xold\n y0 = yold\n xsum = xold\n ysum = yold\n sum = 1\n }\n }\n if (pt == (path[seg].length-1)) {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [x,y]\n }\n }\n }\n //\n // sort path\n //\n if ((vecpath.length > 0) && (sort == true)) {\n var dmin = w*w+h*h\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][0]\n var d = x*x+y*y\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n var sortpath = [vecpath[segmin]]\n vecpath.splice(segmin,1)\n }\n while (vecpath.length > 0) {\n var dmin = w*w+h*h\n var x0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][0]\n var y0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][1]\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][1]\n var d = (x-x0)*(x-x0)+(y-y0)*(y-y0)\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n sortpath[sortpath.length] = vecpath[segmin]\n vecpath.splice(segmin,1)\n }\n }\n }\n else if ((vecpath.length > 0) && (sort == false))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"759","left":"2285","inputs":{},"outputs":{}},"0.32734870523599846":{"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.125'\n mod.dia_mm.value = '3.175'\n mod.cut_in.value = '0.1'\n mod.cut_mm.value = '2.54'\n mod.max_in.value = '0.1'\n mod.max_mm.value = '2.54'\n mod.number.value = '1'\n mod.stepover.value = '0.5'\n mod.merge.value = '1'\n mod.reverse.checked = true\n mod.sort.checked = false\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 }\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//\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 mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"574","left":"1787","inputs":{},"outputs":{}},"0.49036025089153756":{"definition":"//\n// Automatic dogbones for preprocessing images to be machined\n//\n// Sam Calisch\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 = 'dogbone'\n//\n// initialization\n//\nvar init = function() {\n mod.diameter.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.diameter.value != '')\n dogbone()\n }},\n diameter:{type:'number',\n event:function(evt){\n mod.diameter.value = evt.detail\n if (mod.distances != undefined)\n dogbone()\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // diameter value\n //\n div.appendChild(document.createTextNode('diameter (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n dogbone()\n })\n div.appendChild(input)\n mod.diameter = 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// dogbone\n//\nfunction dogbone() {\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 diameter = parseFloat(mod.diameter.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n diameter:diameter,buffer:mod.distances.buffer})\n }\n//\n// dogbone worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var diameter = evt.data.diameter\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] <= 0) {\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\n //pick out ridge points at the right distance\n var distance_value = (diameter/2.) * (Math.sqrt(2)/2.);\n var distance_tol = 1; //Math.sqrt(2)/2. ;\n var r = Math.round(diameter/2);\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n var max_ud = input[ (h-1-row)*w+col ] >= Math.max(input[ (h-1-row)*w+col-1], input[ (h-1-row)*w+col+1]); //up down\n var max_lr = input[ (h-1-row)*w+col ] >= Math.max(input[ (h-1-row-1)*w+col], input[ (h-1-row+1)*w+col]); //left right\n var max_ru = input[ (h-1-row)*w+col ] >= Math.max(input[ (h-1-row-1)*w+col+1], input[ (h-1-row+1)*w+col-1]); //right up\n var max_rd = input[ (h-1-row)*w+col ] >= Math.max(input[ (h-1-row-1)*w+col-1], input[ (h-1-row+1)*w+col+1]); //right up\n //if we are local max in at least two directions\n if( (max_ud+max_lr+max_ru+max_rd) >= 2 && Math.abs(input[ (h-1-row)*w+col]-distance_value) <= distance_tol ) {\n for(var cx=-r; cx<=r; ++cx){\n var yx = Math.ceil(Math.sqrt(r*r-cx*cx));\n for(var cy=-yx; cy<=yx; ++cy){\n output[(h-1-(row+cx))*w*4+(col+cy)*4+0] = 0;\n output[(h-1-(row+cx))*w*4+(col+cy)*4+1] = 0;\n output[(h-1-(row+cx))*w*4+(col+cy)*4+2] = 0;\n output[(h-1-(row+cx))*w*4+(col+cy)*4+3] = 255;\n }\n }\n\n } \n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"210","left":"2960","inputs":{},"outputs":{}},"0.8617147326718335":{"definition":"//\n// ShopBot\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\n\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'ShopBot'\n//\n// initialization\n//\nvar init = function() {\n mod.cutspeed.value = '20'\n mod.plungespeed.value = '20'\n mod.jogspeed.value = '75'\n mod.jogheight.value = '5'\n mod.spindlespeed.value = '10000'\n mod.unitsin.checked = true\n }\n//\n// inputs\n//\nvar inputs = {\n toolpath:{type:'object',\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:'object',\n event:function(str){\n obj = {}\n obj.name = mod.name+\".sbp\"\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 speed\n //\n div.appendChild(document.createTextNode('jog speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.jogspeed = 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 // file units\n //\n div.appendChild(document.createTextNode('file units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsin'\n div.appendChild(input)\n mod.unitsin = input\n div.appendChild(document.createTextNode('in'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsmm'\n div.appendChild(input)\n mod.unitsmm = input\n div.appendChild(document.createTextNode('mm'))\n }\n//\n// local functions\n//\nfunction make_path() {\n if (mod.unitsin.checked)\n var units = 1\n else\n var units = 25.4\n var dx = units*mod.width/mod.dpi\n var nx = mod.width\n var cut_speed = units*parseFloat(mod.cutspeed.value)/25.4\n var plunge_speed = units*parseFloat(mod.plungespeed.value)/25.4\n var jog_speed = units*parseFloat(mod.jogspeed.value)/25.4\n var jog_height = units*parseFloat(mod.jogheight.value)/25.4\n var spindle_speed = parseFloat(mod.spindlespeed.value)\n var scale = dx/(nx-1)\n str = \"SA\\r\\n\" // set to absolute distances\n str += \"TR,\"+spindle_speed+\",1\\r\\n\" // set spindle speed\n str += \"SO,1,1\\r\\n\" // set output number 1 to on\n str += \"pause 2\\r\\n\" // let spindle come up to speed\n str += \"MS,\"+cut_speed.toFixed(4)+\",\"+plunge_speed.toFixed(4)+\"\\r\\n\" // set xy,z speed\n str += \"JS,\"+jog_speed.toFixed(4)+\",\"+jog_speed.toFixed(4)+\"\\r\\n\" // set jog xy,z speed\n str += \"JZ,\"+jog_height.toFixed(4)+\"\\r\\n\" // move up\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 += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\n\"\n str += \"J2,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\"\\r\\n\"\n //\n // move down\n //\n z = scale*mod.path[seg][0][2]\n str += \"MZ,\"+z.toFixed(4)+\"\\r\\n\"\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 += \"M3,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\",\"+z.toFixed(4)+\"\\r\\n\"\n }\n }\n //\n // output file\n //\n str += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\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":"1275","left":"1329","inputs":{},"outputs":{}},"0.10390878290633299":{"definition":"//\n// string variables\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 'sw_array':[{\"partName\":\"rwanda-stool.SLDPRT\",\"unit\":\"meter\",\"thickness\":0.01905,\"count\":1,\"svgArray\":[\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"1127.67015523439\\\" height=\\\"989.826899907394\\\" viewBox=\\\"0 0 0.281917538808599 0.247456724976849\\\" style=\\\"background:black\\\"><path d=\\\" M 0.280917538808598,0.0359877248472174 A 0.35,0.35 0 0,0 0.161236292032361,0.243281724976848 L 0.155737030718329,0.246456724976848 L 0.150237769404297,0.246456724976848 L 0.150237769404298,0.182920175324464 L 0.149983769404299,0.182920175324464 L 0.149983769404299,0.118489816608022 L 0.130933769404299,0.118489816608022 L 0.130933769404299,0.182920175324464 L 0.130679769404298,0.182920175324464 L 0.130679769404297,0.246456724976849 L 0.125180508090266,0.246456724976849 L 0.119681246776236,0.243281724976848 A 0.35,0.35 0 0,0 0,0.0359877248472167 L 2.22044604925031E-16,0.0296377248472162 L 0.00274963065701597,0.024875224847216 L 0.0577738967247917,0.0566434996734087 L 0.0675528967247919,0.0397057748261927 L 0.012528630657016,0.00793749999999993 L 0.0152782613140317,0.0031749999999999 L 0.0207775226280628,0 A 0.35,0.35 0 0,0 0.260140016180536,2.5951463200613E-15 L 0.265639277494567,0.00317500000000245 L 0.268388908151583,0.00793750000000276 L 0.213364642083807,0.0397057748261942 L 0.223143642083807,0.0566434996734103 L 0.278167908151583,0.0248752248472187 L 0.280917538808599,0.0296377248472181 L 0.280917538808598,0.0359877248472174\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.281417538808599 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"1324.8\\\" height=\\\"1324.8\\\" viewBox=\\\"0 0 0.3312 0.3312\\\" style=\\\"background:black\\\"><path d=\\\" M 0.208704299650783,0.151216862423607 L 0.198925299650783,0.134279137576391 L 0.181987574803567,0.14405813757639 L 0.191766574803567,0.160995862423607 L 0.208704299650783,0.151216862423607 M 0.257757710571941,0.122895862423607 L 0.274695435419157,0.113116862423607 L 0.264916435419157,0.0961791375763911 L 0.247978710571941,0.105958137576391 L 0.257757710571941,0.122895862423607 M 0.23192086753497,0.115229137576391 L 0.214983142687754,0.125008137576391 L 0.224762142687754,0.141945862423607 L 0.24169986753497,0.132166862423607 L 0.23192086753497,0.115229137576391 M 0.0822212894280657,0.10595813757639 L 0.0652835645808497,0.0961791375763897 L 0.0555045645808495,0.113116862423606 L 0.0724422894280656,0.122895862423606 L 0.0822212894280657,0.10595813757639 M 0.115216857312253,0.12500813757639 L 0.0982791324650367,0.11522913757639 L 0.0885001324650366,0.132166862423606 L 0.105437857312253,0.141945862423606 L 0.115216857312253,0.12500813757639 M 0.14821242519644,0.14405813757639 L 0.131274700349224,0.13427913757639 L 0.121495700349224,0.151216862423606 L 0.13843342519644,0.160995862423606 L 0.14821242519644,0.14405813757639 M 0.155321000000003,0.190245999999998 L 0.155321000000003,0.209803999999998 L 0.174879000000003,0.209803999999998 L 0.174879000000003,0.190245999999998 L 0.155321000000003,0.190245999999998 M 0.155321000000003,0.228345999999998 L 0.155321000000003,0.247903999999998 L 0.174879000000003,0.247903999999998 L 0.174879000000003,0.228345999999998 L 0.155321000000003,0.228345999999998 M 0.155321000000003,0.266445999999998 L 0.155321000000003,0.286003999999998 L 0.174879000000003,0.286003999999998 L 0.174879000000003,0.266445999999998 L 0.155321000000003,0.266445999999998 M 0.3302,0.1651 A 0.1651,0.1651 0 1,1 0,0.1651 A 0.1651,0.1651 0 1,1 0.3302,0.1651\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.3307 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"1098.95817229398\\\" height=\\\"952.261593287967\\\" viewBox=\\\"0 0 0.274739543073496 0.238065398321992\\\" style=\\\"background:black\\\"><path d=\\\" M 0.273739543073496,0.220134601678009 L 0.263964543073497,0.237065398321992 A 0.45,0.45 0 0,0 0.00977499999999865,0.237065398321992 L 0,0.220134601678008 A 0.45,0.45 0 0,0 0.127094771536749,2.77555756156289E-17 L 0.146644771536747,0 A 0.45,0.45 0 0,0 0.273739543073496,0.220134601678009 M 0.197751436852561,0.198841862423608 L 0.220188423013809,0.211795862423607 L 0.229967423013809,0.194858137576391 L 0.207530436852561,0.181904137576391 L 0.197751436852561,0.198841862423608 M 0.127090771536748,0.0505460000000001 L 0.127090771536748,0.0764540000000001 L 0.146648771536748,0.0764540000000001 L 0.146648771536748,0.0505460000000001 L 0.127090771536748,0.0505460000000001 M 0.127090771536748,0.101346 L 0.127090771536748,0.127254 L 0.146648771536748,0.127254 L 0.146648771536748,0.101346 L 0.127090771536748,0.101346 M 0.0535511200596879,0.211795862423609 L 0.0759881062209351,0.198841862423608 L 0.0662091062209351,0.181904137576392 L 0.0437721200596878,0.194858137576393 L 0.0535511200596879,0.211795862423609 M 0.0975452105719373,0.186395862423608 L 0.119982196733185,0.173441862423608 L 0.110203196733184,0.156504137576392 L 0.0877662105719372,0.169458137576392 L 0.0975452105719373,0.186395862423608 M 0.185973332501559,0.169458137576392 L 0.163536346340311,0.156504137576392 L 0.153757346340312,0.173441862423608 L 0.176194332501559,0.186395862423608 L 0.185973332501559,0.169458137576392\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.274239543073496 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"715.2\\\" height=\\\"2442.4\\\" viewBox=\\\"0 0 0.1788 0.6106\\\" style=\\\"background:black\\\"><path d=\\\" M 0.0644303587164418,0.3302 L 0,0.3302 L 3.81639164714898E-17,0.01905 L 0.0254,0.01905 L 0.0254,0.038354 L 0.0508,0.038354 L 0.0508,0.01905 L 0.0762,0.01905 L 0.0762,0.038354 L 0.127,0.038354 L 0.127,0 L 0.17145,0 A 0.00635,0.00635 0 0,1 0.1778,0.00635 L 0.1778,0.038354 A 4,4 0 0,0 0.10795,0.590296000000001 L 0.0952500000000001,0.590296 L 0.09525,0.6096 L 0.0762,0.6096 L 0.0762,0.590296 L 0.05715,0.590296 L 0.05715,0.6096 L 0.0381,0.6096 L 0.0381,0.590296 L 0.01905,0.590296 L 0.01905,0.6096 L 0,0.6096 L 0,0.34925 L 0.0644303587164418,0.34925 L 0.0644303587164418,0.3302 M 0.0381,0.545846 A 0.00634999999999998,0.00634999999999998 0 0,0 0.0444500000000001,0.552196 L 0.066152723772689,0.552196 A 0.00635000000000026,0.00635000000000026 0 0,0 0.0724886137147865,0.546269082310705 A 4.0381,4.0381 0 0,1 0.0855169733792192,0.394361959562247 A 0.00635000000000038,0.00635000000000038 0 0,0 0.0792015708158069,0.38735 L 0.0444500000000001,0.38735 A 0.00635000000000001,0.00635000000000001 0 0,0 0.0381,0.3937 L 0.0381,0.545846 M 0.0381,0.0828040000000001 L 0.0381,0.28575 A 0.00635000000000002,0.00635000000000002 0 0,0 0.0444500000000001,0.2921 L 0.0919853797681748,0.2921 A 0.00634999999999996,0.00634999999999996 0 0,0 0.0982807110766947,0.286581446760754 A 4.0381,4.0381 0 0,1 0.130298417149589,0.0839540826939461 A 0.00635000000000012,0.00635000000000012 0 0,0 0.12405343437963,0.076454 L 0.0444500000000001,0.076454 A 0.00634999999999999,0.00634999999999999 0 0,0 0.0381,0.0828040000000001\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.1783 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"715.2\\\" height=\\\"2442.4\\\" viewBox=\\\"0 0 0.1788 0.6106\\\" style=\\\"background:black\\\"><path d=\\\" M 0.0644303587164418,0.3302 L 0,0.3302 L 3.81639164714898E-17,0.01905 L 0.0254,0.01905 L 0.0254,0.038354 L 0.0508,0.038354 L 0.0508,0.01905 L 0.0762000000000001,0.01905 L 0.0762000000000001,0.038354 L 0.127,0.038354 L 0.127,0 L 0.17145,0 A 0.00635,0.00635 0 0,1 0.1778,0.00635 L 0.1778,0.038354 A 4,4 0 0,0 0.10795,0.590296000000001 L 0.0952500000000001,0.590296 L 0.09525,0.6096 L 0.0762,0.6096 L 0.0762,0.590296 L 0.05715,0.590296 L 0.05715,0.6096 L 0.0381,0.6096 L 0.0381,0.590296 L 0.01905,0.590296 L 0.01905,0.6096 L 0,0.6096 L 0,0.34925 L 0.0644303587164418,0.34925 L 0.0644303587164418,0.3302 M 0.0381,0.545846 A 0.00634999999999998,0.00634999999999998 0 0,0 0.0444500000000001,0.552196 L 0.066152723772689,0.552196 A 0.00635000000000026,0.00635000000000026 0 0,0 0.0724886137147865,0.546269082310705 A 4.0381,4.0381 0 0,1 0.0855169733792192,0.394361959562247 A 0.00635000000000038,0.00635000000000038 0 0,0 0.0792015708158069,0.38735 L 0.0444500000000001,0.38735 A 0.00635000000000001,0.00635000000000001 0 0,0 0.0381,0.3937 L 0.0381,0.545846 M 0.0381,0.0828040000000001 L 0.0381,0.28575 A 0.00635000000000002,0.00635000000000002 0 0,0 0.0444500000000001,0.2921 L 0.0919853797681748,0.2921 A 0.00634999999999996,0.00634999999999996 0 0,0 0.0982807110766947,0.286581446760754 A 4.0381,4.0381 0 0,1 0.130298417149589,0.0839540826939461 A 0.00635000000000012,0.00635000000000012 0 0,0 0.12405343437963,0.076454 L 0.0444500000000001,0.076454 A 0.00634999999999999,0.00634999999999999 0 0,0 0.0381,0.0828040000000001\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.1783 0.0005), scale(-1, 1)\\\" /></svg>\", \"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" xmlns:xlink=\\\"http://www.w3.org/1999/xlink\\\" width=\\\"715.2\\\" height=\\\"2442.4\\\" viewBox=\\\"0 0 0.1788 0.6106\\\" style=\\\"background:black\\\"><path d=\\\" M 0.113369641283558,0.2794 L 0.1778,0.2794 L 0.1778,0.59055 L 0.1524,0.59055 L 0.1524,0.571246 L 0.127,0.571246 L 0.127,0.59055 L 0.1016,0.59055 L 0.1016,0.571246 L 0.0508,0.571246 L 0.0508,0.6096 L 0.00635000000000002,0.6096 A 0.00635,0.00635 0 0,1 0,0.60325 L 0,0.571246 A 4,4 0 0,0 0.0698499999999999,0.0193039999999987 L 0.08255,0.019304 L 0.0825500000000001,1.11022302462516E-16 L 0.1016,0 L 0.1016,0.019304 L 0.12065,0.0193039999999999 L 0.12065,1.11022302462516E-16 L 0.1397,1.11022302462516E-16 L 0.1397,0.0193039999999999 L 0.15875,0.019304 L 0.15875,0 L 0.1778,1.11022302462516E-16 L 0.1778,0.26035 L 0.113369641283558,0.26035 L 0.113369641283558,0.2794 M 0.1397,0.0637540000000001 A 0.00634999999999998,0.00634999999999998 0 0,0 0.13335,0.057404 L 0.111647276227311,0.0574039999999997 A 0.00635000000000026,0.00635000000000026 0 0,0 0.105311386285214,0.0633309176892949 A 4.0381,4.0381 0 0,1 0.0922830266207809,0.215238040437753 A 0.00635000000000038,0.00635000000000038 0 0,0 0.0985984291841932,0.22225 L 0.13335,0.22225 A 0.00635000000000001,0.00635000000000001 0 0,0 0.1397,0.2159 L 0.1397,0.0637540000000001 M 0.1397,0.526796 L 0.1397,0.32385 A 0.00635000000000002,0.00635000000000002 0 0,0 0.13335,0.3175 L 0.0858146202318253,0.3175 A 0.00634999999999996,0.00634999999999996 0 0,0 0.0795192889233054,0.323018553239246 A 4.0381,4.0381 0 0,1 0.047501582850411,0.525645917306054 A 0.00635000000000012,0.00635000000000012 0 0,0 0.0537465656203701,0.533146 L 0.13335,0.533146 A 0.00634999999999999,0.00634999999999999 0 0,0 0.1397,0.526796\\\" style=\\\"fill:#ffffff;fill-rule:evenodd;\\\" transform=\\\"translate(0.1783 0.0005), scale(-1, 1)\\\" /></svg>\"]}]\n}\n//\n// name\n//\nvar name = 'sw output'\n//\n// initialization\n//\nvar init = function() { }\n//\n// inputs\n//\nvar inputs = {\n }\n//\n// outputs\n//\nvar outputs = {\n sw_array:{type:'object',\n event:function(){\n mods.output(mod,'sw_array',mod.sw_array )}}\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n\n var btn = document.createElement('button');\n btn.style.padding = mods.ui.padding;\n btn.style.margin = 1;\n btn.appendChild(document.createTextNode('send'));\n btn.addEventListener('click',function(){\n outputs.sw_array.event();\n });\n div.appendChild(btn);\n\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n;\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"641","left":"568","inputs":{},"outputs":{}},"0.14150626327820492":{"definition":"//\r\n// ExtractFaces module extracts top faces of the same thickness from Tools/FabLab Connect command of SolidWorks products\r\n// \r\n// Shawn Liu @ Dassault Systemes SolidWorks Corporation\r\n// (c) Massachusetts Institute of Technology 2016\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n//\r\n// name\r\n//\r\nvar name = 'ExtractFaces connect'\r\n//\r\n// initialization\r\n//\r\nvar init = function() {\r\n mod.address = getParameterByName('swIP') || '127.0.0.1'\r\n mod.port = getParameterByName('swPort') || '80'\r\n mod.socket = 0\r\n mod.thickness.value = '0.75'\n socket_open()\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {}\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n SVGArray:{type:'object',\r\n event:function(data){\r\n mods.output(mod, 'SVGArray', JSON.parse(data))\r\n }\r\n }\r\n}\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n div.appendChild(document.createTextNode('server:'))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('address: ' + getParameterByName('swIP')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0port: ' + getParameterByName('swPort')))\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('\\u00a0\\u00a0status: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 12\r\n div.appendChild(input)\r\n mod.status = input\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('open'))\r\n btn.addEventListener('click', function () {\r\n socket_open()\r\n })\r\n div.appendChild(btn)\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.addEventListener('click', function () {\r\n socket_close()\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createElement('br'))\r\n div.appendChild(document.createTextNode('thickness: '))\r\n input = document.createElement('input')\r\n input.type = 'text'\r\n input.size = 10\r\n div.appendChild(input)\r\n mod.thickness = input\r\n div.appendChild(document.createTextNode('(inch)'))\r\n div.appendChild(document.createElement('br'))\r\n var btn = document.createElement('button')\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('Extract SVGs'))\r\n btn.addEventListener('click',function() {\r\n extract_SVGs()\r\n })\r\n div.appendChild(btn)\r\n }\r\n//\r\n// local functions\r\n//\r\n\r\nfunction getParameterByName(name, url) {\r\n if (!url) url = window.location.href;\r\n name = name.replace(/[\\[\\]]/g, \"\\\\$&\");\r\n var regex = new RegExp(\"[?&]\" + name + \"(=([^&#]*)|&|#|$)\"),\r\n results = regex.exec(url);\r\n if (!results) return null;\r\n if (!results[2]) return '';\r\n return decodeURIComponent(results[2].replace(/\\+/g, \" \"));\r\n}\r\n\r\nfunction socket_open() {\r\n var url = \"ws://\"+mod.address+':'+mod.port\r\n mod.socket = new WebSocket(url)\r\n mod.socket.onopen = function(event) {\r\n mod.status.value = \"opened\"\r\n var connect = {}\r\n connect.modCmd = 'connect'\r\n connect.owner = getParameterByName('swOwner')\r\n connect.id = getParameterByName('swID')\r\n socket_send(JSON.stringify(connect))\r\n }\r\n mod.socket.onerror = function(event) {\r\n mod.status.value = \"can not open\"\r\n }\r\n mod.socket.onmessage = function(event) {\r\n mod.status.value = \"receive\"\r\n var swData = JSON.parse(event.data);\r\n if (swData.swType === \"FaceSVGArray\") {\r\n outputs.SVGArray.event(JSON.stringify(swData.data))\r\n }\r\n }\r\n mod.socket.onclose = function (event) {\r\n mod.status.value = \"connection closed\"\r\n }\r\n }\r\nfunction socket_close() {\r\n mod.socket.close()\r\n mod.status.value = \"closed\"\r\n mod.socket = 0\r\n }\r\nfunction socket_send(msg) {\r\n if (mod.socket != 0) {\r\n mod.status.value = \"send\"\r\n mod.socket.send(msg)\r\n }\r\n else {\r\n mod.status.value = \"can't send, not open\"\r\n }\r\n }\r\nfunction extract_SVGs() {\r\n var modcmd = new Object;\r\n modcmd.modCmd = \"AutoExtractFaces\";\r\n modcmd.thickness = Number(mod.thickness.value * 25.4); // inch to mm\r\n socket_send(JSON.stringify(modcmd))\r\n }\r\n\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n mod:mod,\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"265.6174365523354","left":"733.2348731046708","inputs":{},"outputs":{}},"0.07126720042810941":{"definition":"//\r\n// nest multiple SVGs into a single SVG\r\n//\r\n// Sam Calisch\r\n// (c) Massachusetts Institute of Technology 2016\r\n// \r\n// This work may be reproduced, modified, distributed, performed, and \r\n// displayed for any purpose, but must acknowledge the mods\r\n// project. Copyright is retained and must be preserved. The work is \r\n// provided as is; no warranty is provided, and users accept all \r\n// liability.\r\n//\r\n// closure\r\n//\r\n(function(){\r\n//\r\n// module globals\r\n//\r\nvar mod = {}\r\n\r\nvar modParam = {\r\n 'parameters':{\r\n 'stock_width':50.,\r\n 'stock_height':24.,\r\n 'padding':.25} //algorithm parameters, and default values\r\n}\r\n//\r\n// name\r\n//\r\nvar name = 'nest SVG Array'\r\n//\r\n// initialization\r\n//\r\nvar init = function() {\r\n mod.stock_width.value = '50'\n mod.stock_height.value = '24'\n mod.padding.value = '0.25'\n\r\n }\r\n//\r\n// inputs\r\n//\r\nvar inputs = {\r\n SVGArray:{type:'object',\r\n event:function(evt) {\r\n mod.svg_array = evt.detail;\r\n nest(mod.svg_array);\r\n }}}\r\n//\r\n// outputs\r\n//\r\nvar outputs = {\r\n SVG:{type:'string',\r\n event:function(){\r\n var str = new XMLSerializer().serializeToString(mod.bigview);\r\n mods.output(mod,'SVG',str)}},\r\n // TODO: make another output for parts that don't fit\r\n //Leftovers:{type:'object',\r\n // event:function(){\r\n // mods.output(mod,'Leftovers',mod.leftovers)}}\r\n }\r\n//\r\n// interface\r\n//\r\nvar interface = function(div){\r\n mod.div = div\r\n\r\n // on-screen drawing canvas\r\n var smallview = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");//document.createElement('canvas');\r\n smallview.setAttribute('width',mods.ui.canvas);\r\n smallview.setAttribute('height',mods.ui.canvas);\r\n smallview.setAttribute('preserveAspectRatio','xMinYMin meet');\r\n div.appendChild(smallview);\r\n mod.smallview = smallview;\r\n div.appendChild(document.createElement('br'));\r\n\r\n // off-screen image canvas\r\n mod.bigview = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\"); \r\n\r\n //add parameter inputs\r\n Object.keys(modParam.parameters).forEach(function (p) {\r\n var textnode = document.createElement('span');\r\n textnode.innerHTML = p+': ';\r\n textnode.style=\"display: inline-block; width: 80px; font-size: 12px;\";\r\n div.appendChild(textnode);\r\n var input_text = document.createElement('input');\r\n var input_max = document.createElement('input');\r\n var input_range = document.createElement('input') //add slider\r\n\r\n //value text\r\n input_text.type='text';\r\n input_text.size=3;\r\n mod[p] = input_text; //set initial minimum slider value\r\n div.appendChild(input_text);\r\n input_text.addEventListener('blur',function(){\r\n input_range.value = (100 * input_text.value / input_max.value);\r\n nest(mod.svg_array);\r\n });\r\n\r\n //slider\r\n input_range.type = 'range'; \r\n input_range.min = 0; input_range.max = 100;\r\n input_range.value = 50; \r\n input_range.style = '-webkit-appearance: none; width: 80px; height: 0px; border: none; margin-top: -4px; margin-left:2px;';\r\n input_range.addEventListener('input',function(){\r\n input_text.value = input_max.value * input_range.value/100.0;\r\n nest(mod.svg_array);\r\n });\r\n div.appendChild(input_range);\r\n\r\n //max text\r\n input_max.type='text';\r\n input_max.size=2;\r\n input_max.value = 2 * modParam.parameters[p]; //set initial maximum to twice default value\r\n input_max.addEventListener('blur',function(){\r\n input_range.value = 100 * input_text.value / input_max.value;\r\n input_text.value = Math.min( input_text.value, input_max.value ); \r\n nest(mod.svg_array);\r\n });\r\n div.appendChild(input_max);\r\n\r\n div.appendChild(document.createElement('br'));\r\n });\r\n\r\n // view button\r\n var btn = document.createElement('button')\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n btn.appendChild(document.createTextNode('view'))\r\n btn.addEventListener('click',function(){\r\n var win = window.open('')\r\n var btn = document.createElement('button')\r\n btn.appendChild(document.createTextNode('close'))\r\n btn.style.padding = mods.ui.padding\r\n btn.style.margin = 1\r\n btn.addEventListener('click',function(){\r\n win.close()\r\n })\r\n win.document.body.appendChild(btn)\r\n win.document.body.appendChild(document.createElement('br'))\r\n mod.bigview.setAttribute('width', mod.stock_width.value+\"in\");\r\n mod.bigview.setAttribute('height', mod.stock_height.value+\"in\" );\r\n mod.bigview.setAttribute('preserveAspectRatio','xMinYMin meet');\r\n win.document.body.appendChild(mod.bigview);\r\n })\r\n div.appendChild(btn)\r\n div.appendChild(document.createTextNode(' draw grid?'));\r\n var draw_grid = document.createElement('input')\r\n mod.draw_grid = draw_grid;\r\n draw_grid.type = 'checkbox'\r\n draw_grid.name = mod.div.id+'grid'\r\n draw_grid.id = mod.div.id+'grid'\r\n draw_grid.checked = false\r\n draw_grid.addEventListener('change',function(){nest(mod.svg_array);});\r\n div.appendChild(draw_grid)\r\n div.appendChild(document.createElement('br'))\r\n }\r\n//\r\n// local functions\r\n\r\nfunction nest(sw_json){\r\n if (!sw_json) return\r\n //sw_json is text json exported from soliworks of the form [{partTitle:”Part-1”, thickness:20, count:2, svgArray:[svgf1, svgf2, …],{partTitle:”Part-2”, thickness:20, count:3, svgArray:[svgf3, svgf4, …]\r\n //stocksize is an array [width,height] of stock dimensions\r\n //we scale so the stock size takes up the %75 of screen\r\n //padding is an amount to leave between each piece\r\n var unitScale = sw_json[0].unit === \"mm\"?1000:1;\r\n var stocksize = [mod.stock_width.value/39.3*unitScale, mod.stock_height.value/39.3*unitScale]; //convert to meters\r\n //TODO: handle units more gracefully!\r\n var padding = mod.padding.value/39.3*unitScale;\r\n var draw_grid = false;\r\n\r\n //make sure first dimension is longer\r\n //if (stocksize[1] > stocksize[0]) stocksize = [stocksize[1],stocksize[0]];\r\n\r\n //fit stock width to page\r\n var scale = mod.smallview.width/stocksize[0]; \r\n\r\n var partName = sw_json[0]['partName'];\r\n var thickness = sw_json[0]['thickness'];\r\n var count = sw_json[0]['count'];\r\n var svgs = [];\r\n //deal with multiple parts from SW\r\n for(var i=0; i<sw_json.length; ++i){\r\n svgs = svgs.concat(sw_json[i].svgArray);\r\n }\r\n\r\n //create SVG for output\r\n var svgNS = \"http://www.w3.org/2000/svg\";\r\n //var nested = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\r\n mod.smallview.innerHTML=''; //delete previous children\r\n \r\n //mod.smallview.setAttribute('width',(stocksize[0])*scale );\r\n //mod.smallview.setAttribute('height',(stocksize[1])*scale );\r\n mod.smallview.setAttribute('viewBox', \"0 0 \"+stocksize[0]+\" \"+stocksize[1]);\r\n\r\n //draw stock outline\r\n var stock = document.createElementNS(svgNS,'rect');\r\n stock.setAttribute('x', 0); stock.setAttribute('y', 0);\r\n stock.setAttribute('width', stocksize[0]); stock.setAttribute('height', stocksize[1]);\r\n stock.setAttribute('style', 'fill:rgb(0,0,0);')\r\n //stock.setAttribute('class','annotation'); //label for removal on output\r\n mod.smallview.appendChild(stock);\r\n\r\n var gs = [] //container for the g elements\r\n svgs.forEach(function(svg){\r\n var g = document.createElementNS(svgNS,'g');\r\n g.innerHTML = svg;\r\n var svg_tree = g.firstChild;\r\n var vb = svg_tree.getAttribute('viewBox').split(\" \").map(parseFloat);\r\n svg_tree.setAttribute('viewBox',(vb[0]-.5*padding)+\" \"+(vb[1]-.5*padding)+\" \"+(vb[2]+.5*padding)+\" \"+(vb[3]+.5*padding));\r\n svg_tree.setAttribute('width', vb[2]+padding); svg_tree.setAttribute('height', vb[3]+padding);\r\n g.setAttribute('w',vb[2]+padding); //use a foreign tag to bring width and height info along as we tranform \r\n g.setAttribute('h',vb[3]+padding); \r\n mod.smallview.appendChild(g);\r\n gs.push(g);\r\n });\r\n\r\n //orient so long axis is horizontal. if this is not the case, rotate\r\n gs.forEach(function(g){\r\n w = g.getAttribute('w'); h = g.getAttribute('h');\r\n if (h>w){\r\n g.setAttribute('transform','translate(0,'+w+') rotate(-90)' );\r\n g.setAttribute('w',h); g.setAttribute('h',w);\r\n } else{\r\n g.setAttribute('transform','' );\r\n }\r\n });\r\n\r\n //then sort by long dimension in descending order\r\n gs = gs.sort( function(g1,g2){ return g2.getAttribute('w') - g1.getAttribute('w')} );\r\n\r\n\r\n //then place first and calculate the left over rectangles\r\n function fill_rect(b1,b2,remaining_shapes){\r\n if (mod.draw_grid.checked){\r\n node = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\r\n node.setAttribute('x',b1[0]); node.setAttribute('y',b1[1]);\r\n node.setAttribute('width',b2[0]-b1[0]);\r\n node.setAttribute('height',b2[1]-b1[1]);\r\n node.setAttribute('class','annotation'); //label for removal on output\r\n node.setAttribute('style','fill:none;stroke-width:.001;stroke:rgb(0,0,255)');\r\n mod.smallview.appendChild(node);\r\n }\r\n\r\n\r\n //fill a rectangle defined by point b1 to point b2 with the first element from remaining_shapes that fits\r\n var dx = b2[0]-b1[0]; \r\n var dy = b2[1]-b1[1];\r\n for(i=0; i<remaining_shapes.length; i++){\r\n var gi = remaining_shapes[i];\r\n var w = parseFloat(gi.getAttribute('w'));\r\n var h = parseFloat(gi.getAttribute('h'));\r\n if (w <= dx+.000001 && h <= dy+.00001){ //successfully placed shape i\r\n remaining_shapes.splice(i,1); //remove shape i from remaining shapes\r\n gi.setAttribute('transform', 'translate('+(b1[0])+','+(b1[1])+') '+gi.getAttribute('transform')); //use base point as transform\r\n fill_rect([b1[0],b1[1]+h],[b1[0]+w,b2[1]],remaining_shapes); //prioritize filling lower rectangle\r\n fill_rect([b1[0]+w,b1[1]],b2,remaining_shapes); //then fill right rectangle\r\n break; //break out of for loop\r\n }\r\n }\r\n\r\n }\r\n fill_rect([.5*padding,.5*padding],[stocksize[0]-.5*padding,stocksize[1]-.5*padding],gs);\r\n \r\n //HACK: don't show parts that don't fit\r\n gs.forEach(function(g){\r\n g.innerHTML = ''; //delete!\r\n }); \r\n\r\n\r\n //create bigview svg from smallview svg\r\n mod.bigview.setAttribute( 'viewBox', mod.smallview.getAttribute('viewBox') );\r\n mod.bigview.innerHTML = mod.smallview.innerHTML;\r\n mod.bigview.setAttribute('width', mod.stock_width.value+\"in\");\r\n mod.bigview.setAttribute('height', mod.stock_height.value+\"in\" );\r\n\r\n //output events\r\n //mod.leftovers = [];\r\n outputs.SVG.event()\r\n\r\n\r\n /*\r\n\r\n //highlight parts that didn't fit\r\n gs.forEach(function(g){\r\n var svg = g.firstChild;\r\n var style = svg.firstChild.getAttribute('style');\r\n svg.firstChild.setAttribute('style',style+'stroke-width:'+.002*stocksize[0]+'; stroke:rgb(255,0,0)')\r\n });\r\n //nest parts that don't fit, note: this could just be done on another page.\r\n if (gs.length > 0){\r\n fill_rect([.5*padding,.5*padding],[stocksize[0]-.5*padding,stocksize[1]-.5*padding],gs);\r\n }\r\n */\r\n}\r\n\r\n//\r\n// return values\r\n//\r\nreturn ({\r\n mod:mod,\r\n name:name,\r\n init:init,\r\n inputs:inputs,\r\n outputs:outputs,\r\n interface:interface\r\n })\r\n}())\r\n","top":"864.8310762515605","left":"906.7919450294961","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.23780413326993044\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5857417886002868\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5857417886002868\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3135579179893032\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6248369051648597\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.49036025089153756\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.23780413326993044\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.49036025089153756\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.32734870523599846\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"diameter\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.49036025089153756\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"diameter\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6248369051648597\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8617147326718335\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"toolpath\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8617147326718335\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4793941661670936\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.14150626327820492\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVGArray\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07126720042810941\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVGArray\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.07126720042810941\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"SVG\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5857417886002868\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"SVG\\\"}\"}"]} \ No newline at end of file