Esempio n. 1
0
function getContour(pixels, doSimplify) {
  var contour = surfaceNets(pixels, 128)
  if(doSimplify) {
    return simplify(contour.cells, contour.positions, 0.25)
  }
  return {
    edges: contour.cells,
    positions: contour.positions
  }
}
Esempio n. 2
0
fill(array, function(i,j) {
  if(i==0 || j == 0 || i > width - 2 || j > width - 2)
    return 0
  else
    return noise.perlin2(i / 7, j / 9) + noise.perlin2(i / 15, j / 13);
  // return Math.pow(i-16,2) + Math.pow(j-16,2)
})

var svgFile = ['<svg xmlns="http://www.w3.org/2000/svg" width="640" height="640">']
var polygons = [];

for(var threshold=0.1;threshold<1.0;threshold += 0.1){
  var mult = 8,
    offset = -20;

  var complex = surfaceNets(array, threshold);

  // complex.positions.forEach(function(pt) {
  //   pt[0] += 1
  //   pt[1] += 1
  // })

  var faces = getFaces(complex.cells, complex.positions);

  //console.log(faces)

  faces.forEach(function(face){
    var polygon = [];

    face.forEach(function(index){
      var x = complex.positions[index][0];
Esempio n. 3
0
function build (regl, res, f) {
  var data = ndarray(new Float64Array(res*res*res),[res,res,res])
  return surfaceNets(fill(data, function (i,j,k) {
    return f(i/63*2-1,j/63*2-1,k/63*2-1)
  }))
}
Esempio n. 4
0
proto.update = function(params) {
  params = params || {}

  this.dirty = true

  if('contourWidth' in params) {
    this.contourWidth = handleArray(params.contourWidth, Number)
  }
  if('showContour' in params) {
    this.showContour = handleArray(params.showContour, Boolean)
  }
  if('showSurface' in params) {
    this.showSurface = !!params.showSurface
  }
  if('contourTint' in params) {
    this.contourTint = handleArray(params.contourTint, Boolean)
  }
  if('contourColor' in params) {
    this.contourColor = handleColor(params.contourColor)
  }
  if('contourProject' in params) {
    this.contourProject = handleArray(params.contourProject, function(x) {
      return handleArray(x, Boolean)
    })
  }
  if('surfaceProject' in params) {
    this.surfaceProject = params.surfaceProject
  }
  if('dynamicColor' in params) {
    this.dynamicColor = handleColor(params.dynamicColor)
  }
  if('dynamicTint' in params) {
    this.dynamicTint = handleArray(params.dynamicTint, Number)
  }
  if('dynamicWidth' in params) {
    this.dynamicWidth = handleArray(params.dynamicWidth, Number)
  }
  if('opacity' in params) {
    this.opacity = params.opacity
  }

  var field = params.field || (params.coords && params.coords[2]) || null

  if(!field) {
    if(this._field[2].shape[0] || this._field[2].shape[2]) {
      field = this._field[2].lo(1,1).hi(this._field[2].shape[0]-2, this._field[2].shape[1]-2)
    } else {
      field = this._field[2].hi(0,0)
    }
  }

  //Update field
  if('field' in params || 'coords' in params) {
    var fsize = (field.shape[0]+2)*(field.shape[1]+2)

    //Resize if necessary
    if(fsize > this._field[2].data.length) {
      pool.freeFloat(this._field[2].data)
      this._field[2].data = pool.mallocFloat(bits.nextPow2(fsize))
    }

    //Pad field
    this._field[2] = ndarray(this._field[2].data, [field.shape[0]+2, field.shape[1]+2])
    padField(this._field[2], field)

    //Save shape of field
    this.shape = field.shape.slice()
    var shape = this.shape

    //Resize coordinate fields if necessary
    for(var i=0; i<2; ++i) {
      if(this._field[2].size > this._field[i].data.length) {
        pool.freeFloat(this._field[i].data)
        this._field[i].data = pool.mallocFloat(this._field[2].size)
      }
      this._field[i] = ndarray(this._field[i].data, [shape[0]+2, shape[1]+2])
    }

    //Generate x/y coordinates
    if(params.coords) {
      var coords = params.coords
      if(!Array.isArray(coords) || coords.length !== 3) {
        throw new Error('gl-surface: invalid coordinates for x/y')
      }
      for(var i=0; i<2; ++i) {
        var coord = coords[i]
        for(var j=0; j<2; ++j) {
          if(coord.shape[j] !== shape[j]) {
            throw new Error('gl-surface: coords have incorrect shape')
          }
        }
        padField(this._field[i], coord)
      }
    } else if(params.ticks) {
      var ticks = params.ticks
      if(!Array.isArray(ticks) || ticks.length !== 2) {
        throw new Error('gl-surface: invalid ticks')
      }
      for(var i=0; i<2; ++i) {
        var tick = ticks[i]
        if(Array.isArray(tick) || tick.length) {
          tick = ndarray(tick)
        }
        if(tick.shape[0] !== shape[i]) {
          throw new Error('gl-surface: invalid tick length')
        }
        //Make a copy view of the tick array
        var tick2 = ndarray(tick.data, shape)
        tick2.stride[i] = tick.stride[0]
        tick2.stride[i^1] = 0

        //Fill in field array
        padField(this._field[i], tick2)
      }    
    } else {
      for(var i=0; i<2; ++i) {
        var offset = [0,0]
        offset[i] = 1
        this._field[i] = ndarray(this._field[i].data, [shape[0]+2, shape[1]+2], offset, 0)
      }
      this._field[0].set(0,0,0)
      for(var j=0; j<shape[0]; ++j) {
        this._field[0].set(j+1,0,j)
      }
      this._field[0].set(shape[0]+1,0,shape[0]-1)
      this._field[1].set(0,0,0)
      for(var j=0; j<shape[1]; ++j) {
        this._field[1].set(0,j+1,j)
      }
      this._field[1].set(0,shape[1]+1, shape[1]-1)
    }

    //Save shape
    var fields = this._field

    //Compute surface normals
    var fieldSize = fields[2].size
    var dfields = ndarray(pool.mallocFloat(fields[2].size*3*2), [3, shape[0]+2, shape[1]+2, 2])
    for(var i=0; i<3; ++i) {
      gradient(dfields.pick(i), fields[i], 'mirror')
    }
    var normals = ndarray(pool.mallocFloat(fields[2].size*3), [shape[0]+2, shape[1]+2, 3])
    for(var i=0; i<shape[0]+2; ++i) {
      for(var j=0; j<shape[1]+2; ++j) {
        var dxdu = dfields.get(0, i, j, 0)
        var dxdv = dfields.get(0, i, j, 1)
        var dydu = dfields.get(1, i, j, 0)
        var dydv = dfields.get(1, i, j, 1)
        var dzdu = dfields.get(2, i, j, 0)
        var dzdv = dfields.get(2, i, j, 1)

        var nx = dydu * dzdv - dydv * dzdu
        var ny = dzdu * dxdv - dzdv * dxdu
        var nz = dxdu * dydv - dxdv * dydu

        var nl = Math.sqrt(nx*nx + ny * ny + nz * nz)
        if(nl < 1e-8) {
          nl = Math.max(Math.abs(nx), Math.abs(ny), Math.abs(nz))
          if(nl < 1e-8) {
            nz = 1.0
            ny = nx = 0.0
            nl = 1.0
          } else {
            nl = 1.0/ nl
          }
        } else {
          nl = 1.0 / Math.sqrt(nl)
        }

        normals.set(i,j,0, nx*nl)
        normals.set(i,j,1, ny*nl)
        normals.set(i,j,2, nz*nl)
      }
    }
    pool.free(dfields.data)

    //Initialize surface
    var lo = [ Infinity, Infinity, Infinity]
    var hi = [-Infinity,-Infinity,-Infinity]
    var count   = (shape[0]-1) * (shape[1]-1) * 6
    var tverts  = pool.mallocFloat(bits.nextPow2(9*count))
    var tptr    = 0
    var fptr    = 0
    var vertexCount = 0
    for(var i=0; i<shape[0]-1; ++i) {
  j_loop:
      for(var j=0; j<shape[1]-1; ++j) {

        //Test for NaNs
        for(var dx=0; dx<2; ++dx) {
          for(var dy=0; dy<2; ++dy) {
            for(var k=0; k<3; ++k) {
              var f = this._field[k].get(1+i+dx, 1+j+dy)
              if(isNaN(f) || !isFinite(f)) {
                continue j_loop
              }
            }
          }
        }
        for(var k=0; k<6; ++k) {
          var r = i + QUAD[k][0]
          var c = j + QUAD[k][1]

          var tx = this._field[0].get(r+1, c+1)
          var ty = this._field[1].get(r+1, c+1)
          var f  = this._field[2].get(r+1, c+1)
          var nx = normals.get(r+1, c+1, 0)
          var ny = normals.get(r+1, c+1, 1)
          var nz = normals.get(r+1, c+1, 2)

          tverts[tptr++] = r
          tverts[tptr++] = c
          tverts[tptr++] = tx
          tverts[tptr++] = ty
          tverts[tptr++] = f
          tverts[tptr++] = 0
          tverts[tptr++] = nx
          tverts[tptr++] = ny
          tverts[tptr++] = nz

          lo[0] = Math.min(lo[0], tx)
          lo[1] = Math.min(lo[1], ty)
          lo[2] = Math.min(lo[2], f)

          hi[0] = Math.max(hi[0], tx)
          hi[1] = Math.max(hi[1], ty)
          hi[2] = Math.max(hi[2], f)

          vertexCount += 1
        }
      }
    }
    this._vertexCount = vertexCount
    this._coordinateBuffer.update(tverts.subarray(0,tptr))
    pool.freeFloat(tverts)
    pool.free(normals.data)
    
    //Update bounds
    this.bounds = [lo, hi]
  }

  //Update level crossings
  var levelsChanged = false
  if('levels' in params) {
    var levels = params.levels
    if(!Array.isArray(levels[0])) {
      levels = [ [], [], levels ]
    } else {
      levels = levels.slice()
    }
    for(var i=0; i<3; ++i) {
      levels[i] = levels[i].slice()
      levels.sort(function(a,b) {
        return a-b
      })
    }
change_test:
    for(var i=0; i<3; ++i) {
      if(levels[i].length !== this.contourLevels[i].length) {
        levelsChanged = true
        break
      }
      for(var j=0; j<levels[i].length; ++j) {
        if(levels[i][j] !== this.contourLevels[i][j]) {
          levelsChanged = true
          break change_test
        }
      }
    }
    this.contourLevels = levels
  }

  if(levelsChanged) {
    var fields = this._field
    var shape  = this.shape

    //Update contour lines
    var contourVerts = []

    for(var dim=0; dim<3; ++dim) {
      var levels = this.contourLevels[dim]
      var levelOffsets = []
      var levelCounts  = []

      var parts = [0,0]
      var graphParts = [0,0]

      for(var i=0; i<levels.length; ++i) {
        var graph = surfaceNets(this._field[dim], levels[i])
        levelOffsets.push((contourVerts.length/4)|0)
        var vertexCount = 0

  edge_loop:
        for(var j=0; j<graph.cells.length; ++j) {
          var e = graph.cells[j]
          for(var k=0; k<2; ++k) {
            var p = graph.positions[e[k]]

            var x = p[0]
            var ix = Math.floor(x)|0
            var fx = x - ix

            var y = p[1]
            var iy = Math.floor(y)|0
            var fy = y - iy

            var hole = false
  dd_loop:
            for(var dd=0; dd<2; ++dd) {
              parts[dd] = 0.0
              var iu = (dim + dd + 1) % 3            
              for(var dx=0; dx<2; ++dx) {
                var s = dx ? fx : 1.0 - fx
                var r = Math.min(Math.max(ix+dx, 0), shape[0])|0
                for(var dy=0; dy<2; ++dy) {
                  var t = dy ? fy : 1.0 - fy
                  var c = Math.min(Math.max(iy+dy, 0), shape[1])|0

                  var f = this._field[iu].get(r,c)
                  if(!isFinite(f) || isNaN(f)) {
                    hole = true
                    break dd_loop
                  }

                  var w = s * t
                  parts[dd] += w * f
                }
              }
            }

            if(!hole) {
              contourVerts.push(parts[0], parts[1], p[0], p[1])
              vertexCount += 1
            } else {
              if(k > 0) {
                //If we already added first edge, pop off verts
                for(var l=0; l<4; ++l) {
                  contourVerts.pop()
                }
                vertexCount -= 1
              }
              continue edge_loop
            }
          }
        }
        levelCounts.push(vertexCount)
      }

      //Store results
      this._contourOffsets[dim]  = levelOffsets
      this._contourCounts[dim]   = levelCounts
    }

    var floatBuffer = pool.mallocFloat(contourVerts.length)
    for(var i=0; i<contourVerts.length; ++i) {
      floatBuffer[i] = contourVerts[i]
    }
    this._contourBuffer.update(floatBuffer)
    pool.freeFloat(floatBuffer)
  }

  if(params.colormap) {
    this._colorMap.setPixels(genColormap(params.colormap))
  }
}
Esempio n. 5
0
proto.highlight = function(selection) {
  if(!selection) {
    this._dynamicCounts = [0,0,0]
    this.dyanamicLevel = [NaN, NaN, NaN]
    this.highlightLevel = [-1,-1,-1]
    return
  }

  for(var i=0; i<3; ++i) {
    if(this.enableHighlight[i]) {
      this.highlightLevel[i] = selection.level[i]
    } else {
      this.highlightLevel[i] = -1
    }
  }

  var levels
  if(this.snapToData) {
    levels = selection.dataCoordinate
  } else {
    levels = selection.position
  }
  if( (!this.enableDynamic[0] || levels[0] === this.dynamicLevel[0]) &&
      (!this.enableDynamic[1] || levels[1] === this.dynamicLevel[1]) &&
      (!this.enableDynamic[2] || levels[2] === this.dynamicLevel[2]) ) {
    return
  }

  var vertexCount = 0
  var shape = this.shape
  var scratchBuffer = pool.mallocFloat(12 * shape[0] * shape[1]) 
  
  for(var d=0; d<3; ++d) {
    if(!this.enableDynamic[d]) {
      this.dynamicLevel[d] = NaN
      this._dynamicCounts[d] = 0
      continue
    }

    this.dynamicLevel[d] = levels[d]

    var u = (d+1) % 3
    var v = (d+2) % 3

    var f = this._field[d]
    var g = this._field[u]
    var h = this._field[v]

    var graph     = surfaceNets(f, levels[d])
    var edges     = graph.cells
    var positions = graph.positions

    this._dynamicOffsets[d] = vertexCount

    for(var i=0; i<edges.length; ++i) {
      var e = edges[i]
      for(var j=0; j<2; ++j) {
        var p  = positions[e[j]]

        var x  = +p[0]
        var ix = x|0
        var jx = Math.min(ix+1, shape[0])|0
        var fx = x - ix
        var hx = 1.0 - fx
        
        var y  = +p[1]
        var iy = y|0
        var jy = Math.min(iy+1, shape[1])|0
        var fy = y - iy
        var hy = 1.0 - fy
        
        var w00 = hx * hy
        var w01 = hx * fy
        var w10 = fx * hy
        var w11 = fx * fy

        var cu =  w00 * g.get(ix,iy) +
                  w01 * g.get(ix,jy) +
                  w10 * g.get(jx,iy) +
                  w11 * g.get(jx,jy)

        var cv =  w00 * h.get(ix,iy) +
                  w01 * h.get(ix,jy) +
                  w10 * h.get(jx,iy) +
                  w11 * h.get(jx,jy)

        if(isNaN(cu) || isNaN(cv)) {
          if(j) {
            vertexCount -= 1
          }
          break
        }

        scratchBuffer[2*vertexCount+0] = cu
        scratchBuffer[2*vertexCount+1] = cv

        vertexCount += 1
      }
    }

    this._dynamicCounts[d] = vertexCount - this._dynamicOffsets[d]
  }

  this._dynamicBuffer.update(scratchBuffer.subarray(0, 2*vertexCount))
  pool.freeFloat(scratchBuffer)
}
Esempio n. 6
0
function getContour(pixels) {
  var contour = surfaceNets(pixels, 128)
  return simplify(contour.cells, contour.positions, 0.25)
}