normalised cartographic camera

This commit is contained in:
robin 2024-05-05 14:50:26 +02:00
parent 06315a3d52
commit 346e44da48
8 changed files with 128 additions and 65 deletions

View File

@ -46,6 +46,7 @@ add_executable(VtkBase MACOSX_BUNDLE main.cpp
commands/TimerCallbackCommand.cpp
helperClasses/SpawnPointCallback.h
helperClasses/SpawnPointCallback.cpp
helperClasses/NormalisedCartographicCamera.cpp
)
execute_process(

View File

@ -11,8 +11,6 @@ TimerCallbackCommand* TimerCallbackCommand::New(Program *program) {
}
void TimerCallbackCommand::Execute(vtkObject* caller, unsigned long eventId, void* vtkNotUsed(callData)) {
cout << this->time << " " << this->maxTime << endl;
this->time += this->dt;
if (this->time >= this->maxTime) {

View File

@ -13,6 +13,7 @@
#include <vtkVertexGlyphFilter.h>
#include <netcdf>
#include <vtkArrowSource.h>
#include "NormalisedCartographicCamera.h"
using namespace netCDF;
using namespace std;
@ -64,11 +65,15 @@ void EGlyphLayer::readCoordinates() {
this->direction->SetNumberOfTuples(numLats*numLons);
points->Allocate(numLats*numLons);
auto camera = createNormalisedCartographicCamera();
ren->SetActiveCamera(camera);
int i = 0;
for (double lat : lats) {
for (double lon : lons) {
cout << "lon: " << lon << " lat: " << lat << endl;
direction->SetTuple3(i, 0.45, 0.90, 0); //FIXME: read this info from file
points->InsertPoint(i++, (lat*1000-46125)/25, (lon*1000+15875)/43.5, 0); // FIXME: counts on fixed window geometry to map properly; refactor to make use of active window geometry.
points->InsertPoint(i++, lon, lat, 0); // FIXME: counts on fixed window geometry to map properly; refactor to make use of active window geometry.
// see also https://vtk.org/doc/nightly/html/classvtkPolyDataMapper2D.html
}
}
@ -78,7 +83,7 @@ void EGlyphLayer::readCoordinates() {
vtkNew<vtkGlyphSource2D> arrowSource;
arrowSource->SetGlyphTypeToArrow();
arrowSource->SetScale(8); //TODO: set this properly
arrowSource->SetScale(0.2); //TODO: set this properly
arrowSource->Update();
vtkNew<vtkGlyph2D> glyph2D;
@ -90,15 +95,15 @@ void EGlyphLayer::readCoordinates() {
glyph2D->SetVectorModeToUseVector();
glyph2D->Update();
vtkNew<vtkCoordinate> coordinate;
coordinate->SetCoordinateSystemToWorld();
// vtkNew<vtkCoordinate> coordinate;
// coordinate->SetCoordinateSystemToWorld();
vtkNew<vtkPolyDataMapper2D>(mapper);
vtkNew<vtkPolyDataMapper>(mapper);
// mapper->SetTransformCoordinate(coordinate);
mapper->SetInputConnection(glyph2D->GetOutputPort());
mapper->Update();
vtkNew<vtkActor2D> actor;
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
actor->GetProperty()->SetColor(0,0,0);

View File

@ -9,16 +9,23 @@
#include <vtkVertexGlyphFilter.h>
#include <vtkInteractorStyle.h>
#include <vtkInteractorStyleUser.h>
#include <vtkTransform.h>
#include <vtkTransformFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkRenderWindow.h>
#include <vtkCamera.h>
#include "NormalisedCartographicCamera.h"
vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() {
auto newPointCallBack = vtkSmartPointer<SpawnPointCallback>::New();
newPointCallBack->setData(data);
newPointCallBack->setPoints(points);
newPointCallBack->setRen(ren);
return newPointCallBack;
}
// TODO: add interactionStyle functionality
// TODO: add timer + advection (probably from the program class not here)
// TODO: how do we handle mapping between pixelspace and lat/lon (needed for advection)? Current idea: store the vtkPoints in lat/lon system, then apply a transformfilter to map them to the current window geometry. This should allow for a changing viewport as well - we can query the new camera position and map accordingly.
@ -28,74 +35,61 @@ vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() {
// TODO: modelling all this in vtkClasses is workable, but ideally i would want to work with a native C++ class. See if this is doable and feasible.
LGlyphLayer::LGlyphLayer() {
this->ren = vtkSmartPointer<vtkRenderer>::New();
this->ren->SetLayer(2);
this->ren = vtkSmartPointer<vtkRenderer>::New();
this->ren->SetLayer(2);
this->points = vtkSmartPointer<vtkPoints>::New();
this->data = vtkSmartPointer<vtkPolyData>::New();
this->data->SetPoints(this->points);
vtkNew<vtkGlyphSource2D> circleSource;
circleSource->SetGlyphTypeToCircle();
circleSource->SetScale(15);
circleSource->Update();
this->points = vtkSmartPointer<vtkPoints>::New();
this->data = vtkSmartPointer<vtkPolyData>::New();
this->data->SetPoints(this->points);
vtkNew<vtkGlyph2D> glyph2D;
glyph2D->SetSourceConnection(circleSource->GetOutputPort());
glyph2D->SetInputData(this->data);
glyph2D->SetColorModeToColorByScalar();
glyph2D->Update();
vtkNew<vtkGlyphSource2D> circleSource;
circleSource->SetGlyphTypeToCircle();
circleSource->SetScale(1);
circleSource->Update();
vtkNew<vtkPolyDataMapper2D> mapper;
mapper->SetInputConnection(glyph2D->GetOutputPort());
mapper->Update();
vtkNew<vtkGlyph2D> glyph2D;
glyph2D->SetSourceConnection(circleSource->GetOutputPort());
glyph2D->SetInputData(this->data);
glyph2D->SetColorModeToColorByScalar();
glyph2D->Update();
vtkNew<vtkActor2D> actor;
actor->SetMapper(mapper);
actor->GetProperty()->SetColor(1,1,1);
auto camera = createNormalisedCartographicCamera();
ren->SetActiveCamera(camera);
this->ren->AddActor(actor);
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(glyph2D->GetOutputPort());
mapper->Update();
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
this->ren->AddActor(actor);
}
// creates a few points so we can test the updateData function
void LGlyphLayer::spoofPoints() {
this->points->InsertNextPoint(53, 2, 0);
this->points->InsertNextPoint(48.2, 111.01, 0);
this->points->InsertNextPoint(331, 331, 0);
// this->points->InsertNextPoint(200, 200 , 0);
this->points->InsertNextPoint(-4.125, 61.375 , 0);
this->points->InsertNextPoint(4.896555178870355, 52.373557841669516, 0);
// this->points->InsertNextPoint(48.2, 111.01, 0);
// this->points->InsertNextPoint(331, 331, 0);
// this->points->InsertNextPoint(0, 50, 0);
// this->points->InsertNextPoint(200, 200 , 0);
this->points->Modified();
this->points->Modified();
}
// returns new coords for a point; used to test the updateData function
std::pair<double, double> advect(int time, double lat, double lon) {
return {lat+0.1, lon+0.1} ;
return {lat + 0., lon + 0.};
}
// converts a x,y pair from pixel coordinates to real world latitude and longitude.
// TODO: make this more modular by having it interact with the backgroundImage layer (and possibly the camera panning/zooming logic when that is implemented).
std::pair<double, double> pixelToReal(double x, double y) {
//assumes a 661x661 window with a range of [46.125, 62.625] lat and [-15.875, 12.875] lon.
return {(x*25+46125)/1000, (y*43.5-15875)/1000};
}
// converts a lat,lon pair from real world values to pixel coordinates.
// TODO: see above.
std::pair<double, double> realToPixel(double lat, double lon) {
//assumes a 661x661 window with a range of [46.125, 62.625] lat and [-15.875, 12.875] lon.
return {(lat*1000-46125)/25, (lon*1000+15875)/43.5};
}
// FIXME: actually the above functions are a bit of a naive way of modelling these. Much better would be to have the points at the real-world latitude and longitude, and apply a filter in the pipeline to convert them to the appropriate window geometry.
void LGlyphLayer::updateData(int t) {
double point[3];
for (vtkIdType n=0; n < this->points->GetNumberOfPoints(); n++) {
this->points->GetPoint(n, point);
auto grads = pixelToReal(point[0], point[1]);
auto newGrads = advect(n, grads.first, grads.second);
auto newPixs = realToPixel(newGrads.first, newGrads.second);
this->points->SetPoint(n, newPixs.first, newPixs.second, 0);
}
this->points->Modified();
void LGlyphLayer::updateData(int t) {
double point[3];
for (vtkIdType n = 0; n < this->points->GetNumberOfPoints(); n++) {
this->points->GetPoint(n, point);
auto [xNew, yNew] = advect(n, point[0], point[1]);
this->points->SetPoint(n, xNew, yNew, 0);
}
this->points->Modified();
}

View File

@ -0,0 +1,43 @@
#include "NormalisedCartographicCamera.h"
#include <vtkMatrix4x4.h>
vtkSmartPointer<vtkCamera> createNormalisedCartographicCamera() {
const double XMin = -15.875;
const double XMax = 12.875;
const double YMin = 46.125;
const double YMax = 62.625;
double farClipPlane = 100;
double nearClipPlane = 1;
double eyeTransform[] = {
2/(XMax-XMin), 0, 0, -(XMax+XMin)/(XMax-XMin),
0, 2/(YMax-YMin), 0, -(YMax+YMin)/(YMax-YMin),
0, 0, 2/(nearClipPlane-farClipPlane), -(farClipPlane+nearClipPlane)/(farClipPlane-nearClipPlane),
0, 0, 0, 1
};
vtkSmartPointer<vtkCamera> camera = vtkSmartPointer<vtkCamera>::New();
camera->ParallelProjectionOn(); // Enable parallel projection
camera->UseExplicitProjectionTransformMatrixOn();
//// // Calculate the center and the size of the view
double centerX = (XMax + XMin) / 2.0;
double centerY = (YMax + YMin) / 2.0;
double width = XMax - XMin;
double height = YMax - YMin;
//// // Set the camera position, focal point, and view up
// camera->SetPosition(centerX, centerY, 1000); // Place the camera above the center
// camera->SetFocalPoint(centerX, centerY, 0); // Look at the center
// camera->SetViewUp(0, 1, 0); // Set the up vector to be along the Y-axis
////
//// // Set parallel scale
// double parallelScale = std::max(width, height) / 2.0;
// camera->SetParallelScale(parallelScale);
vtkNew<vtkMatrix4x4> projectionMatrix;
projectionMatrix->DeepCopy(eyeTransform);
camera->SetExplicitProjectionTransformMatrix(projectionMatrix);
// camera->SetScreenBottomLeft(XMin, YMin, 0);
// camera->SetScreenTopRight(XMax, YMax, 0);
return camera;
}

View File

@ -0,0 +1,8 @@
#include <vtkCamera.h>
#ifndef VTKBASE_NORMALISEDCARTOGRAPHICCAMERA_H
#define VTKBASE_NORMALISEDCARTOGRAPHICCAMERA_H
#endif //VTKBASE_NORMALISEDCARTOGRAPHICCAMERA_H
vtkSmartPointer<vtkCamera> createNormalisedCartographicCamera();

View File

@ -31,7 +31,14 @@ void SpawnPointCallback::Execute(vtkObject *caller, unsigned long evId, void *ca
int x, y;
interactor->GetEventPosition(x, y);
vtkIdType id = points->InsertNextPoint(x, y, 0);
double worldPos[4] = {2, 0 ,0, 0};
double displayPos[3] = {static_cast<double>(x), static_cast<double>(y), 0.0};
ren->SetDisplayPoint(displayPos);
ren->DisplayToWorld();
ren->GetWorldPoint(worldPos);
cout << "clicked on lon = " << worldPos[0] << " and lat = " << worldPos[1] << endl;
vtkIdType id = points->InsertNextPoint(worldPos[0], worldPos[1], 0);
data->SetPoints(points);
vtkSmartPointer<vtkVertex> vertex = vtkSmartPointer<vtkVertex>::New();
@ -57,4 +64,8 @@ void SpawnPointCallback::setData(const vtkSmartPointer<vtkPolyData> &data) {
void SpawnPointCallback::setPoints(const vtkSmartPointer<vtkPoints> &points) {
this->points = points;
}
}
void SpawnPointCallback::setRen(const vtkSmartPointer<vtkRenderer> &ren) {
this->ren = ren;
}

View File

@ -17,9 +17,12 @@ public:
void setData(const vtkSmartPointer<vtkPolyData> &data);
void setRen(const vtkSmartPointer<vtkRenderer> &ren);
private:
vtkSmartPointer<vtkPolyData> data;
vtkSmartPointer<vtkPoints> points;
vtkSmartPointer<vtkRenderer> ren;
public:
private: