#include <vtkActor.h>
#include <vtkActor2D.h>
#include <vtkAxesActor.h>
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkCubeSource.h>
#include <vtkDataSetMapper.h>
#include <vtkGlyph3DMapper.h>
#include <vtkInteractorStyleSwitch.h>
#include <vtkLabeledDataMapper.h>
#include <vtkLightKit.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkOrientationMarkerWidget.h>
#include <vtkPoints.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkProperty2D.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkSphereSource.h>
#include <vtkTextMapper.h>
#include <vtkTextProperty.h>
#include <vtkTransform.h>
#include <vtkTransformFilter.h>
#include <vtkUnstructuredGrid.h>
#include <vtkVersion.h>

#include <vtkBiQuadraticQuad.h>
#include <vtkBiQuadraticQuadraticHexahedron.h>
#include <vtkBiQuadraticQuadraticWedge.h>
#include <vtkBiQuadraticTriangle.h>
#include <vtkCubicLine.h>
#include <vtkQuadraticEdge.h>
#include <vtkQuadraticHexahedron.h>
#include <vtkQuadraticLinearQuad.h>
#include <vtkQuadraticLinearWedge.h>
#include <vtkQuadraticPolygon.h>
#include <vtkQuadraticPyramid.h>
#include <vtkQuadraticQuad.h>
#include <vtkQuadraticTetra.h>
#include <vtkQuadraticTriangle.h>
#include <vtkQuadraticWedge.h>
#include <vtkTriQuadraticHexahedron.h>

#include <vtk_cli11.h>
#include <vtk_fmt.h>
// clang-format off
#include VTK_FMT(fmt/format.h)
// clang-format on

#if VTK_VERSION_NUMBER >= 90020210809ULL
#define VTK_HAS_COW 1
#endif

#if VTK_HAS_COW
#include <vtkCameraOrientationWidget.h>
#endif

#include <algorithm>
#include <cstdlib>
#include <string>
#include <vector>

using cellPair =
    std::pair<vtkSmartPointer<vtkUnstructuredGrid>, std::array<double, 3>>;
using cellMap = std::map<unsigned int, cellPair>;

namespace {
/**
 * Link the unstructured grid number to the unstructured grid name.
 *
 * @return The map : {index number, unstructured grid name}
 */
std::map<unsigned int, std::string> SpecifyObjects();

/**
 * Make a map consisting of the unstructured grid name,
 *  the unstructured grid and it's orientation as
 *  Azimuth, Elevation and Zoom in degrees.
 *
 * @return The map.
 */
cellMap GetUnstructuredGrids();

// These functions return a vtkUnstructured grid corresponding to the object.
template <typename T> vtkNew<vtkUnstructuredGrid> MakeUG(vtkNew<T>);
vtkNew<vtkUnstructuredGrid> MakeQuadraticPolygon();
vtkNew<vtkUnstructuredGrid> MakeQuadraticPyramid();

/**
 * Make a tile slightly larger or smaller than the bounds in the
 *   X and Z directions and thinner or thicker in the Y direction.
 *
 * A thickness_ratio of zero reduces the tile to an XZ plane.
 *
 * @param bounds - the bounds for the tile.
 * @param expansionFactor - the expansion factor in the XZ plane.
 * @param thicknessRatio - the thickness ratio in the Y direction, >= 0.
 * @param shiftY - used to shift the centre of the plinth in the Y-direction.
 * @return An actor corresponding to the tile.
 */
vtkNew<vtkActor> MakeTile(double const bounds[],
                          double const& expansionFactor = 0.5,
                          double const& thicknessRatio = 0.05,
                          double shiftY = -0.05);

vtkNew<vtkTextProperty> GetTextProperty();
vtkNew<vtkTextProperty> GetLabelProperty();
vtkNew<vtkProperty> GetBackFaceProperty();
vtkNew<vtkProperty> GetPointActorProperty();
vtkNew<vtkProperty> GetActorProperty();
vtkNew<vtkProperty> GetTileProperty();

} // namespace

