src/surface/volume-slice.js
/**
* @file Volume Slice
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @private
*/
import { Vector3 } from '../../lib/three.es6.js'
import { ColormakerRegistry } from '../globals.js'
import { defaults } from '../utils.js'
import { SlicePicker } from '../utils/picker.js'
class VolumeSlice {
constructor (volume, params) {
const p = params || {}
this.dimension = defaults(p.dimension, 'x')
this.positionType = defaults(p.positionType, 'percent')
this.position = defaults(p.position, 30)
this.thresholdType = defaults(p.thresholdType, 'sigma')
this.thresholdMin = defaults(p.thresholdMin, -Infinity)
this.thresholdMax = defaults(p.thresholdMax, Infinity)
this.normalize = defaults(p.normalize, false)
this.volume = volume
}
getPositionFromCoordinate (coord) {
const dim = this.dimension
const v = this.volume
const m = v.matrix
const mp = new Vector3().setFromMatrixPosition(m)[ dim ]
const ms = new Vector3().setFromMatrixScale(m)[ dim ]
let vn
if (dim === 'x') {
vn = v.nx
} else if (dim === 'y') {
vn = v.ny
} else {
vn = v.nz
}
return Math.round((((coord - mp) / (vn / 100)) + 1) / ms)
}
getData (params) {
params = params || {}
const v = this.volume
const d = v.data
const m = v.matrix
let p
if (this.positionType === 'coordinate') {
p = this.getPositionFromCoordinate(this.position)
} else {
p = this.position
}
function pos (dimLen) {
return Math.round((dimLen / 100) * (p - 1))
}
function index (x, y, z, i) {
return (z * v.ny * v.nx + y * v.nx + x) * 3 + i
}
const position = new Float32Array(4 * 3)
const vec = new Vector3()
let width, height
let x
let y
let z
let x0 = 0
let y0 = 0
let z0 = 0
let nx = v.nx
let ny = v.ny
let nz = v.nz
function setVec (x, y, z, offset) {
vec.set(x, y, z).applyMatrix4(m).toArray(position, offset)
}
if (this.dimension === 'x') {
x = pos(v.nx)
y = v.ny - 1
z = v.nz - 1
width = v.nz
height = v.ny
x0 = x
nx = x0 + 1
setVec(x, 0, 0, 0)
setVec(x, y, 0, 3)
setVec(x, 0, z, 6)
setVec(x, y, z, 9)
} else if (this.dimension === 'y') {
x = v.nx - 1
y = pos(v.ny)
z = v.nz - 1
width = v.nz
height = v.nx
y0 = y
ny = y0 + 1
setVec(0, y, 0, 0)
setVec(x, y, 0, 3)
setVec(0, y, z, 6)
setVec(x, y, z, 9)
} else if (this.dimension === 'z') {
x = v.nx - 1
y = v.ny - 1
z = pos(v.nz)
width = v.nx
height = v.ny
z0 = z
nz = z0 + 1
setVec(0, 0, z, 0)
setVec(0, y, z, 3)
setVec(x, 0, z, 6)
setVec(x, y, z, 9)
}
let i = 0
let j = 0
const imageData = new Uint8Array(width * height * 4)
const pickingArray = new Float32Array(width * height)
let tMin, tMax
if (this.thresholdType === 'sigma') {
tMin = v.getValueForSigma(this.thresholdMin)
tMax = v.getValueForSigma(this.thresholdMax)
} else {
tMin = this.thresholdMin
tMax = this.thresholdMax
}
const cp = Object.assign({}, params.colorParams, { volume: v })
if (this.normalize) {
cp.domain = [ 0, 1 ]
}
const colormaker = ColormakerRegistry.getScheme(cp)
const tmp = new Float32Array(3)
const scale = colormaker.getScale()
let min, max, diff
if (this.normalize) {
min = +Infinity
max = -Infinity
for (let iy = y0; iy < ny; ++iy) {
for (let ix = x0; ix < nx; ++ix) {
for (let iz = z0; iz < nz; ++iz) {
const idx = index(ix, iy, iz, 0) / 3
const val = d[ idx ]
if (val < min) min = val
if (val > max) max = val
}
}
}
diff = max - min
}
for (let iy = y0; iy < ny; ++iy) {
for (let ix = x0; ix < nx; ++ix) {
for (let iz = z0; iz < nz; ++iz) {
const idx = index(ix, iy, iz, 0) / 3
let val = d[ idx ]
if (this.normalize) {
val = (val - min) / diff
}
colormaker.colorToArray(scale(val), tmp)
imageData[ i ] = Math.round(tmp[ 0 ] * 255)
imageData[ i + 1 ] = Math.round(tmp[ 1 ] * 255)
imageData[ i + 2 ] = Math.round(tmp[ 2 ] * 255)
imageData[ i + 3 ] = (val > tMin && val < tMax) ? 255 : 0
pickingArray[ j ] = idx
++j
i += 4
}
}
}
const picking = new SlicePicker(pickingArray, v)
return { position, imageData, width, height, picking }
}
}
export default VolumeSlice