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

#include "vtkActor.h"
#include "vtkBitArray.h"
#include "vtkBoundingBox.h"
#include "vtkCompositeDataDisplayAttributes.h"
#include "vtkCompositeDataIterator.h"
#include "vtkCompositeDataSet.h"
#include "vtkCompositeDataSetRange.h"
#include "vtkDataArray.h"
#include "vtkDataObjectTree.h"
#include "vtkDataObjectTreeIterator.h"
#include "vtkDataObjectTreeRange.h"
#include "vtkDataSetAttributes.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkLookupTable.h"
#include "vtkMath.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkSmartPointer.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkTimerLog.h"
#include "vtkTransform.h"
#include "vtkTrivialProducer.h"

#include <cassert>
#include <vector>

VTK_ABI_NAMESPACE_BEGIN
namespace
{
int getNumberOfChildren(vtkDataObjectTree* tree)
{
  int result = 0;
  if (tree)
  {
    vtkDataObjectTreeIterator* it = tree->NewTreeIterator();
    it->SetTraverseSubTree(false);
    it->SetVisitOnlyLeaves(false);
    for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextItem())
    {
      ++result;
    }
    it->Delete();
  }
  return result;
}
}

// Return nullptr if no override is supplied.
vtkObjectFactoryNewMacro(vtkGlyph3DMapper);
vtkCxxSetObjectMacro(vtkGlyph3DMapper, BlockAttributes, vtkCompositeDataDisplayAttributes);

//------------------------------------------------------------------------------
// Construct object with scaling on, scaling mode is by scalar value,
// scale factor = 1.0, the range is (0,1), orient geometry is on, and
// orientation is by vector. Clamping and indexing are turned off. No
// initial sources are defined.
vtkGlyph3DMapper::vtkGlyph3DMapper()
{
  this->SetNumberOfInputPorts(2);

  this->BlockAttributes = nullptr;
  this->Scaling = true;
  this->ScaleMode = NO_DATA_SCALING;
  this->ScaleFactor = 1.0;
  this->Range[0] = 0.0;
  this->Range[1] = 1.0;
  this->Orient = true;
  this->Clamping = false;
  this->SourceIndexing = false;
  this->UseSourceTableTree = false;
  this->UseSelectionIds = false;
  this->OrientationMode = vtkGlyph3DMapper::DIRECTION;

  // Set default arrays.
  this->SetScaleArray(vtkDataSetAttributes::SCALARS);
  this->SetMaskArray(vtkDataSetAttributes::SCALARS);
  this->SetOrientationArray(vtkDataSetAttributes::VECTORS);
  this->SetSelectionIdArray(vtkDataSetAttributes::SCALARS);

  this->Masking = false;
  this->SelectionColorId = 1;
}

