diff --git a/vtk/src/helperClasses/BackgroundImage.h b/vtk/src/helperClasses/BackgroundImage.h index 7026993..1061189 100644 --- a/vtk/src/helperClasses/BackgroundImage.h +++ b/vtk/src/helperClasses/BackgroundImage.h @@ -4,18 +4,33 @@ #include "Layer.h" #include +/** Implements the Layer class for the case of a background image. + * Specifically, reads a backgroundImage given by the imagePath attribute and puts it on layer 0. + */ class BackgroundImage : public Layer { private: std::string imagePath; vtkSmartPointer readerFactory; + /** This private function updates the background image using the imagePath attribute. + */ void updateImage(); public: + /** Constructor. + * @param imagePath : String to the path of the image to use as background. + */ BackgroundImage(std::string imagePath); + /** Getter. + * @return the imagePath attribute. + */ std::string getImagePath(); + + /** Setter. Can be used to change the background image + * @param imagePath : String to the path of the new image to use. + */ void setImagePath(std::string imagePath); }; diff --git a/vtk/src/helperClasses/EGlyphLayer.cpp b/vtk/src/helperClasses/EGlyphLayer.cpp index 6c5050e..7146dc5 100644 --- a/vtk/src/helperClasses/EGlyphLayer.cpp +++ b/vtk/src/helperClasses/EGlyphLayer.cpp @@ -1,4 +1,7 @@ #include "EGlyphLayer.h" +#include +#include +#include #include #include #include @@ -39,8 +42,6 @@ tuple, vector, vector> readGrid() { } - - EGlyphLayer::EGlyphLayer() { this->ren = vtkSmartPointer::New(); this->ren->SetLayer(1); @@ -54,53 +55,56 @@ EGlyphLayer::EGlyphLayer() { void EGlyphLayer::readCoordinates() { vtkNew points; auto [times, lats, lons] = readGrid(); // FIXME: import Robin's readData function and use it + vtkNew direction; + direction->SetName("direction"); + direction->SetNumberOfComponents(3); + direction->SetNumberOfTuples(67*116); //FIXME: use robins function to get num of points + points->Allocate(67*116); - double i = 0; + int i = 0; for (double lat : lats) { for (double lon : lons) { - //FIXME: hard-coded values; should update with window geometry. - points->InsertNextPoint((lat*1000-46125)*661/16500, (lon*1000+15875)*661/28750, 0); + direction->SetTuple3(i, 0.45, 0.90, 0); //FIXME: read this info from file; figure out how to update it dynamically + 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. + // see also https://vtk.org/doc/nightly/html/classvtkPolyDataMapper2D.html } } this->data->SetPoints(points); + this->data->GetPointData()->AddArray(direction); + this->data->GetPointData()->SetActiveVectors("direction"); + + vtkNew arrowSource; + arrowSource->SetGlyphTypeToArrow(); + arrowSource->SetScale(8); //TODO: set this properly + arrowSource->Update(); - // vtkNew arrowSource; - vtkNew arrowSource; vtkNew glyph2D; glyph2D->SetSourceConnection(arrowSource->GetOutputPort()); glyph2D->SetInputData(this->data); + glyph2D->OrientOn(); + glyph2D->ClampingOn(); + glyph2D->SetScaleModeToScaleByVector(); + glyph2D->SetVectorModeToUseVector(); glyph2D->Update(); - vtkNew(mapper); + vtkNew coordinate; + coordinate->SetCoordinateSystemToWorld(); + + vtkNew(mapper); + // mapper->SetTransformCoordinate(coordinate); mapper->SetInputConnection(glyph2D->GetOutputPort()); mapper->Update(); - vtkNew actor; + vtkNew actor; actor->SetMapper(mapper); - vtkNew colors; - actor->GetProperty()->SetColor(colors->GetColor3d("Salmon").GetData()); + actor->GetProperty()->SetColor(0,0,0); + actor->GetProperty()->SetOpacity(0.2); this->ren->AddActor(actor); - - // vtkNew glyphFilter; - // glyphFilter->SetInputData(this->data); - // glyphFilter->Update(); - // - // vtkNew mapper; - // mapper->SetInputConnection(glyphFilter->GetOutputPort()); - // mapper->Update(); - // - // vtkNew colors; - // vtkNew actor; - // actor->SetMapper(mapper); - // actor->GetProperty()->SetColor(colors->GetColor3d("Gold").GetData()); - // actor->GetProperty()->SetPointSize(3); - // - // this->ren->AddActor(actor); } -void EGlyphLayer::updateData(short t) { +void EGlyphLayer::updateData(int t) { } diff --git a/vtk/src/helperClasses/EGlyphLayer.h b/vtk/src/helperClasses/EGlyphLayer.h index e401d07..04d2559 100644 --- a/vtk/src/helperClasses/EGlyphLayer.h +++ b/vtk/src/helperClasses/EGlyphLayer.h @@ -4,15 +4,27 @@ #include "Layer.h" #include +/** Implements the Layer class for the case of a Eulerian visualization. + * Specifically, this class models the eulerian flow-fields of the simulation using the 'glyph' mark and 'direction' and 'form' channels to denote direction and strength of velocities. + */ class EGlyphLayer : public Layer { private: vtkSmartPointer data; + /** This private function sets up the initial coordinates for the glyphs in the dataset. + * It also reads some initial data to actually display. + */ void readCoordinates(); public: + /** Constructor. + */ EGlyphLayer(); - void updateData(short t); + + /** 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); }; diff --git a/vtk/src/helperClasses/LGlyphLayer.cpp b/vtk/src/helperClasses/LGlyphLayer.cpp index 9541079..7607a47 100644 --- a/vtk/src/helperClasses/LGlyphLayer.cpp +++ b/vtk/src/helperClasses/LGlyphLayer.cpp @@ -1,11 +1,89 @@ #include "LGlyphLayer.h" +#include +#include +#include +#include +#include +#include +#include +// 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. +// 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. +// Another challenge is the concept of beaching; dead particles must not be included in the advect function call (wasted computations), but they should not be outright deleted from the vtkPoints either (we still want to display them). Working Solution: have another array of ints in the vtkPolyData, which tracks for how many calls of UpdateData a given particle has not had its position changed. If this int reaches some treshold (5? 10? 3? needs some testing), exclude the particle from the advect call. +// +// 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::New(); this->ren->SetLayer(2); + + this->points = vtkSmartPointer::New(); + this->data = vtkSmartPointer::New(); + this->data->SetPoints(this->points); + + vtkNew circleSource; + circleSource->SetGlyphTypeToCircle(); + circleSource->SetScale(15); + circleSource->Update(); + + vtkNew glyph2D; + glyph2D->SetSourceConnection(circleSource->GetOutputPort()); + glyph2D->SetInputData(this->data); + glyph2D->SetColorModeToColorByScalar(); + glyph2D->Update(); + + vtkNew mapper; + mapper->SetInputConnection(glyph2D->GetOutputPort()); + mapper->Update(); + + vtkNew actor; + actor->SetMapper(mapper); + actor->GetProperty()->SetColor(1,1,1); + + this->ren->AddActor(actor); } -void LGlyphLayer::updateData(short n) { +// 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->Modified(); +} + + +// returns new coords for a point; used to test the updateData function +std::pair advect(int time, double lat, double lon) { + return {lat+1, lon+1} ; +} + + +// 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 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 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(); } diff --git a/vtk/src/helperClasses/LGlyphLayer.h b/vtk/src/helperClasses/LGlyphLayer.h index 775e256..e13257c 100644 --- a/vtk/src/helperClasses/LGlyphLayer.h +++ b/vtk/src/helperClasses/LGlyphLayer.h @@ -4,14 +4,28 @@ #include "Layer.h" #include +/** Implements the Layer class for the case of a Lagrangian visualization. + * Specifically, this class models the Lagrangian particles in the simulation using the 'glyph' mark and 'transparency' channel to denote age. + */ class LGlyphLayer : public Layer { private: vtkSmartPointer points; vtkSmartPointer data; + public: + /** Constructor. + */ LGlyphLayer(); - void updateData(short t); + + /** 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. + */ + void updateData(int t) override; }; diff --git a/vtk/src/helperClasses/Layer.cpp b/vtk/src/helperClasses/Layer.cpp index 8691804..b447f4a 100644 --- a/vtk/src/helperClasses/Layer.cpp +++ b/vtk/src/helperClasses/Layer.cpp @@ -5,3 +5,7 @@ using std::string; vtkSmartPointer Layer::getLayer() { return this->ren; } + +void Layer::updateData(int t) { + cout << "wrong function dimwit" << endl; +} diff --git a/vtk/src/helperClasses/Layer.h b/vtk/src/helperClasses/Layer.h index 753f6bf..7e418b8 100644 --- a/vtk/src/helperClasses/Layer.h +++ b/vtk/src/helperClasses/Layer.h @@ -11,7 +11,16 @@ protected: vtkSmartPointer ren; public: + /** gets the vtkRenderer to assign it to the vtkRenderWindow of the program class. + * @return pointer to the vtkRenderer of this class. + */ virtual vtkSmartPointer getLayer(); + + + /** updates the data in the layer to reflect the given timestamp. + * @param t : the timestamp which the data should reflect. + */ + virtual void updateData(int t); }; #endif diff --git a/vtk/src/helperClasses/Program.cpp b/vtk/src/helperClasses/Program.cpp index b0c52d8..b46ca81 100644 --- a/vtk/src/helperClasses/Program.cpp +++ b/vtk/src/helperClasses/Program.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "Program.h" @@ -7,23 +8,40 @@ void Program::setWinProperties() { this->win->SetWindowName("Simulation"); this->win->SetSize(661, 661); + this->win->SetDesiredUpdateRate(60); this->interact->SetRenderWindow(this->win); + this->interact->Initialize(); + } -// Program::Program() : background(), euler(), lagrange(), win(), interact() { -// setWinProperties(); -// } +void Program::CallbackFunction(vtkObject* caller, long unsigned int eventId, void* clientData, void* callData) { + cout << "timed" << endl; + ((Program *)clientData)->lagrange.updateData(1); + //FIXME: from what i understand this should call the (overriden) updateData function in LGlyphLayer. It's not - and calls the virtual(?) updateData function in Layer instead, for some reason. + //Im too tired to comprehend this right now. +} -Program::Program(Layer bg, Layer e, Layer l) : background(bg), euler(e), lagrange(l), win(), interact() { + +void Program::setupTimer() { + vtkNew callback; + callback->SetCallback(this->CallbackFunction); + callback->SetClientData(this); + this->interact->AddObserver(vtkCommand::TimerEvent, callback); + this->interact->CreateRepeatingTimer(17); // 60 fps == 1000 / 60 == 16.7 ms per frame +} + + +Program::Program(Layer *bg, Layer *e, Layer *l) : background(*bg), euler(*e), lagrange(*l), win(), interact() { this->win = vtkSmartPointer::New(); this->interact = vtkSmartPointer::New(); this->win->SetNumberOfLayers(3); - this->win->AddRenderer(bg.getLayer()); - this->win->AddRenderer(e.getLayer()); - this->win->AddRenderer(l.getLayer()); + this->win->AddRenderer(bg->getLayer()); + this->win->AddRenderer(e->getLayer()); + this->win->AddRenderer(l->getLayer()); setWinProperties(); + setupTimer(); } diff --git a/vtk/src/helperClasses/Program.h b/vtk/src/helperClasses/Program.h index f086bdf..769cc64 100644 --- a/vtk/src/helperClasses/Program.h +++ b/vtk/src/helperClasses/Program.h @@ -16,17 +16,16 @@ private: vtkSmartPointer interact; void setWinProperties(); - + static void CallbackFunction(vtkObject* caller, long unsigned int eventId, void* clientData, void* callData); + void setupTimer(); public: - // Program(); - Program(Layer bg, Layer e, Layer l); + Program(Layer *bg, Layer *e, Layer *l); void setBackground(Layer bg); void setEuler(Layer e); void setLagrange(Layer l); - // void addInteractionStyle(vtkInteractorStyle style); void render(); diff --git a/vtk/src/main.cpp b/vtk/src/main.cpp index bd48eb0..d5aa31d 100644 --- a/vtk/src/main.cpp +++ b/vtk/src/main.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -11,6 +10,7 @@ #include "helperClasses/BackgroundImage.h" #include "helperClasses/EGlyphLayer.h" +#include "helperClasses/LGlyphLayer.h" #include "helperClasses/Program.h" using namespace std; @@ -19,8 +19,10 @@ using namespace std; int main() { auto bg = new BackgroundImage("../../../../data/map_661-661.png"); auto e = new EGlyphLayer(); - auto l = new EGlyphLayer(); - auto program = new Program(*bg, *e, *l); + auto l = new LGlyphLayer(); + l->spoofPoints(); + + auto program = new Program(bg, e, l); program->render(); return EXIT_SUCCESS;