What is the problem with screen shaking

I used a netizen’s own tool to complete the mask cutting under remote rendering, the vtkInteractorStyleDrawPolygon code is below.

import numpy as np
import vtkmodules.all as vtk
from vtkmodules.util import numpy_support as nps
from vtkmodules.vtkCommonCore import vtkCommand


class InteractorStyleDrawPolygon(vtk.vtkInteractorStyle):

    """
    InteractorStyleDrawPolygon is a re-write of the vtk class of the same name in python.
    The python class vtk.vtkInteractorStyleDrawPolygon is not a full wrap of the underlying
    ++ class. For example, vtkInteractorStyleDrawPolygon doesn't have a method for
    'GetPolygonPoints' in Python. It's in the header, source and documentation, but not python.
    Reports are this is because it returns a vtkVector2i, a template based generic class, which
    is difficult to wrap.
    References:
    - http://vtk.1045678.n5.nabble.com/Missing-method-for-vtkInteractorStyleDrawPolygon-td5747100.html
    - https://gitlab.kitware.com/vtk/vtk/blob/master/Interaction/Style/vtkInteractorStyleDrawPolygon.h
    - https://gitlab.kitware.com/vtk/vtk/-/blob/master/Interaction/Style/vtkInteractorStyleDrawPolygon.cxx
    """

    class vtkInternal(object):

        """
        Class internal to InteractorStyleDrawPolygon to help with drawing the polygon
        """

        def __init__(self, parent=None):
            super().__init__()
            self._points = []

        @property
        def points(self):
            return np.array(self._points)

        def AddPoint(self, x, y):
            self._points.append([x,y])

        def GetPoint(self, index):
            if index < 0 or index > len(self._points)-1: return
            return np.array(self._points[index])

        def GetNumberOfPoints(self):
            return len(self._points)

        def Clear(self):
            self._points = []

        def DrawPixels(self, StartPos, EndPos, pixels, size):
            # C++ args: const vtkVector2i& StartPos, const vtkVector2i& EndPos, unsigned char* pixels, const int* size
            # NOTE: ^ operator = bitwise exclusive OR. Same in C++ and Python
            length = int(round(np.linalg.norm(StartPos-EndPos)))
            if length == 0: return
            x1, y1 = StartPos
            x2, y2 = EndPos
            x = [int(round(v)) for v in np.linspace(x1,x2,length)]
            y = [int(round(v)) for v in np.linspace(y1,y2,length)]
            indices = np.array([row * size[0] + col for col,row in zip(x,y)])
            pixels[indices] = 255 ^ pixels[indices]

    def __init__(self, parent=None, interactor=None, renderer=None):
        self.parent     = parent
        self.interactor = interactor
        self.renderer   = renderer
        self.AddObserver("MouseMoveEvent", self.OnMouseMove)
        self.AddObserver("LeftButtonPressEvent", self.OnLeftButtonDown)
        self.AddObserver("LeftButtonReleaseEvent", self.OnLeftButtonUp)
        self.setup()

    def setup(self):
        self.Internal = self.vtkInternal()
        self.StartPosition = np.zeros(2, dtype=np.int32)
        self.EndPosition   = np.zeros(2, dtype=np.int32)
        self.Moving = False
        self.DrawPolygonPixels = True
        self.PixelArray = vtk.vtkUnsignedCharArray()

    def DrawPolygon(self):
        """
        Draw the polygon defined by the mouse move points
        """
        tmpPixelArray = vtk.vtkUnsignedCharArray()
        tmpPixelArray.DeepCopy(self.PixelArray)
        pixels = nps.vtk_to_numpy(tmpPixelArray)
        renWin = self.interactor.GetRenderWindow()
        size = renWin.GetSize()

        # Draw each line segment
        for i in range(self.Internal.GetNumberOfPoints()-1):
            a = self.Internal.GetPoint(i)
            b = self.Internal.GetPoint(i+1)
            self.Internal.DrawPixels(a, b, pixels, size)

        # Draw a line from the end to the start
        if (self.Internal.GetNumberOfPoints() >= 3):
            start = self.Internal.GetPoint(0)
            end   = self.Internal.GetPoint(self.Internal.GetNumberOfPoints()-1)
            self.Internal.DrawPixels(start, end, pixels, size)

        # NOTE: In SetPixelData, must add 0 as 7th variable (in C++ it has a default
        #       value of 0, but not in Python)
        # NOTE: SetPixelData takes a long time to run, particularly if the screen is
        #       maximised, which increases the number of pixels
        renWin.SetPixelData(0, 0, size[0]-1, size[1]-1, pixels.flatten(), 0, 0)
        renWin.Frame()

    def DrawPolygonPixelsOn(self):
        self.DrawPolygonPixels = True

    def DrawPolygonPixelsOff(self):
        self.DrawPolygonPixels = False

    def GetPolygonPoints(self):
        """
        Return the polygon points as a numpy array
        NOTE: This function is not available in wrapped Python vtk
        """
        return self.Internal.points

    def OnLeftButtonDown(self, obj, event):
        """
        Left mouse button press event
        """
        if self.interactor is None: return

        self.Moving = True
        renWin = self.interactor.GetRenderWindow()
        eventPos = self.interactor.GetEventPosition()
        self.StartPosition[0], self.StartPosition[1] = eventPos[0], eventPos[1]
        self.EndPosition = self.StartPosition

        self.PixelArray.Initialize()
        self.PixelArray.SetNumberOfComponents(3)
        size = renWin.GetSize()
        self.PixelArray.SetNumberOfTuples(size[0] * size[1])
        self.pixels = None

        # Note: In GetPixelData, must add 0 as 7th variable (in C++ it has a default
        #       value of 0, but not in Python)
        renWin.GetPixelData(0, 0, size[0]-1, size[1]-1, 1, self.PixelArray, 0)
        self.Internal.Clear()
        self.Internal.AddPoint(self.StartPosition[0], self.StartPosition[1])
        self.InvokeEvent(vtk.vtkCommand.StartInteractionEvent)

        # Call parent function
        #super().OnLeftButtonDown()

    def OnLeftButtonUp(self, obj, event):
        """
        Left mouse button release event
        When LMB is released, a EndPickEvent and EndInteractionEvent are emitted
        NOTE: This is different to the C++ class, which emits a SelectionChangedEvent
              instead of an EndPickEvent
        """
        if self.interactor is None or not self.Moving: return

        if self.DrawPolygonPixels:
            renWin = self.interactor.GetRenderWindow()
            size = renWin.GetSize()
            pixels = nps.vtk_to_numpy(self.PixelArray)
            renWin.SetPixelData(0, 0, size[0]-1, size[1]-1, pixels.flatten(), 0, 0)
            renWin.Frame()

        self.Moving = False
        self.InvokeEvent(vtkCommand.SelectionChangedEvent)
        self.InvokeEvent(vtkCommand.EndPickEvent)
        self.InvokeEvent(vtkCommand.EndInteractionEvent)

        # Call parent function
        #super().OnLeftButtonUp()

    def OnMouseMove(self, obj, event):
        """
        On mouse move event
        """
        if self.interactor is None or not self.Moving: return

        # Get lastest mouse position
        eventPos = self.interactor.GetEventPosition()
        self.EndPosition[0], self.EndPosition[1] = eventPos[0], eventPos[1]
        size = self.interactor.GetRenderWindow().GetSize()
        if self.EndPosition[0] > size[0]-1: self.EndPosition[0] = size[0]-1
        if self.EndPosition[0] < 0: self.EndPosition[0] = 0
        if self.EndPosition[1] > size[1]-1: self.EndPosition[1] = size[1]-1
        if self.EndPosition[1] < 0: self.EndPosition[1] = 0

        # Update the polygon to include the lastest mouse position
        lastPoint = self.Internal.GetPoint(self.Internal.GetNumberOfPoints()-1)
        newPoint  = self.EndPosition
        if np.linalg.norm(lastPoint-newPoint) > 10:
            self.Internal.AddPoint(*newPoint)
            if self.DrawPolygonPixels:
                self.DrawPolygon()

        # Call parent function
        #super().OnMouseMove()

    def SetDrawPolygonPixels(self, drawPolygonPixels):
        self.DrawPolygonPixels = drawPolygonPixels

    def __str__(self):
        """
        Replaces PrintSelf in C++ class
        """
        indent = 2*' '
        s  = super().__str__().rstrip()+'\n'
        s += f'{indent}Moving : {self.Moving}\n'
        s += f'{indent}DrawPolygonPixels: {self.DrawPolygonPixels}\n'
        s += f'{indent}StartPosition: {self.StartPosition[0]}, {self.StartPosition[1]}\n'
        s += f'{indent}EndPosition: {self.EndPosition[0]}, {self.EndPosition[1]}\n'
        return s

when I use the tool to make area selection, why does the program running in the background appear jitter, and changing jitter causes the box selection area to be wrong.
Why does the background window shrink a little when interacting with a web page?

That is just a guess but you should probably fix the interactive ratio to 1 of your remote view.

1 Like

Thank you very much, this was useful for me and really solved my problem

1 Like

Hi, Can you share the code or link for the mask cutting? Is it freestyle or use the ```
vtk.vtkAreaPicker()

Thank you.