{"version":3,"file":"VolumeSlice.cjs","sources":["../../src/misc/VolumeSlice.js"],"sourcesContent":["import { ClampToEdgeWrapping, DoubleSide, LinearFilter, Mesh, MeshBasicMaterial, PlaneGeometry, Texture } from 'three'\n\n/**\n * This class has been made to hold a slice of a volume data\n * @class\n * @param   {Volume} volume    The associated volume\n * @param   {number}       [index=0] The index of the slice\n * @param   {string}       [axis='z']      For now only 'x', 'y' or 'z' but later it will change to a normal vector\n * @see Volume\n */\nclass VolumeSlice {\n  constructor(volume, index, axis) {\n    const slice = this\n    /**\n     * @member {Volume} volume The associated volume\n     */\n    this.volume = volume\n    /**\n     * @member {Number} index The index of the slice, if changed, will automatically call updateGeometry at the next repaint\n     */\n    index = index || 0\n    Object.defineProperty(this, 'index', {\n      get: function () {\n        return index\n      },\n      set: function (value) {\n        index = value\n        slice.geometryNeedsUpdate = true\n        return index\n      },\n    })\n    /**\n     * @member {String} axis The normal axis\n     */\n    this.axis = axis || 'z'\n\n    /**\n     * @member {HTMLCanvasElement} canvas The final canvas used for the texture\n     */\n    /**\n     * @member {CanvasRenderingContext2D} ctx Context of the canvas\n     */\n    this.canvas = document.createElement('canvas')\n    /**\n     * @member {HTMLCanvasElement} canvasBuffer The intermediary canvas used to paint the data\n     */\n    /**\n     * @member {CanvasRenderingContext2D} ctxBuffer Context of the canvas buffer\n     */\n    this.canvasBuffer = document.createElement('canvas')\n    this.updateGeometry()\n\n    const canvasMap = new Texture(this.canvas)\n    canvasMap.minFilter = LinearFilter\n    canvasMap.wrapS = canvasMap.wrapT = ClampToEdgeWrapping\n    if ('colorSpace' in canvasMap) canvasMap.colorSpace = 'srgb'\n    else canvasMap.encoding = 3001 // sRGBEncoding\n    const material = new MeshBasicMaterial({ map: canvasMap, side: DoubleSide, transparent: true })\n    /**\n     * @member {Mesh} mesh The mesh ready to get used in the scene\n     */\n    this.mesh = new Mesh(this.geometry, material)\n    this.mesh.matrixAutoUpdate = false\n    /**\n     * @member {Boolean} geometryNeedsUpdate If set to true, updateGeometry will be triggered at the next repaint\n     */\n    this.geometryNeedsUpdate = true\n    this.repaint()\n\n    /**\n     * @member {Number} iLength Width of slice in the original coordinate system, corresponds to the width of the buffer canvas\n     */\n\n    /**\n     * @member {Number} jLength Height of slice in the original coordinate system, corresponds to the height of the buffer canvas\n     */\n\n    /**\n     * @member {Function} sliceAccess Function that allow the slice to access right data\n     * @see Volume.extractPerpendicularPlane\n     * @param {Number} i The first coordinate\n     * @param {Number} j The second coordinate\n     * @returns {Number} the index corresponding to the voxel in volume.data of the given position in the slice\n     */\n  }\n\n  /**\n   * @member {Function} repaint Refresh the texture and the geometry if geometryNeedsUpdate is set to true\n   * @memberof VolumeSlice\n   */\n  repaint() {\n    if (this.geometryNeedsUpdate) {\n      this.updateGeometry()\n    }\n\n    const iLength = this.iLength,\n      jLength = this.jLength,\n      sliceAccess = this.sliceAccess,\n      volume = this.volume,\n      canvas = this.canvasBuffer,\n      ctx = this.ctxBuffer\n\n    // get the imageData and pixel array from the canvas\n    const imgData = ctx.getImageData(0, 0, iLength, jLength)\n    const data = imgData.data\n    const volumeData = volume.data\n    const upperThreshold = volume.upperThreshold\n    const lowerThreshold = volume.lowerThreshold\n    const windowLow = volume.windowLow\n    const windowHigh = volume.windowHigh\n\n    // manipulate some pixel elements\n    let pixelCount = 0\n\n    if (volume.dataType === 'label') {\n      //this part is currently useless but will be used when colortables will be handled\n      for (let j = 0; j < jLength; j++) {\n        for (let i = 0; i < iLength; i++) {\n          let label = volumeData[sliceAccess(i, j)]\n          label = label >= this.colorMap.length ? (label % this.colorMap.length) + 1 : label\n          const color = this.colorMap[label]\n          data[4 * pixelCount] = (color >> 24) & 0xff\n          data[4 * pixelCount + 1] = (color >> 16) & 0xff\n          data[4 * pixelCount + 2] = (color >> 8) & 0xff\n          data[4 * pixelCount + 3] = color & 0xff\n          pixelCount++\n        }\n      }\n    } else {\n      for (let j = 0; j < jLength; j++) {\n        for (let i = 0; i < iLength; i++) {\n          let value = volumeData[sliceAccess(i, j)]\n          let alpha = 0xff\n          //apply threshold\n          alpha = upperThreshold >= value ? (lowerThreshold <= value ? alpha : 0) : 0\n          //apply window level\n          value = Math.floor((255 * (value - windowLow)) / (windowHigh - windowLow))\n          value = value > 255 ? 255 : value < 0 ? 0 : value | 0\n\n          data[4 * pixelCount] = value\n          data[4 * pixelCount + 1] = value\n          data[4 * pixelCount + 2] = value\n          data[4 * pixelCount + 3] = alpha\n          pixelCount++\n        }\n      }\n    }\n\n    ctx.putImageData(imgData, 0, 0)\n    this.ctx.drawImage(canvas, 0, 0, iLength, jLength, 0, 0, this.canvas.width, this.canvas.height)\n\n    this.mesh.material.map.needsUpdate = true\n  }\n\n  /**\n   * @member {Function} Refresh the geometry according to axis and index\n   * @see Volume.extractPerpendicularPlane\n   * @memberof VolumeSlice\n   */\n  updateGeometry() {\n    const extracted = this.volume.extractPerpendicularPlane(this.axis, this.index)\n    this.sliceAccess = extracted.sliceAccess\n    this.jLength = extracted.jLength\n    this.iLength = extracted.iLength\n    this.matrix = extracted.matrix\n\n    this.canvas.width = extracted.planeWidth\n    this.canvas.height = extracted.planeHeight\n    this.canvasBuffer.width = this.iLength\n    this.canvasBuffer.height = this.jLength\n    this.ctx = this.canvas.getContext('2d')\n    this.ctxBuffer = this.canvasBuffer.getContext('2d')\n\n    if (this.geometry) this.geometry.dispose() // dispose existing geometry\n\n    this.geometry = new PlaneGeometry(extracted.planeWidth, extracted.planeHeight)\n\n    if (this.mesh) {\n      this.mesh.geometry = this.geometry\n      //reset mesh matrix\n      this.mesh.matrix.identity()\n      this.mesh.applyMatrix4(this.matrix)\n    }\n\n    this.geometryNeedsUpdate = false\n  }\n}\n\nexport { VolumeSlice }\n"],"names":["Texture","LinearFilter","ClampToEdgeWrapping","MeshBasicMaterial","DoubleSide","Mesh","PlaneGeometry"],"mappings":"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"}