Skip to content

OrientedArrow

vtk-examples/Cxx/GeometricObjects/OrientedArrow


Description

This example illustrates how to create and display an arrow that passes through two points.

It demonstrates two different ways to apply the transform:

  1. Use vtkTransformPolyDataFilter to create a new transformed polydata. This method is useful if the transformed polydata is needed later in the pipeline, e.g. vtkGlyph3DFilter.

  2. Apply the transform directly to the actor using vtkProp3D's SetUserMatrix. No new data is produced.

Switch between the two methods by #defining USER_MATRIX or leaving out the #define.

See also

Compare this example with OrientedCylinder. The transform is different because the cylinder height direction is along the y-axis and the arrow height is along the x axis.

Other languages

See (Python), (Java), (CSharp)

Question

If you have a question about this example, please use the VTK Discourse Forum

Code

OrientedArrow.cxx

#include <vtkActor.h>
#include <vtkArrowSource.h>
#include <vtkMath.h>
#include <vtkMinimalStandardRandomSequence.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSphereSource.h>
#include <vtkTransform.h>
#include <vtkTransformPolyDataFilter.h>

#include <array>

#define USER_MATRIX

int main(int, char*[])
{
  vtkNew<vtkNamedColors> colors;

  // Set the background color.
  std::array<unsigned char, 4> bkg{{26, 51, 77, 255}};
  colors->SetColor("BkgColor", bkg.data());

  // Create an arrow.
  vtkNew<vtkArrowSource> arrowSource;

  // Generate a random start and end point
  double startPoint[3];
  double endPoint[3];
  vtkNew<vtkMinimalStandardRandomSequence> rng;
  rng->SetSeed(8775070); // For testing.
  for (auto i = 0; i < 3; ++i)
  {
    rng->Next();
    startPoint[i] = rng->GetRangeValue(-10, 10);
    rng->Next();
    endPoint[i] = rng->GetRangeValue(-10, 10);
  }

  // Compute a basis
  double normalizedX[3];
  double normalizedY[3];
  double normalizedZ[3];

  // The X axis is a vector from start to end
  vtkMath::Subtract(endPoint, startPoint, normalizedX);
  double length = vtkMath::Norm(normalizedX);
  vtkMath::Normalize(normalizedX);

  // The Z axis is an arbitrary vector cross X
  double arbitrary[3];
  for (auto i = 0; i < 3; ++i)
  {
    rng->Next();
    arbitrary[i] = rng->GetRangeValue(-10, 10);
  }
  vtkMath::Cross(normalizedX, arbitrary, normalizedZ);
  vtkMath::Normalize(normalizedZ);

  // The Y axis is Z cross X
  vtkMath::Cross(normalizedZ, normalizedX, normalizedY);
  vtkNew<vtkMatrix4x4> matrix;

  // Create the direction cosine matrix
  matrix->Identity();
  for (auto i = 0; i < 3; i++)
  {
    matrix->SetElement(i, 0, normalizedX[i]);
    matrix->SetElement(i, 1, normalizedY[i]);
    matrix->SetElement(i, 2, normalizedZ[i]);
  }

  // Apply the transforms
  vtkNew<vtkTransform> transform;
  transform->Translate(startPoint);
  transform->Concatenate(matrix);
  transform->Scale(length, length, length);

  // Transform the polydata
  vtkNew<vtkTransformPolyDataFilter> transformPD;
  transformPD->SetTransform(transform);
  transformPD->SetInputConnection(arrowSource->GetOutputPort());

  // Create a mapper and actor for the arrow
  vtkNew<vtkPolyDataMapper> mapper;
  vtkNew<vtkActor> actor;
#ifdef USER_MATRIX
  mapper->SetInputConnection(arrowSource->GetOutputPort());
  actor->SetUserMatrix(transform->GetMatrix());
#else
  mapper->SetInputConnection(transformPD->GetOutputPort());
#endif
  actor->SetMapper(mapper);
  actor->GetProperty()->SetColor(colors->GetColor3d("Cyan").GetData());

  // Create spheres for start and end point
  vtkNew<vtkSphereSource> sphereStartSource;
  sphereStartSource->SetCenter(startPoint);
  sphereStartSource->SetRadius(0.8);
  vtkNew<vtkPolyDataMapper> sphereStartMapper;
  sphereStartMapper->SetInputConnection(sphereStartSource->GetOutputPort());
  vtkNew<vtkActor> sphereStart;
  sphereStart->SetMapper(sphereStartMapper);
  sphereStart->GetProperty()->SetColor(colors->GetColor3d("Yellow").GetData());

  vtkNew<vtkSphereSource> sphereEndSource;
  sphereEndSource->SetCenter(endPoint);
  sphereEndSource->SetRadius(0.8);
  vtkNew<vtkPolyDataMapper> sphereEndMapper;
  sphereEndMapper->SetInputConnection(sphereEndSource->GetOutputPort());
  vtkNew<vtkActor> sphereEnd;
  sphereEnd->SetMapper(sphereEndMapper);
  sphereEnd->GetProperty()->SetColor(colors->GetColor3d("Magenta").GetData());

  // Create a renderer, render window, and interactor
  vtkNew<vtkRenderer> renderer;
  vtkNew<vtkRenderWindow> renderWindow;
  renderWindow->AddRenderer(renderer);
  renderWindow->SetWindowName("OrientedArrow");
  vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
  renderWindowInteractor->SetRenderWindow(renderWindow);

  // Add the actor to the scene
  renderer->AddActor(actor);
  renderer->AddActor(sphereStart);
  renderer->AddActor(sphereEnd);
  renderer->SetBackground(colors->GetColor3d("BkgColor").GetData());

  // Render and interact
  renderWindow->Render();
  renderWindowInteractor->Start();

  return EXIT_SUCCESS;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)

project(OrientedArrow)

find_package(VTK COMPONENTS 
  vtkCommonColor
  vtkCommonCore
  vtkCommonDataModel
  vtkCommonTransforms
  vtkFiltersGeneral
  vtkFiltersSources
  vtkInteractionStyle
  vtkRenderingContextOpenGL2
  vtkRenderingCore
  vtkRenderingFreeType
  vtkRenderingGL2PSOpenGL2
  vtkRenderingOpenGL2
  QUIET
)

if (NOT VTK_FOUND)
  message("Skipping OrientedArrow: ${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(OrientedArrow MACOSX_BUNDLE OrientedArrow.cxx )
  target_link_libraries(OrientedArrow PRIVATE ${VTK_LIBRARIES})
else ()
  # include all components
  add_executable(OrientedArrow MACOSX_BUNDLE OrientedArrow.cxx )
  target_link_libraries(OrientedArrow PRIVATE ${VTK_LIBRARIES})
  # vtk_module_autoinit is needed
  vtk_module_autoinit(
    TARGETS OrientedArrow
    MODULES ${VTK_LIBRARIES}
    )
endif ()

Download and Build OrientedArrow

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

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

./OrientedArrow

WINDOWS USERS

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