Delaunay3DDemo

VTKExamples/Cxx/Modelling/Delaunay3DDemo


Description

This example creates a tetrahedral mesh from unorganized points. The example uses the vtkDelaunay3D filter. The resulting mesh will be a solid convex hull of the original points. The example takes the points from a XML PolyData file (.vtp) produces the 3D Delaunay tetrahedralization (both with alpha = 0 and a non-zero alpha), and displays the result on the screen.

For alpha != 0 (right window), the tetra are yellow, the lines are blue and the triangles are red.

Alpha can be changed interactively to see its affect on the resulting surface.

Question

If you have a simple question about this example contact us at VTKExamplesProject If your question is more complex and may require extended discussion, please use the VTK Discourse Forum

Code

Delaunay3DDemo.cxx

#include <vtkDelaunay3D.h>
#include <vtkSmartPointer.h>

#include <vtkCellArray.h>
#include <vtkProperty.h>
#include <vtkDataSetMapper.h>
#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkPolygon.h>
#include <vtkMath.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkCleanPolyData.h>
#include <vtkUnstructuredGrid.h>
#include <vtkCellIterator.h>
#include <vtkCellData.h>
#include <vtkUnsignedCharArray.h>
#include <vtkNamedColors.h>

#include <vtkTextProperty.h>
#include <vtkTextMapper.h>
#include <vtkActor2D.h>

#include <vtkCommand.h>
#include <vtkSliderWidget.h>
#include <vtkSliderRepresentation2D.h>

#include <sstream>

#include <vtksys/SystemTools.hxx>
#include <vtkBYUReader.h>
#include <vtkOBJReader.h>
#include <vtkPLYReader.h>
#include <vtkPolyDataReader.h>
#include <vtkSTLReader.h>
#include <vtkXMLPolyDataReader.h>
#include <vtkSphereSource.h>

namespace
{
vtkSmartPointer<vtkPolyData> ReadPolyData(const char *fileName);
}
namespace
{
void MakeWidget(vtkSmartPointer<vtkSliderWidget> &,
                vtkSmartPointer<vtkDelaunay3D> &,
                vtkSmartPointer<vtkTextMapper> &,
                vtkSmartPointer<vtkRenderer> &,
                vtkSmartPointer<vtkRenderWindowInteractor> &);
}

