
#include <limits>

#include <vtk/vtkActor.h>
#include <vtk/vtkActor2D.h>
#include <vtk/vtkCleanPolyData.h>
#include <vtk/vtkCubeSource.h>
#include <vtk/vtkDoubleArray.h>
#include <vtk/vtkLineSource.h>
#include <vtk/vtkLinearSubdivisionFilter.h>
#include <vtk/vtkLookupTable.h>
#include <vtk/vtkPointData.h>
#include <vtk/vtkPolyData.h>
#include <vtk/vtkPolyDataMapper.h>
#include <vtk/vtkPolyDataMapper2D.h>
#include <vtk/vtkProperty.h>
#include <vtk/vtkProperty2D.h>
#include <vtk/vtkRenderer.h>
#include <vtk/vtkRenderWindow.h>
#include <vtk/vtkRenderWindowInteractor.h>
#include <vtk/vtkSmartPointer.h>
#include <vtk/vtkTriangleFilter.h>

#define MY_SP(class, variable)\
   vtkSmartPointer<class> variable = vtkSmartPointer<class>::New();

//----------------------------------------------------------------------------------------------------

// implemented after the main
void RepresPositionCtrl2D(double dSize, vtkRenderer *ren);
void SetScalars(vtkMapper *Mapper);
void ConfigureMapperLut(vtkMapper *mapper);

//---------------------------------------------------------------------------------------------

int main(int , char* [])
{
   /////////////////////////////////////////////////////////
   MY_SP(vtkRenderer, ren1);
   ren1->SetBackground(0.2, 0.2, 0.2);
   MY_SP(vtkRenderWindow, renWin);
   renWin->SetSize( 1000, 896 );
   renWin->AddRenderer(ren1);
   MY_SP(vtkRenderWindowInteractor, iren);
   iren->SetRenderWindow(renWin);

   /////////////////////////////////////////////////////////
   MY_SP(vtkCubeSource, src);
   src->SetBounds(0,2,0,2,0,2);
   src->Update();

   MY_SP(vtkTriangleFilter, tri);
   tri->SetInputConnection(src->GetOutputPort());
   tri->Update();

   MY_SP(vtkCleanPolyData, clean);
   clean->SetInputConnection(tri->GetOutputPort());
   clean->Update();

   MY_SP(vtkLinearSubdivisionFilter, sub);
   sub->SetInputConnection(clean->GetOutputPort());
   sub->SetNumberOfSubdivisions(1);
   sub->Update();

   vtkPolyData *cube = sub->GetOutput();

   MY_SP(vtkPolyDataMapper, Map);
   Map->SetInput(cube);

   MY_SP(vtkActor, Act);
   Act->SetMapper(Map);
   Act->GetProperty()->SetInterpolationToFlat();

   ren1->AddActor(Act);

   /////////////////////////////////////////////////////////
   RepresPositionCtrl2D(0.2, ren1);

   SetScalars(Map);
   ConfigureMapperLut(Map);

   /////////////////////////////////////////////////////////
   ren1->ResetCamera();
   renWin->Render();
   iren->Start();

   /////////////////////////////////////////////////////////
   return 0;
}

//---------------------------------------------------------------------------------------------

void RepresPositionCtrl2D(double dSize, vtkRenderer *ren)
{
   MY_SP(vtkLineSource, m_LineH);
   m_LineH->SetPoint1(0.0, 0.0, 0.0);
   m_LineH->SetPoint2(dSize, 0.0, 0.0);

   MY_SP(vtkPolyDataMapper2D, m_MapperLH);
   m_MapperLH->SetInputConnection(m_LineH->GetOutputPort());
   m_MapperLH->ScalarVisibilityOff();

   MY_SP(vtkActor2D, m_ActorLH);
   m_ActorLH->GetProperty()->SetColor(1.0, 0.0, 0.0);
   m_ActorLH->GetProperty()->SetLineWidth(5);
   m_ActorLH->SetMapper(m_MapperLH);
   m_ActorLH->PickableOff();

   MY_SP(vtkCoordinate, pCoord);
   pCoord->SetCoordinateSystemToWorld();

   vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
   coord->SetCoordinateSystemToNormalizedViewport();
   coord->SetReferenceCoordinate(pCoord);
   m_MapperLH->SetTransformCoordinate(coord);

   ren->AddActor2D(m_ActorLH);
}

void SetScalars(vtkMapper *Mapper)
{
   MY_SP(vtkDoubleArray, distArray);
   int numPts = Mapper->GetInput()->GetNumberOfPoints();
   distArray->SetNumberOfValues(numPts);

   double *scalars = distArray->WritePointer(0, numPts);
   std::fill(scalars, scalars+numPts, std::numeric_limits<double>::max());
   scalars[0] = -1.0;
   scalars[1] = -0.1;
   scalars[2] = 0.0;
   scalars[3] = 0.2;
   scalars[4] = 0.4;
   scalars[5] = 0.6;
   scalars[6] = 0.8;
   scalars[7] = 10.0;

   distArray->Modified();

   Mapper->GetInput()->GetPointData()->SetScalars(distArray);
   Mapper->GetInput()->GetPointData()->Modified();
   Mapper->Update();
}

void ConfigureMapperLut(vtkMapper *mapper)
{
   if(!mapper)
      return;

   MY_SP(vtkLookupTable, lut);
   lut->SetNumberOfColors(7);
   lut->Build();
   lut->SetTableValue(0, 0.5, 0.0, 1);
   lut->SetTableValue(1, 0, 1.0, 0);
   lut->SetTableValue(2, 1, 0, 0.5);
   lut->SetTableValue(3, 0.0, 0.5, 0.0);
   lut->SetTableValue(4, 0.0, 1, 0.5);
   lut->SetTableValue(5, 0, 0.5, 1);
   lut->SetTableValue(6, 1.0, 1.0, 1.0);
   lut->SetTableRange(-0.2, 1.0);

   mapper->ScalarVisibilityOn();
   mapper->SetLookupTable(lut);
   mapper->UseLookupTableScalarRangeOn();
   ////////////////////////////////////////////////////
   mapper->InterpolateScalarsBeforeMappingOn(); // what's wrong?
   ////////////////////////////////////////////////////
   mapper->Update();
}
