206 lines
5.6 KiB
C++
206 lines
5.6 KiB
C++
#include "EColLayer.h"
|
|
#include <vtkGeneralTransform.h>
|
|
#include <vtkGeoProjection.h>
|
|
#include <vtkGeoTransform.h>
|
|
#include <vtkLookupTable.h>
|
|
#include <vtkPointData.h>
|
|
#include <vtkAbstractTransform.h>
|
|
#include <vtkOutlineSource.h>
|
|
#include <vtkDoubleArray.h>
|
|
#include <vtkProbeFilter.h>
|
|
#include <vtkGlyphSource2D.h>
|
|
#include <vtkRegularPolygonSource.h>
|
|
#include <vtkGlyph2D.h>
|
|
#include <vtkActor2D.h>
|
|
#include <vtkPlaneSource.h>
|
|
#include <vtkNamedColors.h>
|
|
#include <vtkCellData.h>
|
|
#include <vtkPolyDataMapper2D.h>
|
|
#include <vtkPolyDataMapper.h>
|
|
#include <vtkProperty.h>
|
|
#include <vtkProperty2D.h>
|
|
#include <vtkTransform.h>
|
|
#include <vtkVertexGlyphFilter.h>
|
|
#include <vtkArrowSource.h>
|
|
|
|
#include "../CartographicTransformation.h"
|
|
#include "luts.h"
|
|
|
|
using std::numbers::pi;
|
|
|
|
using namespace std;
|
|
|
|
EColLayer::EColLayer(std::shared_ptr<UVGrid> uvGrid) {
|
|
this->ren = vtkSmartPointer<vtkRenderer>::New();
|
|
this->ren->SetLayer(1);
|
|
this->ren->InteractiveOff();
|
|
|
|
this->uvGrid = uvGrid;
|
|
this->numLats = uvGrid->latSize;
|
|
this->numLons = uvGrid->lonSize;
|
|
|
|
this->lutIdx = vtkSmartPointer<vtkIntArray>::New();
|
|
this->lutIdx->SetName("lutIdx");
|
|
this->lutIdx->SetNumberOfComponents(1);
|
|
this->lutIdx->SetNumberOfTuples((numLats-1)*(numLons-1));
|
|
|
|
calcMaxStrength();
|
|
buildLuts();
|
|
readCoordinates();
|
|
}
|
|
|
|
void EColLayer::buildLuts() {
|
|
this->tables.push_back(buildCyclicComplementary());
|
|
this->tables.push_back(buildCyclicContrasting());
|
|
this->tables.push_back(buildCyclicMonochromatic());
|
|
this->tables.push_back(buildCyclicDesaturated());
|
|
|
|
this->activeColourMode = COMPLEMENTARY;
|
|
this->activeSaturationMode = SATURATED;
|
|
}
|
|
|
|
void EColLayer::calcMaxStrength() {
|
|
double u1 = uvGrid->uMin,
|
|
u2 = uvGrid->uMax,
|
|
v1 = uvGrid->vMin,
|
|
v2 = uvGrid->vMax;
|
|
|
|
double a = sqrt(u1*u1 + v1*v1);
|
|
double b = sqrt(u1*u1 + v2*v2);
|
|
double c = sqrt(u2*u2 + v1*v1);
|
|
double d = sqrt(u2*u2 + v2*v2);
|
|
|
|
this->maxStrength = max(a, max(b, max(c, d)));
|
|
}
|
|
|
|
|
|
// TODO: Bit of a superfunction here; can do with some refactoring.
|
|
void EColLayer::readCoordinates() {
|
|
vtkNew<vtkPoints> points;
|
|
points->Allocate(numLats*numLons);
|
|
|
|
vtkNew<vtkPolyData> data;
|
|
data->SetPoints(points);
|
|
data->Allocate((numLats-1)*(numLons-1));
|
|
|
|
auto transformFilter = createCartographicTransformFilter(uvGrid);
|
|
auto transform = transformFilter->GetTransform();
|
|
|
|
int cellId = 0, pointId = 0;
|
|
int latIndex = 0, lonIndex = 0;
|
|
for (auto lon : uvGrid->lons) {
|
|
latIndex = 0;
|
|
for (auto lat : uvGrid->lats) {
|
|
double out[3] = {lon, lat, 0};
|
|
transform->TransformPoint(out, out);
|
|
points->InsertPoint(pointId++, out[0], out[1], 0);
|
|
|
|
// logic for adding cells
|
|
if (latIndex > 0 and lonIndex > 0 ) {
|
|
int idx = latIndex+lonIndex*numLats;
|
|
vtkNew<vtkIdList> l;
|
|
l->SetNumberOfIds(4);
|
|
l->SetId(0, idx-1);
|
|
l->SetId(1, idx-numLats-1);
|
|
l->SetId(2, idx-numLats);
|
|
l->SetId(3, idx);
|
|
data->InsertNextCell(VTK_QUAD, l);
|
|
|
|
// ltake the average of the four surrounding points as the cell's velocity.
|
|
double u=0, v=0;
|
|
for (int j=0; j < 2; j++ ) {
|
|
for (int k=0; k < 2; k++ ) {
|
|
auto [u1, v1] = (*uvGrid)[0, latIndex-j, lonIndex-k];
|
|
u += u1;
|
|
v += v1;
|
|
}
|
|
}
|
|
u /= 4;
|
|
v /= 4;
|
|
this->lutIdx->SetValue(cellId++, calcIndex(u,v, this->maxStrength));
|
|
}
|
|
latIndex++;
|
|
}
|
|
lonIndex++;
|
|
}
|
|
|
|
data->GetCellData()->AddArray(this->lutIdx);
|
|
data->GetCellData()->SetActiveScalars("lutIdx");
|
|
|
|
this->mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
|
|
this->mapper->SetInputData(data);
|
|
setColourMode(COMPLEMENTARY);
|
|
this->mapper->UseLookupTableScalarRangeOn();
|
|
this->mapper->Update();
|
|
|
|
vtkNew<vtkActor> actor;
|
|
actor->SetMapper(this->mapper);
|
|
actor->GetProperty()->SetColor(0, 1, 0);
|
|
actor->GetProperty()->SetOpacity(0.5);
|
|
|
|
this->ren->AddActor(actor);
|
|
}
|
|
|
|
|
|
void EColLayer::updateData(int t) {
|
|
int i = 0;
|
|
for (int lon = 1; lon < uvGrid->lonSize; lon++) {
|
|
for (int lat = 1; lat < uvGrid->latSize; lat++) {
|
|
double u=0, v=0;
|
|
for (int j=0; j < 2; j++ ) {
|
|
for (int k=0; k < 2; k++ ) {
|
|
auto [u1, v1] = (*uvGrid)[t/3600, lat-j, lon-k];
|
|
u += u1;
|
|
v += v1;
|
|
}
|
|
}
|
|
u /= 4;
|
|
v /= 4;
|
|
this->lutIdx->SetValue(i++, calcIndex(u,v, this->maxStrength));
|
|
}
|
|
}
|
|
this->lutIdx->Modified();
|
|
}
|
|
|
|
|
|
void EColLayer::setColourMode(ColourMode mode) {
|
|
this->activeColourMode = mode;
|
|
if (this->activeSaturationMode == DESATURATED) return;
|
|
|
|
this->mapper->SetLookupTable(this->tables[mode]);
|
|
}
|
|
|
|
void EColLayer::setSaturationMode(SaturationMode mode) {
|
|
this->activeSaturationMode = mode;
|
|
|
|
if (mode == DESATURATED) {
|
|
this->mapper->SetLookupTable(this->tables[mode]);
|
|
} else {
|
|
this->mapper->SetLookupTable(this->tables[this->activeColourMode]);
|
|
}
|
|
}
|
|
|
|
|
|
int calcIndex(double u, double v, double maxStren) {
|
|
int angleIdx = calcAngleIndex(u,v);
|
|
int strIdx = calcStrengthIndex(u,v, maxStren);
|
|
|
|
return angleIdx+strIdx*10;
|
|
}
|
|
|
|
int calcAngleIndex(double u, double v) {
|
|
double angle = (atan2(v,u)+pi) * 180 / pi;
|
|
return (int)std::floor(angle/360*10) % 10;
|
|
}
|
|
|
|
// TODO: there's room for improvement in this function.
|
|
// Currently maps all strengths to [0,10) where 10 is assigned when strength >= maxStrength/2
|
|
// But this is completely heuristic - a more accurate calculation could take into account mean/std of velocity strengths, or do something more fancy with the maxStrength value.
|
|
int calcStrengthIndex(double u, double v, double maxStren) {
|
|
double strength = sqrt(u*u + v*v);
|
|
int idx = strength/(maxStren/2)*10;
|
|
|
|
if (idx > 9) idx = 9;
|
|
return idx;
|
|
}
|