diff --git a/particle-track-and-trace/src/CMakeLists.txt b/particle-track-and-trace/src/CMakeLists.txt index 9364e77..ac28701 100644 --- a/particle-track-and-trace/src/CMakeLists.txt +++ b/particle-track-and-trace/src/CMakeLists.txt @@ -7,7 +7,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -find_package(VTK COMPONENTS +find_package(VTK COMPONENTS + GeovisCore CommonColor CommonColor CommonCore diff --git a/particle-track-and-trace/src/CartographicTransformation.cpp b/particle-track-and-trace/src/CartographicTransformation.cpp index 5932c9c..fa96b95 100644 --- a/particle-track-and-trace/src/CartographicTransformation.cpp +++ b/particle-track-and-trace/src/CartographicTransformation.cpp @@ -2,45 +2,91 @@ #include #include #include +#include +#include +#include vtkSmartPointer createNormalisedCamera() { - vtkSmartPointer camera = vtkSmartPointer::New(); - camera->ParallelProjectionOn(); // Enable parallel projection + vtkSmartPointer camera = vtkSmartPointer::New(); + camera->ParallelProjectionOn(); // Enable parallel projection - camera->SetPosition(0, 0, 1000); // Place the camera above the center - camera->SetFocalPoint(0, 0, 0); // Look at the center - camera->SetViewUp(0, 1, 0); // Set the up vector to be along the Y-axis - camera->SetParallelScale(1); // x,y in [-1, 1] + camera->SetPosition(0, 0, 1000); // Place the camera above the center + camera->SetFocalPoint(0, 0, 0); // Look at the center + camera->SetViewUp(0, 1, 0); // Set the up vector to be along the Y-axis + camera->SetParallelScale(1); // x,y in [-1, 1] - return camera; -} - -vtkSmartPointer getCartographicTransformMatrix(const std::shared_ptr uvGrid) { - const double XMin = uvGrid->lonMin(); - const double XMax = uvGrid->lonMax(); - const double YMin = uvGrid->latMin(); - const double YMax = uvGrid->latMax(); - - double eyeTransform[] = { - 2/(XMax-XMin), 0, 0, -(XMax+XMin)/(XMax-XMin), - 0, 2/(YMax-YMin), 0, -(YMax+YMin)/(YMax-YMin), - 0, 0, 1, 0, - 0, 0, 0, 1 - }; - - auto matrix = vtkSmartPointer::New(); - matrix->DeepCopy(eyeTransform); - return matrix; + return camera; } // Assumes Normalised camera is used vtkSmartPointer createCartographicTransformFilter(const std::shared_ptr uvGrid) { - vtkNew transform; + auto proj = vtkSmartPointer::New(); + proj->SetName("merc"); - transform->SetMatrix(getCartographicTransformMatrix(uvGrid)); + auto geoTransform = vtkSmartPointer::New(); + geoTransform->SetDestinationProjection(proj); - vtkSmartPointer transformFilter = vtkSmartPointer::New(); - transformFilter->SetTransform(transform); + const double XMin = uvGrid->lonMin(); + const double XMax = uvGrid->lonMax(); + const double YMin = uvGrid->latMin(); + const double YMax = uvGrid->latMax(); - return transformFilter; + double bottomLeft[3] = {XMin, YMin, 0}; + double topRight[3] = {XMax, YMax, 0}; + geoTransform->TransformPoint(bottomLeft, bottomLeft); + geoTransform->TransformPoint(topRight, topRight); + + double width = topRight[0] - bottomLeft[0]; + double height = topRight[1] - bottomLeft[1]; + + auto scaleIntoNormalisedSpace = vtkSmartPointer::New(); + scaleIntoNormalisedSpace->Scale(2 / (width), 2 / (height), 1); + scaleIntoNormalisedSpace->Translate(-(bottomLeft[0] + topRight[0]) / 2, -(bottomLeft[1] + topRight[1]) / 2, 0); + + auto totalProjection = vtkSmartPointer::New(); + totalProjection->Identity(); + totalProjection->Concatenate(scaleIntoNormalisedSpace); + totalProjection->Concatenate(geoTransform); + + vtkSmartPointer transformFilter = vtkSmartPointer::New(); + transformFilter->SetTransform(totalProjection); + + return transformFilter; } + +vtkSmartPointer createInverseCartographicTransformFilter(const std::shared_ptr uvGrid) { + auto proj = vtkSmartPointer::New(); + proj->SetName("merc"); + + auto geoTransform = vtkSmartPointer::New(); + geoTransform->SetDestinationProjection(proj); + + const double XMin = uvGrid->lonMin(); + const double XMax = uvGrid->lonMax(); + const double YMin = uvGrid->latMin(); + const double YMax = uvGrid->latMax(); + + double bottomLeft[3] = {XMin, YMin, 0}; + double topRight[3] = {XMax, YMax, 0}; + geoTransform->TransformPoint(bottomLeft, bottomLeft); + geoTransform->TransformPoint(topRight, topRight); + geoTransform->Inverse(); + + double width = topRight[0] - bottomLeft[0]; + double height = topRight[1] - bottomLeft[1]; + + auto scaleIntoNormalisedSpace = vtkSmartPointer::New(); + scaleIntoNormalisedSpace->Scale(2 / (width), 2 / (height), 1); + scaleIntoNormalisedSpace->Translate(-(bottomLeft[0] + topRight[0]) / 2, -(bottomLeft[1] + topRight[1]) / 2, 0); + scaleIntoNormalisedSpace->Inverse(); + + auto totalProjection = vtkSmartPointer::New(); + totalProjection->Identity(); + totalProjection->Concatenate(geoTransform); + totalProjection->Concatenate(scaleIntoNormalisedSpace); + + vtkSmartPointer transformFilter = vtkSmartPointer::New(); + transformFilter->SetTransform(totalProjection); + + return transformFilter; +} \ No newline at end of file diff --git a/particle-track-and-trace/src/CartographicTransformation.h b/particle-track-and-trace/src/CartographicTransformation.h index e59be8d..46dc51f 100644 --- a/particle-track-and-trace/src/CartographicTransformation.h +++ b/particle-track-and-trace/src/CartographicTransformation.h @@ -15,17 +15,20 @@ vtkSmartPointer createNormalisedCamera(); /** - * Constructs a 4x4 projection matrix that maps homogenious (longitude, latitude, 0, 1) points - * to the normalised space. - * TODO: This transformation has room for improvement see: - * https://github.com/MakeNEnjoy/interactive-track-and-trace/issues/12 - * @return pointer to 4x4 matrix - */ -vtkSmartPointer getCartographicTransformMatrix(const std::shared_ptr uvGrid); - -/** - * Convenience function that converts the 4x4 projection matrix into a vtkTransformFilter - * @return pointer to transform filter + * Creates a mercator projection and then scales into normalised space. + * @param uvGrid grid that contains latitudes and longitudes + * @return pointer to transform filter */ vtkSmartPointer createCartographicTransformFilter(const std::shared_ptr uvGrid); + +/** + * Creates an inverse mercator projection and then scales into normalised space. + * + * Note that it is not possible to call the Inverse() member function on the transform of the + * createCartographicTransformFilter. It produces the wrong output, + * I believe that this is a bug in VTK, I might make an issue about this sometime. + * @param uvGrid grid that contains latitudes and longitudes + * @return pointer to transform filter + */ +vtkSmartPointer createInverseCartographicTransformFilter(const std::shared_ptr uvGrid); #endif //NORMALISEDCARTOGRAPHICCAMERA_H diff --git a/particle-track-and-trace/src/commands/SpawnPointCallback.cpp b/particle-track-and-trace/src/commands/SpawnPointCallback.cpp index e0c793e..860bb06 100644 --- a/particle-track-and-trace/src/commands/SpawnPointCallback.cpp +++ b/particle-track-and-trace/src/commands/SpawnPointCallback.cpp @@ -17,7 +17,6 @@ void convertDisplayToWorld(vtkRenderer *renderer, int x, int y, double *worldPos } void SpawnPointCallback::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(caller); @@ -39,12 +38,13 @@ void SpawnPointCallback::Execute(vtkObject *caller, unsigned long evId, void *ca ren->SetDisplayPoint(displayPos); ren->DisplayToWorld(); ren->GetWorldPoint(worldPos); - inverseCartographicProjection->MultiplyPoint(worldPos, worldPos); + cout << "clicked on " << worldPos[1] << ", " << worldPos[0] << endl; + inverseCartographicProjection->TransformPoint(worldPos, worldPos); points->InsertNextPoint(worldPos[0], worldPos[1], 0); this->particlesBeached->InsertNextValue(0); - // FIXME: The below lines cause some weird interaction with our vtkTimer. + // FIXME: The below lines cause some weird interaction with our vtkTimer. // see github issue https://github.com/MakeNEnjoy/interactive-track-and-trace/issues/28 this->particlesBeached->Modified(); this->points->Modified(); @@ -75,8 +75,7 @@ void SpawnPointCallback::setRen(const vtkSmartPointer &ren) { void SpawnPointCallback::setUVGrid(const std::shared_ptr &uvGrid) { this->uvGrid = uvGrid; - inverseCartographicProjection = getCartographicTransformMatrix(uvGrid); - inverseCartographicProjection->Invert(); + inverseCartographicProjection = createInverseCartographicTransformFilter(uvGrid)->GetTransform(); } void SpawnPointCallback::setBeached(const vtkSmartPointer &ints) { diff --git a/particle-track-and-trace/src/commands/SpawnPointCallback.h b/particle-track-and-trace/src/commands/SpawnPointCallback.h index da527ae..c8d6767 100644 --- a/particle-track-and-trace/src/commands/SpawnPointCallback.h +++ b/particle-track-and-trace/src/commands/SpawnPointCallback.h @@ -1,13 +1,13 @@ #ifndef SPAWNPOINTCALLBACK_H #define SPAWNPOINTCALLBACK_H - #include #include #include #include #include -#include +#include + #include "../advection/UVGrid.h" class SpawnPointCallback : public vtkCallbackCommand { @@ -33,7 +33,7 @@ private: vtkSmartPointer ren; vtkSmartPointer particlesBeached; std::shared_ptr uvGrid; - vtkSmartPointer inverseCartographicProjection; + vtkSmartPointer inverseCartographicProjection; void Execute(vtkObject *caller, unsigned long evId, void *callData) override; diff --git a/particle-track-and-trace/src/layers/EGlyphLayer.cpp b/particle-track-and-trace/src/layers/EGlyphLayer.cpp index 39970b7..85a9bde 100644 --- a/particle-track-and-trace/src/layers/EGlyphLayer.cpp +++ b/particle-track-and-trace/src/layers/EGlyphLayer.cpp @@ -12,10 +12,13 @@ #include #include #include + #include "../CartographicTransformation.h" #include "../advection/readdata.h" #include "../advection/interpolate.h" +#include "vtkTransform.h" + using namespace std; EGlyphLayer::EGlyphLayer(std::shared_ptr uvGrid) { @@ -41,25 +44,29 @@ void EGlyphLayer::readCoordinates() { this->direction->SetNumberOfTuples(numLats * numLons); points->Allocate(numLats * numLons); + vtkSmartPointer filter = createCartographicTransformFilter(uvGrid); + auto transform = filter->GetTransform(); + int i = 0; int latIndex = 0; for (double lat: uvGrid->lats) { int lonIndex = 0; for (double lon: uvGrid->lons) { auto [u, v] = (*uvGrid)[0, latIndex, lonIndex]; - direction->SetTuple3(i, 5*u, 5*v, 0); - points->InsertPoint(i++, lon, lat, 0); + direction->SetTuple3(i, u/2, v/2, 0); + // pre-transform the points, so we don't have to include the cartographic transform while running the program. + double out[3] = {lon, lat, 0}; + transform->TransformPoint(out, out); + points->InsertPoint(i++, out[0], out[1], 0); lonIndex++; } latIndex++; } + this->data->SetPoints(points); this->data->GetPointData()->AddArray(this->direction); this->data->GetPointData()->SetActiveVectors("direction"); - vtkSmartPointer transformFilter = createCartographicTransformFilter(uvGrid); - transformFilter->SetInputData(data); - vtkNew arrowSource; arrowSource->SetGlyphTypeToArrow(); arrowSource->SetScale(0.2); //TODO: set this properly @@ -67,7 +74,7 @@ void EGlyphLayer::readCoordinates() { vtkNew glyph2D; glyph2D->SetSourceConnection(arrowSource->GetOutputPort()); - glyph2D->SetInputConnection(transformFilter->GetOutputPort()); + glyph2D->SetInputData(data); glyph2D->OrientOn(); glyph2D->ClampingOn(); glyph2D->SetScaleModeToScaleByVector(); @@ -94,7 +101,7 @@ void EGlyphLayer::updateData(int t) { for (int lon = 0; lon < uvGrid->lonSize; lon++) { auto [u, v] = (*uvGrid)[t/3600, lat, lon]; // TODO: 5*v scaling stuff should really be a filter transform - this->direction->SetTuple3(i, 5*u, 5*v, 0); + this->direction->SetTuple3(i, u/2, v/2, 0); i++; } } diff --git a/particle-track-and-trace/src/layers/LGlyphLayer.cpp b/particle-track-and-trace/src/layers/LGlyphLayer.cpp index 243cd29..e2c0fe0 100644 --- a/particle-track-and-trace/src/layers/LGlyphLayer.cpp +++ b/particle-track-and-trace/src/layers/LGlyphLayer.cpp @@ -32,7 +32,7 @@ vtkSmartPointer LGlyphLayer::createSpawnPointCallback() { // Further notes; current thinking is to allow tracking a particle's age by using a scalar array in the VtkPolyData. This would be incremented for every tick/updateData function call. -/** +/** * Build and returns a vtkLookupTable for the given number of colours in grayscale. * @param n : number of colours to add to the SetTableRange * @return : a vtkLookupTable with grayscale colours from [1,1,1,1] to [0.5,0.5,0.5,0.5] in n steps. @@ -95,7 +95,6 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr uvGrid, std::unique_ptrren->AddActor(actor); } - // creates a few points so we can test the updateData function void LGlyphLayer::spoofPoints() { // auto id =this->points->InsertNextPoint(6.532949683882039, 53.24308582564463, 0); // Coordinates of Zernike diff --git a/particle-track-and-trace/src/layers/LGlyphLayer.h b/particle-track-and-trace/src/layers/LGlyphLayer.h index 91bc42a..08c5358 100644 --- a/particle-track-and-trace/src/layers/LGlyphLayer.h +++ b/particle-track-and-trace/src/layers/LGlyphLayer.h @@ -25,13 +25,15 @@ public: */ LGlyphLayer(std::shared_ptr uvGrid, std::unique_ptr advectionKernel); - /** This function spoofs a few points in the dataset. Mostly used for testing. - */ + /** + * This function spoofs a few points in the dataset. Mostly used for testing. + */ void spoofPoints(); - /** updates the glyphs to reflect the given timestamp in the dataset. - * @param t : the time at which to fetch the data. - */ + /** + * updates the glyphs to reflect the given timestamp in the dataset. + * @param t : the time at which to fetch the data. + */ void updateData(int t) override; vtkSmartPointer createSpawnPointCallback(); diff --git a/particle-track-and-trace/src/main.cpp b/particle-track-and-trace/src/main.cpp index f6a794e..131755c 100644 --- a/particle-track-and-trace/src/main.cpp +++ b/particle-track-and-trace/src/main.cpp @@ -29,6 +29,7 @@ int main() { cout << "Starting vtk..." << endl; auto l = new LGlyphLayer(uvGrid, std::move(kernelRK4BoundaryChecked)); + l->spoofPoints(); unique_ptr program = make_unique(DT); program->addLayer(new BackgroundImage("../../../../data/map_661-661.png"));