Datacube.js

'use strict'

var times = require('lodash/times')
var core = require('mathjs/core')
var math = core.create()
math.import(require('mathjs/lib/type/matrix'))
math.import(require('mathjs/lib/function/matrix/subset'))

/**
 * @typedef DatacubeOptions
 * @property {Number[][][]} data
 */

 /**
 * @memberOf Datacube
 * @property {mathjs.Matrix} data
 * @property {Object<string, Number>} midpoints
 * @property {Object<string, Number>} dims lengths of each dimension
 * @property {Number[]} xRange
 * @property {Number[]} yRange
 * @property {Number[]} zRange
 */

 /**
 * @class Datacube
 * @param {DatacubeOptions} opts
 */
function Datacube (opts) {
  this.data = math.matrix(opts.data)
  var size = this.data.size()
  this.dims = {
    x: size[0],
    y: size[1],
    z: size[2]
  }
  this.midpoints = {
    x: Math.floor(this.dims.x / 2),
    y: Math.floor(this.dims.y / 2),
    z: Math.floor(this.dims.z / 2)
  }
  this.xRange = times(this.dims.x)
  this.yRange = times(this.dims.y)
  this.zRange = times(this.dims.z)
}

/**
 * Query a planar slice at the provided point in the remaining axies
 * @param {Object} opts
 * @param {string} opts.axis axis, x, y, or z
 * @param {Number} opts.value
 */
Datacube.prototype.querySlice = function (opts) {
  var index
  if (opts.axis === 'x') {
    index = math.index(opts.value, this.yRange, this.zRange)
    return math.subset(this.data, index).toArray()[0]
  } else if (opts.axis === 'y') {
    index = math.index(this.xRange, opts.value, this.zRange)
    return math.subset(this.data, index).toArray()
    .map(function (y, xi) { return y[0] })
  } else if (opts.axis === 'z') {
    index = math.index(this.xRange, this.yRange, opts.value)
    return math.subset(this.data, index).toArray()
    .map(function (yi, xi) { return yi.map(function (z) { return z[0] }) })
  }
  throw new Error('invalid axis')
}

module.exports = Datacube