Contour filter - add nodes programatically

I’m struggling to add nodes programatically. I can add one, but then things crash when I try to add a second or try to interact to add more nodes.
I have modified this example with about 6 more lines near the end as shown below https://vtk.org/Wiki/VTK/Examples/Cxx/PolyData/PolygonalSurfaceContourLineInterpolator

#include <vtkVersion.h>
#include "vtkSmartPointer.h"

#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkCellArray.h"
#include "vtkImageDataGeometryFilter.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkPolyDataCollection.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSphereSource.h"
#include "vtkTriangleFilter.h"
#include "vtkXMLPolyDataReader.h"

#include "vtkContourWidget.h"
#include "vtkOrientedGlyphContourRepresentation.h"
#include "vtkPolygonalSurfacePointPlacer.h"
#include "vtkPolygonalSurfaceContourLineInterpolator.h"


int main(int argc, char *argv[])
{
  vtkSmartPointer<vtkPolyData> polyData;
  if (argc < 2)
    {
    vtkSmartPointer<vtkSphereSource> sphereSource = 
      vtkSmartPointer<vtkSphereSource>::New();
    sphereSource->SetThetaResolution(40);
    sphereSource->SetPhiResolution(20);
    sphereSource->Update();

    polyData = sphereSource->GetOutput();
    }
  else
    {
    vtkSmartPointer<vtkXMLPolyDataReader> reader = 
      vtkSmartPointer<vtkXMLPolyDataReader>::New();
    reader->SetFileName(argv[1]);
    reader->Update();
    polyData = reader->GetOutput();
    }
      
  // The Dijkistra interpolator will not accept cells that aren't triangles
  vtkSmartPointer<vtkTriangleFilter> triangleFilter = 
    vtkSmartPointer<vtkTriangleFilter>::New();
#if VTK_MAJOR_VERSION <= 5
  triangleFilter->SetInput( polyData );
#else
  triangleFilter->SetInputData( polyData );
#endif
  triangleFilter->Update();
  
  vtkSmartPointer<vtkPolyData> pd = triangleFilter->GetOutput();
  
  //Create a mapper and actor
  vtkSmartPointer<vtkPolyDataMapper> mapper = 
    vtkSmartPointer<vtkPolyDataMapper>::New();
  mapper->SetInputConnection(triangleFilter->GetOutputPort());
 
  vtkSmartPointer<vtkActor> actor = 
    vtkSmartPointer<vtkActor>::New();
  actor->SetMapper(mapper);
  actor->GetProperty()->SetInterpolationToFlat();
  
  // Create the render window, renderer and interactor.
 
  vtkSmartPointer<vtkRenderer> renderer = 
    vtkSmartPointer<vtkRenderer>::New();
  vtkSmartPointer<vtkRenderWindow> renderWindow = 
    vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->AddRenderer(renderer);
  vtkSmartPointer<vtkRenderWindowInteractor> interactor = 
    vtkSmartPointer<vtkRenderWindowInteractor>::New();
  interactor->SetRenderWindow(renderWindow);
  
  // Add the actors to the renderer, set the background and size
  
  renderer->AddActor(actor);
  renderer->SetBackground (.3, .4, .5);

  // Here comes the contour widget stuff...

  vtkSmartPointer<vtkContourWidget> contourWidget = 
    vtkSmartPointer<vtkContourWidget>::New();
  contourWidget->SetInteractor(interactor);
  vtkSmartPointer<vtkOrientedGlyphContourRepresentation> rep = 
    vtkOrientedGlyphContourRepresentation::SafeDownCast(
      contourWidget->GetRepresentation());
  rep->GetLinesProperty()->SetColor(1, 0.2, 0);
  rep->GetLinesProperty()->SetLineWidth(3.0);

  vtkSmartPointer<vtkPolygonalSurfacePointPlacer> pointPlacer =
    vtkSmartPointer<vtkPolygonalSurfacePointPlacer>::New();
  pointPlacer->AddProp(actor);
  pointPlacer->GetPolys()->AddItem( pd );
  rep->SetPointPlacer(pointPlacer);

  vtkSmartPointer<vtkPolygonalSurfaceContourLineInterpolator> interpolator =
    vtkSmartPointer<vtkPolygonalSurfaceContourLineInterpolator>::New();
  interpolator->GetPolys()->AddItem( pd );
  rep->SetLineInterpolator(interpolator);
  
  renderWindow->Render();
  interactor->Initialize();

  contourWidget->EnabledOn();
  contourWidget->Initialize();
  contourWidget->EnabledOff();

  double pt1[3]; pt1[0] = -0.107433; pt1[1] = 0.112549; pt1[2] = 0.474558;
  contourWidget->SetWidgetState(vtkContourWidget::Define);
  contourWidget->GetContourRepresentation()->AddNodeAtWorldPosition(pt1);

  //double pt2[3]; pt2[0] = 0.187556; pt2[1] = 0.187556; pt2[2] = 0.42192;
  //contourWidget->GetContourRepresentation()->AddNodeAtWorldPosition(pt2); // This causes a crash!
 
  contourWidget->EnabledOn();
  interactor->Start();

  return EXIT_SUCCESS;
}

