// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkFixedPointVolumeRayCastMIPHelper.h"

#include "vtkArrayDispatch.h"
#include "vtkCommand.h"
#include "vtkDataArray.h"
#include "vtkDataArrayRange.h"
#include "vtkFixedPointRayCastImage.h"
#include "vtkFixedPointVolumeRayCastMapper.h"
#include "vtkImageData.h"
#include "vtkObjectFactory.h"
#include "vtkRectilinearGrid.h"
#include "vtkRenderWindow.h"
#include "vtkVolume.h"
#include "vtkVolumeProperty.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkFixedPointVolumeRayCastMIPHelper);

// Construct a new vtkFixedPointVolumeRayCastMIPHelper with default values
vtkFixedPointVolumeRayCastMIPHelper::vtkFixedPointVolumeRayCastMIPHelper() = default;

// Destruct a vtkFixedPointVolumeRayCastMIPHelper - clean up any memory used
vtkFixedPointVolumeRayCastMIPHelper::~vtkFixedPointVolumeRayCastMIPHelper() = default;

// This method is called when the interpolation type is nearest neighbor and
// the data contains one component. In the inner loop we will compute the
// maximum value (in native type). After we have a maximum value for the ray
// we will convert it to unsigned short using the scale/shift, then use this
// index to lookup the final color/opacity.
struct vtkFixedPointMIPHelperGenerateImageOneNNFunctor
{
  template <class TArray>
  void operator()(TArray* dataArray, int threadID, int threadCount,
    vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol))
  {
    using T = vtk::GetAPIType<TArray>;
    auto data = vtk::DataArrayValueRange<1>(dataArray).begin();
    VTKKWRCHelper_InitializationAndLoopStartNN
    VTKKWRCHelper_InitializeMIPOneNN
    VTKKWRCHelper_SpaceLeapSetup

    if (cropping)
    {
      int maxValueDefined = 0;
      unsigned short maxIdx = 0;

      for (k = 0; k < numSteps; k++)
      {
        if (k)
        {
          mapper->FixedPointIncrement(pos, dir);
        }

        VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, maxValueDefined, mapper->GetFlipMIPComparison())

        if (!mapper->CheckIfCropped(pos))
        {
          mapper->ShiftVectorDown(pos, spos);
          dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2];
          if (!maxValueDefined ||
            ((mapper->GetFlipMIPComparison() && *dptr < maxValue) ||
              (!mapper->GetFlipMIPComparison() && *dptr > maxValue)))
          {
            maxValue = *dptr;
            maxIdx = static_cast<unsigned short>((maxValue + shift[0]) * scale[0]);
            maxValueDefined = 1;
          }
        }
      }

      if (maxValueDefined)
      {
        VTKKWRCHelper_LookupColorMax(colorTable[0], scalarOpacityTable[0], maxIdx, imagePtr)
      }
      else
      {
        imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0;
      }
    }
    else
    {
      unsigned short maxIdx = static_cast<unsigned short>((maxValue + shift[0]) * scale[0]);

      for (k = 0; k < numSteps; k++)
      {
        if (k)
        {
          mapper->FixedPointIncrement(pos, dir);
        }

        VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, 1, mapper->GetFlipMIPComparison())

        mapper->ShiftVectorDown(pos, spos);
        dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2];
        if (mapper->GetFlipMIPComparison())
        {
          maxValue = (*dptr < maxValue) ? (*dptr) : (maxValue);
        }
        else
        {
          maxValue = (*dptr > maxValue) ? (*dptr) : (maxValue);
        }

        maxIdx = static_cast<unsigned short>((maxValue + shift[0]) * scale[0]);
      }

      VTKKWRCHelper_LookupColorMax(colorTable[0], scalarOpacityTable[0], maxIdx, imagePtr)
    }

    VTKKWRCHelper_IncrementAndLoopEnd
  }
};

