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

#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkColor.h"
#include "vtkColorTransferFunction.h"
#include "vtkCoordinate.h"
#include "vtkFloatArray.h"
#include "vtkImageData.h"
#include "vtkLookupTable.h"
#include "vtkMath.h"
#include "vtkMathTextUtilities.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkPiecewiseFunction.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper2D.h"
#include "vtkProperty2D.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkScalarsToColors.h"
#include "vtkSmartPointer.h"
#include "vtkTextActor.h"
#include "vtkTextProperty.h"
#include "vtkTexture.h"
#include "vtkTexturedActor2D.h"
#include "vtkViewport.h"
#include "vtkWindow.h"

#include <map>
#include <vector>

#include <cstdio> // for snprintf

#undef VTK_DBG_LAYOUT

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkScalarBarActor);

vtkCxxSetObjectMacro(vtkScalarBarActor, LookupTable, vtkScalarsToColors);
vtkCxxSetObjectMacro(vtkScalarBarActor, OpacityFunction, vtkPiecewiseFunction);
vtkCxxSetObjectMacro(vtkScalarBarActor, AnnotationTextProperty, vtkTextProperty);
vtkCxxSetObjectMacro(vtkScalarBarActor, LabelTextProperty, vtkTextProperty);
vtkCxxSetObjectMacro(vtkScalarBarActor, TitleTextProperty, vtkTextProperty);
vtkCxxSetObjectMacro(vtkScalarBarActor, BackgroundProperty, vtkProperty2D);
vtkCxxSetObjectMacro(vtkScalarBarActor, FrameProperty, vtkProperty2D);
vtkCxxSetObjectMacro(vtkScalarBarActor, CustomLabels, vtkDoubleArray);

//------------------------------------------------------------------------------
// Instantiate object with 64 maximum colors; 5 labels; %%-#6.3g label
// format, no title, and vertical orientation. The initial scalar bar
// size is (0.05 x 0.8) of the viewport size.
vtkScalarBarActor::vtkScalarBarActor()
{
  this->P = new vtkScalarBarActorInternal;
  this->LookupTable = nullptr;
  this->OpacityFunction = nullptr;
  this->Position2Coordinate->SetValue(0.17, 0.8);

  this->PositionCoordinate->SetCoordinateSystemToNormalizedViewport();
  this->PositionCoordinate->SetValue(0.82, 0.1);

  this->TextPad = 1;
  this->TitleRatio = 0.5;
  this->BarRatio = 0.375;
  this->MaximumNumberOfColors = 64;
  this->NumberOfLabels = 5;
  this->NumberOfLabelsBuilt = 0;
  this->Orientation = VTK_ORIENT_VERTICAL;
  this->Title = nullptr;
  this->ComponentTitle = nullptr;
  this->VerticalTitleSeparation = 0;

  this->LabelTextProperty = vtkTextProperty::New();
  this->LabelTextProperty->SetFontSize(12);
  this->LabelTextProperty->SetBold(1);
  this->LabelTextProperty->SetItalic(1);
  this->LabelTextProperty->SetShadow(1);
  this->LabelTextProperty->SetFontFamilyToArial();

  this->AnnotationTextProperty = vtkTextProperty::New();
  this->AnnotationTextProperty->SetFontSize(12);
  this->AnnotationTextProperty->SetBold(1);
  this->AnnotationTextProperty->SetItalic(1);
  this->AnnotationTextProperty->SetShadow(1);
  this->AnnotationTextProperty->SetFontFamilyToArial();

  this->TitleTextProperty = vtkTextProperty::New();
  this->TitleTextProperty->ShallowCopy(this->LabelTextProperty);

  this->LabelFormat = new char[8];
  snprintf(this->LabelFormat, 8, "%s", "%-#6.3g");

  this->TitleActor = vtkTextActor::New();
  this->TitleActor->GetPositionCoordinate()->SetReferenceCoordinate(this->PositionCoordinate);

  this->ScalarBar = vtkPolyData::New();
  this->ScalarBarMapper = vtkPolyDataMapper2D::New();
  this->ScalarBarMapper->SetInputData(this->ScalarBar);
  this->ScalarBarActor = vtkActor2D::New();
  this->ScalarBarActor->SetMapper(this->ScalarBarMapper);
  this->ScalarBarActor->GetPositionCoordinate()->SetReferenceCoordinate(this->PositionCoordinate);
  this->LastOrigin[0] = 0;
  this->LastOrigin[1] = 0;
  this->LastSize[0] = 0;
  this->LastSize[1] = 0;

  this->DrawAnnotations = 1;
  this->DrawNanAnnotation = 0;
  this->AnnotationTextScaling = 0;
  this->FixedAnnotationLeaderLineColor = 0;
  this->NanAnnotation = nullptr;
  this->SetNanAnnotation("NaN");
  this->P->NanSwatch = vtkPolyData::New();
  this->P->NanSwatchMapper = vtkPolyDataMapper2D::New();
  this->P->NanSwatchActor = vtkActor2D::New();
  this->P->NanSwatchMapper->SetInputData(this->P->NanSwatch);
  this->P->NanSwatchActor->SetMapper(this->P->NanSwatchMapper);
  this->P->NanSwatchActor->GetPositionCoordinate()->SetReferenceCoordinate(
    this->PositionCoordinate);

  this->DrawBelowRangeSwatch = false;
  this->BelowRangeAnnotation = nullptr;
  this->SetBelowRangeAnnotation("Below");
  this->P->BelowRangeSwatch = vtkPolyData::New();
  this->P->BelowRangeSwatchMapper = vtkPolyDataMapper2D::New();
  this->P->BelowRangeSwatchActor = vtkActor2D::New();
  this->P->BelowRangeSwatchMapper->SetInputData(this->P->BelowRangeSwatch);
  this->P->BelowRangeSwatchActor->SetMapper(this->P->BelowRangeSwatchMapper);
  this->P->BelowRangeSwatchActor->GetPositionCoordinate()->SetReferenceCoordinate(
    this->PositionCoordinate);

  this->DrawAboveRangeSwatch = false;
  this->AboveRangeAnnotation = nullptr;
  this->SetAboveRangeAnnotation("Above");
  this->P->AboveRangeSwatch = vtkPolyData::New();
  this->P->AboveRangeSwatchMapper = vtkPolyDataMapper2D::New();
  this->P->AboveRangeSwatchActor = vtkActor2D::New();
  this->P->AboveRangeSwatchMapper->SetInputData(this->P->AboveRangeSwatch);
  this->P->AboveRangeSwatchActor->SetMapper(this->P->AboveRangeSwatchMapper);
  this->P->AboveRangeSwatchActor->GetPositionCoordinate()->SetReferenceCoordinate(
    this->PositionCoordinate);

  this->AnnotationLeaderPadding = 8.;
  this->P->AnnotationBoxes = vtkPolyData::New();
  this->P->AnnotationBoxesMapper = vtkPolyDataMapper2D::New();
  this->P->AnnotationBoxesActor = vtkActor2D::New();
  this->P->AnnotationBoxesMapper->SetInputData(this->P->AnnotationBoxes);
  this->P->AnnotationBoxesActor->SetMapper(this->P->AnnotationBoxesMapper);
  this->P->AnnotationBoxesActor->GetPositionCoordinate()->SetReferenceCoordinate(
    this->PositionCoordinate);
  this->P->AnnotationLeaders = vtkPolyData::New();
  this->P->AnnotationLeadersMapper = vtkPolyDataMapper2D::New();
  this->P->AnnotationLeadersActor = vtkActor2D::New();
  this->P->AnnotationLeadersMapper->SetInputData(this->P->AnnotationLeaders);
  this->P->AnnotationLeadersActor->SetMapper(this->P->AnnotationLeadersMapper);
  this->P->AnnotationLeadersActor->GetPositionCoordinate()->SetReferenceCoordinate(
    this->PositionCoordinate);

  this->P->TitleBox.Posn[0] = 0;
  this->P->TitleBox.Posn[1] = 0;
  this->P->TitleBox.Size[0] = 0;
  this->P->TitleBox.Size[1] = 0;

  // If opacity is on, a jail like texture is displayed behind it..

  this->UseOpacity = 0;
  this->TextureGridWidth = 10.0;

  this->TexturePolyData = vtkPolyData::New();
  vtkPolyDataMapper2D* textureMapper = vtkPolyDataMapper2D::New();
  textureMapper->SetInputData(this->TexturePolyData);
  this->TextureActor = vtkTexturedActor2D::New();
  this->TextureActor->SetMapper(textureMapper);
  textureMapper->Delete();
  this->TextureActor->GetPositionCoordinate()->SetReferenceCoordinate(this->PositionCoordinate);
  vtkFloatArray* tc = vtkFloatArray::New();
  tc->SetNumberOfComponents(2);
  tc->SetNumberOfTuples(4);
  tc->InsertComponent(0, 0, 0.0);
  tc->InsertComponent(0, 1, 0.0);
  tc->InsertComponent(1, 1, 0.0);
  tc->InsertComponent(3, 0, 0.0);
  this->TexturePolyData->GetPointData()->SetTCoords(tc);
  tc->Delete();

  vtkCellArray* polys2 = vtkCellArray::New();
  polys2->InsertNextCell(4);
  polys2->InsertCellPoint(0);
  polys2->InsertCellPoint(1);
  polys2->InsertCellPoint(2);
  polys2->InsertCellPoint(3);
  polys2->Delete();

  vtkProperty2D* imageProperty = vtkProperty2D::New();
  imageProperty->SetOpacity(0.08);
  this->TextureActor->SetProperty(imageProperty);
  imageProperty->Delete();

  // Create the default texture. Just a "Jail" like grid

  const unsigned int dim = 128;
  vtkImageData* image = vtkImageData::New();
  image->SetDimensions(dim, dim, 1);
  image->AllocateScalars(VTK_UNSIGNED_CHAR, 1);

  for (unsigned int y = 0; y < dim; y++)
  {
    unsigned char* ptr = static_cast<unsigned char*>(image->GetScalarPointer(0, y, 0));
    for (unsigned int x = 0; x < dim; x++)
    {
      *ptr = ((x == y) || (x == (dim - y - 1))) ? 255 : 0;
      ++ptr;
    }
  }

  this->Texture = vtkTexture::New();
  this->Texture->SetInputData(image);
  this->Texture->RepeatOn();
  this->TextureActor->SetTexture(this->Texture);
  image->Delete();

  // Default text position : Above scalar bar if orientation is horizontal
  //                         Right of scalar bar if orientation is vertical
  this->TextPosition = SucceedScalarBar;

  this->MaximumWidthInPixels = VTK_INT_MAX;
  this->MaximumHeightInPixels = VTK_INT_MAX;

  this->BackgroundProperty = vtkProperty2D::New();
  this->FrameProperty = vtkProperty2D::New();

  this->DrawBackground = 0;
  this->Background = vtkPolyData::New();
  this->BackgroundMapper = vtkPolyDataMapper2D::New();
  this->BackgroundMapper->SetInputData(this->Background);
  this->BackgroundActor = vtkActor2D::New();
  this->BackgroundActor->SetMapper(this->BackgroundMapper);
  this->BackgroundActor->GetPositionCoordinate()->SetReferenceCoordinate(this->PositionCoordinate);

#ifdef VTK_DBG_LAYOUT
  this->DrawFrame = 1;
#else  // VTK_DBG_LAYOUT
  this->DrawFrame = 0;
#endif // VTK_DBG_LAYOUT
  this->Frame = vtkPolyData::New();
  this->FrameMapper = vtkPolyDataMapper2D::New();
  this->FrameMapper->SetInputData(this->Frame);
  this->FrameActor = vtkActor2D::New();
  this->FrameActor->SetMapper(this->FrameMapper);
  this->FrameActor->GetPositionCoordinate()->SetReferenceCoordinate(this->PositionCoordinate);

  this->DrawColorBar = 1;
  this->DrawTickLabels = 1;
  this->UnconstrainedFontSize = false;

  this->ForceVerticalTitle = false;
}

