function setupEditor() {
  /**
   *
   */
  function distanceBetween(point1, point2) {
    return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
  }

  /**
   *
   */
  function angleBetween(point1, point2) {
    return Math.atan2(point2.x - point1.x, point2.y - point1.y);
  }

  /**
   *
   */
  function initEditorStats() {
    let nbVoxels = stack2._columns * stack2._rows * stack2._frame.length;
    let textureSize = 4096;
    let textureDimension = textureSize * textureSize;

    for (let i = 0; i < nbVoxels; i++) {
      let rawDataIndex = ~~(i / textureDimension);
      let inRawDataIndex = i % textureDimension;
      let value = stack2.rawData[rawDataIndex][inRawDataIndex];
      editorStats[value] += 1;
    }

    updateEditorStatsDom();
  }

  /**
   *
   */
  function updateEditorStatsDom() {
    for (let i = 0; i < 3; i++) {
      document.getElementById(`editorSegment${i}Label`).innerHTML = segmentsList[i];
      document.getElementById(`editorSegment${i}Value`).innerHTML = editorStats[i];
    }
  }

  /**
   *  Loop through IJK BBox and see if voxel can be mapped to screen
   */
  function mapCanvasToData() {
    for (let i = ijkBBox[0]; i < ijkBBox[1] + 1; i++) {
      for (let j = ijkBBox[2]; j < ijkBBox[3] + 1; j++) {
        for (let k = ijkBBox[4]; k < ijkBBox[5] + 1; k++) {
          // ijk to world
          // center of voxel
          let worldCoordinate = new THREE.Vector3(i, j, k).applyMatrix4(stack2._ijk2LPS);
          // world to screen coordinate
          let screenCoordinates = worldCoordinate.clone();
          screenCoordinates.project(camera);

          screenCoordinates.x = Math.round(((screenCoordinates.x + 1) * canvas.offsetWidth) / 2);
          screenCoordinates.y = Math.round(((-screenCoordinates.y + 1) * canvas.offsetHeight) / 2);
          screenCoordinates.z = 0;

          let pixel = context.getImageData(screenCoordinates.x, screenCoordinates.y, 1, 1).data;
          if (pixel[3] > 0 && i >= 0 && j >= 0 && k >= 0) {
            // find index and texture
            let voxelIndex = i + j * stack2._columns + k * stack2._rows * stack2._columns;

            let textureSize = 4096;
            let textureDimension = textureSize * textureSize;

            let rawDataIndex = ~~(voxelIndex / textureDimension);
            let inRawDataIndex = voxelIndex % textureDimension;

            // update value...
            let oldValue = stack2.rawData[rawDataIndex][inRawDataIndex];
            let newValue = cursor.value;

            if (oldValue != newValue) {
              // update raw data
              stack2.rawData[rawDataIndex][inRawDataIndex] = newValue;

              // update texture that is passed to shader
              textures2[rawDataIndex].image.data = stack2.rawData[rawDataIndex]; // tex;
              textures2[rawDataIndex].needsUpdate = true;

              // update stats
              editorStats[oldValue] -= 1;
              editorStats[newValue] += 1;
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  function drawCircle(x, y) {
    context.beginPath();
    context.arc(x, y, cursor.size, false, Math.PI * 2, false);
    context.closePath();
    context.fill();
    context.stroke();
  }

  /**
   *
   */
  function clearCanvas() {
    context.clearRect(0, 0, context.canvas.width, context.canvas.height);
  }

  /**
   *
   */
  function addEventListeners() {
    /**
     *
     */
    function onMouseDown(e) {
      if (!isEditing) return;

      isDrawing = true;
      lastPoint = {
        x: e.pageX - canvasDiv.offsetLeft,
        y: e.pageY - canvasDiv.offsetTop,
      };
    }

    /**
     *
     */
    function onMouseMove(e) {
      if (!isEditing) return;

      currentPoint = {
        x: e.pageX - canvasDiv.offsetLeft,
        y: e.pageY - canvasDiv.offsetTop,
      };

      context.strokeStyle = cursor.color;
      context.globalCompositeOperation = 'xor';
      context.globalAlpha = 0.5;
      context.fillStyle = cursor.color;

      if (isDrawing) {
        let dist = distanceBetween(lastPoint, currentPoint);
        let angle = angleBetween(lastPoint, currentPoint);

        for (let i = 0; i < dist; i += 5) {
          let x = lastPoint.x + Math.sin(angle) * i;
          let y = lastPoint.y + Math.cos(angle) * i;
          drawCircle(x, y);
        }

        lastPoint = currentPoint;
      } else {
        clearCanvas();
      }

      // draw under the cursor
      context.globalCompositeOperation = 'source-over';
      context.globalAlpha = 1;
      context.fillStyle = 'rgba(0, 0, 0, 0)';
      drawCircle(currentPoint.x, currentPoint.y);
    }

    /**
     *
     */
    function onMouseUp(e) {
      if (!isEditing) return;

      isDrawing = false;
      mapCanvasToData();
      clearCanvas();
      updateEditorStatsDom();
      // draw cursor under mouse
      onMouseMove(e);
    }

    /**
     *
     */
    function updateDOM() {
      // lets events go through or not for scrolling, padding, zooming, etc.
      if (isEditing) {
        canvasDiv.className = 'editing';
        document.getElementById('help').style.display = 'none';
      } else {
        canvasDiv.className = 'exploring';
        document.getElementById('help').style.display = 'block';
      }
    }

    /**
     *
     */
    function onKeyDown(e) {
      if (e.keyCode === 17) {
        isEditing = true;
        isDrawing = false;
        updateDOM();
      }
    }

    /**
     *
     */
    function onKeyUp(e) {
      if (e.keyCode === 17) {
        isEditing = false;
        isDrawing = false;
        clearCanvas();
        updateDOM();
      }
    }

    /**
     *
     */
    function disableRightClick(e) {
      e.preventDefault();
      e.stopPropagation();
      return false;
    }

    // add events listeners
    canvasDiv.addEventListener('mousedown', onMouseDown, false);
    canvasDiv.addEventListener('mousemove', onMouseMove, false);
    canvasDiv.addEventListener('mouseup', onMouseUp, false);
    window.addEventListener('keydown', onKeyDown, false);
    window.addEventListener('keyup', onKeyUp, false);
    canvasDiv.addEventListener('contextmenu', disableRightClick, false);
  }

  addEventListeners();
  initEditorStats();
}
function setupEditor() {
  
  function distanceBetween(point1, point2) {
    return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2));
  }

  
  function angleBetween(point1, point2) {
    return Math.atan2(point2.x - point1.x, point2.y - point1.y);
  }

  
  function initEditorStats() {
    let nbVoxels = stack2._columns * stack2._rows * stack2._frame.length;
    let textureSize = 4096;
    let textureDimension = textureSize * textureSize;

    for (let i = 0; i < nbVoxels; i++) {
      let rawDataIndex = ~~(i / textureDimension);
      let inRawDataIndex = i % textureDimension;
      let value = stack2.rawData[rawDataIndex][inRawDataIndex];
      editorStats[value] += 1;
    }

    updateEditorStatsDom();
  }

  
  function updateEditorStatsDom() {
    for (let i = 0; i < 3; i++) {
      document.getElementById(`editorSegment${i}Label`).innerHTML = segmentsList[i];
      document.getElementById(`editorSegment${i}Value`).innerHTML = editorStats[i];
    }
  }

  
  function mapCanvasToData() {
    for (let i = ijkBBox[0]; i < ijkBBox[1] + 1; i++) {
      for (let j = ijkBBox[2]; j < ijkBBox[3] + 1; j++) {
        for (let k = ijkBBox[4]; k < ijkBBox[5] + 1; k++) {
          
          
          let worldCoordinate = new THREE.Vector3(i, j, k).applyMatrix4(stack2._ijk2LPS);
          
          let screenCoordinates = worldCoordinate.clone();
          screenCoordinates.project(camera);

          screenCoordinates.x = Math.round(((screenCoordinates.x + 1) * canvas.offsetWidth) / 2);
          screenCoordinates.y = Math.round(((-screenCoordinates.y + 1) * canvas.offsetHeight) / 2);
          screenCoordinates.z = 0;

          let pixel = context.getImageData(screenCoordinates.x, screenCoordinates.y, 1, 1).data;
          if (pixel[3] > 0 && i >= 0 && j >= 0 && k >= 0) {
            
            let voxelIndex = i + j * stack2._columns + k * stack2._rows * stack2._columns;

            let textureSize = 4096;
            let textureDimension = textureSize * textureSize;

            let rawDataIndex = ~~(voxelIndex / textureDimension);
            let inRawDataIndex = voxelIndex % textureDimension;

            
            let oldValue = stack2.rawData[rawDataIndex][inRawDataIndex];
            let newValue = cursor.value;

            if (oldValue != newValue) {
              
              stack2.rawData[rawDataIndex][inRawDataIndex] = newValue;

              
              textures2[rawDataIndex].image.data = stack2.rawData[rawDataIndex]; 
              textures2[rawDataIndex].needsUpdate = true;

              
              editorStats[oldValue] -= 1;
              editorStats[newValue] += 1;
            }
          }
        }
      }
    }
  }

  
  function drawCircle(x, y) {
    context.beginPath();
    context.arc(x, y, cursor.size, false, Math.PI * 2, false);
    context.closePath();
    context.fill();
    context.stroke();
  }

  
  function clearCanvas() {
    context.clearRect(0, 0, context.canvas.width, context.canvas.height);
  }

  
  function addEventListeners() {
    
    function onMouseDown(e) {
      if (!isEditing) return;

      isDrawing = true;
      lastPoint = {
        x: e.pageX - canvasDiv.offsetLeft,
        y: e.pageY - canvasDiv.offsetTop,
      };
    }

    
    function onMouseMove(e) {
      if (!isEditing) return;

      currentPoint = {
        x: e.pageX - canvasDiv.offsetLeft,
        y: e.pageY - canvasDiv.offsetTop,
      };

      context.strokeStyle = cursor.color;
      context.globalCompositeOperation = 'xor';
      context.globalAlpha = 0.5;
      context.fillStyle = cursor.color;

      if (isDrawing) {
        let dist = distanceBetween(lastPoint, currentPoint);
        let angle = angleBetween(lastPoint, currentPoint);

        for (let i = 0; i < dist; i += 5) {
          let x = lastPoint.x + Math.sin(angle) * i;
          let y = lastPoint.y + Math.cos(angle) * i;
          drawCircle(x, y);
        }

        lastPoint = currentPoint;
      } else {
        clearCanvas();
      }

      
      context.globalCompositeOperation = 'source-over';
      context.globalAlpha = 1;
      context.fillStyle = 'rgba(0, 0, 0, 0)';
      drawCircle(currentPoint.x, currentPoint.y);
    }

    
    function onMouseUp(e) {
      if (!isEditing) return;

      isDrawing = false;
      mapCanvasToData();
      clearCanvas();
      updateEditorStatsDom();
      
      onMouseMove(e);
    }

    
    function updateDOM() {
      
      if (isEditing) {
        canvasDiv.className = 'editing';
        document.getElementById('help').style.display = 'none';
      } else {
        canvasDiv.className = 'exploring';
        document.getElementById('help').style.display = 'block';
      }
    }

    
    function onKeyDown(e) {
      if (e.keyCode === 17) {
        isEditing = true;
        isDrawing = false;
        updateDOM();
      }
    }

    
    function onKeyUp(e) {
      if (e.keyCode === 17) {
        isEditing = false;
        isDrawing = false;
        clearCanvas();
        updateDOM();
      }
    }

    
    function disableRightClick(e) {
      e.preventDefault();
      e.stopPropagation();
      return false;
    }

    
    canvasDiv.addEventListener('mousedown', onMouseDown, false);
    canvasDiv.addEventListener('mousemove', onMouseMove, false);
    canvasDiv.addEventListener('mouseup', onMouseUp, false);
    window.addEventListener('keydown', onKeyDown, false);
    window.addEventListener('keyup', onKeyUp, false);
    canvasDiv.addEventListener('contextmenu', disableRightClick, false);
  }

  addEventListeners();
  initEditorStats();
}
function setupEditor ( ) { /**
   *
   */ function distanceBetween ( point1 , point2 ) { return Math . sqrt ( Math . pow ( point2 . x - point1 . x , 2 ) + Math . pow ( point2 . y - point1 . y , 2 ) ) ; } /**
   *
   */ function angleBetween ( point1 , point2 ) { return Math . atan2 ( point2 . x - point1 . x , point2 . y - point1 . y ) ; } /**
   *
   */ function initEditorStats ( ) { let nbVoxels = stack2 . _columns * stack2 . _rows * stack2 . _frame . length ; let textureSize = 4096 ; let textureDimension = textureSize * textureSize ; for ( let i = 0 ; i < nbVoxels ; i ++ ) { let rawDataIndex = ~ ~ ( i / textureDimension ) ; let inRawDataIndex = i % textureDimension ; let value = stack2 . rawData [ rawDataIndex ] [ inRawDataIndex ] ; editorStats [ value ] += 1 ; } updateEditorStatsDom ( ) ; } /**
   *
   */ function updateEditorStatsDom ( ) { for ( let i = 0 ; i < 3 ; i ++ ) { document . getElementById ( ` ${ i } ` ) . innerHTML = segmentsList [ i ] ; document . getElementById ( ` ${ i } ` ) . innerHTML = editorStats [ i ] ; } } /**
   *  Loop through IJK BBox and see if voxel can be mapped to screen
   */ function mapCanvasToData ( ) { for ( let i = ijkBBox [ 0 ] ; i < ijkBBox [ 1 ] + 1 ; i ++ ) { for ( let j = ijkBBox [ 2 ] ; j < ijkBBox [ 3 ] + 1 ; j ++ ) { for ( let k = ijkBBox [ 4 ] ; k < ijkBBox [ 5 ] + 1 ; k ++ ) { // ijk to world // center of voxel let worldCoordinate = new THREE . Vector3 ( i , j , k ) . applyMatrix4 ( stack2 . _ijk2LPS ) ; // world to screen coordinate let screenCoordinates = worldCoordinate . clone ( ) ; screenCoordinates . project ( camera ) ; screenCoordinates . x = Math . round ( ( ( screenCoordinates . x + 1 ) * canvas . offsetWidth ) / 2 ) ; screenCoordinates . y = Math . round ( ( ( - screenCoordinates . y + 1 ) * canvas . offsetHeight ) / 2 ) ; screenCoordinates . z = 0 ; let pixel = context . getImageData ( screenCoordinates . x , screenCoordinates . y , 1 , 1 ) . data ; if ( pixel [ 3 ] > 0 && i >= 0 && j >= 0 && k >= 0 ) { // find index and texture let voxelIndex = i + j * stack2 . _columns + k * stack2 . _rows * stack2 . _columns ; let textureSize = 4096 ; let textureDimension = textureSize * textureSize ; let rawDataIndex = ~ ~ ( voxelIndex / textureDimension ) ; let inRawDataIndex = voxelIndex % textureDimension ; // update value... let oldValue = stack2 . rawData [ rawDataIndex ] [ inRawDataIndex ] ; let newValue = cursor . value ; if ( oldValue != newValue ) { // update raw data stack2 . rawData [ rawDataIndex ] [ inRawDataIndex ] = newValue ; // update texture that is passed to shader textures2 [ rawDataIndex ] . image . data = stack2 . rawData [ rawDataIndex ] ; // tex; textures2 [ rawDataIndex ] . needsUpdate = true ; // update stats editorStats [ oldValue ] -= 1 ; editorStats [ newValue ] += 1 ; } } } } } } /**
   *
   */ function drawCircle ( x , y ) { context . beginPath ( ) ; context . arc ( x , y , cursor . size , false , Math . PI * 2 , false ) ; context . closePath ( ) ; context . fill ( ) ; context . stroke ( ) ; } /**
   *
   */ function clearCanvas ( ) { context . clearRect ( 0 , 0 , context . canvas . width , context . canvas . height ) ; } /**
   *
   */ function addEventListeners ( ) { /**
     *
     */ function onMouseDown ( e ) { if ( ! isEditing ) return ; isDrawing = true ; lastPoint = { x : e . pageX - canvasDiv . offsetLeft , y : e . pageY - canvasDiv . offsetTop , } ; } /**
     *
     */ function onMouseMove ( e ) { if ( ! isEditing ) return ; currentPoint = { x : e . pageX - canvasDiv . offsetLeft , y : e . pageY - canvasDiv . offsetTop , } ; context . strokeStyle = cursor . color ; context . globalCompositeOperation = 'xor' ; context . globalAlpha = 0.5 ; context . fillStyle = cursor . color ; if ( isDrawing ) { let dist = distanceBetween ( lastPoint , currentPoint ) ; let angle = angleBetween ( lastPoint , currentPoint ) ; for ( let i = 0 ; i < dist ; i += 5 ) { let x = lastPoint . x + Math . sin ( angle ) * i ; let y = lastPoint . y + Math . cos ( angle ) * i ; drawCircle ( x , y ) ; } lastPoint = currentPoint ; } else { clearCanvas ( ) ; } // draw under the cursor context . globalCompositeOperation = 'source-over' ; context . globalAlpha = 1 ; context . fillStyle = 'rgba(0, 0, 0, 0)' ; drawCircle ( currentPoint . x , currentPoint . y ) ; } /**
     *
     */ function onMouseUp ( e ) { if ( ! isEditing ) return ; isDrawing = false ; mapCanvasToData ( ) ; clearCanvas ( ) ; updateEditorStatsDom ( ) ; // draw cursor under mouse onMouseMove ( e ) ; } /**
     *
     */ function updateDOM ( ) { // lets events go through or not for scrolling, padding, zooming, etc. if ( isEditing ) { canvasDiv . className = 'editing' ; document . getElementById ( 'help' ) . style . display = 'none' ; } else { canvasDiv . className = 'exploring' ; document . getElementById ( 'help' ) . style . display = 'block' ; } } /**
     *
     */ function onKeyDown ( e ) { if ( e . keyCode === 17 ) { isEditing = true ; isDrawing = false ; updateDOM ( ) ; } } /**
     *
     */ function onKeyUp ( e ) { if ( e . keyCode === 17 ) { isEditing = false ; isDrawing = false ; clearCanvas ( ) ; updateDOM ( ) ; } } /**
     *
     */ function disableRightClick ( e ) { e . preventDefault ( ) ; e . stopPropagation ( ) ; return false ; } // add events listeners canvasDiv . addEventListener ( 'mousedown' , onMouseDown , false ) ; canvasDiv . addEventListener ( 'mousemove' , onMouseMove , false ) ; canvasDiv . addEventListener ( 'mouseup' , onMouseUp , false ) ; window . addEventListener ( 'keydown' , onKeyDown , false ) ; window . addEventListener ( 'keyup' , onKeyUp , false ) ; canvasDiv . addEventListener ( 'contextmenu' , disableRightClick , false ) ; } addEventListeners ( ) ; initEditorStats ( ) ; }
FUNCTIONS
FUNCTIONS