We’ve struggled with trying to make VTK widgets work in our application for several years. It was extremely complicated to update state of widgets from outside (we needed that because we had to display widgets in multiple views and VTK widgets cannot share any information), it was very slow (because many objects are created for each handle, etc.), GUI->widget event translation was very rigid (not possible to define shortcuts for specific widget states, basic events, such as click and double-click was very complicated to implement, …), design of various widgets was very inconsistent (there seemed to ne 2-3 different generations of widgets, each suffering from different limitations).

We finally gave up on using VTK’s widgets infrastructure and developed on our own (based on MRML library in 3D Slicer). It took several months to recreate basic widgets (point list, line, angle, open and closed curves, image slice intersection, window/level, camera), but now we can do things that we were just dreaming about in the last few years. Things are much simpler, most VTK data objects (point positions, pickers, interpolators, locators) are stored only once and shared between all widget instances in views, speed is magnitudes faster (we could not have more than a few hundred interactive handles because interaction slowed down with them so much; now we can easily interact with tens of thousands of points).

Unfortunately, this implementation cannot be easily detached from the MRML library, so if you are suffering from the pain of trying to make VTK widgets work then you may either need to develop your own (from scratch or based on our implementation) or use MRML or 3D Slicer. Maybe in a few years we will revisit if the widgets can be decoupled from MRML and merge back to VTK, but for now we are focusing on adding more widgets, improve virtual reality/touchpad/multi-touch support, etc.

2 Likes