int main ( int argc, char *argv[] )
{
  if(argc < 2)
  {
    std::cout << "Usage: " << argv[0]
              << " filename.vtp" << std::endl;
    return EXIT_FAILURE;
  }

  vtkSmartPointer<vtkPolyData> polyData = ReadPolyData(argc > 1 ? argv[1] : "");

  auto color =
    vtkSmartPointer<vtkNamedColors>::New();

  auto originalMapper =
    vtkSmartPointer<vtkDataSetMapper>::New();
  originalMapper->SetInputData(polyData);
  originalMapper->ScalarVisibilityOff();
  originalMapper->SetScalarModeToUseCellData();

  auto originalActor =
    vtkSmartPointer<vtkActor>::New();
  originalActor->SetMapper(originalMapper);
  originalActor->GetProperty()->SetColor(color->GetColor3d("tomato").GetData());
  originalActor->GetProperty()->SetInterpolationToFlat();

  // Clean the polydata. This will remove duplicate points that may be
  // present in the input data.
  auto cleaner =
    vtkSmartPointer<vtkCleanPolyData>::New();
  cleaner->SetInputData (polyData);

  // Generate a tetrahedral mesh from the input points. By
  // default, the generated volume is the convex hull of the points.
  auto delaunay3D =
    vtkSmartPointer<vtkDelaunay3D>::New();
  delaunay3D->SetInputConnection (cleaner->GetOutputPort());

  auto delaunayMapper =
    vtkSmartPointer<vtkDataSetMapper>::New();
  delaunayMapper->SetInputConnection(delaunay3D->GetOutputPort());
  delaunayMapper->SetScalarModeToUseCellData();

  auto delaunayActor =
    vtkSmartPointer<vtkActor>::New();
  delaunayActor->SetMapper(delaunayMapper);
  delaunayActor->GetProperty()->SetColor(color->GetColor3d("banana").GetData());
  delaunayActor->GetProperty()->SetInterpolationToFlat();

  // Generate a mesh from the input points. If Alpha is non-zero, then
  // tetrahedra, triangles, edges and vertices that lie within the
  // alpha radius are output.
  auto delaunay3DAlpha =
    vtkSmartPointer<vtkDelaunay3D>::New();
  delaunay3DAlpha->SetInputConnection (cleaner->GetOutputPort());
  delaunay3DAlpha->SetAlpha(.5);
  delaunay3DAlpha->Update();

  auto cellData =
    vtkSmartPointer<vtkUnsignedCharArray>::New();
  cellData->SetNumberOfComponents(3);

  int numTetras = 0;
  int numLines = 0;
  int numTris = 0;
  int numVerts = 0;

  auto it =
    delaunay3DAlpha->GetOutput()->NewCellIterator();
  for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextCell())
  {
    if (it->GetCellType() == VTK_TETRA)
    {
      numTetras++;
      cellData->InsertNextTypedTuple(color->GetColor3ub("Banana").GetData());
    }
    else if (it->GetCellType() == VTK_LINE)
    {
      numLines++;
      cellData->InsertNextTypedTuple(color->GetColor3ub("Peacock").GetData());
    }
    else if (it->GetCellType() == VTK_TRIANGLE)
    {
      numTris++;
      cellData->InsertNextTypedTuple(color->GetColor3ub("Tomato").GetData());
    }
    else if (it->GetCellType() == VTK_VERTEX)
    {
      numVerts++;
      cellData->InsertNextTypedTuple(color->GetColor3ub("Lime").GetData());
    }
  }
  it->Delete();

  std::stringstream ss;
  ss << "numTetras: " << numTetras << std::endl;
  ss << "numLines: " << numLines << std::endl;
  ss << "numTris: " << numTris << std::endl;
  ss << "numVerts: " << numVerts;

  // Set the cell color depending on cell type
  delaunay3DAlpha->GetOutput()->GetCellData()->SetScalars(cellData);

  auto delaunayAlphaMapper =
    vtkSmartPointer<vtkDataSetMapper>::New();
  delaunayAlphaMapper->SetInputConnection(delaunay3DAlpha->GetOutputPort());
  delaunayAlphaMapper->SetScalarModeToUseCellData();

  auto delaunayAlphaActor =
    vtkSmartPointer<vtkActor>::New();
  delaunayAlphaActor->SetMapper(delaunayAlphaMapper);
  delaunayAlphaActor->GetProperty()->SetPointSize(5.0);
  delaunayAlphaActor->GetProperty()->SetInterpolationToFlat();

  // Visualize

  auto textProperty =
    vtkSmartPointer<vtkTextProperty>::New();
  textProperty->SetFontSize(16);
  textProperty->SetColor(color->GetColor3d("Black").GetData());

  auto textMapper =
    vtkSmartPointer<vtkTextMapper>::New();  
  textMapper->SetInput(ss.str().c_str());
  textMapper->SetTextProperty(textProperty);

  auto textActor =
    vtkSmartPointer<vtkActor2D>::New();
  textActor->SetMapper(textMapper);
  textActor->SetPosition(10, 10);

  // Define viewport ranges
  // (xmin, ymin, xmax, ymax)
  double leftViewport[4] = {0.0, 0.0, 0.33, 1.0};
  double centerViewport[4] = {0.33, 0.0, 0.66, 1.0};
  double rightViewport[4] = {0.66, 0.0, 1.0, 1.0};

  // Shared camera
  auto sharedCamera =
    vtkSmartPointer<vtkCamera>::New();

  // Create a renderer, render window, and interactor
  auto originalRenderer =
    vtkSmartPointer<vtkRenderer>::New();
  originalRenderer->SetActiveCamera(sharedCamera);
  originalRenderer->UseHiddenLineRemovalOn();

  auto delaunayRenderer =
    vtkSmartPointer<vtkRenderer>::New();
  delaunayRenderer->SetActiveCamera(sharedCamera);
  delaunayRenderer->UseHiddenLineRemovalOn();

  auto delaunayAlphaRenderer =
    vtkSmartPointer<vtkRenderer>::New();
  delaunayAlphaRenderer->SetActiveCamera(sharedCamera);
  delaunayAlphaRenderer->UseHiddenLineRemovalOn();

  auto renderWindow =
    vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->SetSize(900,300);

  renderWindow->AddRenderer(originalRenderer);
  originalRenderer->SetViewport(leftViewport);

  renderWindow->AddRenderer(delaunayRenderer);
  delaunayRenderer->SetViewport(centerViewport);

  renderWindow->AddRenderer(delaunayAlphaRenderer);
  delaunayAlphaRenderer->SetViewport(rightViewport);

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

  auto widget =
    vtkSmartPointer<vtkSliderWidget>::New();
  MakeWidget(widget, delaunay3DAlpha, textMapper, delaunayAlphaRenderer, renderWindowInteractor);

  originalRenderer->AddActor(originalActor);
  delaunayRenderer->AddActor(delaunayActor);
  delaunayAlphaRenderer->AddActor(delaunayAlphaActor);
  delaunayAlphaRenderer->AddViewProp(textActor);

  originalRenderer->SetBackground(
    color->GetColor3d("Slate_Grey").GetData());
  delaunayRenderer->SetBackground(
    color->GetColor3d("Light_Grey").GetData());
  delaunayAlphaRenderer->SetBackground(
    color->GetColor3d("Grey").GetData());

  originalRenderer->ResetCamera();
  renderWindow->Render();

  // Render and interact
  renderWindowInteractor->Start();
  return EXIT_SUCCESS;
}

