CallBack
Repository source: CallBack
Description¶
Demonstrate how to set up a callback with client data¶
Getting the camera orientation after interacting with the image is used as an example.
We define a callback passing the active camera as client data and linking the callback to the EndInteractionEvent of the vtkRenderWindowInteractor class. This allows us to get the camera orientation after we manipulate the image. We can then copy/paste this data as needed into our camera to set up a nice initial orientation as shown in the example.
To help orient the cone, we use a vtkOrientationMarkerWidget and a vtkOutlineFilter.
C++¶
There are two methodologies in C++.
- Create a class that inherits from vtkCallbackCommand reimplementing
Execute(
vtkObject*caller, unsigned long evId, void*)
and setting pointers to a client and/or call data as needed. When the class is implemented, it becomes the callback function. - Create a function with this signature:
void f(
vtkObject* caller, long unsigned int evId, void* clientData, void* callData)
and, where needed, create a vtkCallbackCommand setting its callback to the function we have created.
The example demonstrates both approaches.
In the function PrintCameraOrientation note how we convert an array to a vector and get a comma-separated list.
Python¶
In Python the approach is even simpler. We simply define a function to use as the callback with this signature: def MyCallback(obj, ev):
. Then, to pass client data to it, we simply do: MyCallback.myClientData = myClientData
. This relies on the fact that Python functions are in fact objects and we are simply adding new attributes such as myClientData
in this case.
An alternative method is to define a class passsing the needed variables in the __init__
function and then implement a _call__
function that does the work.
Both approaches are demonstrated in the example.
Other languages
See (Python), (PythonicAPI)
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
CallBack.cxx
/*
Demonstrate the use of a callback.
We also add call data.
*/
#include <vtkActor.h>
#include <vtkAxesActor.h>
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkConeSource.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkOrientationMarkerWidget.h>
#include <vtkOutlineFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <iterator>
#include <sstream>
#include <vector>
#define USE_CALLBACKCOMMAND_CLASS
// Uncomment this if you want to use the function instead.
// #undef USE_CALLBACKCOMMAND_CLASS
namespace {
void PrintCameraOrientation(vtkCamera* cam);
void MakeAxesActor(vtkAxesActor* axesActor);
#if defined(USE_CALLBACKCOMMAND_CLASS)
/**
Here we inherit from vtkCallbackCommand and set pointers to any
client and/or call data as needed.
When the class is implemented, it becomes the callback function.
*/
class CameraModifiedCallback : public vtkCallbackCommand
{
public:
static CameraModifiedCallback* New()
{
return new CameraModifiedCallback;
}
// Here we Create a vtkCallbackCommand and reimplement it.
void Execute(vtkObject* caller, unsigned long evId, void*) override
{
// Note the use of reinterpret_cast to cast the caller to the expected type.
auto interactor = reinterpret_cast<vtkRenderWindowInteractor*>(caller);
// Just do this to demonstrate who called callback and the event that
// triggered it.
std::cout << interactor->GetClassName() << " Event Id: " << evId
<< std::endl;
// Now print the camera orientation.
PrintCameraOrientation(this->cam);
}
CameraModifiedCallback() : cam(nullptr)
{
}
// Set pointers to any clientData or callData here.
vtkCamera* cam;
private:
CameraModifiedCallback(const CameraModifiedCallback&) = delete;
void operator=(const CameraModifiedCallback&) = delete;
};
#else
/**
An alternative method is to create a function with this signature:
void f(vtkObject* caller, long unsigned int evId, void* clientData, void*
callData)
and, where needed, create a vtkCallbackCommand setting its callback to the
function we have created.
*/
void vtkCallbackFunc(vtkObject* caller, long unsigned int evId,
void* clientData, void* /*callData*/)
{
// Note the use of reinterpret_cast to cast the caller and callData to the
// expected types.
auto interactor = reinterpret_cast<vtkRenderWindowInteractor*>(caller);
std::cout << interactor->GetClassName() << " Event Id: " << evId
<< std::endl;
auto cam = reinterpret_cast<vtkCamera*>(clientData);
// Now print the camera orientation.
PrintCameraOrientation(cam);
};
#endif
} // namespace
int main(int, char*[])
{
vtkNew<vtkNamedColors> colors;
// Create the Renderer, RenderWindow and RenderWindowInteractor.
vtkNew<vtkRenderer> ren;
vtkNew<vtkRenderWindow> renWin;
renWin->AddRenderer(ren);
vtkNew<vtkRenderWindowInteractor> iren;
iren->SetRenderWindow(renWin);
// Use a cone as a source.
vtkNew<vtkConeSource> source;
source->SetCenter(0, 0, 0);
source->SetRadius(1);
// Use the golden ratio for the height. Because we can!
source->SetHeight(1.6180339887498948482);
source->SetResolution(128);
source->Update();
// Pipeline
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(source->GetOutputPort());
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
actor->GetProperty()->SetColor(colors->GetColor3d("peacock").GetData());
// Lighting
actor->GetProperty()->SetAmbient(0.3);
actor->GetProperty()->SetDiffuse(0.0);
actor->GetProperty()->SetSpecular(1.0);
actor->GetProperty()->SetSpecularPower(20.0);
// Get an outline of the data set for context.
vtkNew<vtkOutlineFilter> outline;
outline->SetInputData(source->GetOutput());
vtkNew<vtkPolyDataMapper> outlineMapper;
outlineMapper->SetInputConnection(outline->GetOutputPort());
vtkNew<vtkActor> outlineActor;
outlineActor->SetMapper(outlineMapper);
outlineActor->GetProperty()->SetColor(colors->GetColor3d("Black").GetData());
// Add the actors to the renderer, set the background and size.
ren->AddActor(actor);
ren->AddActor(outlineActor);
ren->SetBackground(colors->GetColor3d("AliceBlue").GetData());
renWin->SetSize(512, 512);
// Set up a nice camera position.
vtkNew<vtkCamera> camera;
camera->SetPosition(4.6, -2.0, 3.8);
camera->SetFocalPoint(0.0, 0.0, 0.0);
camera->SetClippingRange(3.2, 10.2);
camera->SetViewUp(0.3, 1.0, 0.13);
ren->SetActiveCamera(camera);
renWin->Render();
renWin->SetWindowName("CallBack");
vtkNew<vtkAxesActor> axes;
MakeAxesActor(axes);
vtkNew<vtkOrientationMarkerWidget> om;
om->SetOrientationMarker(axes);
// Position lower left in the viewport.
om->SetViewport(0, 0, 0.2, 0.2);
om->SetInteractor(iren);
om->EnabledOn();
om->InteractiveOn();
#if defined(USE_CALLBACKCOMMAND_CLASS)
// When we implement the class, it automatically becomes the callback
// function.
vtkNew<CameraModifiedCallback> getOrientation;
// Set the camera to use.
getOrientation->cam = ren->GetActiveCamera();
#else
// Create the vtkCallbackCommand.
vtkNew<vtkCallbackCommand> getOrientation;
// Set the callback to the function we created.
getOrientation->SetCallback(vtkCallbackFunc);
// Set the client data.
getOrientation->SetClientData(ren->GetActiveCamera());
#endif
iren->AddObserver(vtkCommand::EndInteractionEvent, getOrientation);
iren->Initialize();
iren->Start();
return EXIT_SUCCESS;
}
namespace {
void MakeAxesActor(vtkAxesActor* axes)
{
axes->SetShaftTypeToCylinder();
axes->SetXAxisLabelText("X");
axes->SetYAxisLabelText("Y");
axes->SetZAxisLabelText("Z");
axes->SetTotalLength(1.0, 1.0, 1.0);
axes->SetCylinderRadius(0.5 * axes->GetCylinderRadius());
axes->SetConeRadius(1.025 * axes->GetConeRadius());
axes->SetSphereRadius(1.5 * axes->GetSphereRadius());
}
/**
Get a comma separated list.
*/
template <typename T> std::string CommaSeparatedList(std::vector<T> v)
{
std::ostringstream os;
std::copy(v.begin(), v.end() - 1, std::ostream_iterator<T>(os, ", "));
os << v.back();
return os.str();
}
/**
Print the camera orientation.
*/
void PrintCameraOrientation(vtkCamera* cam)
{
auto width = 16;
double pos[3];
cam->GetPosition(pos);
double fp[3];
cam->GetFocalPoint(fp);
double vu[3];
cam->GetViewUp(vu);
double cr[2];
cam->GetClippingRange(cr);
std::cout << setw(width) << "Position: "
<< CommaSeparatedList(std::vector<double>(pos, pos + 3))
<< std::endl;
std::cout << setw(width) << "Focal point: "
<< CommaSeparatedList(std::vector<double>(fp, fp + 3)) << std::endl;
std::cout << setw(width) << "Clipping range: "
<< CommaSeparatedList(std::vector<double>(cr, cr + 2)) << std::endl;
std::cout << setw(width) << "View up: "
<< CommaSeparatedList(std::vector<double>(vu, vu + 3)) << std::endl;
std::cout << setw(width) << "Distance: " << cam->GetDistance() << std::endl;
};
} // namespace
CMakeLists.txt¶
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(CallBack)
find_package(VTK COMPONENTS
CommonColor
CommonCore
FiltersModeling
FiltersSources
InteractionStyle
InteractionWidgets
RenderingAnnotation
RenderingContextOpenGL2
RenderingCore
RenderingFreeType
RenderingGL2PSOpenGL2
RenderingOpenGL2
)
if (NOT VTK_FOUND)
message(FATAL_ERROR "CallBack: 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(CallBack MACOSX_BUNDLE CallBack.cxx )
target_link_libraries(CallBack PRIVATE ${VTK_LIBRARIES}
)
# vtk_module_autoinit is needed
vtk_module_autoinit(
TARGETS CallBack
MODULES ${VTK_LIBRARIES}
)
Download and Build CallBack¶
Click here to download CallBack and its CMakeLists.txt file. Once the tarball CallBack.tar has been downloaded and extracted,
cd CallBack/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:
./CallBack
WINDOWS USERS
Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.