Errorbar plotting in 2D (possibly using vtkPythonItem)

For brevity I’ve attached a working solution for anyone else planning on implementing error bars or any other custom object in a 2D plot that requires scaling. The solution was to look at the parent of the PythonItem and convert the polydata of your object into the appropriate screenspace coordinates from that.

I’ve attached some example code that worked for me, it’s easy enough to modify the PythonItem example code to get it to work. I used AddItem() on one of the vtkChartXY items in my scene.


    def convert_xy_to_screen(self, x, y):

        pixel_offset = self.pixel_offset
        xy_offset = self.xy_offset
        scale = self.scale

        px = ((x - xy_offset[0]) * scale[0]) + pixel_offset[0]
        py = ((y - xy_offset[1]) * scale[1]) + pixel_offset[1]

        return px, py

    def scale_polydata(self, polydata):

        points = polydata.GetPoints()
        new_points = vtk.vtkPoints()

        for i in range(points.GetNumberOfPoints()):
            point = points.GetPoint(i)
            point2D = self.convert_xy_to_screen(point[0], point[1])
            new_points.InsertNextPoint(point2D[0], point2D[1], 0.0)

        polydata.SetPoints(new_points)

        return polydata


    def Paint(self, vtkSelf, context2D):

        parent = vtkSelf.GetParent()

        xaxis = parent.GetAxis(1)
        yaxis = parent.GetAxis(0)

        xp1 = xaxis.GetPoint1()
        xp2 = xaxis.GetPoint2()

        xmin = xaxis.GetMinimum()
        xmax = xaxis.GetMaximum()

        yp1 = yaxis.GetPoint1()
        yp2 = yaxis.GetPoint2()

        ymin = yaxis.GetMinimum()
        ymax = yaxis.GetMaximum()

        self.pixel_offset = [xp1[0], yp1[1]]
        self.xy_offset = [xmin, ymin]
        self.scale = [
            (xp2[0] - xp1[0]) / (xmax - xmin),
            (yp2[1] - yp1[1]) / (ymax - ymin),
        ]

        pen = context2D.GetPen()
        brush = context2D.GetBrush()

        pen.SetWidth(self.symbol_thickness)
        pen.SetOpacity(255)
        pen.SetColor([200, 200, 30])

        brush.SetColor(self.color)
        brush.SetOpacity(255)

        new_polydata = self.build_I_symbol()
        scaled_polydata = self.scale_polydata(new_polydata)

        context2D.DrawPolyData(
            0.0,
            0.0,
            scaled_polydata,
            self.polydata.GetCellData().GetScalars(),
            vtk.VTK_SCALAR_MODE_USE_CELL_DATA,
        )
        
        return True

The result is pretty nice, still needs a little more work to properly customise but overall I’m happy.