#!/usr/bin/env python
from vtkmodules.vtkCommonCore import (
    vtkDoubleArray,
    vtkLookupTable,
    vtkPoints,
)
from vtkmodules.vtkCommonDataModel import (
    vtkCellArray,
    vtkPolyData,
)
from vtkmodules.vtkCommonExecutionModel import vtkTrivialProducer
from vtkmodules.vtkFiltersCore import vtkExtractEdges
from vtkmodules.vtkFiltersModeling import vtkBandedPolyDataContourFilter
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkActor2D,
    vtkPolyDataMapper,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer,
)
from vtkmodules.vtkRenderingLabel import vtkLabeledDataMapper
import vtkmodules.vtkInteractionStyle
import vtkmodules.vtkRenderingFreeType
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.util.misc import vtkGetDataRoot
VTK_DATA_ROOT = vtkGetDataRoot()

# This script reproduces issues #17473 and #16900. Both
# are related to handling scalar point values at or close to
# clip values.

# Generate the input polydata. Note that points 1 and 3 have
# scalar values that are equal to clip values
pts=vtkPoints()
s=vtkDoubleArray()
s.SetName("PointScalars")
for coord in [[0,0],[1,0],[0,1],[1,1],
              [1.5,0],[2,0],[2.5,0],[1.5,0.5],[2.5,0.5],[1.5,1],[2,1],[2.5,1]]:
    pts.InsertNextPoint(coord+[0])
for v in [16.25,17.5,17.1,20,
          15,15,15,17.5,17.5+1e-12,20,20,20]:
    s.InsertNextValue(v)
polys=vtkCellArray()
polys.InsertNextCell(4)
for pid in [0,1,3,2]:
    polys.InsertCellPoint(pid)
for cell in [[4,5,7],[5,6,8],[7,10,9],[8,11,10],[7,5,10],[8,10,5]]:
    polys.InsertNextCell(3)
    for p in cell:
        polys.InsertCellPoint(p)
ds=vtkPolyData()
ds.SetPoints(pts)
ds.GetPointData().SetScalars(s)
ds.SetPolys(polys)

src=vtkTrivialProducer()
src.SetOutput(ds)

# Generate contour bands
bands = vtkBandedPolyDataContourFilter()
bands.SetScalarModeToIndex()
bands.GenerateContourEdgesOn()
bands.ClippingOff()
bands.SetInputConnection(src.GetOutputPort())

# This should be handled safely with a warning message
bands.SetNumberOfContours(0)
bands.Update()

# Now add contour values, some of which are equal to point scalars
clip_values=[ 15.0, 16.25, 17.5, 18.75, 20 ]
for v in clip_values:
    bands.SetValue(clip_values.index(v), v)
bands.Update()
assert bands.GetOutput().GetPointData().GetScalars().GetName() == "PointScalars"

# Map the indices to clip values
out_indices=bands.GetOutput().GetCellData().GetArray("Scalars")
out_scalars=vtkDoubleArray()
out_scalars.SetName('values')
out_scalars.SetNumberOfTuples(out_indices.GetNumberOfTuples())
i = 0
while i < out_indices.GetNumberOfTuples():
    index = int(out_indices.GetValue(i))
    out_scalars.SetComponent(i,0,bands.GetValue(index))
    i+=1

# The output data with indices mapped to clip values
poly=vtkPolyData()
poly.ShallowCopy(bands.GetOutput())
poly.GetCellData().SetScalars(out_scalars)

lut = vtkLookupTable()
lut.SetRange(clip_values[0],clip_values[-1])
lut.SetRampToLinear()
lut.SetHueRange(1,1)
lut.SetSaturationRange(0,1)
lut.SetValueRange(0,1)
lut.SetNumberOfColors(len(clip_values)-1)

# Displays the contour bands
bands_mapper = vtkPolyDataMapper()
bands_mapper.SetInputDataObject(poly)
bands_mapper.ScalarVisibilityOn()
bands_mapper.SetScalarModeToUseCellData()
bands_mapper.SetScalarRange(out_scalars.GetRange())
bands_mapper.SetLookupTable(lut)
bands_mapper.UseLookupTableScalarRangeOn()
bands_actor = vtkActor()
bands_actor.SetMapper(bands_mapper)

# Displays the cell edges of the contour bands
band_cell_edges=vtkExtractEdges()
band_cell_edges.SetInputDataObject(poly)
band_cell_edges_mapper = vtkPolyDataMapper()
band_cell_edges_mapper.ScalarVisibilityOff()
band_cell_edges_mapper.SetInputConnection(band_cell_edges.GetOutputPort())
band_cell_edges_actor = vtkActor()
band_cell_edges_actor.SetMapper(band_cell_edges_mapper)
band_cell_edges_actor.GetProperty().SetColor(.4,.4,.4)

# Displays the contour edges generated by the BPDCF,
# somewhat thicker than the cell edges
band_edges_mapper = vtkPolyDataMapper()
band_edges_mapper.SetInputConnection(bands.GetOutputPort(1))
band_edges_actor = vtkActor()
band_edges_actor.SetMapper(band_edges_mapper)
band_edges_actor.GetProperty().SetColor(1,1,1)
band_edges_actor.GetProperty().SetLineWidth(1.3)

# Displays the scalars of the input points of the BPDCF
scalar_value_mapper = vtkLabeledDataMapper()
scalar_value_mapper.SetInputConnection(src.GetOutputPort())
scalar_value_mapper.SetLabelModeToLabelScalars()
scalar_value_mapper.SetLabelFormat("%1.3f")
scalar_value_actor = vtkActor2D()
scalar_value_actor.SetMapper(scalar_value_mapper)

# Set up renderer and camera
r = vtkRenderer()
r.AddViewProp(bands_actor)
r.AddViewProp(band_cell_edges_actor)
r.AddViewProp(band_edges_actor)
r.AddViewProp(scalar_value_actor)
r.SetBackground(.5,.5,.5)
cam=r.GetActiveCamera()
cam.SetPosition(1.25,.5,1)
cam.SetFocalPoint(1.25,.5,0)
cam.SetViewUp(0,1,0)
cam.SetViewAngle(90)

renWin = vtkRenderWindow()
renWin.SetSize(500,300)
renWin.AddRenderer(r)
iren=vtkRenderWindowInteractor()
iren.SetRenderWindow(renWin)
iren.Start()
