#include "EColLayer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../CartographicTransformation.h" #include "luts.h" using std::numbers::pi; using namespace std; EColLayer::EColLayer(std::shared_ptr uvGrid) { this->ren = vtkSmartPointer::New(); this->ren->SetLayer(1); this->ren->InteractiveOff(); this->uvGrid = uvGrid; this->numLats = uvGrid->latSize; this->numLons = uvGrid->lonSize; this->lutIdx = vtkSmartPointer::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 points; points->Allocate(numLats*numLons); vtkNew 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 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::New(); this->mapper->SetInputData(data); setColourMode(COMPLEMENTARY); this->mapper->UseLookupTableScalarRangeOn(); this->mapper->Update(); vtkNew 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; }