feat (wip): implementing camera zooming and panning functionality

This commit is contained in:
Djairo Hougee 2024-05-05 21:45:02 +02:00
parent cd87f383c9
commit 4d743a42c1
14 changed files with 180 additions and 22 deletions

View File

@ -14,6 +14,8 @@ find_package(VTK COMPONENTS
FiltersProgrammable FiltersProgrammable
FiltersSources FiltersSources
ImagingSources ImagingSources
ImagingGeneral
ImagingCore
InteractionStyle InteractionStyle
IOImage IOImage
RenderingContextOpenGL2 RenderingContextOpenGL2
@ -32,21 +34,23 @@ endif()
find_package(netCDF REQUIRED) find_package(netCDF REQUIRED)
add_executable(VtkBase MACOSX_BUNDLE main.cpp add_executable(VtkBase MACOSX_BUNDLE main.cpp
layers/BackgroundImage.cpp CartographicTransformation.cpp
layers/BackgroundImage.h commands/CameraMoveCallback.cpp
layers/EGlyphLayer.cpp commands/CameraMoveCallback.h
layers/EGlyphLayer.h commands/SpawnPointCallback.cpp
layers/Layer.cpp commands/SpawnPointCallback.h
layers/Layer.h
layers/LGlyphLayer.cpp
layers/LGlyphLayer.h
Program.cpp
Program.h
commands/TimerCallbackCommand.h
commands/TimerCallbackCommand.cpp commands/TimerCallbackCommand.cpp
commands/SpawnPointCallback.h commands/TimerCallbackCommand.h
commands/SpawnPointCallback.cpp layers/BackgroundImage.cpp
CartographicTransformation.cpp layers/BackgroundImage.h
layers/EGlyphLayer.cpp
layers/EGlyphLayer.h
layers/Layer.cpp
layers/Layer.h
layers/LGlyphLayer.cpp
layers/LGlyphLayer.h
Program.cpp
Program.h
) )
execute_process( execute_process(

View File

@ -19,7 +19,8 @@
#include "Program.h" #include "Program.h"
#include "commands/TimerCallbackCommand.h" #include "commands/TimerCallbackCommand.h"
#include "commands/SpawnPointCallback.h" #include "CartographicTransformation.h"
#include "commands/CameraMoveCallback.h"
void Program::setWinProperties() { void Program::setWinProperties() {
this->win->SetWindowName("Simulation"); this->win->SetWindowName("Simulation");
@ -40,17 +41,28 @@ void Program::setupTimer() {
this->interact->CreateRepeatingTimer(17); // 60 fps == 1000 / 60 == 16.7 ms per frame this->interact->CreateRepeatingTimer(17); // 60 fps == 1000 / 60 == 16.7 ms per frame
} }
void Program::setupCameraCallback() {
auto callback = vtkSmartPointer<CameraMoveCallback>::New(this->cam);
this->interact->AddObserver(vtkCommand::MouseWheelForwardEvent, callback);
this->interact->AddObserver(vtkCommand::MouseWheelBackwardEvent, callback);
this->interact->AddObserver(vtkCommand::KeyPressEvent, callback);
}
Program::Program() { Program::Program() {
this->win = vtkSmartPointer<vtkRenderWindow>::New(); this->win = vtkSmartPointer<vtkRenderWindow>::New();
this->interact = vtkSmartPointer<vtkRenderWindowInteractor>::New(); this->interact = vtkSmartPointer<vtkRenderWindowInteractor>::New();
this->cam = createNormalisedCamera();
this->win->SetNumberOfLayers(0); this->win->SetNumberOfLayers(0);
setWinProperties(); setWinProperties();
setupTimer(); setupTimer();
setupCameraCallback();
} }
void Program::addLayer(Layer *layer) { void Program::addLayer(Layer *layer) {
layer->setCamera(this->cam);
this->layers.push_back(layer); this->layers.push_back(layer);
this->win->AddRenderer(layer->getLayer()); this->win->AddRenderer(layer->getLayer());
this->win->SetNumberOfLayers(this->win->GetNumberOfLayers() + 1); this->win->SetNumberOfLayers(this->win->GetNumberOfLayers() + 1);
@ -66,6 +78,7 @@ void Program::removeLayer(Layer *layer) {
} }
} }
void Program::updateData(int t) { void Program::updateData(int t) {
win->Render(); win->Render();
for (Layer *l: layers) { for (Layer *l: layers) {

View File

@ -6,7 +6,6 @@
#include <vtkRenderer.h> #include <vtkRenderer.h>
#include "layers/Layer.h" #include "layers/Layer.h"
#include "commands/SpawnPointCallback.h"
/** This class manages the upper levels of the vtk pipeline; it has attributes for the vtkrenderWindow and a vector of Layers to represent a variable number of vtkRenderers. /** This class manages the upper levels of the vtk pipeline; it has attributes for the vtkrenderWindow and a vector of Layers to represent a variable number of vtkRenderers.
* It can also set up a vtkTimer by connecting an instance of TimerCallbackCommand with its contained vtkRenderWindowInteractor. * It can also set up a vtkTimer by connecting an instance of TimerCallbackCommand with its contained vtkRenderWindowInteractor.
@ -25,6 +24,11 @@ private:
*/ */
vtkSmartPointer<vtkRenderWindowInteractor> interact; vtkSmartPointer<vtkRenderWindowInteractor> interact;
/** The camera used by all layers for this program.
*/
vtkSmartPointer<vtkCamera> cam;
/** This function sets some default properties on the vtkRenderWindow. Extracted to its' own function to keep the constructor from becoming cluttered. /** This function sets some default properties on the vtkRenderWindow. Extracted to its' own function to keep the constructor from becoming cluttered.
*/ */
void setWinProperties(); void setWinProperties();
@ -33,8 +37,14 @@ private:
*/ */
void setupTimer(); void setupTimer();
/** This function adds all interactors of each layer to the interactor/window
*/
void setupInteractions(); void setupInteractions();
/** This function sets up the camera's associated movement callbacks..
*/
void setupCameraCallback();
public: public:
/** Constructor. /** Constructor.
*/ */

View File

@ -0,0 +1,74 @@
#include "CameraMoveCallback.h"
#include <vtkVertex.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkCommand.h>
#include <vtkRenderWindow.h>
using std::string;
void CameraMoveCallback::Execute(vtkObject *caller, unsigned long evId,
void *callData) {
// Note the use of reinterpret_cast to cast the caller to the expected type.
auto interactor = reinterpret_cast<vtkRenderWindowInteractor *>(caller);
switch (evId) {
case vtkCommand::KeyPressEvent:
pan(interactor->GetKeySym());
break;
case vtkCommand::MouseWheelForwardEvent:
zoom(true);
break;
case vtkCommand::MouseWheelBackwardEvent:
zoom(false);
break;
default:
break;
}
}
void CameraMoveCallback::zoom(const bool in) {
double pos[3];
this->cam->GetPosition(pos);
if (in) {
pos[2] -= 50;
} else {
pos[2] += 50;
}
this->cam->SetPosition(pos);
}
void CameraMoveCallback::pan(const string dir) {
double pos[3];
this->cam->GetPosition(pos);
if (dir == "Left" or dir == "h") {
pos[0] -= 1;
this->cam->SetPosition(pos);
} else if (dir == "Up" or dir == "j" ) {
pos[1] += 1;
this->cam->SetPosition(pos);
} else if (dir == "Right" or dir == "k" ) {
pos[0] += 1;
this->cam->SetPosition(pos);
} else if (dir == "Down" or dir == "l" ) {
pos[1] -= 1;
this->cam->SetPosition(pos);
}
}
CameraMoveCallback::CameraMoveCallback() : cam(nullptr) {}
CameraMoveCallback *CameraMoveCallback::New(vtkCamera *cam) {
auto me = new CameraMoveCallback;
me->setCam(cam);
return me;
}
void CameraMoveCallback::setCam(const vtkSmartPointer<vtkCamera> &cam) {
this->cam = cam;
}

View File

@ -0,0 +1,27 @@
#ifndef VTKBASE_CAMERAMOVECALLBACK_H
#define VTKBASE_CAMERAMOVECALLBACK_H
#include <vtkCallbackCommand.h>
#include <vtkCamera.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkMatrix4x4.h>
class CameraMoveCallback : public vtkCallbackCommand {
public:
static CameraMoveCallback *New(vtkCamera *cam);
CameraMoveCallback();
void setCam(const vtkSmartPointer<vtkCamera> &cam);
private:
vtkSmartPointer<vtkCamera> cam;
void Execute(vtkObject *caller, unsigned long evId, void *callData) override;
void zoom(const bool in);
void pan(const std::string dir);
};
#endif

View File

@ -39,7 +39,7 @@ void SpawnPointCallback::Execute(vtkObject *caller, unsigned long evId, void *ca
ren->DisplayToWorld(); ren->DisplayToWorld();
ren->GetWorldPoint(worldPos); ren->GetWorldPoint(worldPos);
inverseCartographicProjection->MultiplyPoint(worldPos, worldPos); inverseCartographicProjection->MultiplyPoint(worldPos, worldPos);
cout << "clicked on lon = " << worldPos[0] << " and lat = " << worldPos[1] << endl; // cout << "clicked on lon = " << worldPos[0] << " and lat = " << worldPos[1] << endl;
vtkIdType id = points->InsertNextPoint(worldPos[0], worldPos[1], 0); vtkIdType id = points->InsertNextPoint(worldPos[0], worldPos[1], 0);
data->SetPoints(points); data->SetPoints(points);

View File

@ -2,6 +2,7 @@
#include "../Program.h" #include "../Program.h"
// TODO: add getter/setters to attributes for customizability.
TimerCallbackCommand::TimerCallbackCommand() : dt(3600), maxTime(3600*24*365), time(0) {} TimerCallbackCommand::TimerCallbackCommand() : dt(3600), maxTime(3600*24*365), time(0) {}
TimerCallbackCommand* TimerCallbackCommand::New(Program *program) { TimerCallbackCommand* TimerCallbackCommand::New(Program *program) {
@ -22,7 +23,6 @@ void TimerCallbackCommand::Execute(vtkObject* caller, unsigned long eventId, voi
} }
void TimerCallbackCommand::setProgram(Program *program) { void TimerCallbackCommand::setProgram(Program *program) {
this->program = program; this->program = program;
} }

View File

@ -3,6 +3,7 @@
#include <vtkImageActor.h> #include <vtkImageActor.h>
#include <vtkImageData.h> #include <vtkImageData.h>
#include <vtkImageReader2.h> #include <vtkImageReader2.h>
#include <vtkTransformFilter.h>
using std::string; using std::string;
@ -24,6 +25,13 @@ void BackgroundImage::updateImage() {
imageReader->Update(); imageReader->Update();
imageData = imageReader->GetOutput(); imageData = imageReader->GetOutput();
// TODO: transform the iamge to the range [-1,1] and center it on (0,0)/
// This will allow the backgorundImage to share a camera with our other layers.
// Facilitating the cameraMovement callback.
// vtkSmartPointer<vtkTransformFilter> transformFilter = createCartographicTransformFilter();
// transformFilter->SetInputData(imageData);
vtkNew<vtkImageActor> imageActor; vtkNew<vtkImageActor> imageActor;
imageActor->SetInputData(imageData); imageActor->SetInputData(imageData);
@ -60,3 +68,8 @@ void BackgroundImage::setImagePath(string imagePath) {
this->imagePath = imagePath; this->imagePath = imagePath;
updateImage(); updateImage();
} }
void BackgroundImage::setCamera(vtkCamera *cam) {
// TODO: fix the camera for this layer so this intentionally empty override can be removed.
}

View File

@ -32,6 +32,9 @@ public:
* @param imagePath : String to the path of the new image to use. * @param imagePath : String to the path of the new image to use.
*/ */
void setImagePath(std::string imagePath); void setImagePath(std::string imagePath);
void setCamera(vtkCamera *cam) override;
}; };
#endif #endif

View File

@ -65,13 +65,13 @@ void EGlyphLayer::readCoordinates() {
this->direction->SetNumberOfTuples(numLats*numLons); this->direction->SetNumberOfTuples(numLats*numLons);
points->Allocate(numLats*numLons); points->Allocate(numLats*numLons);
auto camera = createNormalisedCamera(); // auto camera = createNormalisedCamera();
ren->SetActiveCamera(camera); // ren->SetActiveCamera(camera);
int i = 0; int i = 0;
for (double lat : lats) { for (double lat : lats) {
for (double lon : lons) { for (double lon : lons) {
cout << "lon: " << lon << " lat: " << lat << endl; // cout << "lon: " << lon << " lat: " << lat << endl;
direction->SetTuple3(i, 0.45, 0.90, 0); //FIXME: read this info from file direction->SetTuple3(i, 0.45, 0.90, 0); //FIXME: read this info from file
points->InsertPoint(i++, lon, lat, 0); points->InsertPoint(i++, lon, lat, 0);
// see also https://vtk.org/doc/nightly/html/classvtkPolyDataMapper2D.html // see also https://vtk.org/doc/nightly/html/classvtkPolyDataMapper2D.html

View File

@ -42,7 +42,8 @@ LGlyphLayer::LGlyphLayer() {
auto camera = createNormalisedCamera(); auto camera = createNormalisedCamera();
ren->SetActiveCamera(camera); ren->SetActiveCamera(camera);
auto transform = createCartographicTransformFilter(); // TODO: this line seemed to do nothing?
// auto transform = createCartographicTransformFilter();
vtkSmartPointer<vtkTransformFilter> transformFilter = createCartographicTransformFilter(); vtkSmartPointer<vtkTransformFilter> transformFilter = createCartographicTransformFilter();
transformFilter->SetInputData(data); transformFilter->SetInputData(data);

View File

@ -15,3 +15,8 @@ void Layer::updateData(int t) {
void Layer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) { void Layer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) {
// By default, do nothing // By default, do nothing
} }
void Layer::setCamera(vtkCamera *camera) {
this->getLayer()->SetActiveCamera(camera);
}

View File

@ -27,6 +27,11 @@ public:
* @param interactor : pointer to the interactor that observers can be added to. * @param interactor : pointer to the interactor that observers can be added to.
*/ */
virtual void addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor); virtual void addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor);
/** Sets the active camera for the vtkRenderer associated with this layer.
* Used to share one camera between multiple layers.
*/
virtual void setCamera(vtkCamera *camera);
}; };
#endif #endif

View File

@ -12,6 +12,8 @@
#include "layers/EGlyphLayer.h" #include "layers/EGlyphLayer.h"
#include "layers/LGlyphLayer.h" #include "layers/LGlyphLayer.h"
#include "Program.h" #include "Program.h"
#include "CartographicTransformation.h"
using namespace std; using namespace std;
@ -20,6 +22,7 @@ int main() {
auto l = new LGlyphLayer(); auto l = new LGlyphLayer();
l->spoofPoints(); l->spoofPoints();
Program *program = new Program(); Program *program = new Program();
program->addLayer(new BackgroundImage("../../../../data/map_661-661.png")); program->addLayer(new BackgroundImage("../../../../data/map_661-661.png"));
program->addLayer(new EGlyphLayer()); program->addLayer(new EGlyphLayer());