// This method is called when the interpolation type is nearest neighbor and
// the data has two or four dependent components. If it is four, they must be
// unsigned char components.  Compute max of last components in native type,
// then use first component to look up a color (2 component data) or first three
// as the color directly (four component data). Lookup alpha off the last component.
struct vtkFixedPointMIPHelperGenerateImageDependentNNFunctor
{
  template <class TArray>
  void operator()(TArray* dataArray, int threadID, int threadCount,
    vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol))
  {
    using T = vtk::GetAPIType<TArray>;
    auto data = vtk::DataArrayValueRange(dataArray).begin();
    VTKKWRCHelper_InitializationAndLoopStartNN
    VTKKWRCHelper_InitializeMIPMultiNN
    VTKKWRCHelper_SpaceLeapSetup

    int maxValueDefined = 0;
    unsigned short maxIdxS = 0;

    for (k = 0; k < numSteps; k++)
    {
      if (k)
      {
        mapper->FixedPointIncrement(pos, dir);
      }

      VTKKWRCHelper_MIPSpaceLeapCheck(maxIdxS, maxValueDefined, mapper->GetFlipMIPComparison())
      VTKKWRCHelper_CroppingCheckNN(pos)

      mapper->ShiftVectorDown(pos, spos);
      dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2];
      if (!maxValueDefined ||
        ((mapper->GetFlipMIPComparison() && *(dptr + components - 1) < maxValue[components - 1]) ||
          (!mapper->GetFlipMIPComparison() && *(dptr + components - 1) > maxValue[components - 1])))
      {
        for (c = 0; c < components; c++)
        {
          maxValue[c] = *(dptr + c);
        }
        maxIdxS = static_cast<unsigned short>(
          (maxValue[components - 1] + shift[components - 1]) * scale[components - 1]);
        maxValueDefined = 1;
      }
    }

    if (maxValueDefined)
    {
      unsigned short maxIdx[4] = { 0, 0, 0, 0 };
      if (components == 2)
      {
        maxIdx[0] = static_cast<unsigned short>((maxValue[0] + shift[0]) * scale[0]);
        maxIdx[1] = static_cast<unsigned short>((maxValue[1] + shift[1]) * scale[1]);
      }
      else
      {
        maxIdx[0] = static_cast<unsigned short>(maxValue[0]);
        maxIdx[1] = static_cast<unsigned short>(maxValue[1]);
        maxIdx[2] = static_cast<unsigned short>(maxValue[2]);
        maxIdx[3] = static_cast<unsigned short>((maxValue[3] + shift[3]) * scale[3]);
      }

      for (c = 0; c < components; c++)
      {
      }
      VTKKWRCHelper_LookupDependentColorUS(
        colorTable[0], scalarOpacityTable[0], maxIdx, components, imagePtr)
    }
    else
    {
      imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0;
    }

    VTKKWRCHelper_IncrementAndLoopEnd
  }
};

// This method is called when the interpolation type is nearest neighbor and
// the data has more than one independent components. We compute the max of
// each component along the ray in native type, then use the scale/shift to
// convert this into an unsigned short index value. We use the index values
// to lookup the color/opacity per component, then use the component weights to
// blend these into one final color.
struct vtkFixedPointMIPHelperGenerateImageIndependentNNFunctor
{
  template <class TArray>
  void operator()(TArray* dataArray, int threadID, int threadCount,
    vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vol)
  {
    using T = vtk::GetAPIType<TArray>;
    auto data = vtk::DataArrayValueRange(dataArray).begin();
    VTKKWRCHelper_InitializeWeights
    VTKKWRCHelper_InitializationAndLoopStartNN
    VTKKWRCHelper_InitializeMIPMultiNN
    VTKKWRCHelper_SpaceLeapSetupMulti

    int maxValueDefined = 0;
    unsigned short maxIdx[4] = { 0, 0, 0, 0 };

    for (k = 0; k < numSteps; k++)
    {
      if (k)
      {
        mapper->FixedPointIncrement(pos, dir);
      }
      VTKKWRCHelper_CroppingCheckNN(pos)
      VTKKWRCHelper_MIPSpaceLeapPopulateMulti(maxIdx, mapper->GetFlipMIPComparison())

      mapper->ShiftVectorDown(pos, spos);
      dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2];

      if (!maxValueDefined)
      {
        for (c = 0; c < components; c++)
        {
          maxValue[c] = *(dptr + c);
          maxIdx[c] = static_cast<unsigned short>((maxValue[c] + shift[c]) * scale[c]);
        }
        maxValueDefined = 1;
      }
      else
      {
        for (c = 0; c < components; c++)
        {
          if (VTKKWRCHelper_MIPSpaceLeapCheckMulti(c, mapper->GetFlipMIPComparison()) &&
            ((mapper->GetFlipMIPComparison() && *(dptr + c) < maxValue[c]) ||
              (!mapper->GetFlipMIPComparison() && *(dptr + c) > maxValue[c])))
          {
            maxValue[c] = *(dptr + c);
            maxIdx[c] = static_cast<unsigned short>((maxValue[c] + shift[c]) * scale[c]);
          }
        }
      }
    }

    imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0;
    if (maxValueDefined)
    {
      VTKKWRCHelper_LookupAndCombineIndependentColorsMax(
        colorTable, scalarOpacityTable, maxIdx, weights, components, imagePtr)
    }

    VTKKWRCHelper_IncrementAndLoopEnd
  }
};

// This method is called when the interpolation type is linear, the
// data contains one component and scale = 1.0 and shift = 0.0. This is
// the simple case were we do not need to apply scale/shift in the
// inner loop. In the inner loop we compute the eight cell vertex values
// (if we have changed cells). We compute our weights within the cell
// according to our fractional position within the cell, and apply trilinear
// interpolation to compute the index. We find the maximum index along
// the ray, and then use this to look up a final color.
struct vtkFixedPointMIPHelperGenerateImageOneSimpleTrilinFunctor
{
  template <class TArray>
  void operator()(TArray* dataArray, int threadID, int threadCount,
    vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol))
  {
    auto data = vtk::DataArrayValueRange<1>(dataArray).begin();
    VTKKWRCHelper_InitializationAndLoopStartTrilin
    VTKKWRCHelper_InitializeMIPOneTrilin
    VTKKWRCHelper_SpaceLeapSetup

    int maxValueDefined = 0;
    unsigned short maxIdx = 0;
    unsigned int maxScalar = 0;

    for (k = 0; k < numSteps; k++)
    {
      if (k)
      {
        mapper->FixedPointIncrement(pos, dir);
      }

      VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, maxValueDefined, mapper->GetFlipMIPComparison())
      VTKKWRCHelper_CroppingCheckTrilin(pos)

      mapper->ShiftVectorDown(pos, spos);
      if (spos[0] != oldSPos[0] || spos[1] != oldSPos[1] || spos[2] != oldSPos[2])
      {
        oldSPos[0] = spos[0];
        oldSPos[1] = spos[1];
        oldSPos[2] = spos[2];

        dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2];
        VTKKWRCHelper_GetCellScalarValuesSimple(dptr)
        if (mapper->GetFlipMIPComparison())
        {
          maxScalar = (A < B) ? (A) : (B);
          maxScalar = (C < maxScalar) ? (C) : (maxScalar);
          maxScalar = (D < maxScalar) ? (D) : (maxScalar);
          maxScalar = (E < maxScalar) ? (E) : (maxScalar);
          maxScalar = (F < maxScalar) ? (F) : (maxScalar);
          maxScalar = (G < maxScalar) ? (G) : (maxScalar);
          maxScalar = (H < maxScalar) ? (H) : (maxScalar);
        }
        else
        {
          maxScalar = (A > B) ? (A) : (B);
          maxScalar = (C > maxScalar) ? (C) : (maxScalar);
          maxScalar = (D > maxScalar) ? (D) : (maxScalar);
          maxScalar = (E > maxScalar) ? (E) : (maxScalar);
          maxScalar = (F > maxScalar) ? (F) : (maxScalar);
          maxScalar = (G > maxScalar) ? (G) : (maxScalar);
          maxScalar = (H > maxScalar) ? (H) : (maxScalar);
        }
      }

      if (!maxValueDefined ||
        ((mapper->GetFlipMIPComparison() && maxScalar < static_cast<unsigned int>(maxValue)) ||
          (!mapper->GetFlipMIPComparison() && maxScalar > static_cast<unsigned int>(maxValue))))
      {
        VTKKWRCHelper_ComputeWeights(pos)
        VTKKWRCHelper_InterpolateScalar(val)

        if (!maxValueDefined ||
          ((mapper->GetFlipMIPComparison() && val < maxValue) ||
            (!mapper->GetFlipMIPComparison() && val > maxValue)))
        {
          maxValue = val;
          maxIdx = maxValue;
          maxValueDefined = 1;
        }
      }
    }

    if (maxValueDefined)
    {
      VTKKWRCHelper_LookupColorMax(colorTable[0], scalarOpacityTable[0], maxIdx, imagePtr)
    }
    else
    {
      imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0;
    }

    VTKKWRCHelper_IncrementAndLoopEnd
  }
};

