Skip to content

MovableAxes

vtk-examples/Cxx/Visualization/MovableAxes


Description

With vtkAxesActor, a hybrid object with 3D axes and 2D label props, it is not possible to move the labels along with the axes with vtkInteractorStyleTrackballActor. Here we create new axes labels using a different 3D prop: vtkFollower, and update their positions with a custom callback command.

Question

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

Code

MovableAxes.cxx

#include <vtkActor.h>
#include <vtkAssembly.h>
#include <vtkAssemblyPath.h>
#include <vtkAxesActor.h>
#include <vtkCallbackCommand.h>
#include <vtkCommand.h>
#include <vtkConeSource.h>
#include <vtkFollower.h>
#include <vtkInteractorStyleTrackballActor.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkTextActor.h>
#include <vtkVectorText.h>

namespace {
//----------------------------------------------------------------------------
class vtkPositionCallback : public vtkCallbackCommand
{
public:
  static vtkPositionCallback* New()
  {
    return new vtkPositionCallback;
  }

  void Execute(vtkObject* vtkNotUsed(caller), unsigned long vtkNotUsed(event),
               void* vtkNotUsed(callData))
  {
    this->Axes->InitPathTraversal();
    vtkAssemblyPath* path = 0;
    int count = 0;
    vtkFollower* followers[3] = {this->XLabel, this->YLabel, this->ZLabel};
    int followerId = 0;
    while ((path = this->Axes->GetNextPath()) != NULL)
    {
      if (count++ > 2)
      {
        vtkProp3D* prop3D =
            static_cast<vtkProp3D*>(path->GetLastNode()->GetViewProp());
        if (prop3D)
        {
          prop3D->PokeMatrix(path->GetLastNode()->GetMatrix());
          followers[followerId]->SetPosition(prop3D->GetCenter());
          followerId++;
          prop3D->PokeMatrix(NULL);
        }
      }
    }
  }

  vtkPositionCallback() : XLabel(0), YLabel(0), ZLabel(0), Axes(0)
  {
  }

  vtkFollower* XLabel;
  vtkFollower* YLabel;
  vtkFollower* ZLabel;
  vtkAssembly* Axes;
};

} // namespace

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

  vtkNew<vtkConeSource> coneSource;
  coneSource->Update();

  //  vtkPolyData* cone = coneSource->GetOutput();

  // create a mapper
  vtkNew<vtkPolyDataMapper> coneMapper;
  coneMapper->SetInputConnection(coneSource->GetOutputPort());

  // create an actor
  vtkNew<vtkActor> coneActor;
  coneActor->SetMapper(coneMapper);
  coneActor->GetProperty()->SetColor(colors->GetColor3d("Gold").GetData());

  // a renderer and render window
  vtkNew<vtkRenderer> renderer;
  vtkNew<vtkRenderWindow> renderWindow;
  renderWindow->AddRenderer(renderer);
  renderWindow->SetWindowName("MovableAxes");

  // an interactor
  vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
  renderWindowInteractor->SetRenderWindow(renderWindow);

  // Add the actors to the scene.
  renderer->AddActor(coneActor);
  renderer->SetBackground(colors->GetColor3d("SlateGray").GetData());

  // vtkAxesActor is currently not designed to work with
  // vtkInteractorStyleTrackballActor since it is a hybrid object containing
  // both vtkProp3D's and vtkActor2D's, the latter of which does not have a 3D
  // position that can be manipulated.
  vtkNew<vtkAxesActor> axes;

  // Get a copy of the axes' constituent 3D actors and put them into a
  // vtkAssembly so they can be manipulated as one prop.
  vtkNew<vtkPropCollection> collection;
  axes->GetActors(collection);

  collection->InitTraversal();

  vtkNew<vtkAssembly> movableAxes;

  for (int i = 0; i < collection->GetNumberOfItems(); ++i)
  {
    movableAxes->AddPart((vtkProp3D*)collection->GetNextProp());
  }

  renderer->AddActor(movableAxes);

  // Create our own labels that will follow and face the camera.
  vtkNew<vtkFollower> xLabel;
  vtkNew<vtkVectorText> xText;
  vtkNew<vtkPolyDataMapper> xTextMapper;
  xText->SetText("X");
  xTextMapper->SetInputConnection(xText->GetOutputPort());
  xLabel->SetMapper(xTextMapper);
  xLabel->SetScale(0.3);
  xLabel->SetCamera(renderer->GetActiveCamera());
  xLabel->SetPosition(
      ((vtkProp3D*)collection->GetItemAsObject(3))->GetCenter()); // XAxisTip
  xLabel->PickableOff();
  renderer->AddActor(xLabel);

  vtkNew<vtkFollower> yLabel;
  vtkNew<vtkVectorText> yText;
  vtkNew<vtkPolyDataMapper> yTextMapper;
  yText->SetText("Y");
  yTextMapper->SetInputConnection(yText->GetOutputPort());
  yLabel->SetMapper(yTextMapper);
  yLabel->SetScale(0.3);
  yLabel->SetCamera(renderer->GetActiveCamera());
  yLabel->SetPosition(
      ((vtkProp3D*)collection->GetItemAsObject(4))->GetCenter()); // YAxisTip
  yLabel->PickableOff();
  renderer->AddActor(yLabel);

  vtkNew<vtkFollower> zLabel;
  vtkNew<vtkVectorText> zText;
  vtkNew<vtkPolyDataMapper> zTextMapper;
  zText->SetText("Z");
  zTextMapper->SetInputConnection(zText->GetOutputPort());
  zLabel->SetMapper(zTextMapper);
  zLabel->SetScale(0.3);
  zLabel->SetCamera(renderer->GetActiveCamera());
  zLabel->SetPosition(
      ((vtkProp3D*)collection->GetItemAsObject(5))->GetCenter()); // ZAxisTip
  zLabel->PickableOff();
  renderer->AddActor(zLabel);

  // Custom callback to set the positions of the labels.
  vtkNew<vtkPositionCallback> callback;
  callback->XLabel = xLabel;
  callback->YLabel = yLabel;
  callback->ZLabel = zLabel;
  callback->Axes = movableAxes;

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

  vtkNew<vtkInteractorStyleTrackballActor> style;

  renderWindowInteractor->SetInteractorStyle(style);
  style->AddObserver(vtkCommand::InteractionEvent, callback);

  // Begin mouse interaction.
  renderWindowInteractor->Start();

  return EXIT_SUCCESS;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.12 FATAL_ERROR)

project(MovableAxes)

find_package(VTK COMPONENTS 
  CommonColor
  CommonCore
  FiltersSources
  InteractionStyle
  RenderingAnnotation
  RenderingContextOpenGL2
  RenderingCore
  RenderingFreeType
  RenderingGL2PSOpenGL2
  RenderingOpenGL2
)

if (NOT VTK_FOUND)
  message(FATAL_ERROR "MovableAxes: 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(MovableAxes MACOSX_BUNDLE MovableAxes.cxx )
  target_link_libraries(MovableAxes PRIVATE ${VTK_LIBRARIES}
)
# vtk_module_autoinit is needed
vtk_module_autoinit(
  TARGETS MovableAxes
  MODULES ${VTK_LIBRARIES}
)

Download and Build MovableAxes

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

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

./MovableAxes

WINDOWS USERS

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