//------------------------------------------------------------------------------
vtkGlyph3DMapper::~vtkGlyph3DMapper()
{
  this->SetBlockAttributes(nullptr);
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetMaskArray(int fieldAttributeType)
{
  this->SetInputArrayToProcess(
    vtkGlyph3DMapper::MASK, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, fieldAttributeType);
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetMaskArray(const char* maskarrayname)
{
  this->SetInputArrayToProcess(
    vtkGlyph3DMapper::MASK, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, maskarrayname);
}

//------------------------------------------------------------------------------
vtkDataArray* vtkGlyph3DMapper::GetMaskArray(vtkDataSet* input)
{
  if (this->Masking)
  {
    int association = vtkDataObject::FIELD_ASSOCIATION_POINTS;
    return this->GetInputArrayToProcess(vtkGlyph3DMapper::MASK, input, association);
  }
  return nullptr;
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetOrientationArray(const char* orientationarrayname)
{
  this->SetInputArrayToProcess(vtkGlyph3DMapper::ORIENTATION, 0, 0,
    vtkDataObject::FIELD_ASSOCIATION_POINTS, orientationarrayname);
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetOrientationArray(int fieldAttributeType)
{
  this->SetInputArrayToProcess(vtkGlyph3DMapper::ORIENTATION, 0, 0,
    vtkDataObject::FIELD_ASSOCIATION_POINTS, fieldAttributeType);
}

//------------------------------------------------------------------------------
vtkDataArray* vtkGlyph3DMapper::GetOrientationArray(vtkDataSet* input)
{
  if (this->Orient)
  {
    int association = vtkDataObject::FIELD_ASSOCIATION_POINTS;
    return this->GetInputArrayToProcess(vtkGlyph3DMapper::ORIENTATION, input, association);
  }
  return nullptr;
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetScaleArray(const char* scalarsarrayname)
{
  this->SetInputArrayToProcess(
    vtkGlyph3DMapper::SCALE, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, scalarsarrayname);
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetScaleArray(int fieldAttributeType)
{
  this->SetInputArrayToProcess(
    vtkGlyph3DMapper::SCALE, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, fieldAttributeType);
}

//------------------------------------------------------------------------------
vtkDataArray* vtkGlyph3DMapper::GetScaleArray(vtkDataSet* input)
{
  if (this->Scaling && this->ScaleMode != vtkGlyph3DMapper::NO_DATA_SCALING)
  {
    int association = vtkDataObject::FIELD_ASSOCIATION_POINTS;
    vtkDataArray* arr = this->GetInputArrayToProcess(vtkGlyph3DMapper::SCALE, input, association);
    return arr;
  }
  return nullptr;
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetSourceIndexArray(const char* arrayname)
{
  this->SetInputArrayToProcess(
    vtkGlyph3DMapper::SOURCE_INDEX, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, arrayname);
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetSourceIndexArray(int fieldAttributeType)
{
  this->SetInputArrayToProcess(vtkGlyph3DMapper::SOURCE_INDEX, 0, 0,
    vtkDataObject::FIELD_ASSOCIATION_POINTS, fieldAttributeType);
}

//------------------------------------------------------------------------------
vtkDataArray* vtkGlyph3DMapper::GetSourceIndexArray(vtkDataSet* input)
{
  if (this->SourceIndexing)
  {
    int association = vtkDataObject::FIELD_ASSOCIATION_POINTS;
    return this->GetInputArrayToProcess(vtkGlyph3DMapper::SOURCE_INDEX, input, association);
  }
  return nullptr;
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetSelectionIdArray(const char* selectionIdArrayName)
{
  this->SetInputArrayToProcess(vtkGlyph3DMapper::SELECTIONID, 0, 0,
    vtkDataObject::FIELD_ASSOCIATION_POINTS, selectionIdArrayName);
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetSelectionIdArray(int fieldAttributeType)
{
  this->SetInputArrayToProcess(vtkGlyph3DMapper::SELECTIONID, 0, 0,
    vtkDataObject::FIELD_ASSOCIATION_POINTS, fieldAttributeType);
}

//------------------------------------------------------------------------------
vtkDataArray* vtkGlyph3DMapper::GetSelectionIdArray(vtkDataSet* input)
{
  if (this->UseSelectionIds)
  {
    int association = vtkDataObject::FIELD_ASSOCIATION_POINTS;
    vtkDataArray* arr =
      this->GetInputArrayToProcess(vtkGlyph3DMapper::SELECTIONID, input, association);
    return arr;
  }
  return nullptr;
}

//------------------------------------------------------------------------------
vtkUnsignedCharArray* vtkGlyph3DMapper::GetColors(vtkDataSet* input)
{
  return vtkArrayDownCast<vtkUnsignedCharArray>(input->GetPointData()->GetScalars());
}

//------------------------------------------------------------------------------
// Specify a source object at a specified table location.
void vtkGlyph3DMapper::SetSourceConnection(int idx, vtkAlgorithmOutput* algOutput)
{
  if (idx < 0)
  {
    vtkErrorMacro("Bad index " << idx << " for source.");
    return;
  }

  int numConnections = this->GetNumberOfInputConnections(1);
  if (idx < numConnections)
  {
    this->SetNthInputConnection(1, idx, algOutput);
  }
  else if (idx == numConnections && algOutput)
  {
    this->AddInputConnection(1, algOutput);
  }
  else if (algOutput)
  {
    vtkWarningMacro("The source id provided is larger than the maximum "
                    "source id, using "
      << numConnections << " instead.");
    this->AddInputConnection(1, algOutput);
  }
}

//------------------------------------------------------------------------------
// Specify a source object at a specified table location.
void vtkGlyph3DMapper::SetSourceData(int idx, vtkPolyData* pd)
{
  int numConnections = this->GetNumberOfInputConnections(1);

  if (idx < 0 || idx > numConnections)
  {
    vtkErrorMacro("Bad index " << idx << " for source.");
    return;
  }

  vtkTrivialProducer* tp = nullptr;
  if (pd)
  {
    tp = vtkTrivialProducer::New();
    tp->SetOutput(pd);
  }

  if (idx < numConnections)
  {
    if (tp)
    {
      this->SetNthInputConnection(1, idx, tp->GetOutputPort());
    }
    else
    {
      this->SetNthInputConnection(1, idx, nullptr);
    }
  }
  else if (idx == numConnections && tp)
  {
    this->AddInputConnection(1, tp->GetOutputPort());
  }

  if (tp)
  {
    tp->Delete();
  }
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetSourceTableTree(vtkDataObjectTree* tree)
{
  vtkNew<vtkTrivialProducer> tp;
  tp->SetOutput(tree);
  this->SetNumberOfInputConnections(1, 1);
  this->SetInputConnection(1, tp->GetOutputPort());
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetSourceData(vtkPolyData* pd)
{
  this->SetSourceData(0, pd);
}

//------------------------------------------------------------------------------
// Get a pointer to a source object at a specified table location.
vtkPolyData* vtkGlyph3DMapper::GetSource(int idx)
{
  if (idx < 0 || idx >= this->GetNumberOfInputConnections(1))
  {
    return nullptr;
  }

  return vtkPolyData::SafeDownCast(this->GetExecutive()->GetInputData(1, idx));
}

//------------------------------------------------------------------------------
vtkDataObjectTree* vtkGlyph3DMapper::GetSourceTableTree()
{
  return this->UseSourceTableTree
    ? vtkDataObjectTree::SafeDownCast(this->GetExecutive()->GetInputData(1, 0))
    : nullptr;
}

//------------------------------------------------------------------------------
vtkPolyData* vtkGlyph3DMapper::GetSource(int idx, vtkInformationVector* sourceInfo)
{
  vtkInformation* info = sourceInfo->GetInformationObject(idx);
  if (!info)
  {
    return nullptr;
  }
  return vtkPolyData::SafeDownCast(info->Get(vtkDataObject::DATA_OBJECT()));
}

//------------------------------------------------------------------------------
const char* vtkGlyph3DMapper::GetOrientationModeAsString()
{
  switch (this->OrientationMode)
  {
    case vtkGlyph3DMapper::DIRECTION:
      return "Direction";
    case vtkGlyph3DMapper::ROTATION:
      return "Rotation";
    case vtkGlyph3DMapper::QUATERNION:
      return "Quaternion";
  }
  return "Invalid";
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  if (this->UseSourceTableTree)
  {
    if (this->GetNumberOfInputConnections(1) < 2)
    {
      if (this->GetSource(0) != nullptr)
      {
        os << indent << "Source: (" << this->GetSource(0) << ")\n";
      }
      else
      {
        os << indent << "Source: (none)\n";
      }
    }
    else
    {
      os << indent << "A table of " << this->GetNumberOfInputConnections(1)
         << " glyphs has been defined\n";
    }
  }
  else
  {
    os << indent << "SourceTableTree: (" << this->GetSourceTableTree() << ")\n";
  }

  os << indent << "Scaling: " << (this->Scaling ? "On\n" : "Off\n");

  os << indent << "Scale Mode: " << this->GetScaleModeAsString() << endl;
  os << indent << "Scale Factor: " << this->ScaleFactor << "\n";
  os << indent << "Clamping: " << (this->Clamping ? "On\n" : "Off\n");
  os << indent << "Range: (" << this->Range[0] << ", " << this->Range[1] << ")\n";
  os << indent << "Orient: " << (this->Orient ? "On\n" : "Off\n");
  os << indent << "OrientationMode: " << this->GetOrientationModeAsString() << "\n";
  os << indent << "SourceIndexing: " << (this->SourceIndexing ? "On" : "Off") << endl;
  os << indent << "UseSourceTableTree: " << (this->UseSourceTableTree ? "On" : "Off") << endl;
  os << indent << "UseSelectionIds: " << (this->UseSelectionIds ? "On" : "Off") << endl;
  os << indent << "SelectionColorId: " << this->SelectionColorId << endl;
  os << indent << "Masking: " << (this->Masking ? "On" : "Off") << endl;
  os << indent << "BlockAttributes: (" << this->BlockAttributes << ")" << endl;
  if (this->BlockAttributes)
  {
    this->BlockAttributes->PrintSelf(os, indent.GetNextIndent());
  }
}

//------------------------------------------------------------------------------
int vtkGlyph3DMapper::RequestUpdateExtent(
  vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector*)
{
  // get the info objects
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
  vtkInformation* sourceInfo = inputVector[1]->GetInformationObject(0);

  if (sourceInfo)
  {
    sourceInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), 0);
    sourceInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(), 1);
    sourceInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(), 0);
  }
  inInfo->Set(vtkStreamingDemandDrivenPipeline::EXACT_EXTENT(), 1);

  return 1;
}

//------------------------------------------------------------------------------
int vtkGlyph3DMapper::FillInputPortInformation(int port, vtkInformation* info)
{
  if (port == 0)
  {
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
    info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkCompositeDataSet");
    return 1;
  }
  else if (port == 1)
  {
    info->Set(vtkAlgorithm::INPUT_IS_REPEATABLE(), 1);
    info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
    info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataObjectTree");
    info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
    return 1;
  }
  return 0;
}

//------------------------------------------------------------------------------
// Description:
// Return the method of scaling as a descriptive character string.
const char* vtkGlyph3DMapper::GetScaleModeAsString()
{
  if (this->ScaleMode == SCALE_BY_MAGNITUDE)
  {
    return "ScaleByMagnitude";
  }
  else if (this->ScaleMode == SCALE_BY_COMPONENTS)
  {
    return "ScaleByVectorComponents";
  }

  return "NoDataScaling";
}

//------------------------------------------------------------------------------
bool vtkGlyph3DMapper::GetBoundsInternal(vtkDataSet* ds, double ds_bounds[6])
{
  if (ds == nullptr)
  {
    return false;
  }

  ds->GetBounds(ds_bounds);
  // if there is nothing inside the scene, just return uninitializedBounds
  if ((ds_bounds[0] > ds_bounds[1]) && (ds_bounds[2] > ds_bounds[3]) &&
    (ds_bounds[4] > ds_bounds[5]))
  {
    return false;
  }
  // if the input is not conform to what the mapper expects (use vector
  // but no vector data), nothing will be mapped.
  // It make sense to return uninitialized bounds.

  vtkDataArray* scaleArray = this->GetScaleArray(ds);
  vtkDataArray* orientArray = this->GetOrientationArray(ds);
  // TODO:
  // 1. cumulative bbox of all the glyph
  // 2. scale it by scale factor and maximum scalar value (or vector mag)
  // 3. enlarge the input bbox half-way in each direction with the
  // glyphs bbox.

  double den = this->Range[1] - this->Range[0];
  if (den == 0.0)
  {
    den = 1.0;
  }

  if (!this->UseSourceTableTree && this->GetSource(0) == nullptr)
  {
    vtkPolyData* defaultSource = vtkPolyData::New();
    defaultSource->AllocateEstimate(0, 0, 1, 2, 0, 0, 0, 0);
    vtkPoints* defaultPoints = vtkPoints::New();
    defaultPoints->Allocate(6);
    defaultPoints->InsertNextPoint(0, 0, 0);
    defaultPoints->InsertNextPoint(1, 0, 0);
    vtkIdType defaultPointIds[2];
    defaultPointIds[0] = 0;
    defaultPointIds[1] = 1;
    defaultSource->SetPoints(defaultPoints);
    defaultSource->InsertNextCell(VTK_LINE, 2, defaultPointIds);
    defaultSource->Delete();
    defaultSource = nullptr;
    defaultPoints->Delete();
    defaultPoints = nullptr;
  }

  // FB

  // Compute indexRange.
  vtkDataObjectTree* sourceTableTree = this->GetSourceTableTree();
  int numberOfSources = this->UseSourceTableTree ? getNumberOfChildren(sourceTableTree)
                                                 : this->GetNumberOfInputConnections(1);

  if (numberOfSources < 1)
  {
    return true; // just return the dataset bounds.
  }

  vtkPointData* pd = ds->GetPointData();

  int indexRange[2] = { 0, 0 };
  vtkDataArray* indexArray = this->GetSourceIndexArray(ds);
  if (indexArray)
  {
    double range[2];
    pd->GetRange(indexArray->GetName(), range, -1);
    for (int i = 0; i < 2; i++)
    {
      indexRange[i] = static_cast<int>((range[i] - this->Range[0]) * numberOfSources / den);
      indexRange[i] = vtkMath::ClampValue(indexRange[i], 0, numberOfSources - 1);
    }
  }

  vtkBoundingBox bbox; // empty

  double xScaleRange[2] = { 1.0, 1.0 };
  double yScaleRange[2] = { 1.0, 1.0 };
  double zScaleRange[2] = { 1.0, 1.0 };

  if (scaleArray)
  {
    switch (this->ScaleMode)
    {
      case SCALE_BY_MAGNITUDE:
        pd->GetRange(scaleArray->GetName(), xScaleRange, -1);
        yScaleRange[0] = xScaleRange[0];
        yScaleRange[1] = xScaleRange[1];
        zScaleRange[0] = xScaleRange[0];
        zScaleRange[1] = xScaleRange[1];
        break;

      case SCALE_BY_COMPONENTS:
        pd->GetRange(scaleArray->GetName(), xScaleRange, 0);
        pd->GetRange(scaleArray->GetName(), yScaleRange, 1);
        pd->GetRange(scaleArray->GetName(), zScaleRange, 2);
        break;

      default:
        // NO_DATA_SCALING: do nothing, set variables to avoid warnings.
        break;
    }

    if (this->Clamping && this->ScaleMode != NO_DATA_SCALING)
    {
      xScaleRange[0] = vtkMath::ClampAndNormalizeValue(xScaleRange[0], this->Range);
      xScaleRange[1] = vtkMath::ClampAndNormalizeValue(xScaleRange[1], this->Range);
      yScaleRange[0] = vtkMath::ClampAndNormalizeValue(yScaleRange[0], this->Range);
      yScaleRange[1] = vtkMath::ClampAndNormalizeValue(yScaleRange[1], this->Range);
      zScaleRange[0] = vtkMath::ClampAndNormalizeValue(zScaleRange[0], this->Range);
      zScaleRange[1] = vtkMath::ClampAndNormalizeValue(zScaleRange[1], this->Range);
    }
  }

  if (this->UseSourceTableTree)
  {
    if (sourceTableTree)
    {
      auto sTTRange = vtk::Range(sourceTableTree);
      auto sTTIter = sTTRange.begin();

      // Advance to first indexed dataset:
      int idx = indexRange[0];
      std::advance(sTTIter, idx);

      // Add the bounds from the appropriate datasets:
      while (idx <= indexRange[1])
      {
        vtkDataObject* sourceDObj = *sTTIter;

        // The source table tree may have composite nodes:
        vtkCompositeDataSet* sourceCDS = vtkCompositeDataSet::SafeDownCast(sourceDObj);
        vtkCompositeDataIterator* sourceIter = nullptr;
        if (sourceCDS)
        {
          sourceIter = sourceCDS->NewIterator();
          sourceIter->SetSkipEmptyNodes(true);
          sourceIter->InitTraversal();
        }

        // Or, it may just have polydata:
        vtkPolyData* sourcePD = vtkPolyData::SafeDownCast(sourceDObj);

        for (;;)
        {
          // Extract the polydata from the composite dataset if it exists:
          if (sourceIter)
          {
            sourcePD = vtkPolyData::SafeDownCast(sourceIter->GetCurrentDataObject());
          }

          // Get the bounds of the current dataset:
          if (sourcePD)
          {
            double bounds[6];
            sourcePD->GetCellsBounds(bounds);
            if (vtkMath::AreBoundsInitialized(bounds))
            {
              bbox.AddBounds(bounds);
            }
          }

          // Advance the composite source iterator if it exists:
          if (sourceIter)
          {
            sourceIter->GoToNextItem();
          }

          // If the sourceDObj is not composite, or we've exhausted the
          // iterator, break the loop.
          if (!sourceIter || sourceIter->IsDoneWithTraversal())
          {
            break;
          }
        }

        if (sourceIter)
        {
          sourceIter->Delete();
          sourceIter = nullptr;
        }

        // Move to the next node in the source table tree.
        ++sTTIter;
        ++idx;
      }
    }
  }
  else // non-source-table-tree table
  {
    int index = indexRange[0];
    while (index <= indexRange[1])
    {
      vtkPolyData* source = this->GetSource(index);
      // Make sure we're not indexing into empty glyph
      if (source != nullptr)
      {
        double bounds[6];
        source->GetCellsBounds(bounds); // can be invalid/uninitialized
        if (vtkMath::AreBoundsInitialized(bounds))
        {
          bbox.AddBounds(bounds);
        }
      }
      ++index;
    }
  }

  if (this->Scaling)
  {
    vtkBoundingBox bbox2(bbox);
    bbox.Scale(xScaleRange[0], yScaleRange[0], zScaleRange[0]);
    bbox2.Scale(xScaleRange[1], yScaleRange[1], zScaleRange[1]);
    bbox.AddBox(bbox2);
    bbox.Scale(this->ScaleFactor, this->ScaleFactor, this->ScaleFactor);
  }

  if (bbox.IsValid())
  {
    double bounds[6];
    if (orientArray)
    {
      vtkBoundingBox bbox2(bbox);
      bbox2.Scale(-1.0, -1.0, -1.0);
      bbox.AddBox(bbox2);
      // bounding sphere.
      double l = bbox.GetDiagonalLength() / 2.0;
      bounds[0] = -l;
      bounds[1] = l;
      bounds[2] = -l;
      bounds[3] = l;
      bounds[4] = -l;
      bounds[5] = l;
    }
    else
    {
      bbox.GetBounds(bounds);
    }
    int j = 0;
    while (j < 6)
    {
      ds_bounds[j] += bounds[j];
      ++j;
    }
  }
  else
  {
    return false;
  }

  return true;
}

//------------------------------------------------------------------------------
double* vtkGlyph3DMapper::GetBounds()
{
  //  static double bounds[] = {-1.0,1.0, -1.0,1.0, -1.0,1.0};
  vtkMath::UninitializeBounds(this->Bounds);

  // do we have an input
  if (!this->GetNumberOfInputConnections(0))
  {
    return this->Bounds;
  }
  if (!this->Static)
  {
    // For proper clipping, this would be this->Piece,
    // this->NumberOfPieces.
    // But that removes all benefites of streaming.
    // Update everything as a hack for paraview streaming.
    // This should not affect anything else, because no one uses this.
    // It should also render just the same.
    // Just remove this lie if we no longer need streaming in paraview :)

    // first get the bounds from the input
    this->Update();
  }

  vtkDataObject* dobj = this->GetInputDataObject(0, 0);
  vtkDataSet* ds = vtkDataSet::SafeDownCast(dobj);
  if (ds)
  {
    this->GetBoundsInternal(ds, this->Bounds);
    return this->Bounds;
  }

  vtkCompositeDataSet* cd = vtkCompositeDataSet::SafeDownCast(dobj);
  if (!cd)
  {
    return this->Bounds;
  }

  vtkBoundingBox bbox;

  using Opts = vtk::CompositeDataSetOptions;
  for (vtkDataObject* dObj : vtk::Range(cd, Opts::SkipEmptyNodes))
  {
    ds = vtkDataSet::SafeDownCast(dObj);
    if (ds)
    {
      double tmpBounds[6];
      this->GetBoundsInternal(ds, tmpBounds);
      bbox.AddBounds(tmpBounds);
    }
  }
  bbox.GetBounds(this->Bounds);

  return this->Bounds;
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::GetBounds(double bounds[6])
{
  this->Superclass::GetBounds(bounds);
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::Render(vtkRenderer*, vtkActor*)
{
  cerr << "Calling wrong render method!!\n";
}

//------------------------------------------------------------------------------
void vtkGlyph3DMapper::SetInputData(vtkDataObject* input)
{
  this->SetInputDataInternal(0, input);
}

//------------------------------------------------------------------------------
vtkIdType vtkGlyph3DMapper::GetMaxNumberOfLOD()
{
  return 0;
}
VTK_ABI_NAMESPACE_END