// This method is called when the interpolation type is linear, the
// data contains one component and scale != 1.0 or shift != 0.0. This
// means that we need to apply scale/shift in the inner loop to compute
// an unsigned short index value. In the inner loop we compute the eight cell
// vertex values (as unsigned short indices, if we have changed cells). We
// compute our weights within the cell according to our fractional position
// within the cell, and apply trilinear interpolation to compute the index.
// We find the maximum index along the ray, and then use this to look up a
// final color.
struct vtkFixedPointMIPHelperGenerateImageOneTrilinFunctor
{
  template <class TArray>
  void operator()(TArray* dataArray, int threadID, int threadCount,
    vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol))
  {
    auto data = vtk::DataArrayValueRange<1>(dataArray).begin();
    VTKKWRCHelper_InitializationAndLoopStartTrilin
    VTKKWRCHelper_InitializeMIPOneTrilin
    VTKKWRCHelper_SpaceLeapSetup

    int maxValueDefined = 0;
    unsigned short maxIdx = 0;
    for (k = 0; k < numSteps; k++)
    {
      if (k)
      {
        mapper->FixedPointIncrement(pos, dir);
      }

      VTKKWRCHelper_CroppingCheckTrilin(pos)
      VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, maxValueDefined, mapper->GetFlipMIPComparison())

      mapper->ShiftVectorDown(pos, spos);
      if (spos[0] != oldSPos[0] || spos[1] != oldSPos[1] || spos[2] != oldSPos[2])
      {
        oldSPos[0] = spos[0];
        oldSPos[1] = spos[1];
        oldSPos[2] = spos[2];

        dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2];
        VTKKWRCHelper_GetCellScalarValues(dptr, scale[0], shift[0])
      }

      VTKKWRCHelper_ComputeWeights(pos)
      VTKKWRCHelper_InterpolateScalar(val)

      if (!maxValueDefined ||
        ((mapper->GetFlipMIPComparison() && val < maxValue) ||
          (!mapper->GetFlipMIPComparison() && val > maxValue)))
      {
        maxValue = val;
        maxIdx = maxValue;
        maxValueDefined = 1;
      }
    }

    if (maxValueDefined)
    {
      VTKKWRCHelper_LookupColorMax(colorTable[0], scalarOpacityTable[0], maxIdx, imagePtr)
    }
    else
    {
      imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0;
    }

    VTKKWRCHelper_IncrementAndLoopEnd
  }
};

