CollisionDetection

VTKExamples/Cxx/Visualization/CollisionDetection


Description

This examples uses vtkCollisionDetectionFilter to find the intersection between a fixed (solid white) and moving (red wireframe) sphere. The animation places the moving sphere some distance from the fixed sphere and moves the moving sphere until it contacts the fixed sphere.

Three collision modes are available and can be set as the first argument on the command line.

  1. All contacts (0) finds all the contacting cell pairs with two points per collision.
  2. First contact (1) quickly find the first contact point.
  3. Half contacts (2) finds all the contacting cell pairs with one points per collision.

The animation pauses between each frame. The total animation should be 3 seconds.

Three videos on the VTK Examples Project youtube playlist show each of the collision modes.

Other Languages

See (Python)

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

CollisionDetection.cxx

#include "vtkActor.h"
#include "vtkCamera.h"
#include "vtkCollisionDetectionFilter.h"
#include "vtkMatrix4x4.h"
#include "vtkNamedColors.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSmartPointer.h"
#include "vtkSphereSource.h"
#include "vtkTextActor.h"
#include "vtkTextProperty.h"
#include "vtkTransform.h"

#include <chrono>
#include <sstream>
#include <string>
#include <thread>

int main(int argc, char* argv[])
{
  int contactMode = 0;
  if (argc > 1)
  {
    contactMode = std::stoi(std::string(argv[1]));
  }
  auto sphere0 = vtkSmartPointer<vtkSphereSource>::New();
  sphere0->SetRadius(.29);
  sphere0->SetPhiResolution(31);
  sphere0->SetThetaResolution(31);
  sphere0->SetCenter(0.0, 0, 0);

  auto sphere1 = vtkSmartPointer<vtkSphereSource>::New();
  sphere1->SetPhiResolution(30);
  sphere1->SetThetaResolution(30);
  sphere1->SetRadius(0.3);

  auto matrix1 = vtkSmartPointer<vtkMatrix4x4>::New();
  auto transform0 = vtkSmartPointer<vtkTransform>::New();

  auto collide = vtkSmartPointer<vtkCollisionDetectionFilter>::New();
  collide->SetInputConnection(0, sphere0->GetOutputPort());
  collide->SetTransform(0, transform0);
  collide->SetInputConnection(1, sphere1->GetOutputPort());
  collide->SetMatrix(1, matrix1);
  collide->SetBoxTolerance(0.0);
  collide->SetCellTolerance(0.0);
  collide->SetNumberOfCellsPerNode(2);
  if (contactMode == 0)
  {
    collide->SetCollisionModeToAllContacts();
  }
  else if (contactMode == 1)
  {
    collide->SetCollisionModeToFirstContact();
  }
  else
  {
    collide->SetCollisionModeToHalfContacts();
  }
  collide->GenerateScalarsOn();

  // Visualize
  auto colors = vtkSmartPointer<vtkNamedColors>::New();
  auto mapper1 = vtkSmartPointer<vtkPolyDataMapper>::New();
  mapper1->SetInputConnection(collide->GetOutputPort(0));
  mapper1->ScalarVisibilityOff();
  auto actor1 = vtkSmartPointer<vtkActor>::New();
  actor1->SetMapper(mapper1);
  actor1->GetProperty()->BackfaceCullingOn();
  actor1->SetUserTransform(transform0);
  actor1->GetProperty()->SetDiffuseColor(
      colors->GetColor3d("Tomato").GetData());
  actor1->GetProperty()->SetRepresentationToWireframe();

  auto mapper2 = vtkSmartPointer<vtkPolyDataMapper>::New();
  mapper2->SetInputConnection(collide->GetOutputPort(1));

  auto actor2 = vtkSmartPointer<vtkActor>::New();
  actor2->SetMapper(mapper2);
  actor2->GetProperty()->BackfaceCullingOn();
  actor2->SetUserMatrix(matrix1);

  auto mapper3 = vtkSmartPointer<vtkPolyDataMapper>::New();
  mapper3->SetInputConnection(collide->GetContactsOutputPort());
  mapper3->SetResolveCoincidentTopologyToPolygonOffset();

  auto actor3 = vtkSmartPointer<vtkActor>::New();
  actor3->SetMapper(mapper3);
  actor3->GetProperty()->SetColor(colors->GetColor3d("Black").GetData());
  actor3->GetProperty()->SetLineWidth(3.0);

  auto txt = vtkSmartPointer<vtkTextActor>::New();
  txt->GetTextProperty()->SetFontSize(18);

  auto renderer = vtkSmartPointer<vtkRenderer>::New();
  renderer->UseHiddenLineRemovalOn();
  renderer->AddActor(actor1);
  renderer->AddActor(actor2);
  renderer->AddActor(actor3);
  renderer->AddActor(txt);
  renderer->SetBackground(colors->GetColor3d("Gray").GetData());
  renderer->UseHiddenLineRemovalOn();

  auto renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->SetSize(640, 480);
  renderWindow->AddRenderer(renderer);

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

  // Move the first object
  int numSteps = 100;
  double dx = 1.0 / static_cast<double>(numSteps) * 2.0;
  transform0->Translate(-numSteps * dx - .3, 0.0, 0.0);
  renderWindow->Render();
  renderer->GetActiveCamera()->Azimuth(-60);
  renderer->GetActiveCamera()->Elevation(45);
  renderer->GetActiveCamera()->Dolly(1.2);

  renderWindow->SetWindowName("CollisionDetection");
  renderWindow->Render();

  for (int i = 0; i < numSteps; ++i)
  {
    transform0->Translate(dx, 0.0, 0.0);
    renderer->ResetCameraClippingRange();
    std::ostringstream textStream;
    textStream << collide->GetCollisionModeAsString()
               << ": Number of contact cells is "
               << collide->GetNumberOfContacts();
    txt->SetInput(textStream.str().c_str());
    renderWindow->Render();
    if (collide->GetNumberOfContacts() > 0)
    {
      break;
    }
    // The total animation time is 3 seconds
    std::this_thread::sleep_for(std::chrono::milliseconds(3000 / numSteps));
  }
  renderer->ResetCamera();
  renderWindow->Render();
  interactor->Start();
  // In Field Data there will be an array named "ContactCells".
  // This array indexes contacting cells (e.g.) index 50 of array 0
  //  points to a cell (triangle) which contacts/intersects a cell
  //  at index 50 of array 1.
  // You can directly obtain these, see GetContactCells(int i)
  //  in the documentation.
  // collide->GetOutput(0)->Print(std::cout);
  //collide->GetOutput(1)->Print(std::cout);
  return EXIT_SUCCESS;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)

project(CollisionDetection)

find_package(VTK COMPONENTS 
  vtkCommonColor
  vtkCommonCore
  vtkCommonMath
  vtkCommonTransforms
  vtkFiltersModeling
  vtkFiltersSources
  vtkInteractionStyle
  vtkRenderingContextOpenGL2
  vtkRenderingCore
  vtkRenderingFreeType
  vtkRenderingGL2PSOpenGL2
  vtkRenderingOpenGL2 QUIET)
if (NOT VTK_FOUND)
  message("Skipping CollisionDetection: ${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(CollisionDetection MACOSX_BUNDLE CollisionDetection.cxx )
  target_link_libraries(CollisionDetection PRIVATE ${VTK_LIBRARIES})
else ()
  # include all components
  add_executable(CollisionDetection MACOSX_BUNDLE CollisionDetection.cxx )
  target_link_libraries(CollisionDetection PRIVATE ${VTK_LIBRARIES})
  # vtk_module_autoinit is needed
  vtk_module_autoinit(
    TARGETS CollisionDetection
    MODULES ${VTK_LIBRARIES}
    )
endif () 

Download and Build CollisionDetection

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

cd CollisionDetection/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:

./CollisionDetection

WINDOWS USERS

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