Skip to content
Snippets Groups Projects
3D 15.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    // mill raster 3D
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    // Neil Gershenfeld 10/22/21
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    //
    // 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
    //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    var name = 'mill raster 3D'
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    //
    // initialization
    //
    var init = function() {
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       mod.dia_in.value = 0.125
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       mod.dia_mm.value = 25.4*parseFloat(mod.dia_in.value)
       mod.stepover.value = 0.5
    
       mod.error.value = 0.001
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       }
    //
    // inputs
    //
    var inputs = {
       map:{type:'',label:'height map',
          event:function(evt){
    
             mod.map = evt.detail.map
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             mod.xmin = evt.detail.xmin
             mod.xmax = evt.detail.xmax
             mod.ymin = evt.detail.ymin
             mod.ymax = evt.detail.ymax
             mod.zmin = evt.detail.zmin
             mod.zmax = evt.detail.zmax
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             mod.width = evt.detail.width
             mod.height = evt.detail.height
             mod.depth = Math.floor((mod.zmax-mod.zmin)*mod.width/(mod.xmax-mod.xmin))
             mod.mmunits = evt.detail.mmunits
             mod.dpi = mod.width/(mod.mmunits*(mod.xmax-mod.xmin)/25.4)
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             var ctx = mod.img.getContext("2d")
             ctx.canvas.width = mod.width
             ctx.canvas.height = mod.height
             }}}
    //
    // outputs
    //
    var outputs = {
       toolpath:{type:'',
          event:function(){
    
             obj = {}
             obj.path = mod.path
             obj.name = "mill raster 3D"
             obj.dpi = mod.dpi
             obj.width = mod.width
             obj.height = mod.height
             obj.depth = mod.depth
             mods.output(mod,'toolpath',obj)
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             }}}
    //
    // interface
    //
    var interface = function(div){
       mod.div = div
       //
       // tool diameter
       //
       div.appendChild(document.createTextNode('tool diameter'))
       div.appendChild(document.createElement('br'))
       div.appendChild(document.createTextNode('mm: '))
       var input = document.createElement('input')
          input.type = 'text'
          input.size = 6
          input.addEventListener('input',function(){
             mod.dia_in.value = parseFloat(mod.dia_mm.value)/25.4
             })
          div.appendChild(input)
          mod.dia_mm = input
       div.appendChild(document.createTextNode(' in: '))
       var input = document.createElement('input')
          input.type = 'text'
          input.size = 6
          input.addEventListener('input',function(){
             mod.dia_mm.value = parseFloat(mod.dia_in.value)*25.4
             })
          div.appendChild(input)
          mod.dia_in = input
       div.appendChild(document.createElement('br'))
       //
       // stepover
       //
       div.appendChild(document.createTextNode('stepover (0-1): '))
       var input = document.createElement('input')
          input.type = 'text'
          input.size = 6
          div.appendChild(input)
          mod.stepover = input
       div.appendChild(document.createElement('br'))
       //
       // tool shape
       //
       div.appendChild(document.createTextNode('tool shape: '))
       var input = document.createElement('input')
          input.type = 'radio'
          input.name = mod.div.id+'shape'
          input.id = mod.div.id+'flatend'
          input.checked = true
          div.appendChild(input)
          mod.flatend= input
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       div.appendChild(document.createTextNode('flat end'))
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       div.appendChild(document.createElement('br'))
       //
       // direction 
       //
       div.appendChild(document.createTextNode('direction: '))
       var input = document.createElement('input')
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          input.type = 'checkbox'
          input.id = mod.div.id+'xz'
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          input.checked = true
          div.appendChild(input)
    
          mod.dirxz = input
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       div.appendChild(document.createTextNode('xz'))
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       var input = document.createElement('input')
          input.type = 'checkbox'
          input.id = mod.div.id+'yz'
          input.checked = true
          div.appendChild(input)
    
          mod.diryz = input
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       div.appendChild(document.createTextNode('yz'))
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       div.appendChild(document.createElement('br'))
       //
       // fit error 
       //
    
       div.appendChild(document.createTextNode('vector fit: '))
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       //div.appendChild(document.createElement('br'))
       var input = document.createElement('input')
          input.type = 'text'
          input.size = 6
          div.appendChild(input)
          mod.error = input
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       div.appendChild(document.createElement('br'))
       //
       // calculate
       //
       var btn = document.createElement('button')
          btn.style.padding = mods.ui.padding
          btn.style.margin = 1
          var span = document.createElement('span')
             var text = document.createTextNode('calculate')
                mod.label = text
                span.appendChild(text)
             mod.labelspan = span
             btn.appendChild(span)
          btn.addEventListener('click',function(){
             mod.label.nodeValue = 'calculating'
             mod.labelspan.style.fontWeight = 'bold'
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             calculate_path()
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             })
          div.appendChild(btn)
       div.appendChild(document.createTextNode(' '))
       //
       // view
       //
       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 svg = document.getElementById(mod.div.id+'svg')
             var clone = svg.cloneNode(true)
             clone.setAttribute('width',mod.img.width)
             clone.setAttribute('height',mod.img.height)
             win.document.body.appendChild(clone)
             })
          div.appendChild(btn)
       div.appendChild(document.createElement('br'))
       //
       // on-screen SVG
       //
       var svgNS = "http://www.w3.org/2000/svg"
       var svg = document.createElementNS(svgNS,"svg")
       svg.setAttribute('id',mod.div.id+'svg')
       svg.setAttributeNS("http://www.w3.org/2000/xmlns/",
          "xmlns:xlink","http://www.w3.org/1999/xlink")
       svg.setAttribute('width',mods.ui.canvas)
       svg.setAttribute('height',mods.ui.canvas)
       svg.style.backgroundColor = 'rgb(255,255,255)'
       var g = document.createElementNS(svgNS,'g')
       g.setAttribute('id',mod.div.id+'g')
       svg.appendChild(g)
       div.appendChild(svg)
       div.appendChild(document.createElement('br'))
       //
       // off-screen image canvas
       //
       var canvas = document.createElement('canvas')
          mod.img = canvas
       }
    //
    // local functions
    //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    // calculate path
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    function calculate_path() {
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       var blob = new Blob(['('+calculate_path_worker.toString()+'())'])
       var url = window.URL.createObjectURL(blob)
       var webworker = new Worker(url)
       webworker.addEventListener('message',function(evt) {
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          if (evt.data.type == "progress") {
             mod.label.nodeValue = evt.data.value
             return
             }
    
          //
          // webworker handler
          //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          mod.path = evt.data.path
          mod.label.nodeValue = 'calculate'
          mod.labelspan.style.fontWeight = 'normal'
          //
          // clear SVG
          //
          var svg = document.getElementById(mod.div.id+'svg')
          svg.setAttribute('viewBox',"0 0 "+(mod.width-1)+" "+(mod.height-1))
          var g = document.getElementById(mod.div.id+'g')
          svg.removeChild(g)
          var g = document.createElementNS('http://www.w3.org/2000/svg','g')
          g.setAttribute('id',mod.div.id+'g')
          svg.appendChild(g)
          //
          // plot path
          //
          for (var i = 1; i < mod.path[0].length; ++i) {
             var ixp = mod.path[0][i-1][0]
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             var iyp = mod.height-1-mod.path[0][i-1][1]
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             var izp = 0.1*mod.path[0][i-1][2]
             var ix = mod.path[0][i][0]
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             var iy = mod.height-1-mod.path[0][i][1]
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
             var iz = 0.1*mod.path[0][i][2]
    
             var line = document.createElementNS(
                   'http://www.w3.org/2000/svg','line')
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                line.setAttribute('stroke','black')
                line.setAttribute('stroke-width',1)
                line.setAttribute('stroke-linecap','round')
                line.setAttribute('x1',ixp)
                line.setAttribute('y1',iyp-izp)
                line.setAttribute('x2',ix)
                line.setAttribute('y2',iy-iz)
                g.appendChild(line)
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                }
    
          //
          // output path
          //
          outputs.toolpath.event()
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          })
    
       //
       // call webworker
       //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       webworker.postMessage({
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          height:mod.height,width:mod.width,
          error:mod.error.value,
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          xmin:mod.xmin,xmax:mod.xmax,
          ymin:mod.ymin,ymax:mod.ymax,
          zmin:mod.zmin,zmax:mod.zmax,
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          diameter:mod.dia_mm.value,
          stepover:mod.stepover.value,
          mmunits: mod.mmunits,
    
          xz:mod.dirxz.checked,
          yz:mod.diryz.checked,
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          map:mod.map})
       }
    //
    // calculate path worker
    //
    function calculate_path_worker() {
       self.addEventListener('message',function(evt) {
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          var h = evt.data.height
          var w = evt.data.width
    
          var error = evt.data.error
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          var xmin = evt.data.xmin
          var xmax = evt.data.xmax
          var ymin = evt.data.ymin
          var ymax = evt.data.ymax
          var zmin = evt.data.zmin
          var zmax = evt.data.zmax
          var map = evt.data.map
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          var diameter = evt.data.diameter
          var stepover = evt.data.stepover
          var mmunits = evt.data.mmunits
    
          var step = Math.floor(stepover*diameter*w/(mmunits*(xmax-xmin)))
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          var path = [[]]
          //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          // construct tool offset
          //
          var toolsize = Math.floor(diameter*w/(mmunits*(xmax-xmin)))
          var tooloffset = new Float32Array(toolsize*toolsize)
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          var toolmiddle = Math.floor(toolsize/2)
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          for (var x = 0; x < toolsize; ++x)
             for (var y = 0; y < toolsize; ++y) {
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                var r = Math.sqrt((x-toolmiddle)*(x-toolmiddle)+(y-toolmiddle)*(y-toolmiddle))
                if (r <= toolmiddle)
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                   tooloffset[y*toolsize+x] = 0
                else
                   tooloffset[y*toolsize+x] = Number.MAX_VALUE
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                }
          //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          //
    
          if (evt.data.xz) {
             var xstart = 0
             var ystart = h-1
             var zstart = Math.floor((map[ystart*w+xstart]-zmax)*w/(xmax-xmin))
             path[0].push([xstart,h-1-ystart,zstart])
             var xcur = 1
             var ycur = h-1
             var zcur = Math.floor((map[ycur*w+xcur]-zmax)*w/(xmax-xmin))
             var dx = 1
             var dy = 0
             while (1) {
    
                // move to next point
    
                xnext = xcur+dx
                ynext = ycur+dy
                if (ynext <= 0)
                   //
                   // done
                   //
                   break;
                //
                // find offset at next point
                //
                var znext = -Number.MAX_VALUE
                for (var xoffset = 0; xoffset < toolsize; ++xoffset)
                   for (var yoffset = 0; yoffset < toolsize; ++yoffset) {
                      var x = xnext+(xoffset-toolmiddle)
                      var y = ynext+(yoffset-toolmiddle)
                      var offset = tooloffset[yoffset*toolsize+xoffset]
                      var z = Math.floor((map[y*w+x]-zmax)*w/(xmax-xmin))-offset
                      if (z > znext)
                         znext = z
                      }
                //
                // vectorize
                //
                dxcur = xcur-xstart
                dycur = ycur-ystart
                dzcur = zcur-zstart
                dcur = Math.sqrt(dxcur*dxcur+dycur*dycur+dzcur*dzcur)
                nxcur = dxcur/dcur
                nycur = dycur/dcur
                nzcur = dzcur/dcur
                dxnext = xnext-xcur
                dynext = ynext-ycur
                dznext = znext-zcur
                dnext = Math.sqrt(dxnext*dxnext+dynext*dynext+dznext*dznext)
                nxnext = dxnext/dnext
                nynext = dynext/dnext
                nznext = dznext/dnext
                dot = nxcur*nxnext+nycur*nynext+nzcur*nznext
                if (dot <= (1-error)) {
                   path[0].push([xcur,h-1-ycur,zcur])
                   xstart = xcur
                   ystart = ycur
                   zstart = zcur
    
                xcur = xnext
                ycur = ynext
                zcur = znext
                if (xcur == (w-1)) {
                   if (dx == 1) {
                      dx = 0
                      dy = -step
                      }
                   else {
                      dx = -1
                      dy = 0
                      }
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                   }
    
                else if (xcur == 0) {
                   if (dx == -1) {
                      dx = 0
                      dy = -step
                      }
                   else {
                      dx = 1
                      dy = 0
                      }
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                   }
    
                //
                // update progress
                //
                self.postMessage({type:'progress',value:ycur})
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                }
    
             }
          //
          // yz
          //
          if (evt.data.yz) {
             var xstart = w-1
             var ystart = 0
             var zstart = Math.floor((map[ystart*w+xstart]-zmax)*w/(xmax-xmin))
             path[0].push([xstart,h-1-ystart,zstart])
             var xcur = w-1
             var ycur = 1
             var zcur = Math.floor((map[ycur*w+xcur]-zmax)*w/(xmax-xmin))
             var dx = 0
             var dy = 1
             while (1) {
                //
                // move to next point
                //
                xnext = xcur+dx
                ynext = ycur+dy
                if (xnext <= 0)
                   //
                   // done
                   //
                   break;
                //
                // find offset at next point
                //
                var znext = -Number.MAX_VALUE
                for (var xoffset = 0; xoffset < toolsize; ++xoffset)
                   for (var yoffset = 0; yoffset < toolsize; ++yoffset) {
                      var x = xnext+(xoffset-toolmiddle)
                      var y = ynext+(yoffset-toolmiddle)
                      var offset = tooloffset[yoffset*toolsize+xoffset]
                      var z = Math.floor((map[y*w+x]-zmax)*w/(xmax-xmin))-offset
                      if (z > znext)
                         znext = z
                      }
                //
                // vectorize
                //
                dxcur = xcur-xstart
                dycur = ycur-ystart
                dzcur = zcur-zstart
                dcur = Math.sqrt(dxcur*dxcur+dycur*dycur+dzcur*dzcur)
                nxcur = dxcur/dcur
                nycur = dycur/dcur
                nzcur = dzcur/dcur
                dxnext = xnext-xcur
                dynext = ynext-ycur
                dznext = znext-zcur
                dnext = Math.sqrt(dxnext*dxnext+dynext*dynext+dznext*dznext)
                nxnext = dxnext/dnext
                nynext = dynext/dnext
                nznext = dznext/dnext
                dot = nxcur*nxnext+nycur*nynext+nzcur*nznext
                if (dot <= (1-error)) {
                   path[0].push([xcur,h-1-ycur,zcur])
                   xstart = xcur
                   ystart = ycur
                   zstart = zcur
                   }
                xcur = xnext
                ycur = ynext
                zcur = znext
                if (ycur == (w-1)) {
                   if (dy == 1) {
                      dy = 0
                      dx = -step
                      }
                   else {
                      dy = -1
                      dx = 0
                      }
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                   }
    
                else if (ycur == 0) {
                   if (dy == -1) {
                      dy = 0
                      dx = -step
                      }
                   else {
                      dy = 1
                      dx = 0
                      }
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                   }
    
                //
                // update progress
                //
    
                self.postMessage({type:'progress',value:xcur})
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
                }
             }
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          //
          // return
          //
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          self.postMessage({type:'path',path:path})
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
          self.close()
          })
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
       }
    
    Neil Gershenfeld's avatar
    Neil Gershenfeld committed
    //
    // return values
    //
    return ({
       mod:mod,
       name:name,
       init:init,
       inputs:inputs,
       outputs:outputs,
       interface:interface
       })
    }())