Make charts adhere to viewport constraints.
Overview
This MR fixes the following two issues throughout the Charts API:
- Properly set
vtkContextMouseEvent.ScenePos
and use these scene coordinates instead of screen coordinates in the Charts API. This resolves the interaction bug in the multiple viewport case (see #18763 (closed)). - Use the Scene size instead of the RenderWindow size throughout the Charts API. This ensures
vtkChartBox
andvtkChartParallelCoordinates
respect the viewport (and scene) dimensions. Additionally, tooltips are properly kept within the viewport/scene.
Resolves #18763 (closed).
Details
- An extra
Origin
attribute is added tovtkContextScene
, which is set invtkContextActor
(similar to theGeometry
attribute) to match the Renderer's origin. - In
vtkContextInteractorStyle.ConstructMouseEvent
thisOrigin
attribute is used to calculate the correctScenePos
. Before this MR, theScenePos
was exactly the same as theScreenPos
, which caused the interaction bug described in #18763 (closed). - All occurrences of
vtkContextMouseEvent.GetScreenPos()
are replaced byvtkContextMouseEvent.GetScenePos()
to use the local scene coordinates instead. The only two exceptions are forvtkChartBoxData.ScreenPosition
andvtkChartPlotData.ScreenPosition
, which obviously still use the screen coordinates. I also haven't changed any tests accessing or setting the screen coordinates yet; this might still be necessary if they are broken after these changes. - All occurrences of
vtkContextScene.GetViewWidth
andvtkContextScene.GetViewHeight
are replaced byvtkContextScene.GetSceneWidth
andvtkContextScene.GetSceneHeight
. As they are no longer used now, they could be removed but I wasn't sure about that yet.
Example
The table below can be reproduced using this python script
import vtk
def create_xy_chart():
data = [(1, 2), (2, 0), (3, 1)]
chart = vtk.vtkChartXY()
table = vtk.vtkTable()
arrX = vtk.vtkFloatArray()
arrX.SetName(f"X")
arrY = vtk.vtkFloatArray()
arrY.SetName(f"Y")
table.AddColumn(arrX)
table.AddColumn(arrY)
numPoints = len(data)
table.SetNumberOfRows(numPoints)
for p in range(numPoints):
table.SetValue(p, 0, data[p][0])
table.SetValue(p, 1, data[p][1])
plot = chart.AddPlot(vtk.vtkChart.POINTS)
plot.SetInputData(table, 0, 1)
return chart
def create_xyz_chart():
data = [(1, 2, 3), (2, 0, 1), (3, 1, 2)]
chart = vtk.vtkChartXYZ()
table = vtk.vtkTable()
arrX = vtk.vtkFloatArray()
arrX.SetName(f"X")
arrY = vtk.vtkFloatArray()
arrY.SetName(f"Y")
arrZ = vtk.vtkFloatArray()
arrZ.SetName(f"Z")
table.AddColumn(arrX)
table.AddColumn(arrY)
table.AddColumn(arrZ)
numPoints = len(data)
table.SetNumberOfRows(numPoints)
for p in range(numPoints):
table.SetValue(p, 0, data[p][0])
table.SetValue(p, 1, data[p][1])
table.SetValue(p, 2, data[p][2])
plot = vtk.vtkPlotPoints3D()
plot.SetInputData(table)
chart.AddPlot(plot)
chart.SetMargins(vtk.vtkVector4i(40, 40, 40, 40))
return chart
def create_parallel_chart():
data = [(1, 2, 3), (2, 0, 1), (3, 1, 2)]
chart = vtk.vtkChartParallelCoordinates()
table = vtk.vtkTable()
numFields = len(data)
for f in range(numFields):
arr = vtk.vtkFloatArray()
arr.SetName(f"Field{f}")
table.AddColumn(arr)
numPoints = len(data[0])
table.SetNumberOfRows(numPoints)
for f in range(numFields):
for p in range(numPoints):
table.SetValue(p, f, data[f][p])
plot = chart.GetPlot(0)
plot.SetInputData(table)
return chart
def create_2dhist_chart():
data = [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
chart = vtk.vtkChartHistogram2D()
img = vtk.vtkImageData()
img.SetExtent(0, len(data)-1, 0, len(data[0])-1, 0, 0)
img.AllocateScalars(vtk.VTK_INT, 1)
for i in range(len(data)):
for j in range(len(data[0])):
img.SetScalarComponentFromFloat(i, j, 0, 0, data[i][j])
cs = vtk.vtkColorSeries()
cs.SetColorScheme(vtk.vtkColorSeries.BREWER_DIVERGING_SPECTRAL_11)
numColors = cs.GetNumberOfColors()
colors = [c / 255 for i in range(numColors) for c in cs.GetColor(i)]
tf = vtk.vtkColorTransferFunction()
tf.BuildFunctionFromTable(0, 4, numColors, colors)
chart.SetInputData(img)
chart.SetTransferFunction(tf)
return chart
def create_box_chart():
data = [0, 1, 1, 2, 3, 3, 4]
chart = vtk.vtkChartBox()
dataTable = vtk.vtkTable()
arrD = vtk.vtkIntArray()
arrD.SetName(f"Data")
dataTable.AddColumn(arrD)
labels = vtk.vtkStringArray()
labels.InsertNextValue(f"Data")
numData = len(data)
dataTable.SetNumberOfRows(numData)
for p in range(numData):
dataTable.SetValue(p, 0, data[p])
quartiles = vtk.vtkComputeQuartiles()
quartiles.SetInputData(dataTable)
quartiles.Update()
plot = chart.GetPlot(0)
plot.SetInputData(quartiles.GetOutput())
plot.SetLabels(labels)
chart.SetColumnVisibilityAll(True)
return chart
def create_pie_chart():
data = [1, 2, 3, 4]
chart = vtk.vtkChartPie()
table = vtk.vtkTable()
arrS = vtk.vtkFloatArray()
arrS.SetName(f"Segments")
table.AddColumn(arrS)
labels = vtk.vtkStringArray()
numPoints = len(data)
table.SetNumberOfRows(numPoints)
for p in range(numPoints):
table.SetValue(p, 0, data[p])
labels.InsertNextValue(f"Segment {p}")
plot = chart.AddPlot(0)
plot.SetInputData(table)
plot.SetInputArray(0, f"Segments")
plot.SetLabels(labels)
return chart
if __name__ == "__main__":
width, height = 600, 600
viewport_min = (0.1, 0.1)
viewport_max = (0.9, 0.9)
chart_type = "xy"
renwin = vtk.vtkRenderWindow()
renwin.SetSize(width, height)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renwin)
renderer = vtk.vtkRenderer()
renderer.SetBackground([1, 1, 1])
# x_min, y_min, x_max, y_max
renderer.SetViewport(*viewport_min, *viewport_max)
renwin.AddRenderer(renderer)
chart_scene = vtk.vtkContextScene()
chart_actor = vtk.vtkContextActor()
chart_actor.SetScene(chart_scene)
renderer.AddActor(chart_actor)
chart_scene.SetRenderer(renderer)
chart = {
"xy": create_xy_chart,
"xyz": create_xyz_chart,
"parallel": create_parallel_chart,
"2dhist": create_2dhist_chart,
"box": create_box_chart,
"pie": create_pie_chart
}[chart_type]()
chart_scene.AddItem(chart)
style = vtk.vtkContextInteractorStyle()
style.SetScene(chart_scene)
iren.SetInteractorStyle(style)
renwin.Render()
iren.Start()
Chart | Before | After |
---|---|---|
vtkChartXY |
||
vtkChartXYZ |
||
vtkChartParallelCoordinates |
||
vtkChartHistogram2D |
||
vtkChartBox |
||
vtkChartPie |
Edited by dcbr