4

I have a basic question as i am new to VTK. I have to draw live point cloud data in VTK. I have modified the code given in How to display point cloud in vtk in different colors?.

The pointcloud should update for number of times given in iterations(here 30). I have used Initialize() to avoid blocking control flow as mentioned in some solutions, in every iteration the point cloud is updated, and render() is called so that it can update the window with new data.

I cannot figure out why this is blocking the control flow, and the data is not updated. Only once the iterations are over, after renderWindowInteractor.Start() is called, the interaction is enabled.

import vtk
from numpy import random

class VtkPointCloud:

    def __init__(self, zMin=-10.0, zMax=10.0, maxNumPoints=1e6):
        self.maxNumPoints = maxNumPoints
        self.vtkPolyData = vtk.vtkPolyData()
        self.clearPoints()
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputData(self.vtkPolyData)
        mapper.SetColorModeToDefault()
        mapper.SetScalarRange(zMin, zMax)
        mapper.SetScalarVisibility(1)
        self.vtkActor = vtk.vtkActor()
        self.vtkActor.SetMapper(mapper)

    def addPoint(self, point):
        if self.vtkPoints.GetNumberOfPoints() < self.maxNumPoints:
            pointId = self.vtkPoints.InsertNextPoint(point[:])
            self.vtkDepth.InsertNextValue(point[2])
            self.vtkCells.InsertNextCell(1)
            self.vtkCells.InsertCellPoint(pointId)
        else:
            r = random.randint(0, self.maxNumPoints)
            self.vtkPoints.SetPoint(r, point[:])
        self.vtkCells.Modified()
        self.vtkPoints.Modified()
        self.vtkDepth.Modified()

    def clearPoints(self):
        self.vtkPoints = vtk.vtkPoints()
        self.vtkCells = vtk.vtkCellArray()
        self.vtkDepth = vtk.vtkDoubleArray()
        self.vtkDepth.SetName('DepthArray')
        self.vtkPolyData.SetPoints(self.vtkPoints)
        self.vtkPolyData.SetVerts(self.vtkCells)
        self.vtkPolyData.GetPointData().SetScalars(self.vtkDepth)
        self.vtkPolyData.GetPointData().SetActiveScalars('DepthArray')

def func(pointCloud):
    # Renderer
    renderer = vtk.vtkRenderer()
    renderer.AddActor(pointCloud.vtkActor)
    renderer.SetBackground(.2, .3, .4)
    renderer.ResetCamera()

    # Render Window
    renderWindow = vtk.vtkRenderWindow()

    renderWindow.AddRenderer(renderer)

    # Interactor
    renderWindowInteractor = vtk.vtkRenderWindowInteractor()
    renderWindowInteractor.SetRenderWindow(renderWindow)

    # Begin Interaction
    renderWindow.Render()
    renderWindowInteractor.Initialize()
    return renderWindow,renderWindowInteractor

def main(iter):
    while iter > 0:
        pointCloud = VtkPointCloud()
        for k in xrange(10000):
            point = 20*(random.rand(3)-0.5)
            pointCloud.addPoint(point)
        pointCloud.addPoint([0,0,0])
        pointCloud.addPoint([0,0,0])
        pointCloud.addPoint([0,0,0])
        pointCloud.addPoint([0,0,0])
        if iter == 30:
            renderWindow,renderWindowInteractor = func(pointCloud)
        else:
            #pointCloud.vtkPolyData.Modified()
            renderWindow.Render()
        iter -= 1
    renderWindowInteractor.Start()

main(30)
Shubham Singh
  • 73
  • 1
  • 6

2 Answers2

3

So you want to do an animation. A better practice is to follow this sample explaining how to do use a TimerEvent.

Here is what it would look like with your code:

import vtk
from numpy import random


class VtkPointCloud:

    def __init__(self, zMin=-10.0, zMax=10.0, maxNumPoints=1e6):
        self.maxNumPoints = maxNumPoints
        self.vtkPolyData = vtk.vtkPolyData()
        self.clearPoints()
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputData(self.vtkPolyData)
        mapper.SetColorModeToDefault()
        mapper.SetScalarRange(zMin, zMax)
        mapper.SetScalarVisibility(1)
        self.vtkActor = vtk.vtkActor()
        self.vtkActor.SetMapper(mapper)

    def addPoint(self, point):
        if self.vtkPoints.GetNumberOfPoints() < self.maxNumPoints:
            pointId = self.vtkPoints.InsertNextPoint(point[:])
            self.vtkDepth.InsertNextValue(point[2])
            self.vtkCells.InsertNextCell(1)
            self.vtkCells.InsertCellPoint(pointId)
        else:
            r = random.randint(0, self.maxNumPoints)
            self.vtkPoints.SetPoint(r, point[:])
        self.vtkCells.Modified()
        self.vtkPoints.Modified()
        self.vtkDepth.Modified()

    def clearPoints(self):
        self.vtkPoints = vtk.vtkPoints()
        self.vtkCells = vtk.vtkCellArray()
        self.vtkDepth = vtk.vtkDoubleArray()
        self.vtkDepth.SetName('DepthArray')
        self.vtkPolyData.SetPoints(self.vtkPoints)
        self.vtkPolyData.SetVerts(self.vtkCells)
        self.vtkPolyData.GetPointData().SetScalars(self.vtkDepth)
        self.vtkPolyData.GetPointData().SetActiveScalars('DepthArray')


class AddPointCloudTimerCallback():
    def __init__(self, renderer, iterations):
        self.iterations = iterations
        self.renderer = renderer

    def execute(self, iren, event):
        if self.iterations == 0:
            iren.DestroyTimer(self.timerId)
        pointCloud = VtkPointCloud()
        self.renderer.AddActor(pointCloud.vtkActor)
        pointCloud.clearPoints()
        for k in xrange(10000):
            point = 20*(random.rand(3)-0.5)
            pointCloud.addPoint(point)
        pointCloud.addPoint([0,0,0])
        pointCloud.addPoint([0,0,0])
        pointCloud.addPoint([0,0,0])
        pointCloud.addPoint([0,0,0])
        iren.GetRenderWindow().Render()
        if self.iterations == 30:
            self.renderer.ResetCamera()

        self.iterations -= 1

if __name__ == "__main__":
    # Renderer
    renderer = vtk.vtkRenderer()
    renderer.SetBackground(.2, .3, .4)
    renderer.ResetCamera()

    # Render Window
    renderWindow = vtk.vtkRenderWindow()

    renderWindow.AddRenderer(renderer)

    # Interactor
    renderWindowInteractor = vtk.vtkRenderWindowInteractor()
    renderWindowInteractor.SetRenderWindow(renderWindow)
    renderWindowInteractor.Initialize()

    # Initialize a timer for the animation
    addPointCloudTimerCallback = AddPointCloudTimerCallback(renderer, 30)
    renderWindowInteractor.AddObserver('TimerEvent', addPointCloudTimerCallback.execute)
    timerId = renderWindowInteractor.CreateRepeatingTimer(10)
    addPointCloudTimerCallback.timerId = timerId

    # Begin Interaction
    renderWindow.Render()
    renderWindowInteractor.Start()

Note that I renamed iter to iterations because iter is a reserved name in Python.

Vincent Vidal
  • 323
  • 2
  • 11
  • 1
    thanks, this is exactly what i wanted. Just a small correction, renderWindowInteractor should be initialized before creating the timer. – Shubham Singh May 26 '17 at 16:07
  • You are welcome. I do not see the error you mention. Also, I am quite new here, but I think that there is a button to mark a question as "resolved", for "cleaning" purpose. – Vincent Vidal May 26 '17 at 17:28
  • Thank you, excellent suggestion. I just thought at first that by "initialized" you meant "object creation" (was already the case), but in fact you meant "calling the method Initialize()". – Vincent Vidal May 26 '17 at 18:51
  • Note for performances: with high (and known) number of added elements, it is better to call `SetNumberOfPoints` (resp `SetNumberOfTuples` for depth) and add each element with `SetPoint` (resp `SetValue`). – Nico Vuaille Apr 15 '22 at 07:56
0

Using suggestion from @nico-vuaille, the solution becomes

import vtk
import numpy as np

from vtk.util.numpy_support import vtk_to_numpy, numpy_to_vtk
from vtk.numpy_interface import dataset_adapter as dsa