int main(int argc, char* argv[])
{
  CLI::App app{
      "Demonstrate the isoparametric cell types found in VTK. "
      "The numbers define the ordering of the points making the cell."};

  // Define options
  auto wireframeOn{false};
  app.add_flag("-w, --wireframe", wireframeOn, "Render a wireframe.");
  auto backfaceOn{false};
  app.add_flag("-b, --backface", backfaceOn,
               "Display the back face in a different colour.");
  unsigned int objectNum = -1;
  app.add_option("-o, --object_number", objectNum,
                 "The number corresponding to the object.");
  auto plinthOff{false};
  app.add_flag("-n, --noPlinth", plinthOff, "Remove the plinth.");
  CLI11_PARSE(app, argc, argv);
  if (wireframeOn && backfaceOn)
  {
    std::cerr << "error: argument -b/--backface: not allowed with argument "
                 "-w/--wireframe"
              << std::endl;
    return EXIT_FAILURE;
  }

  auto objects = SpecifyObjects();
  // The order here should match the order in specify_objects().
  std::vector<unsigned int> objectOrder{21, 22, 23, 36, 24, 25, 26, 27,
                                        28, 29, 30, 31, 32, 33, 34, 35};

  // Check for a single object.
  auto singleObject = false;
  if (objectNum != -1)
  {
    if (std::count(objectOrder.cbegin(), objectOrder.cend(), objectNum) > 0)
    {
      singleObject = true;
    }
    else
    {
      std::cerr << "Object not found.\n"
                   "Please enter the number corresponding to the object.\n"
                << "Available objects are:" << std::endl;
      for (auto obj : objectOrder)
      {
        std::cerr << fmt::format("{:s} (={:d})", objects[obj], obj)
                  << std::endl;
      }
      return EXIT_FAILURE;
    }
  }
  else
  {
    singleObject = false;
  }

  vtkNew<vtkNamedColors> colors;

  // Create one sphere for all.
  vtkNew<vtkSphereSource> sphere;
  sphere->SetPhiResolution(21);
  sphere->SetThetaResolution(21);
  sphere->SetRadius(0.04);

  auto cells = GetUnstructuredGrids();
  // The text to be displayed in the viewport.
  std::vector<std::string> names;
  //  The keys of the objects selected for display.
  std::vector<unsigned int> keys;
  if (singleObject)
  {
    names.push_back(fmt::format("{:s} (={:d})", objects[objectNum], objectNum));
    keys.push_back(objectNum);
  }
  else
  {
    for (auto obj : objectOrder)
    {
      names.push_back(fmt::format("{:s} (={:d})", objects[obj], obj));
      keys.push_back(obj);
    }
  }

  std::vector<unsigned int> addPlinth{24, 25, 12, 26, 27, 29, 31, 32, 33};
  std::vector<unsigned int> lines{21, 35};

  // Set up the viewports.
  auto gridRowDimensions = 4;
  auto gridColumnDimensions = 4;
  auto rendererSize = 300;
  if (singleObject)
  {
    gridRowDimensions = 1;
    gridColumnDimensions = 1;
    rendererSize = 1200;
  }
  std::array<int, 2> windowSize{gridColumnDimensions * rendererSize,
                                gridRowDimensions * rendererSize};

  auto blank = cells.size();
  std::vector<std::string> blankViewports;

  std::map<std::string, std::array<double, 4>> viewports;
  for (int row = 0; row < gridRowDimensions; row++)
  {
    for (int col = 0; col < gridColumnDimensions; col++)
    {
      int index = row * gridColumnDimensions + col;

      // Set the renderer's viewport dimensions (xmin, ymin, xmax, ymax)
      //  within the render window.
      // Note that for the Y values, we need to subtract the row index
      //  from grid rows because the viewport Y axis points upwards
      //  and we want to draw the grid from top to down.
      std::array<double, 4> viewport{
          static_cast<double>(col) / gridColumnDimensions,
          static_cast<double>(gridRowDimensions - row - 1) / gridRowDimensions,
          static_cast<double>(col + 1) / gridColumnDimensions,
          static_cast<double>(gridRowDimensions - row) / gridRowDimensions};
      // std::cout << viewport[0] << " " << viewport[1]
      //           << " " << viewport[2] << " "<< viewport[3] << std::endl;
      if (index < blank)
      {
        viewports[names[index]] = viewport;
      }
      else
      {
        auto s = fmt::format("vp_{:d}_{:d}", col, row);
        viewports[s] = viewport;
        blankViewports.push_back(s);
      }
    }
  }

  std::map<std::string, vtkSmartPointer<vtkRenderer>> renderers;

  vtkNew<vtkRenderWindow> renWin;
  renWin->SetWindowName("IsoparametricCellsDemo");
  renWin->SetSize(windowSize.data());

  vtkNew<vtkRenderWindowInteractor> iRen;
  iRen->SetRenderWindow(renWin);
  auto is = vtkInteractorStyleSwitch::SafeDownCast(iRen->GetInteractorStyle());
  if (is)
  {
    is->SetCurrentStyleToTrackballCamera();
  }

  // Create and link the mappers, actors and renderers together.
  std::string singleObjectName{""};
  unsigned int idx = 0;
  for (const auto& key : keys)
  {
    std::cout << "Creating: " << names[idx] << std::endl;

    if (singleObject)
    {
      singleObjectName = names[idx];
    }

    auto textProperty = GetTextProperty();
    if (singleObject)
    {
      textProperty->SetFontSize(int(rendererSize / 28));
    }
    else
    {
      textProperty->SetFontSize(int(rendererSize / 24));
    }

    vtkNew<vtkTextMapper> textMapper;
    textMapper->SetTextProperty(textProperty);
    textMapper->SetInput(names[idx].c_str());

    vtkNew<vtkActor2D> textActor;
    textActor->SetMapper(textMapper);
    textActor->SetPosition(rendererSize / 2.0, 8);

    vtkNew<vtkDataSetMapper> mapper;
    mapper->SetInputData(cells[key].first);
    vtkNew<vtkActor> actor;
    actor->SetMapper(mapper);
    actor->SetProperty(GetActorProperty());

    if (wireframeOn ||
        std::find(lines.cbegin(), lines.cend(), key) != lines.cend())
    {
      actor->GetProperty()->SetRepresentationToWireframe();
      actor->GetProperty()->SetLineWidth(2);
      actor->GetProperty()->SetOpacity(1);
      actor->GetProperty()->SetColor(colors->GetColor3d("Black").GetData());
    }
    else
    {
      if (backfaceOn)
      {
        actor->SetBackfaceProperty(GetBackFaceProperty());
      }
    }

    // Label the points.
    auto labelProperty = GetLabelProperty();
    if (singleObject)
    {
      labelProperty->SetFontSize(int(rendererSize / 36));
    }
    else
    {
      labelProperty->SetFontSize(int(rendererSize / 16));
    }

    vtkNew<vtkLabeledDataMapper> labelMapper;
    labelMapper->SetInputData(cells[key].first);
    labelMapper->SetLabelTextProperty(labelProperty);

    vtkNew<vtkActor2D> labelActor;
    labelActor->SetMapper(labelMapper);

    // Glyph the points.
    vtkNew<vtkGlyph3DMapper> pointMapper;
    pointMapper->SetInputData(cells[key].first);
    pointMapper->SetSourceConnection(sphere->GetOutputPort());
    pointMapper->ScalingOff();
    pointMapper->ScalarVisibilityOff();

    vtkNew<vtkActor> pointActor;
    pointActor->SetMapper(pointMapper);
    pointActor->SetProperty(GetPointActorProperty());

    vtkNew<vtkRenderer> renderer;
    renderer->SetBackground(colors->GetColor3d("LightSteelBlue").GetData());
    renderer->SetViewport(viewports[names[idx]].data());

    vtkNew<vtkLightKit> lightKit;
    lightKit->AddLightsToRenderer(renderer);

    renderer->AddViewProp(textActor);
    renderer->AddViewProp(actor);
    renderer->AddViewProp(labelActor);
    renderer->AddViewProp(pointActor);
    if (!plinthOff)
    {
      if (std::find(addPlinth.cbegin(), addPlinth.cend(), key) !=
          addPlinth.cend())
      {
        auto tileActor =
            MakeTile(cells[key].first->GetBounds(), 0.5, 0.01, -0.05);
        tileActor->SetProperty(GetTileProperty());
        renderer->AddViewProp(tileActor);
      }
    }

    renderer->ResetCamera();
    renderer->GetActiveCamera()->Azimuth(cells[key].second[0]);
    renderer->GetActiveCamera()->Elevation(cells[key].second[1]);
    renderer->GetActiveCamera()->Dolly(cells[key].second[2]);
    renderer->ResetCameraClippingRange();

    renderers[names[idx]] = renderer;
    renWin->AddRenderer(renderers[names[idx]]);

    ++idx;
  }

  for (const auto& key : blankViewports)
  {
    vtkNew<vtkRenderer> renderer;
    renderer->SetBackground(colors->GetColor3d("LightSteelBlue").GetData());
    renderer->SetViewport(viewports[key].data());
    renWin->AddRenderer(renderer);

    renderers[key] = renderer;
    renWin->AddRenderer(renderers[key]);
  }

#if VTK_HAS_COW
  vtkNew<vtkCameraOrientationWidget> camOrientManipulator;
#else
  vtkNew<vtkAxesActor> axes;
  vtkNew<vtkOrientationMarkerWidget> widget;
#endif

  if (singleObject)
  {
#if VTK_HAS_COW
    camOrientManipulator->SetParentRenderer(renderers[singleObjectName]);
    camOrientManipulator->SetInteractor(iRen);
    // Enable the widget.
    camOrientManipulator->On();
#else
    double rgba[4]{0.0, 0.0, 0.0, 0.0};
    colors->GetColor("Carrot", rgba);
    widget->SetOutlineColor(rgba[0], rgba[1], rgba[2]);
    widget->SetOrientationMarker(axes);
    widget->SetCurrentRenderer(renderers[singleObjectName]);
    widget->SetInteractor(iRen);
    widget->SetViewport(0.0, 0.0, 0.2, 0.2);
    widget->EnabledOn();
    widget->InteractiveOn();
#endif
  }

  iRen->Initialize();
  renWin->Render();
  iRen->Start();

  return EXIT_SUCCESS;
}