// This method is used when the interpolation type is linear, the data has
// two or four components and the components are not considered independent.
// For four component d>>(VTKKW_FP_SHIFT - 8));ata, the data must be unsigned char in type. In the
// inner loop we get the data value for the eight cell corners (if we have
// changed cells) for all components as unsigned shorts (we use the
// scale/shift to ensure the correct range). We compute our weights within
// the cell according to our fractional position within the cell, and apply
// trilinear interpolation to compute the index values. For two component data,
// We use the first index to lookup a color and the second to look up an opacity
// for this sample. For four component data we use the first three components
// directly as a color, then we look up the opacity using the fourth component.
// We then composite this into the color computed so far along the ray, and
// check if we can terminate at this point (if the accumulated opacity is
// higher than some threshold).
struct vtkFixedPointMIPHelperGenerateImageDependentTrilinFunctor
{
  template <class TArray>
  void operator()(TArray* dataArray, int threadID, int threadCount,
    vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol))
  {
    auto data = vtk::DataArrayValueRange(dataArray).begin();
    VTKKWRCHelper_InitializationAndLoopStartTrilin
    VTKKWRCHelper_InitializeMIPMultiTrilin
    VTKKWRCHelper_SpaceLeapSetup

    int maxValueDefined = 0;
    unsigned short maxIdx = 0;
    for (k = 0; k < numSteps; k++)
    {
      if (k)
      {
        mapper->FixedPointIncrement(pos, dir);
      }

      VTKKWRCHelper_CroppingCheckTrilin(pos)
      VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, maxValueDefined, mapper->GetFlipMIPComparison())

      mapper->ShiftVectorDown(pos, spos);
      if (spos[0] != oldSPos[0] || spos[1] != oldSPos[1] || spos[2] != oldSPos[2])
      {
        oldSPos[0] = spos[0];
        oldSPos[1] = spos[1];
        oldSPos[2] = spos[2];

        if (components == 2)
        {
          for (c = 0; c < components; c++)
          {
            dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2] + c;
            VTKKWRCHelper_GetCellComponentScalarValues(dptr, c, scale[c], shift[c])
          }
        }
        else
        {
          for (c = 0; c < 3; c++)
          {
            dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2] + c;
            VTKKWRCHelper_GetCellComponentRawScalarValues(dptr, c)
          }
          dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2] + c;
          VTKKWRCHelper_GetCellComponentScalarValues(dptr, 3, scale[3], shift[3])
        }
      }

      VTKKWRCHelper_ComputeWeights(pos)
      VTKKWRCHelper_InterpolateScalarComponent(val, c, components)

      if (!maxValueDefined ||
        ((mapper->GetFlipMIPComparison() && (val[components - 1] < maxValue[components - 1])) ||
          (!mapper->GetFlipMIPComparison() && (val[components - 1] > maxValue[components - 1]))))
      {
        for (c = 0; c < components; c++)
        {
          maxValue[c] = val[c];
        }
        maxIdx = static_cast<unsigned short>(
          (maxValue[components - 1] + shift[components - 1]) * scale[components - 1]);
        maxValueDefined = 1;
      }
    }

    if (maxValueDefined)
    {
      VTKKWRCHelper_LookupDependentColorUS(
        colorTable[0], scalarOpacityTable[0], maxValue, components, imagePtr)
    }
    else
    {
      imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0;
    }

    VTKKWRCHelper_IncrementAndLoopEnd
  }
};

// This method is used when the interpolation type is linear, the data has
// more than one component and the components are considered independent. In
// the inner loop we get the data value for the eight cell corners (if we have
// changed cells) for all components as an unsigned shorts (we have to use the
// scale/shift to ensure that we obtained unsigned short indices) We compute
// our weights within the cell according to our fractional position within the
// cell, and apply trilinear interpolation to compute a value for each
// component. We do this for each sample along the ray to find a maximum value
// per component, then we look up a color/opacity for each component and blend
// them according to the component weights.
struct vtkFixedPointMIPHelperGenerateImageIndependentTrilinFunctor
{
  template <class TArray>
  void operator()(TArray* dataArray, int threadID, int threadCount,
    vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vol)
  {
    auto data = vtk::DataArrayValueRange(dataArray).begin();
    VTKKWRCHelper_InitializeWeights
    VTKKWRCHelper_InitializationAndLoopStartTrilin
    VTKKWRCHelper_InitializeMIPMultiTrilin

    int maxValueDefined = 0;
    for (k = 0; k < numSteps; k++)
    {
      if (k)
      {
        mapper->FixedPointIncrement(pos, dir);
      }

      VTKKWRCHelper_CroppingCheckTrilin(pos)

      mapper->ShiftVectorDown(pos, spos);
      if (spos[0] != oldSPos[0] || spos[1] != oldSPos[1] || spos[2] != oldSPos[2])
      {
        oldSPos[0] = spos[0];
        oldSPos[1] = spos[1];
        oldSPos[2] = spos[2];

        for (c = 0; c < components; c++)
        {
          dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2] + c;
          VTKKWRCHelper_GetCellComponentScalarValues(dptr, c, scale[c], shift[c])
        }
      }

      VTKKWRCHelper_ComputeWeights(pos)
      VTKKWRCHelper_InterpolateScalarComponent(val, c, components)

      if (!maxValueDefined)
      {
        for (c = 0; c < components; c++)
        {
          maxValue[c] = val[c];
        }
        maxValueDefined = 1;
      }
      else
      {
        for (c = 0; c < components; c++)
        {
          if ((mapper->GetFlipMIPComparison() && val[c] < maxValue[c]) ||
            (!mapper->GetFlipMIPComparison() && val[c] > maxValue[c]))
          {
            maxValue[c] = val[c];
          }
        }
      }
    }

    imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0;
    if (maxValueDefined)
    {
      VTKKWRCHelper_LookupAndCombineIndependentColorsMax(
        colorTable, scalarOpacityTable, maxValue, weights, components, imagePtr)
    }

    VTKKWRCHelper_IncrementAndLoopEnd
  }
};

void vtkFixedPointVolumeRayCastMIPHelper::GenerateImage(
  int threadID, int threadCount, vtkVolume* vol, vtkFixedPointVolumeRayCastMapper* mapper)
{
  auto* dataArray = mapper->GetCurrentScalars();

  // Nearest Neighbor interpolate
  if (mapper->ShouldUseNearestNeighborInterpolation(vol))
  {
    // One component data
    if (mapper->GetCurrentScalars()->GetNumberOfComponents() == 1)
    {
      vtkFixedPointMIPHelperGenerateImageOneNNFunctor functor;
      if (!vtkArrayDispatch::Dispatch::Execute(
            dataArray, functor, threadID, threadCount, mapper, vol))
      {
        functor(dataArray, threadID, threadCount, mapper, vol);
      }
    }
    // More that one independent components
    else if (vol->GetProperty()->GetIndependentComponents())
    {
      vtkFixedPointMIPHelperGenerateImageIndependentNNFunctor functor;
      if (!vtkArrayDispatch::Dispatch::Execute(
            dataArray, functor, threadID, threadCount, mapper, vol))
      {
        functor(dataArray, threadID, threadCount, mapper, vol);
      }
    }
    // Dependent (color) components
    else
    {
      vtkFixedPointMIPHelperGenerateImageDependentNNFunctor functor;
      if (!vtkArrayDispatch::Dispatch::Execute(
            dataArray, functor, threadID, threadCount, mapper, vol))
      {
        functor(dataArray, threadID, threadCount, mapper, vol);
      }
    }
  }
  // Trilinear Interpolation
  else
  {
    // One component
    if (mapper->GetCurrentScalars()->GetNumberOfComponents() == 1)
    {
      // Scale == 1.0 and shift == 0.0 - simple case (faster)
      if (mapper->GetTableScale()[0] == 1.0 && mapper->GetTableShift()[0] == 0.0)
      {
        vtkFixedPointMIPHelperGenerateImageOneSimpleTrilinFunctor functor;
        if (!vtkArrayDispatch::Dispatch::Execute(
              dataArray, functor, threadID, threadCount, mapper, vol))
        {
          functor(dataArray, threadID, threadCount, mapper, vol);
        }
      }
      // Scale != 1.0 or shift != 0.0 - must apply scale/shift in inner loop
      else
      {
        vtkFixedPointMIPHelperGenerateImageOneTrilinFunctor functor;
        if (!vtkArrayDispatch::Dispatch::Execute(
              dataArray, functor, threadID, threadCount, mapper, vol))
        {
          functor(dataArray, threadID, threadCount, mapper, vol);
        }
      }
    }
    // Independent components (more than one)
    else if (vol->GetProperty()->GetIndependentComponents())
    {
      vtkFixedPointMIPHelperGenerateImageIndependentTrilinFunctor functor;
      if (!vtkArrayDispatch::Dispatch::Execute(
            dataArray, functor, threadID, threadCount, mapper, vol))
      {
        functor(dataArray, threadID, threadCount, mapper, vol);
      }
    }
    // Dependent components
    else
    {
      vtkFixedPointMIPHelperGenerateImageDependentTrilinFunctor functor;
      if (!vtkArrayDispatch::Dispatch::Execute(
            dataArray, functor, threadID, threadCount, mapper, vol))
      {
        functor(dataArray, threadID, threadCount, mapper, vol);
      }
    }
  }
}

// Print method for vtkFixedPointVolumeRayCastMIPHelper
void vtkFixedPointVolumeRayCastMIPHelper::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}
VTK_ABI_NAMESPACE_END
