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
FiltersSources
ImagingSources
ImagingGeneral
ImagingCore
InteractionStyle
IOImage
RenderingContextOpenGL2
@ -32,6 +34,13 @@ endif()
find_package(netCDF REQUIRED)
add_executable(VtkBase MACOSX_BUNDLE main.cpp
CartographicTransformation.cpp
commands/CameraMoveCallback.cpp
commands/CameraMoveCallback.h
commands/SpawnPointCallback.cpp
commands/SpawnPointCallback.h
commands/TimerCallbackCommand.cpp
commands/TimerCallbackCommand.h
layers/BackgroundImage.cpp
layers/BackgroundImage.h
layers/EGlyphLayer.cpp
@ -42,11 +51,6 @@ add_executable(VtkBase MACOSX_BUNDLE main.cpp
layers/LGlyphLayer.h
Program.cpp
Program.h
commands/TimerCallbackCommand.h
commands/TimerCallbackCommand.cpp
commands/SpawnPointCallback.h
commands/SpawnPointCallback.cpp
CartographicTransformation.cpp
)
execute_process(

View File

@ -19,7 +19,8 @@
#include "Program.h"
#include "commands/TimerCallbackCommand.h"
#include "commands/SpawnPointCallback.h"
#include "CartographicTransformation.h"
#include "commands/CameraMoveCallback.h"
void Program::setWinProperties() {
this->win->SetWindowName("Simulation");
@ -40,17 +41,28 @@ void Program::setupTimer() {
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() {
this->win = vtkSmartPointer<vtkRenderWindow>::New();
this->interact = vtkSmartPointer<vtkRenderWindowInteractor>::New();
this->cam = createNormalisedCamera();
this->win->SetNumberOfLayers(0);
setWinProperties();
setupTimer();
setupCameraCallback();
}
void Program::addLayer(Layer *layer) {
layer->setCamera(this->cam);
this->layers.push_back(layer);
this->win->AddRenderer(layer->getLayer());
this->win->SetNumberOfLayers(this->win->GetNumberOfLayers() + 1);
@ -66,6 +78,7 @@ void Program::removeLayer(Layer *layer) {
}
}
void Program::updateData(int t) {
win->Render();
for (Layer *l: layers) {

View File

@ -6,7 +6,6 @@
#include <vtkRenderer.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.
* 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;
/** 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.
*/
void setWinProperties();
@ -33,8 +37,14 @@ private:
*/
void setupTimer();
/** This function adds all interactors of each layer to the interactor/window
*/
void setupInteractions();
/** This function sets up the camera's associated movement callbacks..
*/
void setupCameraCallback();
public:
/** 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->GetWorldPoint(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);
data->SetPoints(points);

View File

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

View File

@ -3,6 +3,7 @@
#include <vtkImageActor.h>
#include <vtkImageData.h>
#include <vtkImageReader2.h>
#include <vtkTransformFilter.h>
using std::string;
@ -24,6 +25,13 @@ void BackgroundImage::updateImage() {
imageReader->Update();
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;
imageActor->SetInputData(imageData);
@ -60,3 +68,8 @@ void BackgroundImage::setImagePath(string imagePath) {
this->imagePath = imagePath;
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.
*/
void setImagePath(std::string imagePath);
void setCamera(vtkCamera *cam) override;
};
#endif

View File

@ -65,13 +65,13 @@ void EGlyphLayer::readCoordinates() {
this->direction->SetNumberOfTuples(numLats*numLons);
points->Allocate(numLats*numLons);
auto camera = createNormalisedCamera();
ren->SetActiveCamera(camera);
// auto camera = createNormalisedCamera();
// ren->SetActiveCamera(camera);
int i = 0;
for (double lat : lats) {
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
points->InsertPoint(i++, lon, lat, 0);
// see also https://vtk.org/doc/nightly/html/classvtkPolyDataMapper2D.html

View File

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

View File

@ -15,3 +15,8 @@ void Layer::updateData(int t) {
void Layer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) {
// 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.
*/
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

View File

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