Skip to content
Snippets Groups Projects

Make charts adhere to viewport constraints.

Merged dcbr requested to merge dcbr/vtk:multi_viewport_charts_fix into master
All threads resolved!

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 and vtkChartParallelCoordinates respect the viewport (and scene) dimensions. Additionally, tooltips are properly kept within the viewport/scene.

Resolves #18763 (closed).

Details

  1. An extra Origin attribute is added to vtkContextScene, which is set in vtkContextActor (similar to the Geometry attribute) to match the Renderer's origin.
  2. In vtkContextInteractorStyle.ConstructMouseEvent this Origin attribute is used to calculate the correct ScenePos. Before this MR, the ScenePos was exactly the same as the ScreenPos, which caused the interaction bug described in #18763 (closed).
  3. All occurrences of vtkContextMouseEvent.GetScreenPos() are replaced by vtkContextMouseEvent.GetScenePos() to use the local scene coordinates instead. The only two exceptions are for vtkChartBoxData.ScreenPosition and vtkChartPlotData.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.
  4. All occurrences of vtkContextScene.GetViewWidth and vtkContextScene.GetViewHeight are replaced by vtkContextScene.GetSceneWidth and vtkContextScene.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 image image
vtkChartXYZ image image
vtkChartParallelCoordinates image image
vtkChartHistogram2D image image
vtkChartBox image image
vtkChartPie image image
Edited by dcbr

Merge request reports

Loading
Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
    • 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 line Do: reformat to rewrite the MR source branch automatically.

      Warnings:

      • please consider adding a changelog entry in a file ending with .md in Documentation/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.

  • This topic has been reformatted and pushed; please fetch from the source repository and reset your local branch to continue with further development on the reformatted commits.

  • Kitware Robot added 1 commit

    added 1 commit

    • 0fd1168b - Make charts adhere to viewport constraints.

    Compare with previous version

  • Warnings:

    • please consider adding a changelog entry in a file ending with .md in Documentation/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.

  • dcbr resolved all threads

    resolved all threads

  • dcbr changed the description

    changed the description

  • dcbr added 1 commit

    added 1 commit

    • e57d49aa - First attempt at fixing failing tests.

    Compare with previous version

  • Warnings:

    • please consider adding a changelog entry in a file ending with .md in Documentation/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.

  • Timothee Chabat
  • Timothee Chabat
  • Timothee Chabat
  • 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 :slight_smile:

      All occurrences of vtkContextScene.GetViewWidth and vtkContextScene.GetViewHeight are replaced by vtkContextScene.GetSceneWidth and vtkContextScene.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.

  • dcbr added 1 commit

    added 1 commit

    • 64519df8 - Improve docs, fix failing tests.

    Compare with previous version

  • Warnings:

    • please consider adding a changelog entry in a file ending with .md in Documentation/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.

  • dcbr resolved all threads

    resolved all threads

  • dcbr marked this merge request as ready

    marked this merge request as ready

  • dcbr added 104 commits

    added 104 commits

    Compare with previous version

  • Warnings:

    • please consider adding a changelog entry in a file ending with .md in Documentation/release/dev.

    The warnings do not need to be fixed, but it is recommended to do so.

  • dcbr added 1 commit

    added 1 commit

    • 7bc9c6c4 - Make charts adhere to viewport constraints.

    Compare with previous version

  • Warnings:

    • please consider adding a changelog entry in a file ending with .md in Documentation/release/dev.

    The warnings do not need to be fixed, but it is recommended to do so.

  • Author Contributor

    @timothee.chabat This is ready for a final review and merge.

  • Timothee Chabat
  • +2 , CI is green on last run

    please confirm @mwestphal , I think this is ready to merge

  • Timothee Chabat resolved all threads

    resolved all threads

  • +1, Awesome work @dcbr

  • everything's clean, merging

    Do: merge

  • Timothee Chabat mentioned in commit 953c9c34

    mentioned in commit 953c9c34

  • merged

  • dcbr resolved all threads

    resolved all threads

  • Please register or sign in to reply
    Loading