NGL@1.0.0-beta.7 Home Manual Reference Source Gallery

src/geometry/spatial-hash.js

/**
 * @file Spatial Hash
 * @author Alexander Rose <alexander.rose@weirdbyte.de>
 * @private
 */

// import { Debug, Log } from "../globals.js";

function SpatialHash (atomStore, boundingBox) {
  var exp = 3

  var bb = boundingBox
  var minX = bb.min.x
  var minY = bb.min.y
  var minZ = bb.min.z
  var boundX = ((bb.max.x - minX) >> exp) + 1
  var boundY = ((bb.max.y - minY) >> exp) + 1
  var boundZ = ((bb.max.z - minZ) >> exp) + 1

  var n = boundX * boundY * boundZ
  var an = atomStore.count

  var xArray = atomStore.x
  var yArray = atomStore.y
  var zArray = atomStore.z

  var i, j

  var count = 0
  var grid = new Uint32Array(n)
  var bucketIndex = new Int32Array(an)
  for (i = 0; i < an; ++i) {
    var x = (xArray[ i ] - minX) >> exp
    var y = (yArray[ i ] - minY) >> exp
    var z = (zArray[ i ] - minZ) >> exp
    var idx = (((x * boundY) + y) * boundZ) + z
    if ((grid[ idx ] += 1) === 1) {
      count += 1
    }
    bucketIndex[ i ] = idx
  }

  var bucketCount = new Uint16Array(count)
  for (i = 0, j = 0; i < n; ++i) {
    var c = grid[ i ]
    if (c > 0) {
      grid[ i ] = j + 1
      bucketCount[ j ] = c
      j += 1
    }
  }

  var bucketOffset = new Uint32Array(count)
  for (i = 1; i < count; ++i) {
    bucketOffset[ i ] += bucketOffset[ i - 1 ] + bucketCount[ i - 1 ]
  }

  var bucketFill = new Uint16Array(count)
  var bucketArray = new Int32Array(an)
  for (i = 0; i < an; ++i) {
    var bucketIdx = grid[ bucketIndex[ i ] ]
    if (bucketIdx > 0) {
      var k = bucketIdx - 1
      bucketArray[ bucketOffset[ k ] + bucketFill[ k ] ] = i
      bucketFill[ k ] += 1
    }
  }

  function within (x, y, z, r) {
    const result = []

    eachWithin(x, y, z, r, atomIndex => result.push(atomIndex))

    return result
  }

  function eachWithin (x, y, z, r, callback) {
    var rSq = r * r

    var loX = Math.max(0, (x - r - minX) >> exp)
    var loY = Math.max(0, (y - r - minY) >> exp)
    var loZ = Math.max(0, (z - r - minZ) >> exp)

    var hiX = Math.min(boundX, (x + r - minX) >> exp)
    var hiY = Math.min(boundY, (y + r - minY) >> exp)
    var hiZ = Math.min(boundZ, (z + r - minZ) >> exp)

    var result = []

    for (var ix = loX; ix <= hiX; ++ix) {
      for (var iy = loY; iy <= hiY; ++iy) {
        for (var iz = loZ; iz <= hiZ; ++iz) {
          var idx = (((ix * boundY) + iy) * boundZ) + iz
          var bucketIdx = grid[ idx ]

          if (bucketIdx > 0) {
            var k = bucketIdx - 1
            var offset = bucketOffset[ k ]
            var count = bucketCount[ k ]
            var end = offset + count

            for (var i = offset; i < end; ++i) {
              var atomIndex = bucketArray[ i ]
              var dx = xArray[ atomIndex ] - x
              var dy = yArray[ atomIndex ] - y
              var dz = zArray[ atomIndex ] - z

              const dSq = dx * dx + dy * dy + dz * dz
              if (dSq <= rSq) callback(atomIndex, dSq)
            }
          }
        }
      }
    }

    return result
  }

  // API

  this.within = within
  this.eachWithin = eachWithin
}

export default SpatialHash