feat: beaching of particles
This commit is contained in:
parent
e674dde67b
commit
0bde78ee7d
|
|
@ -42,9 +42,11 @@ void SpawnPointCallback::Execute(vtkObject *caller, unsigned long evId, void *ca
|
||||||
inverseCartographicProjection->MultiplyPoint(worldPos, worldPos);
|
inverseCartographicProjection->MultiplyPoint(worldPos, worldPos);
|
||||||
|
|
||||||
points->InsertNextPoint(worldPos[0], worldPos[1], 0);
|
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
|
// see github issue https://github.com/MakeNEnjoy/interactive-track-and-trace/issues/28
|
||||||
|
this->particlesBeached->Modified();
|
||||||
this->points->Modified();
|
this->points->Modified();
|
||||||
ren->GetRenderWindow()->Render();
|
ren->GetRenderWindow()->Render();
|
||||||
}
|
}
|
||||||
|
|
@ -76,3 +78,7 @@ void SpawnPointCallback::setUVGrid(const std::shared_ptr<UVGrid> &uvGrid) {
|
||||||
inverseCartographicProjection = getCartographicTransformMatrix(uvGrid);
|
inverseCartographicProjection = getCartographicTransformMatrix(uvGrid);
|
||||||
inverseCartographicProjection->Invert();
|
inverseCartographicProjection->Invert();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpawnPointCallback::setBeached(const vtkSmartPointer<vtkIntArray> &ints) {
|
||||||
|
this->particlesBeached = ints;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,15 @@ public:
|
||||||
|
|
||||||
void setRen(const vtkSmartPointer<vtkRenderer> &ren);
|
void setRen(const vtkSmartPointer<vtkRenderer> &ren);
|
||||||
|
|
||||||
|
void setBeached(const vtkSmartPointer<vtkIntArray> &parts);
|
||||||
|
|
||||||
void setUVGrid(const std::shared_ptr<UVGrid> &uvGrid);
|
void setUVGrid(const std::shared_ptr<UVGrid> &uvGrid);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
vtkSmartPointer<vtkPolyData> data;
|
vtkSmartPointer<vtkPolyData> data;
|
||||||
vtkSmartPointer<vtkPoints> points;
|
vtkSmartPointer<vtkPoints> points;
|
||||||
vtkSmartPointer<vtkRenderer> ren;
|
vtkSmartPointer<vtkRenderer> ren;
|
||||||
|
vtkSmartPointer<vtkIntArray> particlesBeached;
|
||||||
std::shared_ptr<UVGrid> uvGrid;
|
std::shared_ptr<UVGrid> uvGrid;
|
||||||
vtkSmartPointer<vtkMatrix4x4> inverseCartographicProjection;
|
vtkSmartPointer<vtkMatrix4x4> inverseCartographicProjection;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@
|
||||||
#include "../commands/SpawnPointCallback.h"
|
#include "../commands/SpawnPointCallback.h"
|
||||||
#include <vtkActor2D.h>
|
#include <vtkActor2D.h>
|
||||||
#include <vtkGlyph2D.h>
|
#include <vtkGlyph2D.h>
|
||||||
|
#include <vtkLookupTable.h>
|
||||||
#include <vtkGlyphSource2D.h>
|
#include <vtkGlyphSource2D.h>
|
||||||
#include <vtkNamedColors.h>
|
#include <vtkNamedColors.h>
|
||||||
|
#include <vtkPointData.h>
|
||||||
#include <vtkPolyDataMapper2D.h>
|
#include <vtkPolyDataMapper2D.h>
|
||||||
#include <vtkProperty.h>
|
#include <vtkProperty.h>
|
||||||
#include <vtkProperty2D.h>
|
#include <vtkProperty2D.h>
|
||||||
|
|
@ -20,16 +22,34 @@
|
||||||
|
|
||||||
vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() {
|
vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() {
|
||||||
auto newPointCallBack = vtkSmartPointer<SpawnPointCallback>::New();
|
auto newPointCallBack = vtkSmartPointer<SpawnPointCallback>::New();
|
||||||
newPointCallBack->setData(data);
|
newPointCallBack->setData(this->data);
|
||||||
newPointCallBack->setPoints(points);
|
newPointCallBack->setPoints(this->points);
|
||||||
newPointCallBack->setRen(ren);
|
newPointCallBack->setRen(this->ren);
|
||||||
newPointCallBack->setUVGrid(uvGrid);
|
newPointCallBack->setUVGrid(this->uvGrid);
|
||||||
|
newPointCallBack->setBeached(this->particlesBeached);
|
||||||
return newPointCallBack;
|
return newPointCallBack;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
// 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.
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
vtkSmartPointer<vtkLookupTable> buildLut(int n) {
|
||||||
|
vtkNew<vtkLookupTable> lut;
|
||||||
|
lut->SetNumberOfColors(n);
|
||||||
|
lut->SetTableRange(0, n-1);
|
||||||
|
lut->SetScaleToLinear();
|
||||||
|
lut->Build();
|
||||||
|
for (int i=0; i < n; i++) {
|
||||||
|
lut->SetTableValue(i, 1-(0.5*i/(n-1)), 1-(0.5*i/(n-1)), 1-(0.5*i/(n-1)), 1-(0.5*i/(n-1)));
|
||||||
|
}
|
||||||
|
// set the last value to separate fully beached particles from those that have simply not moved in a bit.
|
||||||
|
lut->SetTableValue(n-1, 0, 0, 0, 0.25);
|
||||||
|
return lut;
|
||||||
|
}
|
||||||
|
|
||||||
LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<AdvectionKernel> advectionKernel) {
|
LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<AdvectionKernel> advectionKernel) {
|
||||||
this->ren = vtkSmartPointer<vtkRenderer>::New();
|
this->ren = vtkSmartPointer<vtkRenderer>::New();
|
||||||
|
|
@ -39,6 +59,13 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<Advecti
|
||||||
this->data = vtkSmartPointer<vtkPolyData>::New();
|
this->data = vtkSmartPointer<vtkPolyData>::New();
|
||||||
this->data->SetPoints(this->points);
|
this->data->SetPoints(this->points);
|
||||||
|
|
||||||
|
this->particlesBeached = vtkSmartPointer<vtkIntArray>::New();
|
||||||
|
this->particlesBeached->SetName("particlesBeached");
|
||||||
|
this->particlesBeached->SetNumberOfComponents(0);
|
||||||
|
|
||||||
|
this->data->GetPointData()->AddArray(this->particlesBeached);
|
||||||
|
this->data->GetPointData()->SetActiveScalars("particlesBeached");
|
||||||
|
|
||||||
advector = std::move(advectionKernel);
|
advector = std::move(advectionKernel);
|
||||||
this->uvGrid = uvGrid;
|
this->uvGrid = uvGrid;
|
||||||
|
|
||||||
|
|
@ -47,17 +74,19 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<Advecti
|
||||||
|
|
||||||
vtkNew<vtkGlyphSource2D> circleSource;
|
vtkNew<vtkGlyphSource2D> circleSource;
|
||||||
circleSource->SetGlyphTypeToCircle();
|
circleSource->SetGlyphTypeToCircle();
|
||||||
circleSource->SetScale(0.05);
|
circleSource->SetScale(0.02);
|
||||||
circleSource->Update();
|
circleSource->Update();
|
||||||
|
|
||||||
vtkNew<vtkGlyph2D> glyph2D;
|
vtkNew<vtkGlyph2D> glyph2D;
|
||||||
glyph2D->SetSourceConnection(circleSource->GetOutputPort());
|
glyph2D->SetSourceConnection(circleSource->GetOutputPort());
|
||||||
glyph2D->SetInputConnection(transformFilter->GetOutputPort());
|
glyph2D->SetInputConnection(transformFilter->GetOutputPort());
|
||||||
glyph2D->SetColorModeToColorByScalar();
|
glyph2D->SetScaleModeToDataScalingOff();
|
||||||
glyph2D->Update();
|
glyph2D->Update();
|
||||||
|
|
||||||
vtkNew<vtkPolyDataMapper> mapper;
|
vtkNew<vtkPolyDataMapper> mapper;
|
||||||
mapper->SetInputConnection(glyph2D->GetOutputPort());
|
mapper->SetInputConnection(glyph2D->GetOutputPort());
|
||||||
|
mapper->SetLookupTable(buildLut(this->beachedAtNumberOfTimes));
|
||||||
|
mapper->SetScalarRange(0, this->beachedAtNumberOfTimes);
|
||||||
mapper->Update();
|
mapper->Update();
|
||||||
|
|
||||||
vtkNew<vtkActor> actor;
|
vtkNew<vtkActor> actor;
|
||||||
|
|
@ -66,17 +95,22 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<Advecti
|
||||||
this->ren->AddActor(actor);
|
this->ren->AddActor(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// creates a few points so we can test the updateData function
|
// creates a few points so we can test the updateData function
|
||||||
void LGlyphLayer::spoofPoints() {
|
void LGlyphLayer::spoofPoints() {
|
||||||
this->points->InsertNextPoint(-4.125, 61.375 , 0);
|
// auto id =this->points->InsertNextPoint(6.532949683882039, 53.24308582564463, 0); // Coordinates of Zernike
|
||||||
// this->points->InsertNextPoint(6.532949683882039, 53.24308582564463, 0); // Coordinates of Zernike
|
// this->particlesBeached->SetValue(id, 0);
|
||||||
// this->points->InsertNextPoint(5.315307819255385, 60.40001057122271, 0); // Coordinates of Bergen
|
// id = this->points->InsertNextPoint(5.315307819255385, 60.40001057122271, 0); // Coordinates of Bergen
|
||||||
// this->points->InsertNextPoint( 6.646210231365825, 46.52346296009023, 0); // Coordinates of Lausanne
|
// this->particlesBeached->SetValue(id, 0);
|
||||||
// this->points->InsertNextPoint(-6.553894313570932, 62.39522131195857, 0); // Coordinates of the top of the Faroe islands
|
// id = this->points->InsertNextPoint( 6.646210231365825, 46.52346296009023, 0); // Coordinates of Lausanne
|
||||||
|
// this->particlesBeached->SetValue(id, 0);
|
||||||
|
// id = this->points->InsertNextPoint(-6.553894313570932, 62.39522131195857, 0); // Coordinates of the top of the Faroe islands
|
||||||
|
// this->particlesBeached->SetValue(id, 0);
|
||||||
|
|
||||||
for (int i=0; i < 330; i+=5) {
|
for (int i=0; i < 330; i+=5) {
|
||||||
for (int j=0; j < 330; j+=5) {
|
for (int j=0; j < 330; j+=5) {
|
||||||
this->points->InsertNextPoint(-15.875+(12.875+15.875)/330*j, 46.125+(62.625-46.125)/330*i, 0);
|
this->points->InsertNextPoint(-15.875+(12.875+15.875)/330*j, 46.125+(62.625-46.125)/330*i, 0);
|
||||||
|
this->particlesBeached->InsertNextValue(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,16 +119,41 @@ void LGlyphLayer::spoofPoints() {
|
||||||
|
|
||||||
void LGlyphLayer::updateData(int t) {
|
void LGlyphLayer::updateData(int t) {
|
||||||
const int SUPERSAMPLINGRATE = 4;
|
const int SUPERSAMPLINGRATE = 4;
|
||||||
double point[3];
|
double point[3], oldX, oldY;
|
||||||
for (vtkIdType n = 0; n < this->points->GetNumberOfPoints(); n++) {
|
bool modifiedData = false;
|
||||||
|
|
||||||
|
// iterate over every point.
|
||||||
|
for (vtkIdType n=0; n < this->points->GetNumberOfPoints(); n++) {
|
||||||
|
int beachedFor = this->particlesBeached->GetValue(n);
|
||||||
|
// first check: only update non-beached particles.
|
||||||
|
if (beachedFor < this->beachedAtNumberOfTimes) {
|
||||||
this->points->GetPoint(n, point);
|
this->points->GetPoint(n, point);
|
||||||
for (int i = 0; i < SUPERSAMPLINGRATE; i++) {
|
// second check: only update points within our grid's boundary.
|
||||||
std::tie(point[1], point[0]) = advector->advect(t, point[1], point[0], (t-lastT)/SUPERSAMPLINGRATE);
|
if (point[0] < this->uvGrid->lonMin() or point[0] > this->uvGrid->lonMax() or point[1] < this->uvGrid->latMin() or point[1] > this->uvGrid->latMax()) {
|
||||||
|
// sets any particle out of bounds to be beached - so it gets assigned the right colour in the lookup table.
|
||||||
|
this->particlesBeached->SetValue(n, this->beachedAtNumberOfTimes);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
this->points->SetPoint(n, point[0], point[1], 0);
|
|
||||||
|
oldX = point[0]; oldY = point[1];
|
||||||
|
|
||||||
|
// supersampling
|
||||||
|
for (int i=0; i < SUPERSAMPLINGRATE; i++) {
|
||||||
|
std::tie(point[1], point[0]) = advector->advect(t, point[1], point[0], (t-this->lastT)/SUPERSAMPLINGRATE);
|
||||||
}
|
}
|
||||||
lastT = t;
|
|
||||||
this->points->Modified();
|
// if the particle's location remains unchanged, increase beachedFor number. Else, decrease it and update point position.
|
||||||
|
if (oldX == point[0] and oldY == point[1]) {
|
||||||
|
this->particlesBeached->SetValue(n, beachedFor+1);
|
||||||
|
} else {
|
||||||
|
this->particlesBeached->SetValue(n, std::max(beachedFor-1, 0));
|
||||||
|
this->points->SetPoint(n, point);
|
||||||
|
modifiedData = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (modifiedData) this->points->Modified();
|
||||||
|
this->lastT = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LGlyphLayer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) {
|
void LGlyphLayer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) {
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,11 @@ class LGlyphLayer : public Layer {
|
||||||
private:
|
private:
|
||||||
vtkSmartPointer<vtkPoints> points;
|
vtkSmartPointer<vtkPoints> points;
|
||||||
vtkSmartPointer<vtkPolyData> data;
|
vtkSmartPointer<vtkPolyData> data;
|
||||||
|
vtkSmartPointer<vtkIntArray> particlesBeached;
|
||||||
std::unique_ptr<AdvectionKernel> advector;
|
std::unique_ptr<AdvectionKernel> advector;
|
||||||
std::shared_ptr<UVGrid> uvGrid;
|
std::shared_ptr<UVGrid> uvGrid;
|
||||||
int lastT = 1000;
|
int lastT = 1000;
|
||||||
|
int beachedAtNumberOfTimes = 20;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Constructor.
|
/** Constructor.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue