2
votes

Intention

I wrote a VTK application that generates a spiral using vtkPoints > vtkPolyLine > vtkPolyData > vtkPolyDataMapper and displays it. This works fine, if done static at the initialization of the program. Now, I want to add new data points dynamically. The intention is to visualize measurements in real time, so new data will be added in certain intervals.

Issues

Currently, I just implemented a TimerEvent to update the vtkPoints and vtkPolyLine. But, the program just shows the static data generated before the vtkRenderWindowInteractor was started. I also tried to use "Modified()" and "Update()" calls to nearly all objects, tried to remove, regenerate and add a new actor to the renderer -- but without success! I added my C++ code below...

Related-Questions

The following mailing list question is about this issues, but the solution given doen't work for me: http://public.kitware.com/pipermail/vtkusers/2006-November/038377.html

The following question seems to be related, but there are no useful answers: VTK: update data points in renderWindow at every simulation timestep

Questions

  1. How to tell VTK that the vtkPolyData object has changed?
  2. Which of the VTK UsersGuide should I probably have a closer look at?

Details / Source Code

I'm using Visual Studio Community 2017 and VTK 8.0.0, both compiled as Win32 target.

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkRenderingContextOpenGL2);
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);

#include <vtkSmartPointer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkConeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkProperty.h>

#include <vtkPoints.h>
#include <vtkPolyLine.h>


vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
vtkSmartPointer<vtkPolyLine> polyLine = vtkSmartPointer<vtkPolyLine>::New();

int numOfPoints = 0;
double t = 0;

void NextPoint() {
    double x = t * cos(t);
    double y = t * sin(t);
    points->InsertNextPoint(x, y, t);
    polyLine->GetPointIds()->InsertNextId(numOfPoints);

    numOfPoints++;
    t += 0.1;
}

vtkSmartPointer<vtkPolyData> generateEllipse() {
    // Add some points so we actually see something at all...
    for (int i = 0; i < 100; ++i) {
        NextPoint();
    }

    vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
    cells->InsertNextCell(polyLine);

    vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
    polyData->SetPoints(points);
    polyData->SetLines(cells);
    
    return polyData;
}


class vtkTimerCallback : public vtkCommand
{
public:
    static vtkTimerCallback *New()
    {
        vtkTimerCallback *cb = new vtkTimerCallback;
        cb->TimerCount = 0;
        return cb;
    }

    virtual void Execute(vtkObject *vtkNotUsed(caller), unsigned long eventId,
        void *vtkNotUsed(callData))
    {
        if (vtkCommand::TimerEvent == eventId)
        {
            NextPoint(); // Add another point to polyData

            ++this->TimerCount;
            cout << this->TimerCount << endl;
        }
        
    }

private:
    int TimerCount;

};



int main(int argc, char** argv) {
    vtkSmartPointer<vtkRenderWindow> renderWindow =
        vtkSmartPointer<vtkRenderWindow>::New();

    vtkSmartPointer<vtkRenderWindowInteractor> rwi = vtkSmartPointer<vtkRenderWindowInteractor>::New();
    rwi->SetRenderWindow(renderWindow);

    vtkSmartPointer<vtkPolyData> data = generateEllipse();

    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputData(data);

    vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
    actor->SetMapper(mapper);
    actor->GetProperty()->SetDiffuseColor(255, 255, 0);

    vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
    renderWindow->AddRenderer(renderer);
    renderer->AddActor(actor);
    renderer->ResetCamera();

    renderWindow->Render();
    
    // Add Timer Event...
    rwi->Initialize();
    vtkSmartPointer<vtkTimerCallback> cb = vtkSmartPointer<vtkTimerCallback>::New();
    rwi->AddObserver(vtkCommand::TimerEvent, cb);
    int timerId = rwi->CreateRepeatingTimer(100); // every 100ms
    std::cout << "timerId: " << timerId << std::endl;

    // Start Displaying...
    rwi->Start();

    return 0;
}
2

2 Answers

3
votes

the problem is that the cells are not stored by pointer - when you call cells->InsertNextCell(polyLine); the data is copied, not pointed to, in order to create an efficient storage of the cells in an array (the whole implementation is actually in the header of vtkCellArray so you can check it out). So then when you update polyLine, it has no effect in the polydata, because the polydata have their own copy that you did not update. The following code works for me (you have to expose the polydata and the cellArray):

