Example #1
0
function detectPitch(signal, options) {
  options = options || {}
  var xs
  if(signal.shape) {
    xs = signal.shape[0]
  } else {
    xs = signal.length
  }
  
  var i, j, k
  var n = bits.nextPow2(2*xs)
  var re_arr = pool.mallocFloat(n)
  var im_arr = pool.mallocFloat(n)
  var X = ndarray.ctor(re_arr, [n], [1], 0)
  var Y = ndarray.ctor(im_arr, [n], [1], 0)
  /*
  //Initialize array depending on if it is a typed array
  if(signal.shape) {
    X.shape[0] = xs
    ops.assign(X, signal)
    X.shape[0] = n
  } else {
    re_arr.set(signal)
  }
  zero(re_arr, xs, n)
  zero(im_arr, 0, n)
  
  //Autocorrelate
  fft(1, X, Y)
  square(re_arr, im_arr)
  fft(-1, X, Y)
  */
  //Detect pitch
  var threshold = options.threshold || 0.9
  var period = findPeriod(
          signal,//re_arr,
          options.start_bin || 0,
          signal.length,
          threshold)
  
  //Free temporary arrays
  pool.freeFloat(re_arr)
  pool.freeFloat(im_arr)
  
  return period
}
Example #2
0
function ndfft(dir, x, y) {
  var shape = x.shape
    , d = shape.length
    , size = 1
    , stride = new Array(d)
    , pad = 0
    , i, j
  for(i=d-1; i>=0; --i) {
    stride[i] = size
    size *= shape[i]
    pad = Math.max(pad, fftm.scratchMemory(shape[i]))
    if(x.shape[i] !== y.shape[i]) {
      throw new Error('Shape mismatch, real and imaginary arrays must have same size')
    }
  }
  var buf_size = 4 * size + pad
  var buffer
  if( x.dtype === 'array' ||
      x.dtype === 'float64' ||
      x.dtype === 'custom' ) {
    buffer = pool.mallocDouble(buf_size)
  } else {
    buffer = pool.mallocFloat(buf_size)
  }
  var x1 = ndarray(buffer, shape.slice(0), stride, 0)
    , y1 = ndarray(buffer, shape.slice(0), stride.slice(0), size)
    , x2 = ndarray(buffer, shape.slice(0), stride.slice(0), 2*size)
    , y2 = ndarray(buffer, shape.slice(0), stride.slice(0), 3*size)
    , tmp, n, s1, s2
    , scratch_ptr = 4 * size
  
  //Copy into x1/y1
  ops.assign(x1, x)
  ops.assign(y1, y)
  
  for(i=d-1; i>=0; --i) {
    fftm(dir, size/shape[i], shape[i], buffer, x1.offset, y1.offset, scratch_ptr)
    if(i === 0) {
      break
    }
    
    //Compute new stride for x2/y2
    n = 1
    s1 = x2.stride
    s2 = y2.stride
    for(j=i-1; j<d; ++j) {
      s2[j] = s1[j] = n
      n *= shape[j]
    }
    for(j=i-2; j>=0; --j) {
      s2[j] = s1[j] = n
      n *= shape[j]
    }
    
    //Transpose
    ops.assign(x2, x1)
    ops.assign(y2, y1)
    
    //Swap buffers
    tmp = x1
    x1 = x2
    x2 = tmp
    tmp = y1
    y1 = y2
    y2 = tmp
  }
  
  //Copy result back into x
  ops.assign(x, x1)
  ops.assign(y, y1)
  
  pool.free(buffer)
}
Example #3
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))
  }
}
Example #4
0
function SurfacePlot(
  gl, 
  shape, 
  bounds, 
  shader, 
  pickShader, 
  coordinates, 
  vao, 
  colorMap,
  contourShader,
  contourPickShader,
  contourBuffer,
  contourVAO,
  dynamicBuffer,
  dynamicVAO) {

  this.gl                 = gl
  this.shape              = shape
  this.bounds             = bounds

  this._shader            = shader
  this._pickShader        = pickShader
  this._coordinateBuffer  = coordinates
  this._vao               = vao
  this._colorMap          = colorMap

  this._contourShader     = contourShader
  this._contourPickShader = contourPickShader
  this._contourBuffer     = contourBuffer
  this._contourVAO        = contourVAO
  this._contourOffsets    = [[], [], []]
  this._contourCounts     = [[], [], []]
  this._vertexCount       = 0
  
  this._pickResult        = new SurfacePickResult([0,0,0], [0,0], [0,0], [0,0,0], [0,0,0])

  this._dynamicBuffer     = dynamicBuffer
  this._dynamicVAO        = dynamicVAO
  this._dynamicOffsets    = [0,0,0]
  this._dynamicCounts     = [0,0,0]

  this.contourWidth       = [ 1, 1, 1 ]
  this.contourLevels      = [[1], [1], [1]]
  this.contourTint        = [0, 0, 0]
  this.contourColor       = [[0.5,0.5,0.5,1], [0.5,0.5,0.5,1], [0.5,0.5,0.5,1]]

  this.showContour        = true
  this.showSurface        = true

  this.enableHighlight    = [true, true, true]
  this.highlightColor     = [[0,0,0,1], [0,0,0,1], [0,0,0,1]]
  this.highlightTint      = [ 1, 1, 1 ]
  this.highlightLevel     = [-1, -1, -1]

  //Dynamic contour options
  this.enableDynamic      = [ true, true, true ]
  this.dynamicLevel       = [ NaN, NaN, NaN ]
  this.dynamicColor       = [ [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1] ]
  this.dynamicTint        = [ 1, 1, 1 ]
  this.dynamicWidth       = [ 1, 1, 1 ]

  this.axesBounds         = [[Infinity,Infinity,Infinity],[-Infinity,-Infinity,-Infinity]]
  this.surfaceProject     = [ false, false, false ]
  this.contourProject     = [[ false, false, false ],
                             [ false, false, false ],
                             [ false, false, false ]]

  //Store xyz fields, need this for picking
  this._field             = [ 
      ndarray(pool.mallocFloat(1024), [0,0]), 
      ndarray(pool.mallocFloat(1024), [0,0]), 
      ndarray(pool.mallocFloat(1024), [0,0]) ]

  this.pickId             = 1
  this.clipBounds         = [[-Infinity,-Infinity,-Infinity],[Infinity,Infinity,Infinity]]
  
  this.snapToData         = false

  this.opacity            = 1.0

  this.lightPosition      = [10, 10000, 0]
  this.ambientLight       = 0.8
  this.diffuseLight       = 0.8
  this.specularLight      = 2.0
  this.roughness          = 0.5
  this.fresnel            = 1.5

  this.dirty              = true
}
Example #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)
}