Skip to content

Delaunay3DDemo

vtk-examples/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 question about this example, please use the VTK Discourse Forum

Code

Delaunay3DDemo.cxx

#include <vtkActor.h>
#include <vtkActor2D.h>
#include <vtkCamera.h>
#include <vtkCellData.h>
#include <vtkCellIterator.h>
#include <vtkCleanPolyData.h>
#include <vtkCommand.h>
#include <vtkDataSetMapper.h>
#include <vtkDelaunay3D.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPolyData.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSliderRepresentation2D.h>
#include <vtkSliderWidget.h>
#include <vtkSmartPointer.h>
#include <vtkTextMapper.h>
#include <vtkTextProperty.h>
#include <vtkUnstructuredGrid.h>

#include <sstream>

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

namespace {
vtkSmartPointer<vtkPolyData> ReadPolyData(const char* fileName);

void MakeWidget(vtkSliderWidget*, vtkDelaunay3D*, vtkTextMapper*, vtkRenderer*,
                vtkRenderWindowInteractor*);
} // namespace

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

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

  vtkNew<vtkNamedColors> color;

  vtkNew<vtkDataSetMapper> originalMapper;
  originalMapper->SetInputData(polyData);
  originalMapper->ScalarVisibilityOff();
  originalMapper->SetScalarModeToUseCellData();

  vtkNew<vtkActor> originalActor;
  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.
  vtkNew<vtkCleanPolyData> cleaner;
  cleaner->SetInputData(polyData);

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

  vtkNew<vtkDataSetMapper> delaunayMapper;
  delaunayMapper->SetInputConnection(delaunay3D->GetOutputPort());
  delaunayMapper->SetScalarModeToUseCellData();

  vtkNew<vtkActor> delaunayActor;
  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.
  vtkNew<vtkDelaunay3D> delaunay3DAlpha;
  delaunay3DAlpha->SetInputConnection(cleaner->GetOutputPort());
  delaunay3DAlpha->SetAlpha(0.0105);
  delaunay3DAlpha->Update();

  vtkNew<vtkUnsignedCharArray> cellData;
  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);

  vtkNew<vtkDataSetMapper> delaunayAlphaMapper;
  delaunayAlphaMapper->SetInputConnection(delaunay3DAlpha->GetOutputPort());
  delaunayAlphaMapper->SetScalarModeToUseCellData();

  vtkNew<vtkActor> delaunayAlphaActor;
  delaunayAlphaActor->SetMapper(delaunayAlphaMapper);
  delaunayAlphaActor->GetProperty()->SetPointSize(5.0);
  delaunayAlphaActor->GetProperty()->SetInterpolationToFlat();

  // Visualize

  vtkNew<vtkTextProperty> textProperty;
  textProperty->SetFontSize(16);
  textProperty->SetColor(color->GetColor3d("Black").GetData());

  vtkNew<vtkTextMapper> textMapper;
  textMapper->SetInput(ss.str().c_str());
  textMapper->SetTextProperty(textProperty);

  vtkNew<vtkActor2D> textActor;
  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
  vtkNew<vtkCamera> sharedCamera;

  // Create a renderer, render window, and interactor.
  vtkNew<vtkRenderer> originalRenderer;
  originalRenderer->SetActiveCamera(sharedCamera);
  originalRenderer->UseHiddenLineRemovalOn();

  vtkNew<vtkRenderer> delaunayRenderer;
  delaunayRenderer->SetActiveCamera(sharedCamera);
  delaunayRenderer->UseHiddenLineRemovalOn();

  vtkNew<vtkRenderer> delaunayAlphaRenderer;
  delaunayAlphaRenderer->SetActiveCamera(sharedCamera);
  delaunayAlphaRenderer->UseHiddenLineRemovalOn();

  vtkNew<vtkRenderWindow> renderWindow;
  renderWindow->SetSize(900, 300);
  renderWindow->SetWindowName("Delaunay3DDemo");

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

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

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

  vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
  renderWindowInteractor->SetRenderWindow(renderWindow);

  vtkNew<vtkSliderWidget> widget;
  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();

    vtkNew<vtkUnsignedCharArray> cellData;
    cellData->SetNumberOfComponents(3);

    // Set the cell color depending on cell type.
    vtkNew<vtkNamedColors> color;
    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(vtkSliderWidget* widget, vtkDelaunay3D* delaunay3D,
                vtkTextMapper* textMapper, vtkRenderer* renderer,
                vtkRenderWindowInteractor* interactor)
{
  // Setup a slider widget for each varying parameter
  double tubeWidth(.02);
  double sliderLength(.04);
  double titleHeight(.04);
  double labelHeight(.04);

  vtkNew<vtkSliderRepresentation2D> sliderRepAlpha;

  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();

  vtkNew<SliderCallbackAlpha> callbackAlpha;
  callbackAlpha->Delaunay3D = delaunay3D;
  callbackAlpha->TextMapper = textMapper;

  widget->AddObserver(vtkCommand::InteractionEvent, callbackAlpha);
}

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

CMakeLists.txt

cmake_minimum_required(VERSION 3.12 FATAL_ERROR)

project(Delaunay3DDemo)

find_package(VTK COMPONENTS 
)

if (NOT VTK_FOUND)
  message(FATAL_ERROR "Delaunay3DDemo: Unable to find the VTK build folder.")
endif()

# Prevent a "command line is too long" failure in Windows.
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")
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}
)

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.