class VtkPointCloud:
    def __init__(self, zMin=-10.0, zMax=10.0, maxNumPoints=1e6):
        self.maxNumPoints = maxNumPoints
        self.vtkPolyData = vtk.vtkPolyData()
        self.clearPoints()
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputData(self.vtkPolyData)
        mapper.SetColorModeToDefault()
        mapper.SetScalarRange(zMin, zMax)
        mapper.SetScalarVisibility(1)
        self.vtkActor = vtk.vtkActor()
        self.vtkActor.SetMapper(mapper)
        self.count = 0
    def addPoints(self, points):
        nPoints = len(points)
        if self.vtkPoints.GetNumberOfPoints() < self.maxNumPoints:
            self.vtkPoints.SetData(dsa.numpyTovtkDataArray(points, "Points"))
            depths = points[:,2].flatten()
            self.vtkDepth.SetArray(depths,depths.size, True)
            self.vtkDepth.array = depths
            cells = np.c_[np.ones(nPoints,dtype=np.int64),
                          np.arange(nPoints)].flatten()
            self.vtkCells.SetCells(nPoints, numpy_to_vtk(cells, deep = 1, array_type = vtk.vtkIdTypeArray().GetDataType()))                
        self.vtkCells.Modified()
        self.vtkPoints.Modified()
        self.vtkDepth.Modified()

    def addPoint(self, point):
        # SetNumberOfPoints, SetNumberOfTuples... SetPoint, SetValue
        if self.vtkPoints.GetNumberOfPoints() < self.maxNumPoints:
            pointId = self.vtkPoints.InsertNextPoint(point[:])
            self.vtkDepth.InsertNextValue(point[2])
            self.vtkCells.InsertNextCell(1)
            self.vtkCells.InsertCellPoint(pointId)
        else:
            r = np.random.randint(0, self.maxNumPoints)
            self.vtkPoints.SetPoint(r, point[:])
        self.vtkCells.Modified()
        self.vtkPoints.Modified()
        self.vtkDepth.Modified()

    def clearPoints(self):
        self.vtkPoints = vtk.vtkPoints()
        self.vtkCells = vtk.vtkCellArray()
        self.vtkDepth = vtk.vtkDoubleArray()
        self.vtkDepth.SetName('DepthArray')
        self.vtkPolyData.SetPoints(self.vtkPoints)
        self.vtkPolyData.SetVerts(self.vtkCells)
        self.vtkPolyData.GetPointData().SetScalars(self.vtkDepth)
        self.vtkPolyData.GetPointData().SetActiveScalars('DepthArray')

global lort
class AddPointCloudTimerCallback():
    def __init__(self, renderer, iterations):
        self.iterations = iterations
        self.renderer = renderer

    def execute(self, iren, event):
        if self.iterations == 0:
            iren.DestroyTimer(self.timerId)
        pointCloud = VtkPointCloud()
        self.renderer.AddActor(pointCloud.vtkActor)
        pointCloud.clearPoints()
        pointCloud.addPoints(20*(np.random.rand(10000,3) - 0.5))
        pointCloud.addPoint([0,0,0])
        pointCloud.addPoint([0,0,0])
        pointCloud.addPoint([0,0,0])
        pointCloud.addPoint([0,0,0])
        iren.GetRenderWindow().Render()
        if self.iterations == 30:
            self.renderer.ResetCamera()
        self.iterations -= 1

if __name__ == "__main__":
    # Renderer
    renderer = vtk.vtkRenderer()
    renderer.SetBackground(.2, .3, .4)
    renderer.ResetCamera()

    # Render Window
    renderWindow = vtk.vtkRenderWindow()

    renderWindow.AddRenderer(renderer)

    # Interactor
    renderWindowInteractor = vtk.vtkRenderWindowInteractor()
    renderWindowInteractor.SetRenderWindow(renderWindow)
    renderWindowInteractor.Initialize()

    # Initialize a timer for the animation
    addPointCloudTimerCallback = AddPointCloudTimerCallback(renderer, 100)
    renderWindowInteractor.AddObserver('TimerEvent', addPointCloudTimerCallback.execute)
    timerId = renderWindowInteractor.CreateRepeatingTimer(10)
    addPointCloudTimerCallback.timerId = timerId

    # Begin Interaction
    renderWindow.Render()
    renderWindowInteractor.Start()
Jens Munk
  • 4,627
  • 1
  • 25
  • 40