namespace {

std::map<unsigned int, std::string> SpecifyObjects()
{
  return std::map<unsigned int, std::string>{
      {21, "VTK_QUADRATIC_EDGE"},
      {22, "VTK_QUADRATIC_TRIANGLE"},
      {23, "VTK_QUADRATIC_QUAD"},
      {36, "VTK_QUADRATIC_POLYGON"},
      {24, "VTK_QUADRATIC_TETRA"},
      {25, "VTK_QUADRATIC_HEXAHEDRON"},
      {26, "VTK_QUADRATIC_WEDGE"},
      {27, "VTK_QUADRATIC_PYRAMID"},
      {28, "VTK_BIQUADRATIC_QUAD"},
      {29, "VTK_TRIQUADRATIC_HEXAHEDRON"},
      {30, "VTK_QUADRATIC_LINEAR_QUAD"},
      {31, "VTK_QUADRATIC_LINEAR_WEDGE"},
      {32, "VTK_BIQUADRATIC_QUADRATIC_WEDGE"},
      {33, "VTK_BIQUADRATIC_QUADRATIC_HEXAHEDRON"},
      {34, "VTK_BIQUADRATIC_TRIANGLE"},
      {35, "VTK_CUBIC_LINE"},
  };
}

cellMap GetUnstructuredGrids()
{
  return cellMap{
      {21, cellPair(MakeUG(vtkNew<vtkQuadraticEdge>()), {0, 0, 0.8})},
      {22, cellPair(MakeUG(vtkNew<vtkQuadraticTriangle>()), {0, 0, 0})},
      {23, cellPair(MakeUG(vtkNew<vtkQuadraticQuad>()), {0, 0, 0})},
      {36, cellPair(MakeQuadraticPolygon(), {0, 0, 0})},
      {24, cellPair(MakeUG(vtkNew<vtkQuadraticTetra>()), {20, 20, 1.0})},
      {25, cellPair(MakeUG(vtkNew<vtkQuadraticHexahedron>()), {-30, 12, 0.95})},
      {26, cellPair(MakeUG(vtkNew<vtkQuadraticWedge>()), {45, 15, 1.0})},
      {27, cellPair(MakeQuadraticPyramid(), {-110, 8, 1.0})},
      {28, cellPair(MakeUG(vtkNew<vtkBiQuadraticQuad>()), {0, 0, 0})},
      {29,
       cellPair(MakeUG(vtkNew<vtkTriQuadraticHexahedron>()), {-15, 15, 0.95})},
      {30, cellPair(MakeUG(vtkNew<vtkQuadraticLinearQuad>()), {0, 0, 0})},
      {31,
       cellPair(MakeUG(vtkNew<vtkQuadraticLinearWedge>()), {60, 22.5, 1.0})},
      {32,
       cellPair(MakeUG(vtkNew<vtkBiQuadraticQuadraticWedge>()),
                {70, 22.5, 1.0})},
      {33,
       cellPair(MakeUG(vtkNew<vtkBiQuadraticQuadraticHexahedron>()),
                {-15, 15, 0.95})},
      {34, cellPair(MakeUG(vtkNew<vtkBiQuadraticTriangle>()), {0, 0, 0})},
      {35, cellPair(MakeUG(vtkNew<vtkCubicLine>()), {0, 0, 0.85})},
  };
}

template <typename T> vtkNew<vtkUnstructuredGrid> MakeUG(vtkNew<T> aCell)
{
  double* pcoords = aCell->GetParametricCoords();
  for (int i = 0; i < aCell->GetNumberOfPoints(); ++i)
  {
    aCell->GetPointIds()->SetId(i, i);
    aCell->GetPoints()->SetPoint(i, *(pcoords + 3 * i), *(pcoords + 3 * i + 1),
                                 *(pcoords + 3 * i + 2));
  }

  vtkNew<vtkUnstructuredGrid> ug;
  ug->SetPoints(aCell->GetPoints());
  ug->InsertNextCell(aCell->GetCellType(), aCell->GetPointIds());
  return ug;
}

vtkNew<vtkUnstructuredGrid> MakeQuadraticPolygon()
{
  vtkNew<vtkQuadraticPolygon> quadraticPolygon;

  quadraticPolygon->GetPointIds()->SetNumberOfIds(8);
  quadraticPolygon->GetPointIds()->SetId(0, 0);
  quadraticPolygon->GetPointIds()->SetId(1, 1);
  quadraticPolygon->GetPointIds()->SetId(2, 2);
  quadraticPolygon->GetPointIds()->SetId(3, 3);
  quadraticPolygon->GetPointIds()->SetId(4, 4);
  quadraticPolygon->GetPointIds()->SetId(5, 5);
  quadraticPolygon->GetPointIds()->SetId(6, 6);
  quadraticPolygon->GetPointIds()->SetId(7, 7);

  quadraticPolygon->GetPoints()->SetNumberOfPoints(8);
  quadraticPolygon->GetPoints()->SetPoint(0, 0.0, 0.0, 0.0);
  quadraticPolygon->GetPoints()->SetPoint(1, 2.0, 0.0, 0.0);
  quadraticPolygon->GetPoints()->SetPoint(2, 2.0, 2.0, 0.0);
  quadraticPolygon->GetPoints()->SetPoint(3, 0.0, 2.0, 0.0);
  quadraticPolygon->GetPoints()->SetPoint(4, 1.0, 0.0, 0.0);
  quadraticPolygon->GetPoints()->SetPoint(5, 2.0, 1.0, 0.0);
  quadraticPolygon->GetPoints()->SetPoint(6, 1.0, 2.0, 0.0);
  quadraticPolygon->GetPoints()->SetPoint(7, 0.0, 1.0, 0.0);
  quadraticPolygon->GetPoints()->SetPoint(5, 3.0, 1.0, 0.0);

  vtkNew<vtkUnstructuredGrid> ug;
  ug->SetPoints(quadraticPolygon->GetPoints());
  ug->InsertNextCell(quadraticPolygon->GetCellType(),
                     quadraticPolygon->GetPointIds());
  return ug;
}

vtkNew<vtkUnstructuredGrid> MakeQuadraticPyramid()
{
  vtkNew<vtkQuadraticPyramid> aCell;

  double* pcoords = aCell->GetParametricCoords();
  for (int i = 0; i < aCell->GetNumberOfPoints(); ++i)
  {
    aCell->GetPointIds()->SetId(i, i);
    aCell->GetPoints()->SetPoint(i, *(pcoords + 3 * i), *(pcoords + 3 * i + 1),
                                 *(pcoords + 3 * i + 2));
  }

  vtkNew<vtkUnstructuredGrid> ug;
  ug->SetPoints(aCell->GetPoints());
  ug->InsertNextCell(aCell->GetCellType(), aCell->GetPointIds());

  vtkNew<vtkTransform> t;
  t->RotateX(-90);
  t->Translate(0, 0, 0);

  vtkNew<vtkTransformFilter> tf;
  tf->SetTransform(t);
  tf->SetInputData(ug);
  tf->Update();

  // Put the transformed points back.
  ug->SetPoints(tf->GetOutput()->GetPoints());

  return ug;
}

vtkNew<vtkActor> MakeTile(double const bounds[], double const& expansionFactor,
                          double const& thicknessRatio, double shiftY)
{
  std::vector<double> d_xyz = {bounds[1] - bounds[0], bounds[3] - bounds[2],
                               bounds[5] - bounds[4]};
  auto thickness = d_xyz[2] * std::abs(thicknessRatio);
  std::vector<double> center = {(bounds[1] + bounds[0]) / 2.0,
                                bounds[2] - thickness / 2.0,
                                (bounds[5] + bounds[4]) / 2.0};
  auto x_length = bounds[1] - bounds[0] + (d_xyz[0] * expansionFactor);
  auto z_length = bounds[5] - bounds[4] + (d_xyz[2] * expansionFactor);

  vtkNew<vtkCubeSource> plane;
  plane->SetCenter(center[0], center[1] + shiftY, center[2]);
  plane->SetXLength(x_length);
  plane->SetYLength(thickness);
  plane->SetZLength(z_length);

  vtkNew<vtkPolyDataMapper> planeMapper;
  planeMapper->SetInputConnection(plane->GetOutputPort());

  vtkNew<vtkActor> planeActor;
  planeActor->SetMapper(planeMapper);

  return planeActor;
}

vtkNew<vtkTextProperty> GetTextProperty()
{
  vtkNew<vtkNamedColors> colors;

  vtkNew<vtkTextProperty> pty;
  pty->BoldOn();
  pty->SetJustificationToCentered();
  pty->SetColor(colors->GetColor3d("Black").GetData());
  return pty;
}

vtkNew<vtkTextProperty> GetLabelProperty()
{
  vtkNew<vtkNamedColors> colors;

  vtkNew<vtkTextProperty> pty;
  pty->BoldOn();
  pty->ShadowOn();
  pty->SetJustificationToCentered();
  pty->SetColor(colors->GetColor3d("DeepPink").GetData());
  return pty;
}

vtkNew<vtkProperty> GetBackFaceProperty()
{
  vtkNew<vtkNamedColors> colors;

  vtkNew<vtkProperty> pty;
  pty->SetAmbientColor(colors->GetColor3d("LightSalmon").GetData());
  pty->SetDiffuseColor(colors->GetColor3d("OrangeRed").GetData());
  pty->SetSpecularColor(colors->GetColor3d("White").GetData());
  pty->SetSpecular(0.2);
  pty->SetDiffuse(1.0);
  pty->SetAmbient(0.2);
  pty->SetSpecularPower(20.0);
  pty->SetOpacity(1.0);
  return pty;
}

vtkNew<vtkProperty> GetActorProperty()
{
  vtkNew<vtkNamedColors> colors;

  vtkNew<vtkProperty> pty;
  pty->SetAmbientColor(colors->GetColor3d("DarkSalmon").GetData());
  pty->SetDiffuseColor(colors->GetColor3d("Seashell").GetData());
  pty->SetSpecularColor(colors->GetColor3d("White").GetData());
  pty->SetSpecular(0.5);
  pty->SetDiffuse(0.7);
  pty->SetAmbient(0.5);
  pty->SetSpecularPower(20.0);
  pty->SetOpacity(0.8);
  pty->EdgeVisibilityOn();
  pty->SetLineWidth(3);
  return pty;
}

vtkNew<vtkProperty> GetPointActorProperty()
{
  vtkNew<vtkNamedColors> colors;

  vtkNew<vtkProperty> pty;
  pty->SetAmbientColor(colors->GetColor3d("Gold").GetData());
  pty->SetDiffuseColor(colors->GetColor3d("Yellow").GetData());
  pty->SetSpecularColor(colors->GetColor3d("White").GetData());
  pty->SetSpecular(0.5);
  pty->SetDiffuse(0.7);
  pty->SetAmbient(0.5);
  pty->SetSpecularPower(20.0);
  pty->SetOpacity(1.0);
  return pty;
}

vtkNew<vtkProperty> GetTileProperty()
{
  vtkNew<vtkNamedColors> colors;

  vtkNew<vtkProperty> pty;
  pty->SetAmbientColor(colors->GetColor3d("SteelBlue").GetData());
  pty->SetDiffuseColor(colors->GetColor3d("LightSteelBlue").GetData());
  pty->SetSpecularColor(colors->GetColor3d("White").GetData());
  pty->SetSpecular(0.5);
  pty->SetDiffuse(0.7);
  pty->SetAmbient(0.5);
  pty->SetSpecularPower(20.0);
  pty->SetOpacity(0.8);
  pty->EdgeVisibilityOn();
  pty->SetLineWidth(1);
  return pty;
}

} // namespace