//------------------------------------------------------------------------------
// Release any graphics resources that are being consumed by this actor.
// The parameter window could be used to determine which graphic
// resources to release.
void vtkScalarBarActor::ReleaseGraphicsResources(vtkWindow* win)
{
  this->TitleActor->ReleaseGraphicsResources(win);
  if (!this->P->TextActors.empty())
  {
    vtkScalarBarActorInternal::ActorVector::iterator it;
    for (it = this->P->TextActors.begin(); it != this->P->TextActors.end(); ++it)
    {
      (*it)->ReleaseGraphicsResources(win);
    }
  }
  for (size_t i = 0; i < this->P->AnnotationLabels.size(); ++i)
  {
    this->P->AnnotationLabels[i]->ReleaseGraphicsResources(win);
  }
  this->ScalarBarActor->ReleaseGraphicsResources(win);
  this->P->NanSwatchActor->ReleaseGraphicsResources(win);
  this->P->AboveRangeSwatchActor->ReleaseGraphicsResources(win);
  this->P->BelowRangeSwatchActor->ReleaseGraphicsResources(win);
  this->P->AnnotationBoxesActor->ReleaseGraphicsResources(win);
  this->P->AnnotationLeadersActor->ReleaseGraphicsResources(win);
  this->BackgroundActor->ReleaseGraphicsResources(win);
  this->FrameActor->ReleaseGraphicsResources(win);
  this->Texture->ReleaseGraphicsResources(win);
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::GetScalarBarRect(int rect[4], vtkViewport* viewport)
{
  vtkCoordinate* origin = this->ScalarBarActor->GetPositionCoordinate();
  int* vpPos = origin->GetComputedViewportValue(viewport);
  rect[0] = vpPos[0];
  rect[1] = vpPos[1];

  const double* bounds = this->ScalarBar->GetBounds();
  rect[0] += static_cast<int>(bounds[0] + 0.5);
  rect[1] += static_cast<int>(bounds[2] + 0.5);
  rect[2] = static_cast<int>(bounds[1] - bounds[0] + 0.5);
  rect[3] = static_cast<int>(bounds[3] - bounds[2] + 0.5);
}

//------------------------------------------------------------------------------
vtkScalarBarActor::~vtkScalarBarActor()
{
  delete[] this->LabelFormat;
  this->LabelFormat = nullptr;

  this->TitleActor->Delete();
  this->SetNanAnnotation(nullptr);
  this->SetBelowRangeAnnotation(nullptr);
  this->SetAboveRangeAnnotation(nullptr);

  this->ScalarBar->Delete();
  this->ScalarBarMapper->Delete();
  this->ScalarBarActor->Delete();

  this->P->NanSwatch->Delete();
  this->P->BelowRangeSwatch->Delete();
  this->P->AboveRangeSwatch->Delete();
  this->P->NanSwatchMapper->Delete();
  this->P->AboveRangeSwatchMapper->Delete();
  this->P->BelowRangeSwatchMapper->Delete();
  this->P->NanSwatchActor->Delete();
  this->P->AboveRangeSwatchActor->Delete();
  this->P->BelowRangeSwatchActor->Delete();

  this->P->AnnotationBoxes->Delete();
  this->P->AnnotationBoxesMapper->Delete();
  this->P->AnnotationBoxesActor->Delete();

  this->P->AnnotationLeaders->Delete();
  this->P->AnnotationLeadersMapper->Delete();
  this->P->AnnotationLeadersActor->Delete();

  delete[] this->Title;
  this->Title = nullptr;

  delete[] this->ComponentTitle;
  this->ComponentTitle = nullptr;

  this->SetLookupTable(nullptr);
  this->SetOpacityFunction(nullptr);
  this->SetAnnotationTextProperty(nullptr);
  if (this->CustomLabels != nullptr)
  {
    this->CustomLabels->Delete();
  }
  this->SetLabelTextProperty(nullptr);
  this->SetTitleTextProperty(nullptr);
  this->Texture->Delete();
  this->TextureActor->Delete();
  this->TexturePolyData->Delete();
  this->Background->Delete();
  this->BackgroundMapper->Delete();
  this->BackgroundActor->Delete();
  this->Frame->Delete();
  this->FrameMapper->Delete();
  this->FrameActor->Delete();
  this->SetBackgroundProperty(nullptr);
  this->SetFrameProperty(nullptr);
  delete this->P;
}

//------------------------------------------------------------------------------
int vtkScalarBarActor::RenderOverlay(vtkViewport* viewport)
{
  if (!this->RebuildLayoutIfNeeded(viewport))
  {
    return 0;
  }

  int renderedSomething = 0;

  // Is the viewport's RenderWindow capturing GL2PS-special props? We'll need
  // to handle this specially to get the texture to show up right.
  if (vtkRenderer* renderer = vtkRenderer::SafeDownCast(viewport))
  {
    if (vtkRenderWindow* renderWindow = renderer->GetRenderWindow())
    {
      if (renderWindow->GetCapturingGL2PSSpecialProps())
      {
        renderer->CaptureGL2PSSpecialProp(this);
      }
    }
  }

  // Everything is built, just have to render
  if (this->DrawBackground)
  {
    renderedSomething += this->BackgroundActor->RenderOverlay(viewport);
  }

  if (this->UseOpacity && this->DrawColorBar)
  {
    renderedSomething += this->TextureActor->RenderOverlay(viewport);
  }

  // Draw either the scalar bar (non-indexed mode) or
  // the annotated value boxes (indexed mode).
  if (!this->LookupTable->GetIndexedLookup())
  {
    if (this->DrawColorBar)
    {
      renderedSomething += this->ScalarBarActor->RenderOverlay(viewport);
    }

    if (this->DrawTickLabels)
    {
      vtkScalarBarActorInternal::ActorVector::iterator it;
      for (it = this->P->TextActors.begin(); it != this->P->TextActors.end(); ++it)
      {
        renderedSomething += (*it)->RenderOverlay(viewport);
      }
    }
  }
  else if (this->DrawColorBar)
  {
    renderedSomething += this->P->AnnotationBoxesActor->RenderOverlay(viewport);
  }

  if (this->DrawNanAnnotation)
  {
    renderedSomething += this->P->NanSwatchActor->RenderOverlay(viewport);
  }

  if (this->DrawBelowRangeSwatch)
  {
    renderedSomething += this->P->BelowRangeSwatchActor->RenderOverlay(viewport);
  }

  if (this->DrawAboveRangeSwatch)
  {
    renderedSomething += this->P->AboveRangeSwatchActor->RenderOverlay(viewport);
  }

  if (this->DrawFrame)
  {
    renderedSomething += this->FrameActor->RenderOverlay(viewport);
  }

  if (this->Title != nullptr)
  {
    renderedSomething += this->TitleActor->RenderOverlay(viewport);
  }

  if (this->DrawAnnotations)
  {
    if (!this->P->AnnotationLabels.empty())
    {
      renderedSomething += this->P->AnnotationLeadersActor->RenderOverlay(viewport);
      for (size_t i = 0; i < this->P->AnnotationLabels.size(); ++i)
      {
        renderedSomething += this->P->AnnotationLabels[i]->RenderOverlay(viewport);
      }
    }
  }

  renderedSomething = (renderedSomething > 0) ? (1) : (0);
  return renderedSomething;
}

int vtkScalarBarActor::RebuildLayoutIfNeeded(vtkViewport* viewport)
{
  if (!this->LookupTable)
  {
    vtkWarningMacro(<< "Need a lookup table to render a scalar bar");
    return 0;
  }

  if (!this->TitleTextProperty)
  {
    vtkErrorMacro(<< "Need title text property to render a scalar bar");
    return 0;
  }

  if (!this->LabelTextProperty)
  {
    vtkErrorMacro(<< "Need label text property to render a scalar bar");
    return 0;
  }

  if (!this->AnnotationTextProperty)
  {
    vtkErrorMacro(<< "Need annotation text property to render a scalar bar");
    return 0;
  }

  // Check to see whether we have to rebuild everything
  int positionsHaveChanged = 0;
  if (viewport->GetMTime() > this->BuildTime ||
    (viewport->GetVTKWindow() && viewport->GetVTKWindow()->GetMTime() > this->BuildTime))
  {
    // if the viewport has changed we may - or may not need
    // to rebuild, it depends on if the projected coords change
    int size[2];
    int* barOrigin;
    barOrigin = this->PositionCoordinate->GetComputedViewportValue(viewport);
    size[0] = this->Position2Coordinate->GetComputedViewportValue(viewport)[0] - barOrigin[0];
    size[1] = this->Position2Coordinate->GetComputedViewportValue(viewport)[1] - barOrigin[1];

    // Check if we have bounds on the maximum size
    size[0] = size[0] > this->MaximumWidthInPixels ? this->MaximumWidthInPixels : size[0];
    size[1] = size[1] > this->MaximumHeightInPixels ? this->MaximumHeightInPixels : size[1];

    if (this->LastSize[0] != size[0] || this->LastSize[1] != size[1] ||
      this->LastOrigin[0] != barOrigin[0] || this->LastOrigin[1] != barOrigin[1])
    {
      positionsHaveChanged = 1;
    }
  }

  // Check to see whether we have to rebuild everything
  if (positionsHaveChanged || this->GetMTime() > this->BuildTime ||
    this->LookupTable->GetMTime() > this->BuildTime ||
    this->AnnotationTextProperty->GetMTime() > this->BuildTime ||
    this->LabelTextProperty->GetMTime() > this->BuildTime ||
    this->TitleTextProperty->GetMTime() > this->BuildTime ||
    this->BackgroundProperty->GetMTime() > this->BuildTime ||
    this->FrameProperty->GetMTime() > this->BuildTime)

  {
    this->RebuildLayout(viewport);
  }
  return 1;
}

//------------------------------------------------------------------------------
int vtkScalarBarActor::RenderOpaqueGeometry(vtkViewport* viewport)
{
  if (!this->RebuildLayoutIfNeeded(viewport))
  {
    return 0;
  }

  int renderedSomething = 0;

  // Everything is built, just have to render
  if (this->Title != nullptr)
  {
    renderedSomething += this->TitleActor->RenderOpaqueGeometry(viewport);
  }
  // Draw either the scalar bar (non-indexed mode) or
  // the annotated value boxes (indexed mode).
  if (!this->LookupTable->GetIndexedLookup())
  {
    if (this->DrawColorBar)
    {
      renderedSomething += this->ScalarBarActor->RenderOpaqueGeometry(viewport);
    }
    vtkScalarBarActorInternal::ActorVector::iterator ait;
    for (ait = this->P->TextActors.begin(); ait != this->P->TextActors.end(); ++ait)
    {
      renderedSomething += (*ait)->RenderOpaqueGeometry(viewport);
    }
  }
  else
  {
    if (this->DrawColorBar)
    {
      renderedSomething += this->P->AnnotationBoxesActor->RenderOpaqueGeometry(viewport);
    }
  }

  if (this->DrawNanAnnotation)
  {
    renderedSomething += this->P->NanSwatchActor->RenderOpaqueGeometry(viewport);
  }

  if (this->DrawBelowRangeSwatch)
  {
    renderedSomething += this->P->BelowRangeSwatchActor->RenderOpaqueGeometry(viewport);
  }

  if (this->DrawAboveRangeSwatch)
  {
    renderedSomething += this->P->AboveRangeSwatchActor->RenderOpaqueGeometry(viewport);
  }

  // Draw the annotation leaders and labels
  if (this->DrawAnnotations)
  {
    if (!this->P->AnnotationLabels.empty())
    {
      renderedSomething += this->P->AnnotationLeadersActor->RenderOpaqueGeometry(viewport);
      for (size_t i = 0; i < this->P->AnnotationLabels.size(); ++i)
      {
        renderedSomething += this->P->AnnotationLabels[i]->RenderOpaqueGeometry(viewport);
      }
    }
  }

  renderedSomething = (renderedSomething > 0) ? (1) : (0);

  return renderedSomething;
}

//------------------------------------------------------------------------------
// Description:
// Does this prop have some translucent polygonal geometry?
vtkTypeBool vtkScalarBarActor::HasTranslucentPolygonalGeometry()
{ // TODO: Handle case when IndexedLookup is true and any colors in the palette
  // have an alpha value, as the color swatches drawn by
  // this->P->AnnotationBoxesActor have 1 translucent triangle for each
  // alpha-swatch.
  return 0;
}

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

  if (this->LookupTable)
  {
    os << indent << "Lookup Table:\n";
    this->LookupTable->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "Lookup Table: (none)\n";
  }

  if (this->TitleTextProperty)
  {
    os << indent << "Title Text Property:\n";
    this->TitleTextProperty->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "Title Text Property: (none)\n";
  }

  if (this->LabelTextProperty)
  {
    os << indent << "Label Text Property:\n";
    this->LabelTextProperty->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "Label Text Property: (none)\n";
  }

  if (this->AnnotationTextProperty)
  {
    os << indent << "Annotation Text Property:\n";
    this->AnnotationTextProperty->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "Annotation Text Property: (none)\n";
  }

  os << indent << "Title: " << (this->Title ? this->Title : "(none)") << "\n";
  os << indent << "ComponentTitle: " << (this->ComponentTitle ? this->ComponentTitle : "(none)")
     << "\n";
  os << indent << "Maximum Number Of Colors: " << this->MaximumNumberOfColors << "\n";
  os << indent << "Number Of Automatic Labels: " << this->NumberOfLabels << "\n";
  os << indent << "Number Of Custom Labels: "
     << (this->CustomLabels != nullptr ? this->CustomLabels->GetNumberOfTuples() : 0) << "\n";
  os << indent << "Using Custom Labels: " << (this->UseCustomLabels ? "On" : "Off") << "\n";
  os << indent << "Number Of Labels Built: " << this->NumberOfLabelsBuilt << "\n";

  os << indent << "Orientation: ";
  if (this->Orientation == VTK_ORIENT_HORIZONTAL)
  {
    os << "Horizontal\n";
  }
  else
  {
    os << "Vertical\n";
  }

  os << indent << "Label Format: " << this->LabelFormat << "\n";
  os << indent << "UseOpacity: " << this->UseOpacity << "\n";
  if (this->UseOpacity)
  {
    os << indent << "TextureGridWidth: " << this->TextureGridWidth << "\n";
    os << indent << "TextureActor:\n";
    this->TextureActor->PrintSelf(os, indent.GetNextIndent());
  }
  if (this->TextPosition == vtkScalarBarActor::PrecedeScalarBar)
  {
    os << indent << "TextPosition: PrecedeScalarBar\n";
  }
  else
  {
    os << indent << "TextPosition: SucceedScalarBar\n";
  }

  os << indent << "MaximumWidthInPixels: " << this->MaximumWidthInPixels << endl;
  os << indent << "MaximumHeightInPixels: " << this->MaximumHeightInPixels << endl;

  os << indent << "DrawAnnotations: " << this->DrawAnnotations << endl;
  os << indent << "DrawNanAnnotation: " << this->DrawNanAnnotation << endl;
  os << indent << "NanAnnotation: " << (this->NanAnnotation ? this->NanAnnotation : "(none)")
     << endl;
  os << indent << "AnnotationLeaderPadding: " << this->AnnotationLeaderPadding << endl;
  os << indent << "AnnotationTextScaling: " << this->AnnotationTextScaling << endl;
  os << indent << "VerticalTitleSeparation: " << VerticalTitleSeparation << endl;

  os << indent << "DrawBelowRangeSwatch: " << this->DrawBelowRangeSwatch << endl;
  os << indent << "BelowRangeAnnotation: "
     << (this->BelowRangeAnnotation ? this->BelowRangeAnnotation : "(none)") << endl;

  os << indent << "DrawAboveRangeSwatch: " << this->DrawAboveRangeSwatch << endl;
  os << indent << "AboveRangeAnnotation: "
     << (this->AboveRangeAnnotation ? this->AboveRangeAnnotation : "(none)") << endl;

  os << indent << "DrawBackground: " << this->DrawBackground << "\n";
  os << indent << "Background Property:\n";
  this->BackgroundProperty->PrintSelf(os, indent.GetNextIndent());
  os << indent << "DrawFrame: " << this->DrawFrame << "\n";
  os << indent << "Frame Property:\n";
  this->FrameProperty->PrintSelf(os, indent.GetNextIndent());
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ShallowCopy(vtkProp* prop)
{
  vtkScalarBarActor* a = vtkScalarBarActor::SafeDownCast(prop);
  if (a != nullptr)
  {
    this->SetPosition2(a->GetPosition2());
    this->SetLookupTable(a->GetLookupTable());
    this->SetMaximumNumberOfColors(a->GetMaximumNumberOfColors());
    this->SetOrientation(a->GetOrientation());
    this->SetAnnotationTextProperty(a->GetAnnotationTextProperty());
    this->SetLabelTextProperty(a->GetLabelTextProperty());
    this->SetTitleTextProperty(a->GetTitleTextProperty());
    this->SetLabelFormat(a->GetLabelFormat());
    this->SetTitle(a->GetTitle());
    this->SetCustomLabels(a->GetCustomLabels());
    this->SetUseCustomLabels(a->GetUseCustomLabels());
    this->GetPositionCoordinate()->SetCoordinateSystem(
      a->GetPositionCoordinate()->GetCoordinateSystem());
    this->GetPositionCoordinate()->SetValue(a->GetPositionCoordinate()->GetValue());
    this->GetPosition2Coordinate()->SetCoordinateSystem(
      a->GetPosition2Coordinate()->GetCoordinateSystem());
    this->GetPosition2Coordinate()->SetValue(a->GetPosition2Coordinate()->GetValue());
    this->SetDrawBackground(a->GetDrawBackground());
    this->SetBackgroundProperty(a->GetBackgroundProperty());
    this->SetDrawFrame(a->GetDrawFrame());
    this->SetFrameProperty(a->GetFrameProperty());
  }

  // Now do superclass
  this->vtkActor2D::ShallowCopy(prop);
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::RebuildLayout(vtkViewport* viewport)
{
  vtkDebugMacro(<< "Rebuilding subobjects");

  this->P->Viewport = viewport;
  this->FreeLayoutStorage();

  // Permute indices used to measure width and height
  // so that thickness and length according to the orientation
  // of the scalar bar instead.
  if (this->Orientation == VTK_ORIENT_VERTICAL)
  {
    this->P->TL[0] = 0;
    this->P->TL[1] = 1;
  }
  else
  {
    this->P->TL[0] = 1;
    this->P->TL[1] = 0;
  }
  this->P->NumNotes = this->LookupTable->GetNumberOfAnnotatedValues();

  // Warning: The order of these calls is extremely important
  // as each updates members of this->P used by later methods!
  this->ComputeFrame();
  this->ComputeScalarBarThickness();
  this->ComputeSwatchPad();
  this->LayoutNanSwatch();
  this->LayoutBelowRangeSwatch();
  this->LayoutAboveRangeSwatch();

  this->PrepareTitleText();
  this->LayoutTitle();
  this->ComputeScalarBarLength();

  this->LayoutAboveRangeSwatchPosn();

  this->LayoutTicks();

  this->LayoutAnnotations();

  if (this->UnconstrainedFontSize)
  {
    this->LayoutForUnconstrainedFont();
  }

  // Now generate/configure the VTK datasets and actors that
  // illustrate the scalar bar when rendered using the
  // layout computed above.
  this->ConfigureAnnotations();
  this->ConfigureFrame();
  this->ConfigureScalarBar();
  this->ConfigureTitle();
  this->ConfigureTicks();
  this->ConfigureNanSwatch();
  this->ConfigureAboveBelowRangeSwatch(false);
  this->ConfigureAboveBelowRangeSwatch(true);
#ifdef VTK_DBG_LAYOUT
  this->DrawBoxes();
#endif // VTK_DBG_LAYOUT
  this->BuildTime.Modified();
}

namespace
{
void AddBox(vtkPoints* pts, vtkCellArray* lines, vtkScalarBarBox& box, int tl[2])
{
  vtkIdType pid[5];
  pid[0] = pts->InsertNextPoint(box.Posn[0], box.Posn[1], 0.);
  pid[1] = pts->InsertNextPoint(box.Posn[0] + box.Size[tl[0]], box.Posn[1], 0.);
  pid[2] = pts->InsertNextPoint(box.Posn[0] + box.Size[tl[0]], box.Posn[1] + box.Size[tl[1]], 0.);
  pid[3] = pts->InsertNextPoint(box.Posn[0], box.Posn[1] + box.Size[tl[1]], 0.);

  pid[4] = pid[0];
  for (int i = 0; i < 4; ++i)
  {
    lines->InsertNextCell(2, pid + i);
  }
}
}

void vtkScalarBarActor::DrawBoxes()
{
  vtkPoints* pts = this->Frame->GetPoints();
  vtkCellArray* lines = this->Frame->GetLines();

  AddBox(pts, lines, this->P->ScalarBarBox, this->P->TL);
  AddBox(pts, lines, this->P->NanBox, this->P->TL);
  AddBox(pts, lines, this->P->TitleBox, this->P->TL);
  if (this->NumberOfLabelsBuilt > 0)
  {
    AddBox(pts, lines, this->P->TickBox, this->P->TL);
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ComputeFrame()
{
  // get the viewport size in display coordinates
  int* p0;
  int* p1;
  int size[2];
  p0 = this->PositionCoordinate->GetComputedViewportValue(this->P->Viewport);
  p1 = this->Position2Coordinate->GetComputedViewportValue(this->P->Viewport);
  for (int i = 0; i < 2; ++i)
  {
    // this->P->Frame.Posn[i] = p0[i];
    this->P->Frame.Posn[i] = 0; // Translate the frame's coordinate system to p0
    size[i] = p1[i] - p0[i];
  }

  // Check if we have bounds on the maximum size
  size[0] = size[0] > this->MaximumWidthInPixels ? this->MaximumWidthInPixels : size[0];
  size[1] = size[1] > this->MaximumHeightInPixels ? this->MaximumHeightInPixels : size[1];

  for (int i = 0; i < 2; ++i)
  {
    this->P->Frame.Size[i] = size[this->P->TL[i]];
  }

  this->LastOrigin[0] = p0[0];
  this->LastOrigin[1] = p0[1];
  this->LastSize[0] = size[0];
  this->LastSize[1] = size[1];
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ComputeScalarBarThickness()
{
  // We do not set Size[1] (length), since the title bounds may encroach
  // on it in the vertical orientation.
  this->P->ScalarBarBox.Size[0] = static_cast<int>(ceil(this->P->Frame.Size[0] * this->BarRatio));

  // The lower-left corner of the scalar bar may be estimated here
  // as identical to the Frame position in one or both coordinates,
  // depending on whether tick marks should precede the scalar bar or
  // not and on the orientation.
  //
  // It will be altered later in this->LayoutTicks to account
  // for the half-height/width of tick labels.
  this->P->ScalarBarBox.Posn = this->P->Frame.Posn;
  if (this->TextPosition == PrecedeScalarBar)
  {
    this->P->ScalarBarBox.Posn[this->P->TL[0]] +=
      this->P->Frame.Size[0] - this->P->ScalarBarBox.Size[0];
  }

  // Now knock the thickness down and nudge the bar so the bar doesn't hug the frame.
  double nudge = this->P->ScalarBarBox.Size[0] / 8.;
  if (nudge > this->TextPad)
  {
    nudge = this->TextPad;
  }
  this->P->ScalarBarBox.Size[0] = static_cast<int>(this->P->ScalarBarBox.Size[0] - nudge);
  this->P->ScalarBarBox.Posn[this->P->TL[0]] =
    static_cast<int>(this->P->ScalarBarBox.Posn[this->P->TL[0]] +
      (nudge * (this->TextPosition == PrecedeScalarBar ? -1 : 1)));
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ComputeSwatchPad()
{
  if (this->P->NumNotes)
  {
    this->P->SwatchPad = this->P->Frame.Size[1] / this->P->NumNotes > 16.
      ? 4.
      : (this->P->Frame.Size[1] / this->P->NumNotes / 4.);
  }
  else
  {
    this->P->SwatchPad = 4.;
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::LayoutNanSwatch()
{
  // We don't have ScalarBarBox.Size[1] set yet; use the frame width instead.
  this->P->NanSwatchSize = static_cast<double>(
    this->P->ScalarBarBox.Size[0] > this->P->Frame.Size[1] / 4 ? this->P->Frame.Size[1] / 4
                                                               : this->P->ScalarBarBox.Size[0]);
  if (this->P->NanSwatchSize < 4 && this->P->Frame.Size[1] > 16)
  {
    this->P->NanSwatchSize = 4;
  }
  if (!this->DrawNanAnnotation)
  {
    this->P->NanSwatchSize = 0;
  }

  if (this->Orientation == VTK_ORIENT_VERTICAL)
  {
    this->P->NanBox.Posn[0] = this->P->ScalarBarBox.Posn[0];
    this->P->NanBox.Posn[1] = this->P->Frame.Posn[1] + this->TextPad;
    this->P->ScalarBarBox.Posn[1] = static_cast<int>(
      this->P->ScalarBarBox.Posn[1] + (this->P->NanSwatchSize + this->P->SwatchPad));
  }
  else // HORIZONTAL
  {
    this->P->NanBox.Posn = this->P->ScalarBarBox.Posn;
    this->P->NanBox.Posn[this->P->TL[1]] = static_cast<int>(
      this->P->NanBox.Posn[this->P->TL[1]] + (this->P->Frame.Size[1] - this->P->NanSwatchSize));
  }
  this->P->NanBox.Size[0] = this->P->ScalarBarBox.Size[0];
  this->P->NanBox.Size[1] = static_cast<int>(this->P->NanSwatchSize);
  if (this->P->NanBox.Size[1] > 2 * this->TextPad)
  {
    this->P->NanBox.Size[1] -= this->TextPad;
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::LayoutBelowRangeSwatch()
{
  // We don't have ScalarBarBox.Size[1] set yet; use the frame width instead.
  this->P->BelowRangeSwatchSize = static_cast<double>(
    this->P->ScalarBarBox.Size[0] > this->P->Frame.Size[1] / 4 ? this->P->Frame.Size[1] / 4
                                                               : this->P->ScalarBarBox.Size[0]);
  if (this->P->BelowRangeSwatchSize < 4 && this->P->Frame.Size[1] > 16)
  {
    this->P->BelowRangeSwatchSize = 4;
  }
  if (!this->DrawBelowRangeSwatch)
  {
    this->P->BelowRangeSwatchSize = 0;
  }

  if (this->Orientation == VTK_ORIENT_VERTICAL)
  {
    this->P->BelowRangeSwatchBox.Posn[0] = this->P->ScalarBarBox.Posn[0];
    this->P->BelowRangeSwatchBox.Posn[1] = this->P->Frame.Posn[1] + this->TextPad;

    // move away because of nan annotation
    if (this->DrawNanAnnotation)
    {
      this->P->BelowRangeSwatchBox.Posn[1] = static_cast<int>(
        this->P->BelowRangeSwatchBox.Posn[1] + (this->P->NanBox.Size[1] + this->P->SwatchPad));
    }
    this->P->ScalarBarBox.Posn[1] =
      static_cast<int>(this->P->ScalarBarBox.Posn[1] + this->P->BelowRangeSwatchSize);
  }
  else // HORIZONTAL
  {
    this->P->BelowRangeSwatchBox.Posn = this->P->ScalarBarBox.Posn;
  }

  this->P->BelowRangeSwatchBox.Size[0] = this->P->ScalarBarBox.Size[0];
  this->P->BelowRangeSwatchBox.Size[1] = static_cast<int>(this->P->BelowRangeSwatchSize);
  if (this->P->BelowRangeSwatchBox.Size[1] > 2 * this->TextPad)
  {
    this->P->BelowRangeSwatchBox.Size[1] -= this->TextPad;
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::LayoutAboveRangeSwatch()
{
  // We don't have ScalarBarBox.Size[1] set yet; use the frame width instead.
  this->P->AboveRangeSwatchSize = static_cast<double>(
    this->P->ScalarBarBox.Size[0] > this->P->Frame.Size[1] / 4 ? this->P->Frame.Size[1] / 4
                                                               : this->P->ScalarBarBox.Size[0]);
  if (this->P->AboveRangeSwatchSize < 4 && this->P->Frame.Size[1] > 16)
  {
    this->P->AboveRangeSwatchSize = 4;
  }
  if (!this->DrawAboveRangeSwatch)
  {
    this->P->AboveRangeSwatchSize = 0;
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::LayoutAboveRangeSwatchPosn()
{
  if (this->Orientation == VTK_ORIENT_VERTICAL)
  {
    this->P->AboveRangeSwatchBox.Posn[0] = this->P->ScalarBarBox.Posn[0];
    this->P->AboveRangeSwatchBox.Posn[1] =
      this->P->Frame.Posn[1] + this->TextPad + this->P->ScalarBarBox.Size[1] + this->P->SwatchPad;

    if (this->DrawNanAnnotation)
    {
      this->P->AboveRangeSwatchBox.Posn[1] = static_cast<int>(
        this->P->AboveRangeSwatchBox.Posn[1] + this->P->SwatchPad + this->P->NanBox.Size[1]);
    }

    if (this->DrawBelowRangeSwatch)
    {
      this->P->AboveRangeSwatchBox.Posn[1] = static_cast<int>(this->P->AboveRangeSwatchBox.Posn[1] +
        this->P->SwatchPad + this->P->BelowRangeSwatchBox.Size[1]);
    }
  }
  else // HORIZONTAL
  {
    this->P->AboveRangeSwatchBox.Posn = this->P->ScalarBarBox.Posn;
    this->P->AboveRangeSwatchBox.Posn[this->P->TL[1]] =
      static_cast<int>(this->P->AboveRangeSwatchBox.Posn[this->P->TL[1]] +
        (this->P->Frame.Size[1] - this->P->AboveRangeSwatchSize));

    if (this->DrawNanAnnotation)
    {
      this->P->AboveRangeSwatchBox.Posn[this->P->TL[1]] =
        static_cast<int>(this->P->AboveRangeSwatchBox.Posn[this->P->TL[1]] -
          (this->P->NanBox.Size[this->P->TL[1]] + this->P->SwatchPad));
    }
  }
  this->P->AboveRangeSwatchBox.Size[0] = this->P->ScalarBarBox.Size[0];
  this->P->AboveRangeSwatchBox.Size[1] = static_cast<int>(this->P->AboveRangeSwatchSize);
  if (this->P->AboveRangeSwatchBox.Size[1] > 2 * this->TextPad)
  {
    this->P->AboveRangeSwatchBox.Size[1] -= this->TextPad;
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::PrepareTitleText()
{
  // Update actor with the latest title/subtitle
  if (this->ComponentTitle && strlen(this->ComponentTitle) > 0)
  {
    // need to account for a space between title & component and null term
    char* combinedTitle = new char[strlen(this->Title) + strlen(this->ComponentTitle) + 2];
    strcpy(combinedTitle, this->Title);
    strcat(combinedTitle, " ");
    strcat(combinedTitle, this->ComponentTitle);
    this->TitleActor->SetInput(combinedTitle);
    delete[] combinedTitle;
  }
  else
  {
    this->TitleActor->SetInput(this->Title);
  }

  if (this->TitleTextProperty->GetMTime() > this->BuildTime)
  {
    // Shallow copy here so that the size of the title prop is not affected
    // by the automatic adjustment of its text mapper's size (i.e. its
    // mapper's text property is identical except for the font size
    // which will be modified later). This allows text actors to
    // share the same text property, and in that case specifically allows
    // the title and label text prop to be the same.
    this->TitleActor->GetTextProperty()->ShallowCopy(this->TitleTextProperty);
    this->TitleActor->GetTextProperty()->SetJustificationToCentered();
    this->TitleActor->GetTextProperty()->SetVerticalJustification(
      this->TextPosition == PrecedeScalarBar ? VTK_TEXT_BOTTOM : VTK_TEXT_TOP);
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::LayoutTitle()
{
  if (this->Title == nullptr || !strlen(this->Title))
  {
    this->P->TitleBox.Posn = vtkTuple<int, 2>(0.);
    this->P->TitleBox.Size = vtkTuple<int, 2>(0.);
    return;
  }

  int targetWidth, targetHeight;
  if (!this->ForceVerticalTitle)
  {
    // Title spans entire width of frame at top, regardless of orientation.
    targetWidth = static_cast<int>(this->P->Frame.Size[this->P->TL[0]]) - 2 * this->TextPad;
  }
  else
  {
    targetWidth = static_cast<int>(
      (this->P->Frame.Size[0] - this->P->ScalarBarBox.Size[0] - this->TextPad) * this->TitleRatio);
  }

  if (!this->ForceVerticalTitle)
  {
    // Height is either: at most half the frame height or
    // a fixed portion of the frame remaining after subtracting the
    // scalar bar's thickness.
    //
    // When laid out horizontally, ticks share vertical space with title.
    // We want the title to be larger (18pt vs 14pt).
    targetHeight = static_cast<int>(
      (this->Orientation == VTK_ORIENT_VERTICAL || this->LookupTable->GetIndexedLookup())
        ? ceil(this->P->Frame.Size[this->P->TL[1]] / 2. - this->TextPad)
        : (this->P->Frame.Size[0] - this->P->ScalarBarBox.Size[0] -
            (this->TextPosition == SucceedScalarBar ? this->P->ScalarBarBox.Posn[this->P->TL[0]]
                                                    : 0) -
            this->TextPad) *
          this->TitleRatio);
  }
  else
  {
    targetHeight = static_cast<int>(this->P->Frame.Size[1] / 3.0);
  }

  this->TitleActor->SetOrientation(this->ForceVerticalTitle ? 90 : 0);

  if (this->UnconstrainedFontSize)
  {
    this->TitleActor->GetTextProperty()->SetFontSize(this->TitleTextProperty->GetFontSize());
  }
  else
  {
    this->TitleActor->SetConstrainedFontSize(this->P->Viewport, targetWidth, targetHeight);
  }

  // Now fetch the actual size from the actor and use it to
  // update the box size and position.
  double titleSize[2] = { 0, 0 };
  this->TitleActor->GetSize(this->P->Viewport, titleSize);
  this->TitleActor->GetTextProperty()->SetVerticalJustificationToTop();
  for (int i = 0; i < 2; ++i)
  {
    this->P->TitleBox.Size[this->P->TL[i]] = static_cast<int>(ceil(titleSize[i]));
  }

  if (this->ForceVerticalTitle)
  {
    this->P->TitleBox.Posn[0] = this->TextPosition == vtkScalarBarActor::PrecedeScalarBar
      ? this->P->Frame.Posn[0]
      : this->P->Frame.Posn[0] + this->P->Frame.Size[this->P->TL[0]] -
        (this->P->TitleBox.Size[this->P->TL[0]] + this->TextPad);
    this->P->TitleBox.Posn[1] = this->P->Frame.Posn[1] +
      (this->P->Frame.Size[this->P->TL[1]] - this->P->TitleBox.Size[this->P->TL[1]]) / 2;
  }
  else
  {
    this->P->TitleBox.Posn[0] =
      this->P->Frame.Posn[0] + (this->P->Frame.Size[this->P->TL[0]] - titleSize[0]) / 2;
    this->P->TitleBox.Posn[1] =
      static_cast<int>(this->P->Frame.Posn[1] + this->P->Frame.Size[this->P->TL[1]]);
    if (this->Orientation == VTK_ORIENT_VERTICAL ||
      this->TextPosition == vtkScalarBarActor::SucceedScalarBar)
    {
      this->P->TitleBox.Posn[1] -= this->P->TitleBox.Size[this->P->TL[1]] + this->TextPad +
        static_cast<int>(this->FrameProperty->GetLineWidth());
    }
    else
    {
      this->P->TitleBox.Posn[1] = this->P->Frame.Posn[1] + this->TextPad -
        static_cast<int>(this->FrameProperty->GetLineWidth());
    }
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ComputeScalarBarLength()
{
  if (!this->ForceVerticalTitle)
  {
    this->P->ScalarBarBox.Size[1] = this->Orientation == VTK_ORIENT_VERTICAL
      ? this->P->Frame.Size[1] - this->P->TitleBox.Size[1] - this->VerticalTitleSeparation
      : this->P->Frame.Size[1];
  }
  else
  {
    this->P->ScalarBarBox.Size[1] = this->P->Frame.Size[1];
  }

  // The scalar bar does not include the Nan Swatch, the Below Range Swatch and
  // the Above Range Swatch.
  this->P->ScalarBarBox.Size[1] =
    static_cast<int>(this->P->ScalarBarBox.Size[1] - (this->P->NanSwatchSize + this->P->SwatchPad));

  // Correct Swatch behevior while keeping compat with images from tests
  if (this->P->BelowRangeSwatchSize > 0)
  {
    this->P->ScalarBarBox.Size[1] -= this->P->BelowRangeSwatchSize + this->P->SwatchPad;
  }

  if (this->P->AboveRangeSwatchSize > 0)
  {
    this->P->ScalarBarBox.Size[1] -= this->P->AboveRangeSwatchSize;
    if (this->P->NanSwatchSize > 0)
    {
      this->P->ScalarBarBox.Size[1] -= this->P->SwatchPad;
    }
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::LayoutTicks()
{
  if (this->LookupTable->GetIndexedLookup())
  { // no tick marks in indexed lookup mode.
    this->NumberOfLabelsBuilt = 0;
    return;
  }

  // find the best size for the ticks
  const double* range = this->LookupTable->GetRange();
  char string[512];
  double val, rng, offset;
  int i;
  if (this->UseCustomLabels)
  {
    this->NumberOfLabelsBuilt =
      this->CustomLabels != nullptr ? this->CustomLabels->GetNumberOfTuples() : 0;
  }
  else
  {
    this->NumberOfLabelsBuilt = this->NumberOfLabels;
  }

  // TODO: this should be optimized, maybe by keeping a list of
  // allocated mappers, in order to avoid creation/destruction of
  // their underlying text properties (i.e. each time a mapper is
  // created, text properties are created and shallow-assigned a font size
  // which value might be "far" from the target font size).
  this->P->TextActors.resize(this->NumberOfLabelsBuilt);
  this->P->TextActorAnchors.resize(this->NumberOfLabelsBuilt);

  // Does this map have its scale set to log?
  vtkTypeBool isLogTable = this->LookupTable->UsingLogScale();
  if (isLogTable)
  {
    offset = log10(range[0]);
    rng = (log10(range[1]) - offset);
  }
  else
  {
    offset = range[0];
    rng = range[1] - offset;
  }
  for (i = 0; i < this->NumberOfLabelsBuilt; i++)
  {
    this->P->TextActors[i].TakeReference(vtkTextActor::New());
    // Get or compute the actual data value and its normalized position
    if (this->UseCustomLabels)
    {
      // We should be guaranteed above that this->CustomLabels is not null
      // at this point (otherwise NumberOfLabelsBuilt == 0), but let's be extra
      // careful
      val = this->CustomLabels != nullptr ? this->CustomLabels->GetValue(i) : 1.0;
      if (rng > 0)
      {
        if (isLogTable)
        {
          if (val > 0)
          {
            this->P->TextActorAnchors[i] = (log10(val) - offset) / rng;
          }
          else
          {
            this->P->TextActorAnchors[i] = -1.;
          }
        }
        else
        {
          this->P->TextActorAnchors[i] = (val - offset) / rng;
        }
      }
      else if (val == range[0])
      {
        this->P->TextActorAnchors[i] = 0.5;
      }
      else
      {
        this->P->TextActorAnchors[i] = -1.;
      }
    }
    else
    {
      if (this->NumberOfLabelsBuilt > 1)
      {
        val = static_cast<double>(i) / (this->NumberOfLabelsBuilt - 1);
      }
      else
      {
        val = 0.5;
      }
      this->P->TextActorAnchors[i] = val;
      val = offset + val * rng;
      if (isLogTable)
      {
        val = pow(10., val);
      }
    }

    snprintf(string, 511, this->LabelFormat, val);
    this->P->TextActors[i]->SetInput(string);

    // Shallow copy here so that the size of the label prop is not affected
    // by the automatic adjustment of its text mapper's size (i.e. its
    // mapper's text property is identical except for the font size
    // which will be modified later). This allows text actors to
    // share the same text property, and in that case specifically allows
    // the title and label text prop to be the same.
    this->P->TextActors[i]->GetTextProperty()->ShallowCopy(this->LabelTextProperty);

    this->P->TextActors[i]->SetProperty(this->GetProperty());
    this->P->TextActors[i]->GetPositionCoordinate()->SetReferenceCoordinate(
      this->PositionCoordinate);
  }

  if (this->NumberOfLabelsBuilt > 0)
  {
    int labelSize[2];
    labelSize[0] = labelSize[1] = 0;
    int targetWidth, targetHeight;

    this->P->TickBox.Posn = this->P->ScalarBarBox.Posn;
    if (this->Orientation == VTK_ORIENT_VERTICAL)
    { // NB. Size[0] = width, Size[1] = height
      // Ticks share the width with the scalar bar
      this->P->TickBox.Size[0] =
        this->P->Frame.Size[0] - this->P->ScalarBarBox.Size[0] - this->TextPad * 3;
      // Tick height could be adjusted if title text is
      // lowered by box constraints, but we won't bother:
      this->P->TickBox.Size[1] = this->P->Frame.Size[1] - this->P->TitleBox.Size[1] -
        3 * this->TextPad - this->VerticalTitleSeparation;
      if (this->ForceVerticalTitle)
      {
        this->P->TickBox.Size[0] -= this->P->TitleBox.Size[0];
        this->P->TickBox.Size[1] += this->P->TitleBox.Size[1];
      }
      // Tick box height also reduced by NaN swatch size, if present:
      if (this->DrawNanAnnotation)
      {
        this->P->TickBox.Size[1] = static_cast<int>(
          this->P->TickBox.Size[1] - (this->P->NanBox.Size[1] + this->P->SwatchPad));
      }
      if (this->DrawBelowRangeSwatch)
      {
        this->P->TickBox.Size[1] = static_cast<int>(
          this->P->TickBox.Size[1] - (this->P->BelowRangeSwatchBox.Size[1] + this->P->SwatchPad));
      }
      if (this->DrawAboveRangeSwatch)
      {
        this->P->TickBox.Size[1] = static_cast<int>(
          this->P->TickBox.Size[1] - (this->P->AboveRangeSwatchBox.Size[1] + this->P->SwatchPad));
      }

      if (this->TextPosition == vtkScalarBarActor::PrecedeScalarBar)
      {
        this->P->TickBox.Posn[0] = this->TextPad;
        if (this->ForceVerticalTitle)
        {
          this->P->TickBox.Posn[0] += this->P->TitleBox.Size[0];
        }
      }
      else
      {
        this->P->TickBox.Posn[0] += this->P->ScalarBarBox.Size[0] + 2 * this->TextPad;
      }

      targetWidth = this->P->TickBox.Size[0];
      targetHeight = static_cast<int>(
        (this->P->TickBox.Size[1] - this->TextPad * (this->NumberOfLabelsBuilt - 1)) /
        this->NumberOfLabelsBuilt);
    }
    else
    { // NB. Size[1] = width, Size[0] = height
      // Ticks span the entire width of the frame
      this->P->TickBox.Size[1] = this->P->ScalarBarBox.Size[1];
      // Ticks share vertical space with title and scalar bar.
      this->P->TickBox.Size[0] = this->P->Frame.Size[0] - this->P->ScalarBarBox.Size[0] -
        4 * this->TextPad - this->P->TitleBox.Size[0];

      if (this->TextPosition == vtkScalarBarActor::PrecedeScalarBar)
      {
        this->P->TickBox.Posn[1] =
          this->P->TitleBox.Size[0] + 2 * this->TextPad + this->P->TitleBox.Posn[1];
      }
      else
      {
        this->P->TickBox.Posn[1] += this->P->ScalarBarBox.Size[0];
      }
      targetWidth = static_cast<int>(
        (this->P->TickBox.Size[1] - this->TextPad * (this->NumberOfLabelsBuilt - 1)) /
        this->NumberOfLabelsBuilt);
      targetHeight = this->P->TickBox.Size[0];
    }

    if (!this->UnconstrainedFontSize)
    {
      vtkTextActor::SetMultipleConstrainedFontSize(this->P->Viewport, targetWidth, targetHeight,
        this->P->TextActors.PointerArray(), this->NumberOfLabelsBuilt, labelSize);
    }

    // Now adjust scalar bar size by the half-size of the first and last ticks
    this->P->ScalarBarBox.Posn[this->P->TL[1]] += labelSize[this->P->TL[1]] / 2.;
    this->P->ScalarBarBox.Size[1] -= labelSize[this->P->TL[1]];
    this->P->TickBox.Posn[this->P->TL[1]] =
      static_cast<int>(this->P->TickBox.Posn[this->P->TL[1]] + (labelSize[this->P->TL[1]] / 2.));
    this->P->TickBox.Size[1] -= labelSize[this->P->TL[1]];

    if (this->Orientation == VTK_ORIENT_HORIZONTAL)
    {
      this->P->ScalarBarBox.Posn[0] += this->P->BelowRangeSwatchSize;

      this->P->TickBox.Posn[0] += this->P->BelowRangeSwatchSize;

      this->P->TickBox.Posn[1] += this->TextPad * (this->TextPosition == PrecedeScalarBar ? -1 : 1);
      this->P->TickBox.Size[1] -= this->TextPad;
    }
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::LayoutForUnconstrainedFont()
{
  if (this->UnconstrainedFontSize)
  {
    // recompute tickbox layout max sizes
    int labelWidth = 0;
    int labelHeight = 0;
    double fontSize[2] = { 0, 0 };
    for (size_t i = 0; i < this->P->TextActors.size(); ++i)
    {
      this->P->TextActors.at(i)->GetSize(this->P->Viewport, fontSize);
      if (fontSize[0] > labelWidth)
      {
        labelWidth = fontSize[0];
      }

      if (fontSize[1] > labelHeight)
      {
        labelHeight = fontSize[1];
      }
    }

    if (this->Orientation == VTK_ORIENT_VERTICAL)
    {
      this->P->TitleBox.Posn[1] += labelHeight * 0.75;
      this->P->TickBox.Size[0] = labelWidth;
      if (this->TextPosition == vtkScalarBarActor::PrecedeScalarBar)
      {
        this->P->TickBox.Posn[0] = -labelWidth + this->P->Frame.Size[0] -
          this->P->ScalarBarBox.Size[0] - (labelWidth * 0.05);
      }
    }
    else
    {
      if (this->TextPosition == vtkScalarBarActor::PrecedeScalarBar)
      {
        this->P->TitleBox.Posn[1] = this->P->Frame.Posn[1] + this->P->ScalarBarBox.Posn[1] -
          this->P->TitleBox.Size[this->P->TL[1]] - labelHeight;
      }
      else
      {
        this->P->TitleBox.Posn[1] =
          this->P->Frame.Posn[1] + this->P->ScalarBarBox.Size[this->P->TL[1]] + labelHeight;
      }
    }
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::LayoutAnnotations()
{
  if (this->DrawAnnotations)
  {
    const double* range = this->LookupTable->GetRange();

    this->MapAnnotationLabels(this->LookupTable, this->P->ScalarBarBox.Posn[this->P->TL[1]],
      this->P->ScalarBarBox.Size[1], range);
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ConfigureFrame()
{
  // set frame structure
  vtkPoints* frPts = vtkPoints::New();
  frPts->SetNumberOfPoints(5);
  vtkCellArray* frLines = vtkCellArray::New();
  frLines->AllocateEstimate(1, 5);

  this->FrameActor->SetProperty(this->FrameProperty);
  this->Frame->Initialize();
  this->Frame->SetPoints(frPts);
  this->Frame->SetLines(frLines);
  frPts->Delete();
  frLines->Delete();

  // set background structure
  vtkPoints* bgPts = vtkPoints::New();
  bgPts->SetNumberOfPoints(4);
  vtkCellArray* bgPolys = vtkCellArray::New();
  bgPolys->AllocateEstimate(1, 4);

  this->BackgroundActor->SetProperty(this->BackgroundProperty);
  this->Background->Initialize();
  this->Background->SetPoints(bgPts);
  this->Background->SetPolys(bgPolys);
  bgPts->Delete();
  bgPolys->Delete();

  double x[3];
  x[2] = 0.;

  // generate background and frame points and cell
  x[0] = 0;
  x[1] = 0;
  bgPts->SetPoint(0, x);
  frPts->SetPoint(0, x);
  frPts->SetPoint(4, x);

  x[0] = 0;
  x[1] = this->P->Frame.Size[this->P->TL[1]] - 0.5;
  bgPts->SetPoint(1, x);
  frPts->SetPoint(1, x);

  x[0] = this->P->Frame.Size[this->P->TL[0]] - 0.5;
  x[1] = this->P->Frame.Size[this->P->TL[1]] - 0.5;
  bgPts->SetPoint(2, x);
  frPts->SetPoint(2, x);

  x[0] = this->P->Frame.Size[this->P->TL[0]] - 0.5;
  x[1] = 0;
  bgPts->SetPoint(3, x);
  frPts->SetPoint(3, x);

  vtkIdType bgIds[5] = { 0, 1, 2, 3, 4 };
  bgPolys->InsertNextCell(4, bgIds);
  frLines->InsertNextCell(5, bgIds);
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ConfigureScalarBar()
{
  vtkScalarsToColors* lut = this->LookupTable;
  const double* range = lut->GetRange();
  this->P->NumColors =
    lut->GetIndexedLookup() ? lut->GetNumberOfAnnotatedValues() : this->MaximumNumberOfColors;
  this->P->NumSwatches = this->P->NumColors + (this->DrawNanAnnotation ? 1 : 0);
  int numPts = 2 * (this->P->NumColors + 1) + (this->DrawNanAnnotation ? 4 : 0);

  this->P->NumSwatches = this->P->NumColors + (this->DrawBelowRangeSwatch ? 1 : 0);
  numPts += (this->DrawBelowRangeSwatch ? 4 : 0);

  this->P->SwatchPts = vtkPoints::New();
  this->P->SwatchPts->SetNumberOfPoints(numPts);
  this->P->Polys = vtkCellArray::New();
  this->P->Polys->AllocateEstimate(this->P->NumSwatches, 4);
  this->P->SwatchColors = vtkUnsignedCharArray::New();

  unsigned int nComponents = ((this->UseOpacity) ? 4 : 3);
  this->P->SwatchColors->SetNumberOfComponents(nComponents);
  this->P->SwatchColors->SetNumberOfTuples(this->P->NumSwatches);

  this->ScalarBarActor->SetProperty(this->GetProperty());
  this->ScalarBar->Initialize();
  this->ScalarBar->SetPoints(this->P->SwatchPts);
  this->ScalarBar->SetPolys(this->P->Polys);
  this->ScalarBar->GetCellData()->SetScalars(this->P->SwatchColors);
  this->P->SwatchPts->Delete();
  this->P->Polys->Delete();
  this->P->SwatchColors->Delete();

  double delta = static_cast<double>(this->P->ScalarBarBox.Size[1]) / this->P->NumColors;
  double x[3];
  x[2] = 0.;
  for (int i = 0;
       i < numPts / 2 - (this->DrawNanAnnotation ? 2 : 0) - (this->DrawBelowRangeSwatch ? 2 : 0);
       ++i)
  {
    x[this->P->TL[0]] = this->P->ScalarBarBox.Posn[this->P->TL[0]];
    x[this->P->TL[1]] = this->P->ScalarBarBox.Posn[this->P->TL[1]] + i * delta;
    this->P->SwatchPts->SetPoint(2 * i, x);

    x[this->P->TL[0]] = this->P->ScalarBarBox.Posn[this->P->TL[0]] + this->P->ScalarBarBox.Size[0];
    this->P->SwatchPts->SetPoint(2 * i + 1, x);
  }

  // polygons & cell colors
  unsigned char* rgb;
  double rgba[4];
  vtkIdType ptIds[4];
  for (int i = 0; i < this->P->NumColors; ++i)
  {
    ptIds[0] = 2 * i;
    ptIds[1] = ptIds[0] + 1;
    ptIds[2] = ptIds[1] + 2;
    ptIds[3] = ptIds[0] + 2;
    this->P->Polys->InsertNextCell(4, ptIds);
    double rgbval;
    if (this->LookupTable->UsingLogScale())
    {
      rgbval =
        log10(range[0]) + (i + 0.5) * (log10(range[1]) - log10(range[0])) / this->P->NumColors;
      rgbval = pow(10.0, rgbval);
    }
    else
    {
      rgbval =
        range[0] + (range[1] - range[0]) * ((i + 0.5) / static_cast<double>(this->P->NumColors));
    }
    lut->GetColor(rgbval, rgba);
    if (this->OpacityFunction)
    {
      rgba[3] = this->OpacityFunction->GetValue(rgbval);
    }
    else
    {
      rgba[3] = lut->GetOpacity(rgbval);
    }
    // write into array directly
    rgb = this->P->SwatchColors->GetPointer(nComponents * i);
    rgb[0] = static_cast<unsigned char>(rgba[0] * 255.);
    rgb[1] = static_cast<unsigned char>(rgba[1] * 255.);
    rgb[2] = static_cast<unsigned char>(rgba[2] * 255.);
    if (this->P->SwatchColors->GetNumberOfComponents() > 3)
    {
      rgb[3] = static_cast<unsigned char>(this->UseOpacity ? rgba[3] * 255. : 255.);
    }
  }

  // Set up a texture actor as an alternative to the 1-quad-per-color
  // scalar bar polydata.
  vtkPoints* texturePoints = vtkPoints::New();
  texturePoints->SetNumberOfPoints(4);
  this->TexturePolyData->SetPoints(texturePoints);
  texturePoints->SetPoint(0, 0.0, 0.0, 0.0);

  double p1[2], p2[2];
  p1[0] = this->P->ScalarBarBox.Posn[0];
  p1[1] = this->P->ScalarBarBox.Posn[1];
  p2[0] = p1[0] + this->P->ScalarBarBox.Size[this->P->TL[0]];
  p2[1] = p1[1] + this->P->ScalarBarBox.Size[this->P->TL[1]];

  texturePoints->SetPoint(0, p1[0], p1[1], 0.0);
  texturePoints->SetPoint(1, p2[0], p1[1], 0.0);
  texturePoints->SetPoint(2, p2[0], p2[1], 0.0);
  texturePoints->SetPoint(3, p1[0], p2[1], 0.0);
  texturePoints->Delete();

  double barWidth = this->P->ScalarBarBox.Size[this->P->TL[0]];
  double barHeight = this->P->ScalarBarBox.Size[this->P->TL[1]];
  vtkDataArray* tc = this->TexturePolyData->GetPointData()->GetTCoords();
  tc->SetTuple2(1, barWidth / this->TextureGridWidth, 0.0);
  tc->SetTuple2(2, barWidth / this->TextureGridWidth, barHeight / this->TextureGridWidth);
  tc->SetTuple2(3, 0.0, barHeight / this->TextureGridWidth);
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ConfigureTitle()
{
  if (!this->ForceVerticalTitle)
  {
    this->TitleActor->SetPosition(
      this->P->TitleBox.Posn[0] + this->P->TitleBox.Size[this->P->TL[0]] / 2,
      this->TitleActor->GetTextProperty()->GetVerticalJustification() == VTK_TEXT_BOTTOM
        ? this->P->TitleBox.Posn[1]
        : this->P->TitleBox.Posn[1] + this->P->TitleBox.Size[this->P->TL[1]]);
  }
  else
  {
    this->TitleActor->SetPosition(this->P->TitleBox.Posn[0],
      this->P->TitleBox.Posn[1] + this->P->TitleBox.Size[this->P->TL[1]] / 2);
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ConfigureTicks()
{
  double val, anchor;
  double sizeTextData[2];
  for (int i = 0; i < this->NumberOfLabelsBuilt; ++i)
  {
    anchor = this->P->TextActorAnchors[i];
    this->P->TextActors[i]->SetVisibility((anchor > -1e-6 && anchor < 1 + 1e-6));
    val = anchor * this->P->TickBox.Size[1] + this->P->TickBox.Posn[this->P->TL[1]];
    this->P->TextActors[i]->GetSize(this->P->Viewport, sizeTextData);
    if (this->Orientation == VTK_ORIENT_VERTICAL)
    { // VERTICAL
      this->P->TextActors[i]->GetTextProperty()->SetJustification(
        this->TextPosition == PrecedeScalarBar ? VTK_TEXT_RIGHT : VTK_TEXT_LEFT);
      this->P->TextActors[i]->GetTextProperty()->SetVerticalJustificationToBottom();
      this->P->TextActors[i]->SetPosition(this->TextPosition == vtkScalarBarActor::PrecedeScalarBar
          ? this->P->TickBox.Posn[0] + this->P->TickBox.Size[0]
          : this->P->TickBox.Posn[0],
        // this->P->TickBox.Posn[0],
        val - 0.5 * sizeTextData[1]);
    }
    else
    { // HORIZONTAL
      this->P->TextActors[i]->GetTextProperty()->SetJustificationToCentered();
      this->P->TextActors[i]->GetTextProperty()->SetVerticalJustification(
        this->TextPosition == PrecedeScalarBar ? VTK_TEXT_TOP : VTK_TEXT_BOTTOM);
      this->P->TextActors[i]->SetPosition(val,
        this->TextPosition == PrecedeScalarBar ? this->P->TickBox.Posn[1] + this->P->TickBox.Size[0]
                                               : this->P->TickBox.Posn[1]);
    }
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ConfigureNanSwatch()
{
  if (!this->DrawNanAnnotation)
  {
    return;
  }

  int numPts = 4;
  vtkPoints* pts = vtkPoints::New();
  pts->SetNumberOfPoints(numPts);
  vtkCellArray* polys = vtkCellArray::New();
  polys->AllocateEstimate(1, 4);
  vtkUnsignedCharArray* colors = vtkUnsignedCharArray::New();
  colors->SetNumberOfComponents(4); // RGBA
  colors->SetNumberOfTuples(1);

  this->P->NanSwatch->Initialize();
  this->P->NanSwatch->SetPoints(pts);
  this->P->NanSwatch->SetPolys(polys);
  this->P->NanSwatch->GetCellData()->SetScalars(colors);
  pts->Delete();
  polys->Delete();
  colors->Delete();

  double x[3];
  x[2] = 0.;

  for (int j = 0; j < 2; ++j)
  {
    x[j] = this->P->NanBox.Posn[j];
  }
  int i = 0;
  pts->SetPoint(i++, x);
  x[0] += this->P->NanBox.Size[this->P->TL[0]];
  pts->SetPoint(i++, x);
  x[1] += this->P->NanBox.Size[this->P->TL[1]];
  pts->SetPoint(i++, x);
  x[0] -= this->P->NanBox.Size[this->P->TL[0]];
  pts->SetPoint(i++, x);

  // Add the swatch to the polydata and color it:
  unsigned char* rgb;
  double rgba[4];
  vtkIdType ptIds[4];
  ptIds[0] = 0;
  ptIds[1] = 1;
  ptIds[2] = 2;
  ptIds[3] = 3;
  polys->InsertNextCell(4, ptIds);
  this->LookupTable->GetIndexedColor(-1, rgba);
  rgb = colors->GetPointer(0); // write into array directly
  rgb[0] = static_cast<unsigned char>(rgba[0] * 255.);
  rgb[1] = static_cast<unsigned char>(rgba[1] * 255.);
  rgb[2] = static_cast<unsigned char>(rgba[2] * 255.);
  rgb[3] = static_cast<unsigned char>(this->UseOpacity ? rgba[3] * 255. : 255.);
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ConfigureAboveBelowRangeSwatch(bool above)
{
  // Check above/below
  vtkPolyData* swatch;
  vtkScalarBarBox* box;
  if (above)
  {
    if (!this->DrawAboveRangeSwatch)
    {
      return;
    }
    swatch = this->P->AboveRangeSwatch;
    box = &this->P->AboveRangeSwatchBox;
  }
  else
  {
    if (!this->DrawBelowRangeSwatch)
    {
      return;
    }
    swatch = this->P->BelowRangeSwatch;
    box = &this->P->BelowRangeSwatchBox;
  }

  int numPts = 4;
  vtkNew<vtkPoints> pts;
  pts->SetNumberOfPoints(numPts);
  vtkNew<vtkCellArray> polys;
  polys->AllocateEstimate(1, 4);
  vtkNew<vtkUnsignedCharArray> colors;
  colors->SetNumberOfComponents(4); // RGBA
  colors->SetNumberOfTuples(1);

  swatch->Initialize();
  swatch->SetPoints(pts);
  swatch->SetPolys(polys);
  swatch->GetCellData()->SetScalars(colors);
  double x[3];
  x[2] = 0.;

  for (int j = 0; j < 2; ++j)
  {
    x[j] = box->Posn[j];
  }
  int i = 0;
  pts->SetPoint(i++, x);
  x[0] += box->Size[this->P->TL[0]];
  pts->SetPoint(i++, x);
  x[1] += box->Size[this->P->TL[1]];
  pts->SetPoint(i++, x);
  x[0] -= box->Size[this->P->TL[0]];
  pts->SetPoint(i++, x);

  // Add the swatch to the polydata and color it:
  double rgba[4] = { 1.0, 1.0, 1.0, 1.0 };
  vtkIdType ptIds[4] = { 0, 1, 2, 3 };
  polys->InsertNextCell(4, ptIds);
  this->LookupTable->GetIndexedColor(-1, rgba);

  vtkLookupTable* lt = vtkLookupTable::SafeDownCast(this->LookupTable);
  vtkColorTransferFunction* ctf = vtkColorTransferFunction::SafeDownCast(this->LookupTable);

  if (lt)
  {
    if (above)
    {
      lt->GetAboveRangeColor(rgba);
    }
    else
    {
      lt->GetBelowRangeColor(rgba);
    }
  }
  else if (ctf)
  {
    if (above)
    {
      ctf->GetAboveRangeColor(rgba);
    }
    else
    {
      ctf->GetBelowRangeColor(rgba);
    }
  }

  unsigned char* rgb = colors->GetPointer(0); // write into array directly
  rgb[0] = static_cast<unsigned char>(rgba[0] * 255.);
  rgb[1] = static_cast<unsigned char>(rgba[1] * 255.);
  rgb[2] = static_cast<unsigned char>(rgba[2] * 255.);
  rgb[3] = static_cast<unsigned char>(this->UseOpacity ? rgba[3] * 255. : 255.);
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::ConfigureAnnotations()
{
  // I. Create an actor for each valid label.
  int numNotes = static_cast<int>(this->P->Labels.size());
  if (!numNotes)
  {
    return;
  }

  this->P->AnnotationLabels.resize(numNotes);
  this->P->AnnotationAnchors.resize(numNotes);
  this->P->AnnotationColors.resize(numNotes);
  int i = 0;
  for (auto it = this->P->Labels.begin(); it != this->P->Labels.end(); ++it, ++i)
  {
    this->P->AnnotationAnchors[i] = it->first;
    this->P->AnnotationColors[i] = this->P->LabelColors[it->first];
    this->P->AnnotationLabels[i] = vtkSmartPointer<vtkTextActor>::New();
    if (this->P->Viewport && this->AnnotationTextScaling)
    {
      this->P->AnnotationLabels[i]->SetTextScaleModeToViewport();
      this->P->AnnotationLabels[i]->ComputeScaledFont(this->P->Viewport);
    }
    this->P->AnnotationLabels[i]->GetTextProperty()->ShallowCopy(this->AnnotationTextProperty);

    this->P->AnnotationLabels[i]->SetProperty(this->GetProperty());
    // NB: If passed an empty string, pass a single space to the renderer;
    // empty strings get rendered as blobs which is Highly Undesirable.
    // Also render an empty string if the annotation can't be placed on the bar.
    this->P->AnnotationLabels[i]->SetInput(it->second.c_str());
    this->P->AnnotationLabels[i]->GetPositionCoordinate()->SetReferenceCoordinate(
      this->PositionCoordinate);
  }

  // Position each label and, in indexed mode, create the color swatches.
  if (this->LookupTable->GetIndexedLookup())
  {
    int indexedDenom = this->P->NumNotes;
    // Must reset the color on the leader lines since vtkTextProperty
    // doesn't inherit vtkProperty.
    // FIXME: Only set leader color when CellData scalars aren't used.
    this->P->AnnotationLeadersActor->GetProperty()->SetColor(
      this->AnnotationTextProperty->GetColor());
    this->P->AnnotationLeadersActor->GetProperty()->SetOpacity(
      this->AnnotationTextProperty->GetOpacity());
    // this->ScalarBar will not be drawn; instead, draw padded boxes
    // and leaders to labels for each annotated value.
    // Since labels are user-provided, we render with vtkTextActor
    // to allow fancy-ness.
    // 2 triangles per annotation: half-opaque, half-translucent:
    int numPts = 4 * indexedDenom;
    vtkPoints* pts = vtkPoints::New();
    pts->SetNumberOfPoints(numPts);
    vtkCellArray* polys = vtkCellArray::New();
    polys->AllocateEstimate(2 * indexedDenom, 3);
    vtkUnsignedCharArray* colors = vtkUnsignedCharArray::New();
    colors->SetNumberOfComponents(4); // RGBA
    colors->SetNumberOfTuples(2 * indexedDenom);

    this->P->AnnotationBoxes->Initialize();
    this->P->AnnotationBoxes->SetPoints(pts);
    this->P->AnnotationBoxes->SetPolys(polys);
    this->P->AnnotationBoxes->GetCellData()->SetScalars(colors);
    // this->P->AnnotationBoxes->SetProperty( this->GetProperty() );
    pts->Delete();
    polys->Delete();
    colors->Delete();

    // Use the nicely-provided scalar bar position to place
    // the annotated value swatches.
    double swatchC0, swatchC1; // fixed swatch coordinates
    double delta = static_cast<double>(this->P->ScalarBarBox.Size[1]) / indexedDenom;
    double x[3];
    x[2] = 0.;
    unsigned char* rgb;
    vtkIdType ptIds[4];
    swatchC0 = this->P->ScalarBarBox.Posn[this->P->TL[0]];
    swatchC1 = swatchC0 + this->P->ScalarBarBox.Size[0];
    if (this->Orientation == VTK_ORIENT_VERTICAL)
    {
      this->PlaceAnnotationsVertically(
        this->TextPosition == vtkScalarBarActor::SucceedScalarBar ? swatchC0 : swatchC1,
        this->P->ScalarBarBox.Posn[1], this->P->ScalarBarBox.Size[this->P->TL[0]],
        this->P->ScalarBarBox.Size[this->P->TL[1]], delta, this->P->SwatchPad);
      double top = this->P->ScalarBarBox.Posn[1] + this->P->ScalarBarBox.Size[this->P->TL[1]];
      for (i = 0; i < indexedDenom; ++i)
      {
        x[0] = swatchC0;
        x[1] = top - i * delta - this->P->SwatchPad;
        pts->SetPoint(4 * i, x);
        x[0] = swatchC1;
        pts->SetPoint(4 * i + 1, x);
        x[1] -= delta - this->P->SwatchPad * 2;
        pts->SetPoint(4 * i + 2, x);
        x[0] = swatchC0;
        pts->SetPoint(4 * i + 3, x);
      }
    }
    else
    {
      this->PlaceAnnotationsHorizontally(this->P->ScalarBarBox.Posn[0], swatchC1,
        this->P->ScalarBarBox.Size[1], this->P->ScalarBarBox.Size[0], delta, this->P->SwatchPad);
      for (i = 0; i < indexedDenom; ++i)
      {
        x[0] = this->P->ScalarBarBox.Posn[0] + i * delta + this->P->SwatchPad;
        x[1] = swatchC0;
        pts->SetPoint(4 * i, x);
        x[0] += delta - this->P->SwatchPad * 2;
        pts->SetPoint(4 * i + 1, x);
        x[1] = swatchC1;
        pts->SetPoint(4 * i + 2, x);
        x[0] -= delta - this->P->SwatchPad * 2;
        pts->SetPoint(4 * i + 3, x);
      }
    }
    for (i = 0; i < indexedDenom; ++i)
    {
      ptIds[0] = 4 * i;
      ptIds[1] = ptIds[0] + 1;
      ptIds[2] = ptIds[0] + 2;
      polys->InsertNextCell(3, ptIds);

      ptIds[1] = ptIds[2];
      ptIds[2] = ptIds[0] + 3;
      polys->InsertNextCell(3, ptIds);

      double rgbaF[4];
      this->LookupTable->GetIndexedColor(
        i == this->P->NumNotes ? -1 : i % this->LookupTable->GetNumberOfAvailableColors(), rgbaF);
      rgb = colors->GetPointer(
        /* numComponents */ 4 *
        /* numCells/swatch */ 2 *
        /* swatch */ i); // write into array directly
      rgb[0] = static_cast<unsigned char>(rgbaF[0] * 255.);
      rgb[1] = static_cast<unsigned char>(rgbaF[1] * 255.);
      rgb[2] = static_cast<unsigned char>(rgbaF[2] * 255.);
      rgb[3] = static_cast<unsigned char>(rgbaF[3] * 255.);
      rgb[4] = static_cast<unsigned char>(rgbaF[0] * 255.);
      rgb[5] = static_cast<unsigned char>(rgbaF[1] * 255.);
      rgb[6] = static_cast<unsigned char>(rgbaF[2] * 255.);
      rgb[7] = 255; // second triangle is always opaque
    }
  }
  else
  {
    if (this->Orientation == VTK_ORIENT_VERTICAL)
    {
      this->PlaceAnnotationsVertically(this->TextPosition == vtkScalarBarActor::SucceedScalarBar
          ? this->P->ScalarBarBox.Posn[this->P->TL[0]]
          : this->P->ScalarBarBox.Posn[this->P->TL[0]] + this->P->ScalarBarBox.Size[0],
        this->P->ScalarBarBox.Posn[1], this->P->ScalarBarBox.Size[0], this->P->ScalarBarBox.Size[1],
        0., this->P->SwatchPad);
    }
    else // HORIZONTAL
    {
      this->PlaceAnnotationsHorizontally(this->P->ScalarBarBox.Posn[0],
        this->P->ScalarBarBox.Posn[1] + this->P->ScalarBarBox.Size[0],
        this->P->ScalarBarBox.Size[1], this->P->ScalarBarBox.Size[0], 0., this->P->SwatchPad);
    }
  }
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::FreeLayoutStorage()
{
  // Delete previously constructed objects
  if (this->P->Viewport && this->P->Viewport->GetVTKWindow())
  {
    vtkWindow* win = this->P->Viewport->GetVTKWindow();
    if (!this->P->TextActors.empty())
    {
      vtkScalarBarActorInternal::ActorVector::iterator it;
      for (it = this->P->TextActors.begin(); it != this->P->TextActors.end(); ++it)
      {
        (*it)->ReleaseGraphicsResources(win);
      }
    }
    for (size_t i = 0; i < this->P->AnnotationLabels.size(); ++i)
    {
      this->P->AnnotationLabels[i]->ReleaseGraphicsResources(win);
    }
  }

  this->P->TextActors.clear();
  this->P->TextActorAnchors.clear();
  this->P->AnnotationLabels.clear();
  this->P->AnnotationAnchors.clear();
  this->P->AnnotationColors.clear();
}

//------------------------------------------------------------------------------
void vtkScalarBarActor::SizeTitle(double* titleSize, int* size, vtkViewport* viewport)
{
  titleSize[0] = titleSize[1] = 0;

  if (this->Title == nullptr || !strlen(this->Title))
  {
    return;
  }

  int targetWidth, targetHeight;
  if (this->Orientation == VTK_ORIENT_VERTICAL)
  {
    targetWidth = static_cast<int>(0.9 * size[0]);
    targetHeight = static_cast<int>(0.1 * size[1]);
  }
  else
  {
    targetWidth = size[0];
    /*
       targetHeight = static_cast<int>(
       (this->LookupTable->GetIndexedLookup() ? 0.5 : 0.25) * size[1]);
     */
    if (this->LookupTable->GetIndexedLookup())
    {
      targetHeight = static_cast<int>(0.5 * size[1]);
    }
    else
    {
      double sizeTextData[2] = { 0, 0 };
      if (!this->P->TextActors.empty())
      { // Don't overlap tick-mark labels:
        this->P->TextActors[0]->GetSize(viewport, sizeTextData);
      }
      // The scalar bar takes half the height.
      // Subtract tick-label height and padding
      targetHeight = static_cast<int>((0.5 - 2 * 0.05) * size[1] - sizeTextData[1]);
    }
  }

  this->TitleActor->SetConstrainedFontSize(viewport, targetWidth, targetHeight);

  this->TitleActor->GetSize(viewport, titleSize);
}

//------------------------------------------------------------------------------
int vtkScalarBarActor::MapAnnotationLabels(
  vtkScalarsToColors* lkup, double start, double delta, const double* range)
{
  int numNotes = lkup->GetNumberOfAnnotatedValues();
  bool indexed = lkup->GetIndexedLookup() != 0;
  bool vertical = (this->Orientation == VTK_ORIENT_VERTICAL);
  vtkColor4d fltCol;
  double drange = range[1] - range[0];
  // I. If we are not in indexed mode, we must sort the labels that we can
  //    position by their order of appearance (since placement gives
  //    precedence to the median label). Hence, we use a map to accumulate
  //    labels.
  this->P->Labels.clear();
  this->P->LabelColors.clear();
  if (numNotes > 0)
  {
    for (int i = 0; i < numNotes; ++i)
    {
      std::string label = lkup->GetAnnotation(i);
      lkup->GetAnnotationColor(lkup->GetAnnotatedValue(i), fltCol.GetData());
      double x;
      bool canPositionLabel = !label.empty();
      if (canPositionLabel)
      {
        if (indexed)
        {
          // Vertical orientation in indexed lookup mode is a special case:
          // The first swatch is placed at the top (highest y coordinate).
          // All other cases (all horizontal, interval-mode vertical) order
          // labels from lowest coordinate to highest.
          x = vertical ? start + (numNotes - i - 0.5) * delta / numNotes
                       : start + (i + 0.5) * delta / numNotes;
        }
        else
        {
          vtkVariant pos = lkup->GetAnnotatedValue(i);
          x = pos.ToDouble(&canPositionLabel);
          if (canPositionLabel)
          { // Also do not draw if label is outside the scalar bar range.
            canPositionLabel = (x >= range[0] && x <= range[1]);
          }
          x = canPositionLabel ? (start + (x - range[0]) * delta / drange) : vtkMath::Nan();
        }
      }
      if (canPositionLabel)
      {
        this->P->Labels[x] = label;
        // Obtain a color for leader lines
        vtkColor3ub intCol;
        for (int j = 0; j < 3; ++j)
        {
          intCol.GetData()[j] = static_cast<unsigned char>(fltCol.GetData()[j] * 255.);
        }
        this->P->LabelColors[x] = intCol;
      }
    }
  }

  // II. Optionally add a NaN label.
  if (this->DrawNanAnnotation && this->NanAnnotation && this->NanAnnotation[0] != '\0')
  {
    // Get the NaN color
    lkup->GetIndexedColor(-1, fltCol.GetData());
    vtkColor3ub intCol;
    for (int j = 0; j < 3; ++j)
    {
      intCol.GetData()[j] = static_cast<unsigned char>(fltCol.GetData()[j] * 255.);
    }
    double x = this->P->NanBox.Posn[this->P->TL[1]] + this->P->NanBox.Size[1] / 2.;
    this->P->Labels[x] = this->NanAnnotation;
    this->P->LabelColors[x] = intCol;
  }

  // III. Optionally add a below range label.
  if (this->DrawBelowRangeSwatch && this->BelowRangeAnnotation &&
    this->BelowRangeAnnotation[0] != '\0')
  {
    // Get the below range color
    lkup->GetIndexedColor(-1, fltCol.GetData());
    vtkColor3ub intCol;
    for (int j = 0; j < 3; ++j)
    {
      intCol.GetData()[j] = static_cast<unsigned char>(fltCol.GetData()[j] * 255.);
    }
    double x =
      this->P->BelowRangeSwatchBox.Posn[this->P->TL[1]] + this->P->BelowRangeSwatchBox.Size[1] / 2.;
    this->P->Labels[x] = this->BelowRangeAnnotation;
    this->P->LabelColors[x] = intCol;
  }

  // IV. Optionally add an above range label.
  if (this->DrawAboveRangeSwatch && this->AboveRangeAnnotation &&
    this->AboveRangeAnnotation[0] != '\0')
  {
    // Get the above range color
    lkup->GetIndexedColor(-1, fltCol.GetData());
    vtkColor3ub intCol;
    for (int j = 0; j < 3; ++j)
    {
      intCol.GetData()[j] = static_cast<unsigned char>(fltCol.GetData()[j] * 255.);
    }
    double x =
      this->P->AboveRangeSwatchBox.Posn[this->P->TL[1]] + this->P->AboveRangeSwatchBox.Size[1] / 2.;
    this->P->Labels[x] = this->AboveRangeAnnotation;
    this->P->LabelColors[x] = intCol;
  }

  // V. Give subclasses a chance to edit the label map.
  this->EditAnnotations();
  return static_cast<int>(this->P->Labels.size());
}

//------------------------------------------------------------------------------
int vtkScalarBarActor::PlaceAnnotationsVertically(double barX, double barY,
  double vtkNotUsed(barWidth), double barHeight, double vtkNotUsed(delta), double pad)
{
  if (!this->LookupTable)
  {
    return 0;
  }

#define VTK_ANN_VLAYOUT(j, dir, delt)                                                              \
  do                                                                                               \
  {                                                                                                \
    ctr = this->P->AnnotationAnchors[j];                                                           \
    ll[0] = lpts->InsertNextPoint(xl0, ctr, 0.);                                                   \
    this->P->AnnotationLabels[j]->GetSize(this->P->Viewport, tsz);                                 \
    hh = (tsz[1] + pad) / 2.; /* label half-height, incl. padding */                               \
    if (((dir) < 0 && ctr + hh > dnCum) || ((dir) > 0 && ctr - hh < upCum))                        \
      ctr = (delt) + (dir)*hh;                                                                     \
    this->P->AnnotationLabels[j]->GetTextProperty()->SetJustification(                             \
      this->TextPosition == PrecedeScalarBar ? VTK_TEXT_LEFT : VTK_TEXT_RIGHT);                    \
    this->P->AnnotationLabels[j]->GetTextProperty()->SetVerticalJustificationToCentered();         \
    this->P->AnnotationLabels[j]->SetPosition(barX +                                               \
        (this->TextPosition == PrecedeScalarBar ? 1 : -1) * (pad + this->AnnotationLeaderPadding), \
      ctr);                                                                                        \
    ll[1] = lpts->InsertNextPoint(xl1, ctr, 0.);                                                   \
    llines->InsertNextCell(2, ll);                                                                 \
    llcolors->InsertNextTypedTuple(this->P->AnnotationColors[j].GetData());                        \
    if (upCum < ctr + hh)                                                                          \
      upCum = ctr + hh;                                                                            \
    if (dnCum > ctr - hh)                                                                          \
      dnCum = ctr - hh;                                                                            \
  } while (false)

  int numNotes = static_cast<int>(this->P->AnnotationLabels.size());
  vtkPoints* lpts = vtkPoints::New();
  vtkCellArray* llines = vtkCellArray::New();
  vtkUnsignedCharArray* llcolors = vtkUnsignedCharArray::New();
  llcolors->SetName("Leader Line Colors");
  llcolors->SetNumberOfComponents(3);
  llcolors->Allocate(numNotes);
  lpts->Allocate(2 * numNotes);
  llines->AllocateEstimate(numNotes, 2);
  this->P->AnnotationLeaders->Initialize();
  this->P->AnnotationLeaders->SetPoints(lpts);
  this->P->AnnotationLeaders->SetLines(llines);
  if (this->FixedAnnotationLeaderLineColor)
  {
    this->P->AnnotationLeaders->GetCellData()->SetScalars(nullptr);
  }
  else
  {
    this->P->AnnotationLeaders->GetCellData()->SetScalars(llcolors);
  }

  // Start at the center and move outward (both up and down),
  // accumulating label heights as we go.
  int ic = numNotes / 2;
  int dn, up;
  double dnCum, upCum, ctr, hh;
  double tsz[2];
  // leader-line endpoint x-coordinates:
  double xl0 = barX + (this->TextPosition == PrecedeScalarBar ? 1 : -1) * pad / 2.;
  double xl1 = barX +
    (this->TextPosition == PrecedeScalarBar ? 1 : -1) * (pad / 2. + this->AnnotationLeaderPadding);
  vtkIdType ll[2]; // leader-line endpoint IDs
  if (2 * ic == numNotes)
  {
    dn = ic - 1;
    up = ic;
    dnCum = barY + barHeight;
    upCum = barY;
  }
  else
  {
    dn = ic - 1;
    up = ic + 1;
    dnCum = barY + barHeight;
    upCum = barY;
    VTK_ANN_VLAYOUT(ic, 0, dnCum);
    /*
       cout
       << "ic: " << ic << " ctr: " << ctr << " hh: " << hh
       << " uc: " << upCum << " dc: " << dnCum
       << " t:" << this->P->AnnotationLabels[ic]->GetInput() << endl;
     */
  }
  for (; dn >= 0; --dn, ++up)
  {
    VTK_ANN_VLAYOUT(dn, -1, dnCum);
    /*
       cout
       << "dn: " << dn << " ctr: " << ctr << " hh: " << hh
       << " uc: " << upCum << " dc: " << dnCum
       << " t:" << this->P->AnnotationLabels[dn]->GetInput() << endl;
     */
    VTK_ANN_VLAYOUT(up, 1, upCum);
    /*
       cout
       << "up: " << up << " ctr: " << ctr << " hh: " << hh
       << " uc: " << upCum << " dc: " << dnCum
       << " t:" << this->P->AnnotationLabels[up]->GetInput() << endl;
     */
  }

  lpts->Delete();
  llines->Delete();
  llcolors->Delete();
  return numNotes;
}

//------------------------------------------------------------------------------
struct vtkScalarBarHLabelInfo
{
  double X[2]; // padded left-right label bounds
  double Y[2]; // padded top-bottom label bounds
  int Justification;
  double Anchor[2]; // x-y coordinates of anchor point
};

//------------------------------------------------------------------------------
// A non-overlapping label placer for a horizontal array of annotated swatches.
// When space is tight, It displaces labels vertically and uses broken leader
// lines to relate labels back to swatches.
struct vtkScalarBarHLabelPlacer
{
  std::vector<vtkScalarBarHLabelInfo> Places;
  unsigned Ctr;
  double Y0;
  double XBounds[2];
  int NumPlaced;
  double Pad;
  double LeaderPad;
  double Dir;   // displacement direction (either +1 or -1)
  bool HaveCtr; // Is a label at the dead center? (i.e., is Places.size() odd?)

  vtkScalarBarHLabelPlacer(
    unsigned n, double y0, double dir, double xmin, double xmax, double pad, double leaderPad)
    : Places(n)
    , Ctr(n % 2 ? n / 2 : n / 2 - 1)
    , Y0(y0)
    , NumPlaced(0)
    , Pad(pad)
    , LeaderPad(leaderPad)
    , Dir(dir < 0 ? -1. : 1.)
    , HaveCtr(n % 2 != 0)
  {
    this->XBounds[0] = xmin;
    this->XBounds[1] = xmax;
  }

  void Place(unsigned i, double xBest, double wd, double ht)
  {
    vtkScalarBarHLabelInfo& placement(this->Places[i]);
    unsigned farLo, farHi;
    int medNeighbor;
    int posRelToCenter = (i == this->Ctr && this->HaveCtr) ? 0 : (i > this->Ctr ? 1 : -1);

    if (posRelToCenter == 0 || this->NumPlaced == 0)
    { // center label
      placement.Y[0] = this->Y0 + this->Dir * (this->LeaderPad + this->Pad);
      // Note Y[1] has un-padded bounds on distal y axis! Required below.
      placement.Y[1] = placement.Y[0] + this->Dir * ht;
      placement.X[0] = xBest - wd / 2. - this->Pad;
      placement.X[1] = xBest + wd / 2. + this->Pad;
      placement.Justification = VTK_TEXT_CENTERED;
      placement.Anchor[0] = xBest;
      // Vertical justification changes, but Y[0] is always anchor:
      placement.Anchor[1] = placement.Y[0];
    }
    else // placing *a lateral* (as opposed to *the medial*) label.
    {
      // First: Horizontal placement.
      // Check immediate medial neighbor to see if placement can occur
      // without more displacement.
      bool needToDisplace = false;
      if (posRelToCenter == 1)
      {
        // label is right-justified;
        // placement.X[1] bounded from above by XBounds[1]

        // Furthest label we have placed so far.
        farLo = 2 * this->Ctr + (this->HaveCtr ? 0 : 1) - i;
        farHi = this->Ctr; // The closest label we might overlap is dead center.
        medNeighbor = i - 1;
        placement.Justification = VTK_TEXT_RIGHT;
        placement.X[1] = xBest;
        placement.X[0] = placement.X[1] - wd - 2 * this->Pad;
        placement.Anchor[0] = placement.X[1]; // - this->Pad;
        if (xBest - wd < this->Places[medNeighbor].X[1])
        { // We must displace
          needToDisplace = true;
        }
      }
      else // posRelToCenter == -1
      {
        // label is left-justified;
        // placement.X[0] bounded from below by XBounds[0] or left neighbor

        // The center label is the closest label we might overlap:
        farLo = this->Ctr + (this->HaveCtr ? 0 : 1);
        // The furthest label to the right we have placed so far.
        farHi = 2 * this->Ctr - i - (this->HaveCtr ? 1 : 0);
        medNeighbor = i + 1;
        if (!this->HaveCtr && medNeighbor >= static_cast<int>(farHi))
        {
          medNeighbor = -1;
        }
        placement.Justification = VTK_TEXT_LEFT;
        placement.X[0] = xBest;
        placement.X[1] = placement.X[0] + wd + 2 * this->Pad;
        placement.Anchor[0] = placement.X[0];
        if (medNeighbor >= 0 && xBest + wd > this->Places[medNeighbor].X[0])
        {
          // we must displace; put the label where it makes sense:
          // bounded on left by swatch edge.
          needToDisplace = true;
        }
      }
      // Second: Vertical placement. Displace label to avoid overlap if need be.
      if (!needToDisplace)
      {
        placement.Y[0] = (medNeighbor >= 0 ? this->Places[medNeighbor].Y[0]
                                           : this->Y0 + this->Dir * (this->LeaderPad + this->Pad));
        placement.Y[1] = placement.Y[0] + this->Dir * ht;
        // placement.X[1] = placement.X[0] + wd;
        placement.Anchor[1] = placement.Y[0];
      }
      else
      { // must displace... find out by how much
        // I. At least as much as immediate medial neighbor
        placement.Y[0] = this->Places[medNeighbor].Y[1] + this->Dir * this->Pad;
        for (unsigned j = farLo; j <= farHi; ++j)
        {
          // II. Check whether label has any y overlap && any x overlap.
          // There are 2 cases: one for labels above swatches, the other
          // for labels below swatches.
          if ((this->Dir < 0 && placement.Y[0] > this->Places[j].Y[1] &&
                (i > j ? placement.X[0] <= this->Places[j].X[1]
                       : placement.X[1] >= this->Places[j].X[0])) ||
            (this->Dir > 0 && placement.Y[0] < this->Places[j].Y[1] &&
              (i > j ? placement.X[0] <= this->Places[j].X[1]
                     : placement.X[1] >= this->Places[j].X[0])))
          {
            placement.Y[0] = this->Places[j].Y[1] + this->Dir * this->Pad;
          }
        }
        placement.Y[1] = placement.Y[0] + this->Dir * ht;
        // Vertical justification changes, but Y[0] is always anchor
        placement.Anchor[1] = placement.Y[0];
      }
    }
    ++this->NumPlaced;
  }

  void BreakLeader(vtkScalarBarHLabelInfo& label, double& curY, int j, vtkPoints* pts,
    vtkCellArray* lines, vtkUnsignedCharArray* colors, const vtkColor3ub& color)
  {
    vtkScalarBarHLabelInfo& other(this->Places[j]);
    vtkIdType pt;
    if (label.Anchor[0] > other.X[0] && label.Anchor[0] < other.X[1] &&
      ((this->Dir > 0 && label.Anchor[1] >= other.Y[0]) ||
        (this->Dir < 0 && label.Anchor[1] <= other.Y[0])))
    {
      pt = pts->InsertNextPoint(label.Anchor[0], other.Y[0], 0.);
      lines->InsertCellPoint(pt);
      lines->InsertNextCell(2);
      colors->InsertNextTypedTuple(color.GetData());
      curY = other.Y[1];
      pt = pts->InsertNextPoint(label.Anchor[0], curY, 0.);
      lines->InsertCellPoint(pt);
    }
  }

  // Only called after all labels are placed
  void AddBrokenLeader(int lidx, vtkPoints* pts, vtkCellArray* lines, vtkUnsignedCharArray* colors,
    const vtkColor3ub& color)
  {
    vtkIdType pt;
    vtkScalarBarHLabelInfo& label(this->Places[lidx]);

    // I. Insert first vertex near swatch:
    lines->InsertNextCell(2);
    colors->InsertNextTypedTuple(color.GetData());
    double curY = this->Y0 + this->Dir * this->Pad / 2.;
    pt = pts->InsertNextPoint(label.Anchor[0], curY, 0.);
    lines->InsertCellPoint(pt);

    // II. Loop over all labels checking for interference.
    // Where found, close current line and start new one on the other side.
    int ic = static_cast<int>(this->Places.size()) / 2;
    int lf, rt;
    bool done = false;
    if (!this->HaveCtr)
    {
      lf = ic - 1;
      rt = ic;
    }
    else
    {
      lf = ic - 1;
      rt = ic + 1;
      if (lidx == ic)
      {
        done = true;
      }
      else
      {
        // cout << "Break " << lidx << " with " << ic << "\n";
        this->BreakLeader(label, curY, ic, pts, lines, colors, color);
      }
    }
    if (!done)
    {
      for (; lf >= 0; --lf, ++rt)
      {
        if (lf == lidx)
          break;
        // cout << "Break " << lidx << " with " << lf << "\n";
        this->BreakLeader(label, curY, lf, pts, lines, colors, color);
        if (rt == lidx)
          break;
        // cout << "Break " << lidx << " with " << rt << "\n";
        this->BreakLeader(label, curY, rt, pts, lines, colors, color);
      }
    }

    // III. Finally, close the open line segment with the label anchor point.
    pt = pts->InsertNextPoint(label.Anchor[0], label.Anchor[1] - this->Dir * this->Pad / 2., 0.);
    lines->InsertCellPoint(pt);
  }
};

//------------------------------------------------------------------------------
/**\brief Non-overlapping label placer for a horizontal array of swatches.
 *
 * A set of rules are enforced during layout:
 * <ul>
 * <li> Any label may be wider than the entire legend.
 * <li> The center label should be centered on the center swatch
 * <li> No other label should extend beyond the legend's matching lateral
 *   extent (i.e., a label to the left of center
 *   should never extend beyond the left bounds of its swatch).
 * <li> To enforce this, labels may be displaced vertically (distally) away
 *   from the legend.
 * <li> Broken leaders should be drawn connecting each displaced label to
 *   its swatch, with breaks where long labels from the centerline or beyond
 *   obstruct it.
 * </ul>
 *
 * The algorithm for performing the layout enforces these rules as follows:
 * Labels are placed starting with the central (medial) label
 * and moving outwards;
 * this provides a consistent placement as the actor is resized.
 * First the horizontal label position is determined by examining the
 * width of the label and the extents of its medial neighbor (which will
 * have been placed already).
 * The vertical displacement is then computed by either copying the medial
 * neighbor's displacement (if no interference with the neighbor was required)
 * or incrementing the displacement beyond its immediate
 * neighbor and checking all other relevant labels for interference.
 */
int vtkScalarBarActor::PlaceAnnotationsHorizontally(
  double barX, double barY, double barWidth, double barHeight, double vtkNotUsed(delta), double pad)
{
  if (!this->LookupTable)
  {
    return 0;
  }

#define VTK_ANN_HLAYOUT(j, placer)                                                                 \
  do                                                                                               \
  {                                                                                                \
    this->P->AnnotationLabels[j]->GetTextProperty()->SetJustification(                             \
      (placer).Places[j].Justification);                                                           \
    this->P->AnnotationLabels[j]->GetTextProperty()->SetVerticalJustification(                     \
      (placer).Dir > 0 ? VTK_TEXT_BOTTOM : VTK_TEXT_TOP);                                          \
    this->P->AnnotationLabels[j]->SetPosition((placer).Places[j].Anchor);                          \
    (placer).AddBrokenLeader(j, lpts, llines, llcolors, this->P->AnnotationColors[j]);             \
  } while (false)

  int numNotes = static_cast<int>(this->P->AnnotationLabels.size());
  bool precede = this->TextPosition == vtkScalarBarActor::PrecedeScalarBar;
  vtkScalarBarHLabelPlacer placer(numNotes, precede ? barY : barY - barHeight, precede ? 1 : -1,
    barX, barX + barWidth, pad, this->AnnotationLeaderPadding);

  vtkPoints* lpts = vtkPoints::New();
  vtkCellArray* llines = vtkCellArray::New();
  vtkUnsignedCharArray* llcolors = vtkUnsignedCharArray::New();
  llcolors->SetName("Leader Line Color");
  llcolors->SetNumberOfComponents(3);
  llcolors->Allocate(numNotes * numNotes);
  // TODO: Improve estimates, but we don't know how many breaks there will be:
  lpts->Allocate(numNotes * numNotes);
  llines->AllocateEstimate(numNotes * numNotes, 2);
  this->P->AnnotationLeaders->Initialize();
  this->P->AnnotationLeaders->SetPoints(lpts);
  this->P->AnnotationLeaders->SetLines(llines);
  if (this->FixedAnnotationLeaderLineColor)
  {
    this->P->AnnotationLeaders->GetCellData()->SetScalars(nullptr);
  }
  else
  {
    this->P->AnnotationLeaders->GetCellData()->SetScalars(llcolors);
  }

  // Start at the center and move outward (both up and down),
  // accumulating label displacement as we go.
  int ic = numNotes / 2;
  int lf, rt;
  double tsz[2];
  if (2 * ic == numNotes)
  {
    lf = ic - 1;
    rt = ic;
  }
  else
  {
    lf = ic - 1;
    rt = ic + 1;
    this->P->AnnotationLabels[ic]->GetSize(this->P->Viewport, tsz);
    placer.Place(ic, this->P->AnnotationAnchors[ic], tsz[0], tsz[1]);
    VTK_ANN_HLAYOUT(ic, placer);
  }
  for (; lf >= 0; --lf, ++rt)
  {
    this->P->AnnotationLabels[lf]->GetSize(this->P->Viewport, tsz);
    placer.Place(lf, this->P->AnnotationAnchors[lf], tsz[0], tsz[1]);
    VTK_ANN_HLAYOUT(lf, placer);
    this->P->AnnotationLabels[rt]->GetSize(this->P->Viewport, tsz);
    placer.Place(rt, this->P->AnnotationAnchors[rt], tsz[0], tsz[1]);
    VTK_ANN_HLAYOUT(rt, placer);
  }

  lpts->Delete();
  llines->Delete();
  llcolors->Delete();
  return numNotes;
}
VTK_ABI_NAMESPACE_END