I had some similar problems to add points to the vtkcontourLineWidget. First I came up with deactivating the vtkPolygonalSurfaceContouLineInterpolator(which actually caused the crash in my case). But the curves didn’t match the contour precisely. Finaly I found a solution for it ( based on the same example:

indent preformatted text by 4 spaces
#include <vtkSmartPointer.h>

#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkCellArray.h>
#include <vtkImageDataGeometryFilter.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolyDataCollection.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSphereSource.h>
#include <vtkTriangleFilter.h>
#include <vtkXMLPolyDataReader.h>
#include <vtkXMLPolyDataWriter.h>

#include <vtkContourWidget.h>
#include <vtkOrientedGlyphContourRepresentation.h>
#include <vtkPolygonalSurfacePointPlacer.h>
#include <vtkPolygonalSurfaceContourLineInterpolator.h>
#include

int main(int argc, char *argv[])
{
vtkSmartPointer polyData;
if (argc < 2)
{
vtkSmartPointer sphereSource =
vtkSmartPointer::New();
sphereSource->SetThetaResolution(80);
sphereSource->SetPhiResolution(80);
sphereSource->SetEndTheta(180);
sphereSource->SetRadius(0.5);
sphereSource->Update();

polyData = sphereSource->GetOutput();

}
else
{
vtkSmartPointer reader =
vtkSmartPointer::New();
reader->SetFileName(argv[1]);
reader->Update();
polyData = reader->GetOutput();
}

// The Dijkistra interpolator will not accept cells that aren’t triangles
vtkSmartPointer triangleFilter =
vtkSmartPointer::New();
triangleFilter->SetInputData( polyData );
triangleFilter->Update();

vtkSmartPointer pd = triangleFilter->GetOutput();

//Create a mapper and actor
vtkSmartPointer mapper =
vtkSmartPointer::New();
mapper->SetInputConnection(triangleFilter->GetOutputPort());

vtkSmartPointer actor =
vtkSmartPointer::New();
actor->SetMapper(mapper);
actor->GetProperty()->SetInterpolationToFlat();

// Create the render window, renderer and interactor.

vtkSmartPointer renderer =
vtkSmartPointer ::New();
vtkSmartPointer renderWindow =
vtkSmartPointer ::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer interactor =
vtkSmartPointer ::New();
interactor->SetRenderWindow(renderWindow);

// Add the actors to the renderer, set the background and size

renderer->AddActor(actor);
renderer->SetBackground (.3, .4, .5);

// Here comes the contour widget stuff…

vtkSmartPointer interpolator =
vtkSmartPointer::New();
interpolator->GetPolys()->AddItem( pd );

vtkSmartPointer contourWidget =
vtkSmartPointer::New();
contourWidget->SetInteractor(interactor);
contourWidget->EnabledOn();
interactor->Initialize();

vtkSmartPointer rep =
dynamic_cast<vtkOrientedGlyphContourRepresentation*>(
contourWidget->GetRepresentation());
rep->GetLinesProperty()->SetColor(1, 0.2, 0);
rep->GetLinesProperty()->SetLineWidth(3.0);
rep->SetLineInterpolator(interpolator);

vtkSmartPointer pointPlacer =
vtkSmartPointer::New();
rep->SetPointPlacer(pointPlacer);
rep->SetRenderer(renderer);
rep->SetShowSelectedNodes(true);
rep->Modified();
renderWindow->Render();
pointPlacer->AddProp(actor);
pointPlacer->GetPolys()->AddItem( pd );
pointPlacer->Modified();

// put some Points to the pointplacer and the representation
pointPlacer->UpdateNodeWorldPosition(polyData->GetPoint(2914), 2914);
pointPlacer->UpdateNodeWorldPosition(polyData->GetPoint(3861), 3861);
pointPlacer->UpdateNodeWorldPosition(polyData->GetPoint(2696), 2696);
rep->AddNodeAtWorldPosition(polyData->GetPoint(2914));
rep->AddNodeAtWorldPosition(polyData->GetPoint(3861));
rep->AddNodeAtWorldPosition(polyData->GetPoint(2696));

//contourWidget->Initialize(polyData, 1, idList);
contourWidget->SetWidgetState(1);
rep->SetVisibility(true);
interactor->Start();

return EXIT_SUCCESS;
}

Hi there,
I am quite new to vtk, and I was wondering based on the code example given here, how would you change the color of a specific point in the contour given its coordinates?
I know how to change all points colors, for example:
rep->GetProperty()->SetColor(0.0, 0.0, 0.0)

Thanks a lot for the help!

And this MRLM library could be used in another VTK application ? And there is a little examples of how to accomplish and use that ? If is possible, where can be found this MRLM component ?

Kindly thank you for this valuable informations and honesty.

It is the other way around: you can use VTK (and any other Python packages, C++ libraries, etc.) in this application. We might package the MRML library as a standalone Python package, but this is not planned in the near future.

I already use VTK in my app, I am trying to position programmatically seed points and line widgets and seem to be not so easy.

I have a little question: MRML library would be available as C++ flavour, not Python only, right ?

MRML is fully Python-wrapped, so all features are available from Python just as well as C++. In fact nowadays most developers use Python to extend and customize 3D Slicer.