namespace
{
// These callbacks do the actual work.
// Callbacks for the interactions
class SliderCallbackAlpha : public vtkCommand
{
public:
  static SliderCallbackAlpha *New()
  {
    return new SliderCallbackAlpha;
  }
  virtual void Execute(vtkObject *caller, unsigned long, void*)
  {
    vtkSliderWidget *sliderWidget =
      reinterpret_cast<vtkSliderWidget*>(caller);
    double value = static_cast<vtkSliderRepresentation2D *>(sliderWidget->GetRepresentation())->GetValue();
    this->Delaunay3D->SetAlpha(value);
    this->Delaunay3D->Update();

    auto cellData =
      vtkSmartPointer<vtkUnsignedCharArray>::New();
    cellData->SetNumberOfComponents(3);

    // Set the cell color depending on cell type
    auto color =
      vtkSmartPointer<vtkNamedColors>::New();
    int numTetras = 0;
    int numLines = 0;
    int numTris = 0;
    int numVerts = 0;

    auto it =
      this->Delaunay3D->GetOutput()->NewCellIterator();
    for (it->InitTraversal(); !it->IsDoneWithTraversal(); it->GoToNextCell())
    {
      if (it->GetCellType() == VTK_TETRA)
      {
        numTetras++;
        cellData->InsertNextTypedTuple(color->GetColor3ub("Banana").GetData());
      }
      else if (it->GetCellType() == VTK_LINE)
      {
        numLines++;
        cellData->InsertNextTypedTuple(color->GetColor3ub("Peacock").GetData());
      }
      else if (it->GetCellType() == VTK_TRIANGLE)
      {
        numTris++;
        cellData->InsertNextTypedTuple(color->GetColor3ub("Tomato").GetData());
      }
      else if (it->GetCellType() == VTK_VERTEX)
      {
        numVerts++;
        cellData->InsertNextTypedTuple(color->GetColor3ub("Lime").GetData());
      }
    }
    it->Delete();

    this->Delaunay3D->GetOutput()->GetCellData()->SetScalars(cellData);
    std::stringstream ss;
    ss << "numTetras: " << numTetras << std::endl;
    ss << "numLines: " << numLines << std::endl;
    ss << "numTris: " << numTris << std::endl;
    ss << "numVerts: " << numVerts;
    this->TextMapper->SetInput(ss.str().c_str());

  }
  SliderCallbackAlpha():Delaunay3D(0),TextMapper(0) {}
  vtkDelaunay3D *Delaunay3D;
  vtkTextMapper *TextMapper;
};

void
MakeWidget(vtkSmartPointer<vtkSliderWidget> &widget,
           vtkSmartPointer<vtkDelaunay3D> &delaunay3D,
           vtkSmartPointer<vtkTextMapper> &textMapper,
           vtkSmartPointer<vtkRenderer> &renderer,
           vtkSmartPointer<vtkRenderWindowInteractor> &interactor)
{
  // Setup a slider widget for each varying parameter
  double tubeWidth(.02);
  double sliderLength(.04);
  double titleHeight(.04);
  double labelHeight(.04);

  auto sliderRepAlpha =
    vtkSmartPointer<vtkSliderRepresentation2D>::New();

  sliderRepAlpha->SetRenderer(renderer);

  sliderRepAlpha->SetMinimumValue(0.0001);
  sliderRepAlpha->SetMaximumValue(1.0);
  sliderRepAlpha->SetValue(delaunay3D->GetAlpha());
  sliderRepAlpha->SetTitleText("Alpha");
  sliderRepAlpha->SetRenderer(renderer);
  sliderRepAlpha->GetPoint1Coordinate()->SetValue(0.1, .1);
  sliderRepAlpha->GetPoint1Coordinate()->SetCoordinateSystemToNormalizedViewport();
  sliderRepAlpha->GetPoint2Coordinate()->SetValue(.9, .1);
  sliderRepAlpha->GetPoint2Coordinate()->SetCoordinateSystemToNormalizedViewport();

  sliderRepAlpha->SetTubeWidth(tubeWidth);
  sliderRepAlpha->SetSliderLength(sliderLength);
  sliderRepAlpha->SetTitleHeight(titleHeight);
  sliderRepAlpha->SetLabelHeight(labelHeight);
  sliderRepAlpha->SetEndCapLength(tubeWidth*1.5);
  sliderRepAlpha->SetSliderWidth(tubeWidth*1.5);
  sliderRepAlpha->BuildRepresentation();

  widget->SetRepresentation(sliderRepAlpha);
  widget->SetAnimationModeToAnimate();
  widget->SetNumberOfAnimationSteps(2);
  widget->SetInteractor(interactor);
  widget->EnabledOn();

  auto callbackAlpha =
    vtkSmartPointer<SliderCallbackAlpha>::New();
  callbackAlpha->Delaunay3D = delaunay3D;
  callbackAlpha->TextMapper = textMapper;

  widget->AddObserver(vtkCommand::InteractionEvent,callbackAlpha);
}
}
namespace
{
vtkSmartPointer<vtkPolyData> ReadPolyData(const char *fileName)
{
  vtkSmartPointer<vtkPolyData> polyData;
  std::string extension = vtksys::SystemTools::GetFilenameExtension(std::string(fileName));
  if (extension == ".ply")
  {
    auto reader =
      vtkSmartPointer<vtkPLYReader>::New();
    reader->SetFileName (fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".vtp")
  {
    auto reader =
      vtkSmartPointer<vtkXMLPolyDataReader>::New();
    reader->SetFileName (fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".obj")
  {
    auto reader =
      vtkSmartPointer<vtkOBJReader>::New();
    reader->SetFileName (fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".stl")
  {
    auto reader =
      vtkSmartPointer<vtkSTLReader>::New();
    reader->SetFileName (fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".vtk")
  {
    auto reader =
      vtkSmartPointer<vtkPolyDataReader>::New();
    reader->SetFileName (fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else if (extension == ".g")
  {
    auto reader =
      vtkSmartPointer<vtkBYUReader>::New();
    reader->SetGeometryFileName (fileName);
    reader->Update();
    polyData = reader->GetOutput();
  }
  else
  {
    auto source =
      vtkSmartPointer<vtkSphereSource>::New();
    source->Update();
    polyData = source->GetOutput();
  }
  return polyData;
}
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)

project(Delaunay3DDemo)

find_package(VTK COMPONENTS 
  vtkCommonColor
  vtkCommonCore
  vtkCommonDataModel
  vtkFiltersCore
  vtkFiltersSources
  vtkIOGeometry
  vtkIOLegacy
  vtkIOPLY
  vtkIOXML
  vtkInteractionStyle
  vtkInteractionWidgets
  vtkRenderingContextOpenGL2
  vtkRenderingCore
  vtkRenderingFreeType
  vtkRenderingGL2PSOpenGL2
  vtkRenderingOpenGL2 QUIET)
if (NOT VTK_FOUND)
  message("Skipping Delaunay3DDemo: ${VTK_NOT_FOUND_MESSAGE}")
  return ()
endif()
message (STATUS "VTK_VERSION: ${VTK_VERSION}")
if (VTK_VERSION VERSION_LESS "8.90.0")
  # old system
  include(${VTK_USE_FILE})
  add_executable(Delaunay3DDemo MACOSX_BUNDLE Delaunay3DDemo.cxx )
  target_link_libraries(Delaunay3DDemo PRIVATE ${VTK_LIBRARIES})
else ()
  # include all components
  add_executable(Delaunay3DDemo MACOSX_BUNDLE Delaunay3DDemo.cxx )
  target_link_libraries(Delaunay3DDemo PRIVATE ${VTK_LIBRARIES})
  # vtk_module_autoinit is needed
  vtk_module_autoinit(
    TARGETS Delaunay3DDemo
    MODULES ${VTK_LIBRARIES}
    )
endif () 

Download and Build Delaunay3DDemo

Click here to download Delaunay3DDemo and its CMakeLists.txt file. Once the tarball Delaunay3DDemo.tar has been downloaded and extracted,

cd Delaunay3DDemo/build 

If VTK is installed:

cmake ..

If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:

cmake -DVTK_DIR:PATH=/home/me/vtk_build ..

Build the project:

make

and run it:

./Delaunay3DDemo

WINDOWS USERS

Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.