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 |
![]() |
![]() |
Merge request reports
Activity
- Resolved by dcbr
Errors:
- commit 212ddbea is not allowed because the following files are not formatted according to the 'clang-format' check:
Rendering/Context2D/vtkBlockItem.cxx
,Views/Context2D/vtkContextInteractorStyle.cxx
. Post a comment ending in the lineDo: reformat
to rewrite the MR source branch automatically.
Warnings:
- please consider adding a changelog entry in a file ending with
.md
inDocumentation/release/dev
. - the merge request is marked as a work-in-progress.
The warnings do not need to be fixed, but it is recommended to do so.
Please rewrite commits to fix the errors listed above (adding fixup commits will not resolve the errors) and force-push the branch again to update the merge request.
- commit 212ddbea is not allowed because the following files are not formatted according to the 'clang-format' check:
- Resolved by dcbr
- Resolved by dcbr
- Resolved by Timothee Chabat
- Resolved by Timothee Chabat
- Resolved by dcbr
Hey @dcbr this looks good ! I've done a few comments on your MR but in general the solution looks right
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.Maybe there is a use case for getting the view width so let's keep them, however we might want to make the documentation more exlicit about the differences between the 2 APIs so the errors you fixed are not done again in other classes.
added 104 commits
-
64519df8...2a2b374e - 101 commits from branch
vtk:master
- de37e731 - Make charts adhere to viewport constraints.
- fc83e119 - First attempt at fixing failing tests.
- e8ed70aa - Improve docs, fix failing tests.
Toggle commit list-
64519df8...2a2b374e - 101 commits from branch
@timothee.chabat This is ready for a final review and merge.
- Resolved by Timothee Chabat
+2 , CI is green on last run
please confirm @mwestphal , I think this is ready to merge
+1, Awesome work @dcbr
- Resolved by dcbr
CI is not clean though, you need to update your fork tags
git fetch origin git push gitlab --tags
mentioned in commit 953c9c34
mentioned in commit timothee.chabat/paraview@96f99056
mentioned in commit timothee.chabat/paraview@2752852e
mentioned in commit timothee.chabat/paraview@ac5c49e2
mentioned in commit spiros.tsalikis/paraview@dcda04da
mentioned in commit mwestphal/paraview@aa910a6b
mentioned in commit timothee.chabat/paraview@aea65d66
mentioned in commit timothee.chabat/paraview@8af0b370
mentioned in commit mwestphal/paraview@a57baf6a
mentioned in commit mwestphal/paraview@1203f44a
mentioned in commit timothee.chabat/paraview@5f3b01ad
mentioned in commit timothee.chabat/paraview@4876c6ae
mentioned in commit mwestphal/paraview@154945b2
mentioned in commit timothee.chabat/paraview@10852beb
mentioned in commit timothee.chabat/paraview@466b8f1f
mentioned in commit timothee.chabat/paraview@2f53616c
mentioned in merge request paraview/paraview!6116 (merged)