virtual void Execute(vtkObject *vtkNotUsed(caller), unsigned long eventId,
    void *vtkNotUsed(callData))
{
    if (vtkCommand::TimerEvent == eventId)
    {
        NextPoint(); // Add another point to polyData

        cells->Initialize(); // reset the cells to remove the old spiral
        cells->InsertNextCell(polyLine); // re-insert the updated spiral
        cells->Modified(); // required to update
        data->Modified(); // required to update
        ++this->TimerCount;
        cout << polyLine->GetNumberOfPoints() << endl;
        renderWindow->Render(); // refresh the render window after each update
    }
}
1
votes

Yesterday I worked out an alternative solution using a vtkProgrammableDataObjectSource as DataSource. Tomj's solution is the more direct and simple solution... However, there is no C++ Example Code at vtk.org that explains how to use vtkProgrammableDataObjectSource and I had to work it out by trial and error. So I'll post it here, as it might help others:

#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkRenderingContextOpenGL2);
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);

#include <vtkSmartPointer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkConeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkProperty.h>
#include <vtkPoints.h>
#include <vtkPolyLine.h>
#include <vtkProgrammableFilter.h>
#include <vtkCallbackCommand.h>
#include <vtkPolyDataStreamer.h>

#include <vtkProgrammableDataObjectSource.h>


vtkSmartPointer<vtkProgrammableDataObjectSource> pDOS = vtkSmartPointer<vtkProgrammableDataObjectSource>::New();
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();

vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
vtkSmartPointer<vtkPolyLine> polyLine = vtkSmartPointer<vtkPolyLine>::New();

int numOfPoints = 0;
double t = 0;

void NextPoint() {
    double x = t * cos(t);
    double y = t * sin(t);
    points->InsertNextPoint(x, y, t);
    polyLine->GetPointIds()->InsertNextId(numOfPoints);

    numOfPoints++;
    t += 0.1;
}

void generateEllipse(void *caller) {
    vtkProgrammableDataObjectSource *pDOS = vtkProgrammableDataObjectSource::SafeDownCast((vtkObjectBase*)caller);

    vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
    cells->InsertNextCell(polyLine);

    vtkSmartPointer<vtkPolyData> polyData = vtkSmartPointer<vtkPolyData>::New();
    polyData->SetPoints(points);
    polyData->SetLines(cells);

    pDOS->SetOutput(polyData);
}


int counter2 = 0;
void TimerCallbackFunction(vtkObject* caller, long unsigned int vtkNotUsed(eventId), void* clientData, void* vtkNotUsed(callData)) {
    cout << "timer callback: " << counter2 << endl;

// To avoid globals we can implement this later... 
//  vtkSmartPointer<vtkProgrammableDataObjectSource> pDOS =
//      static_cast<vtkProgrammableDataObjectSource*>(clientData);

    vtkRenderWindowInteractor *rwi =
        static_cast<vtkRenderWindowInteractor*>(caller);

    NextPoint();

    pDOS->Modified();
    rwi->Render();
    renderer->ResetCamera(); // Optional: Reposition Camera, so it displays the whole object

    counter2++;
}



int main(int argc, char** argv) {
    vtkSmartPointer<vtkRenderWindow> renderWindow =
        vtkSmartPointer<vtkRenderWindow>::New();

    vtkSmartPointer<vtkRenderWindowInteractor> rwi = vtkSmartPointer<vtkRenderWindowInteractor>::New();
    rwi->SetRenderWindow(renderWindow);


    pDOS->SetExecuteMethod(&generateEllipse, pDOS);

    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputConnection(pDOS->GetOutputPort());

    vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
    actor->SetMapper(mapper);
    actor->GetProperty()->SetDiffuseColor(255, 255, 0);

    renderWindow->AddRenderer(renderer);
    renderer->AddActor(actor);
    renderer->ResetCamera();

    renderWindow->Render();

    // Add Timer Event...
    vtkSmartPointer<vtkCallbackCommand> timerCallback = vtkSmartPointer<vtkCallbackCommand>::New();
    timerCallback->SetCallback(TimerCallbackFunction);

    rwi->Initialize();
    rwi->CreateRepeatingTimer(100);
    rwi->AddObserver(vtkCommand::TimerEvent, timerCallback);


    // Start Displaying...
    rwi->Start();

    return 0;
}