feat: swappable colour schemes

This commit is contained in:
Djairo Hougee 2024-05-28 17:59:54 +02:00
parent 305a812dfa
commit 23359f9f54
21 changed files with 491 additions and 170 deletions

View File

@ -59,6 +59,8 @@ add_executable(ParticleTrackTrace MACOSX_BUNDLE main.cpp
layers/Technique.cpp layers/Technique.cpp
layers/Technique.h layers/Technique.h
layers/enums.h layers/enums.h
layers/luts.cpp
layers/luts.h
Program.cpp Program.cpp
Program.h Program.h
commands/TimerCallbackCommand.h commands/TimerCallbackCommand.h

View File

@ -57,7 +57,7 @@ Program::Program(QWidget *parent): QVTKOpenGLNativeWidget(parent) {
setRenderWindow(this->win); setRenderWindow(this->win);
this->interact = win->GetInteractor(); this->interact = win->GetInteractor();
this->cam = createNormalisedCamera(); this->cam = createNormalisedCamera();
this->activeIdx = -1; this->activeTech = NOTECH;
this->win->SetNumberOfLayers(0); this->win->SetNumberOfLayers(0);
setWinProperties(); setWinProperties();
@ -75,38 +75,44 @@ void Program::removeTechnique(Technique *technique) {
auto it = std::find(this->techniques.begin(), this->techniques.end(), technique); auto it = std::find(this->techniques.begin(), this->techniques.end(), technique);
if (it != this->techniques.end()) { if (it != this->techniques.end()) {
int idx = it - this->techniques.begin(); int idx = it - this->techniques.begin();
if (idx == this->activeIdx) { if (idx == this->activeTech) {
throw std::out_of_range("Can't remove active technique."); throw std::out_of_range("Can't remove active technique.");
} }
this->techniques.erase(it); this->techniques.erase(it);
this->activeIdx = -1; this->activeTech = NOTECH;
setActiveTechnique(0); setActiveTechnique(COLGLYPH);
} }
} }
void Program::requestRender() {
this->win->Render();
}
void Program::updateData(int t) { void Program::updateData(int t) {
// FIXME: think on how to update techniques; do we update all? just active? unsure. // FIXME: think on how to update techniques; do we update all? just active? unsure.
win->Render(); win->Render();
this->techniques[this->activeIdx]->updateData(t); for (Technique *tech : this->techniques) {
tech->updateData(t);
}
} }
void Program::setActiveTechnique(int idx) { void Program::setActiveTechnique(ActiveTechnique tech) {
// Only change things if a different technique has been selected. // Only change things if a different technique has been selected.
if (idx == this->activeIdx) { if (tech == this->activeTech) {
return; return;
} }
// check the given idx is valid. // check the given idx is valid.
if (idx >= this->techniques.size()) { if (tech >= this->techniques.size()) {
throw std::out_of_range("Index out of range!"); throw std::out_of_range("Index out of range!");
} }
if (this->activeIdx >= 0 and this->activeIdx < this->techniques.size()) if (this->activeTech >= 0 and this->activeTech < this->techniques.size())
this->techniques[this->activeIdx]->unbind(this->win, this->interact); this->techniques[this->activeTech]->unbind(this->win, this->interact);
this->techniques[idx]->bind(this->win, this->interact); this->techniques[tech]->bind(this->win, this->interact);
this->activeIdx = idx; this->activeTech = tech;
this->win->Render(); this->win->Render();
} }

View File

@ -19,7 +19,7 @@ private:
/** This attribute models a variable number of vtkRenderers, managed through the abstract Technique class. /** This attribute models a variable number of vtkRenderers, managed through the abstract Technique class.
*/ */
std::vector<Technique *> techniques; std::vector<Technique *> techniques;
int activeIdx; ActiveTechnique activeTech;
/** The window this program's layers render to. /** The window this program's layers render to.
@ -72,10 +72,10 @@ public:
*/ */
void updateData(int t); void updateData(int t);
// TODO: commenting
void requestRender();
// TODO: using an idx to indicate which technique to use is not ideal; use an enum instead? But then the question is where to put it... void setActiveTechnique(ActiveTechnique tech);
void setActiveTechnique(int idx);
vtkSmartPointer<vtkCamera> getCamera(); vtkSmartPointer<vtkCamera> getCamera();

View File

@ -69,7 +69,7 @@ void MainWindow::setupTechniques() {
program->addTechnique(technique1); program->addTechnique(technique1);
program->addTechnique(technique2); program->addTechnique(technique2);
program->setActiveTechnique(0); program->setActiveTechnique(COLGLYPH);
// TODO: implement feature to call this function on widget // TODO: implement feature to call this function on widget
// l->spoofPoints(); // l->spoofPoints();
@ -85,6 +85,7 @@ void MainWindow::setupTechniques() {
void MainWindow::on_FirstButton_clicked(bool checked) { void MainWindow::on_FirstButton_clicked(bool checked) {
if (checked) { if (checked) {
ui->program->setActiveTechnique(COLGLYPH); ui->program->setActiveTechnique(COLGLYPH);
ui->program->requestRender();
} }
} }
@ -92,6 +93,7 @@ void MainWindow::on_FirstButton_clicked(bool checked) {
void MainWindow::on_SecondButton_clicked(bool checked) { void MainWindow::on_SecondButton_clicked(bool checked) {
if (checked) { if (checked) {
ui->program->setActiveTechnique(GLYPHCOL); ui->program->setActiveTechnique(GLYPHCOL);
ui->program->requestRender();
} }
} }
@ -99,8 +101,9 @@ void MainWindow::on_SecondButton_clicked(bool checked) {
void MainWindow::on_ComplementaryButton_clicked(bool checked) { void MainWindow::on_ComplementaryButton_clicked(bool checked) {
if (checked) { if (checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setColorMode(COMPLEMENTARY); t->setColourMode(COMPLEMENTARY);
} }
ui->program->requestRender();
} }
} }
@ -108,8 +111,9 @@ void MainWindow::on_ComplementaryButton_clicked(bool checked) {
void MainWindow::on_ContrastingButton_clicked(bool checked) { void MainWindow::on_ContrastingButton_clicked(bool checked) {
if (checked) { if (checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setColorMode(CONTRASTING); t->setColourMode(CONTRASTING);
} }
ui->program->requestRender();
} }
} }
@ -117,8 +121,9 @@ void MainWindow::on_ContrastingButton_clicked(bool checked) {
void MainWindow::on_MonochromaticButton_clicked(bool checked) { void MainWindow::on_MonochromaticButton_clicked(bool checked) {
if (checked) { if (checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setColorMode(MONOCHROMATIC); t->setColourMode(MONOCHROMATIC);
} }
ui->program->requestRender();
} }
} }
@ -128,6 +133,7 @@ void MainWindow::on_SaturateButton_clicked(bool checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setSaturationMode(SATURATED); t->setSaturationMode(SATURATED);
} }
ui->program->requestRender();
} }
} }
@ -137,6 +143,7 @@ void MainWindow::on_DesaturateButton_clicked(bool checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setSaturationMode(DESATURATED); t->setSaturationMode(DESATURATED);
} }
ui->program->requestRender();
} }
} }
@ -146,6 +153,7 @@ void MainWindow::on_CircleButton_clicked(bool checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setGlyphStyle(CIRCLE); t->setGlyphStyle(CIRCLE);
} }
ui->program->requestRender();
} }
} }
@ -155,6 +163,7 @@ void MainWindow::on_TriangleButton_clicked(bool checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setGlyphStyle(TRIANGLE); t->setGlyphStyle(TRIANGLE);
} }
ui->program->requestRender();
} }
} }
@ -164,6 +173,7 @@ void MainWindow::on_SquareButton_clicked(bool checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setGlyphStyle(SQUARE); t->setGlyphStyle(SQUARE);
} }
ui->program->requestRender();
} }
} }
@ -173,6 +183,7 @@ void MainWindow::on_HexagonButton_clicked(bool checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setGlyphStyle(HEXAGON); t->setGlyphStyle(HEXAGON);
} }
ui->program->requestRender();
} }
} }
@ -182,6 +193,7 @@ void MainWindow::on_FullySampledButton_clicked(bool checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setSamplingMode(FULLYSAMPLED); t->setSamplingMode(FULLYSAMPLED);
} }
ui->program->requestRender();
} }
} }
@ -191,6 +203,7 @@ void MainWindow::on_RegularlySubsampledButton_clicked(bool checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setSamplingMode(REGULARLYSUBSAMPLED); t->setSamplingMode(REGULARLYSUBSAMPLED);
} }
ui->program->requestRender();
} }
} }
@ -200,6 +213,7 @@ void MainWindow::on_IregularlySubsampledButton_clicked(bool checked) {
for (Technique *t : ui->program->getTechniques()) { for (Technique *t : ui->program->getTechniques()) {
t->setSamplingMode(IRREGULARLYSUBSAMPLED); t->setSamplingMode(IRREGULARLYSUBSAMPLED);
} }
ui->program->requestRender();
} }
} }

View File

@ -31,6 +31,12 @@ UVGrid::UVGrid(string path) {
for (auto vel: views::zip(us, vs)) { for (auto vel: views::zip(us, vs)) {
uvData.push_back(Vel(vel)); uvData.push_back(Vel(vel));
} }
// FIXME: should really read these from file instead of hard-coding them.
this->uMin = -0.381482899188995;
this->uMax = 0.566494882106781;
this->vMin = -0.381482899188995;
this->vMax = 0.470820993185043;
} }
const Vel &UVGrid::operator[](size_t timeIndex, size_t latIndex, size_t lonIndex) const { const Vel &UVGrid::operator[](size_t timeIndex, size_t latIndex, size_t lonIndex) const {

View File

@ -25,6 +25,14 @@ public:
size_t latSize; size_t latSize;
size_t lonSize; size_t lonSize;
/**
* Minimum and Maximum values of the u and v variables.
*/
double uMin;
double uMax;
double vMin;
double vMax;
/** /**
* Assuming grid is a regular grid, gives the longitudinal spacing of grid. * Assuming grid is a regular grid, gives the longitudinal spacing of grid.
* @return longitudinal spacing * @return longitudinal spacing

View File

@ -43,6 +43,7 @@ void SpawnPointCallback::Execute(vtkObject *caller, unsigned long evId, void *ca
points->InsertNextPoint(worldPos[0], worldPos[1], 0); points->InsertNextPoint(worldPos[0], worldPos[1], 0);
this->particlesBeached->InsertNextValue(0); this->particlesBeached->InsertNextValue(0);
this->particlesAge->InsertNextValue(0); this->particlesAge->InsertNextValue(0);
this->lutIdx->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
@ -79,3 +80,8 @@ void SpawnPointCallback::setBeached(const vtkSmartPointer<vtkIntArray> &ints) {
void SpawnPointCallback::setAge(const vtkSmartPointer<vtkIntArray> &ints) { void SpawnPointCallback::setAge(const vtkSmartPointer<vtkIntArray> &ints) {
this->particlesAge = ints; this->particlesAge = ints;
} }
void SpawnPointCallback::setIdx(const vtkSmartPointer<vtkIntArray> &idx) {
this->lutIdx = idx;
}

View File

@ -24,6 +24,7 @@ public:
void setBeached(const vtkSmartPointer<vtkIntArray> &parts); void setBeached(const vtkSmartPointer<vtkIntArray> &parts);
void setAge(const vtkSmartPointer<vtkIntArray> &parts); void setAge(const vtkSmartPointer<vtkIntArray> &parts);
void setIdx(const vtkSmartPointer<vtkIntArray> &idx);
void setUVGrid(const std::shared_ptr<UVGrid> &uvGrid); void setUVGrid(const std::shared_ptr<UVGrid> &uvGrid);
@ -32,6 +33,7 @@ private:
vtkSmartPointer<vtkRenderer> ren; vtkSmartPointer<vtkRenderer> ren;
vtkSmartPointer<vtkIntArray> particlesBeached; vtkSmartPointer<vtkIntArray> particlesBeached;
vtkSmartPointer<vtkIntArray> particlesAge; vtkSmartPointer<vtkIntArray> particlesAge;
vtkSmartPointer<vtkIntArray> lutIdx;
std::shared_ptr<UVGrid> uvGrid; std::shared_ptr<UVGrid> uvGrid;
vtkSmartPointer<vtkAbstractTransform> inverseCartographicProjection; vtkSmartPointer<vtkAbstractTransform> inverseCartographicProjection;

View File

@ -24,6 +24,7 @@
#include <vtkArrowSource.h> #include <vtkArrowSource.h>
#include "../CartographicTransformation.h" #include "../CartographicTransformation.h"
#include "luts.h"
using std::numbers::pi; using std::numbers::pi;
@ -38,59 +39,41 @@ EColLayer::EColLayer(std::shared_ptr<UVGrid> uvGrid) {
this->numLats = uvGrid->latSize; this->numLats = uvGrid->latSize;
this->numLons = uvGrid->lonSize; this->numLons = uvGrid->lonSize;
this->strength = vtkSmartPointer<vtkDoubleArray>::New(); this->lutIdx = vtkSmartPointer<vtkIntArray>::New();
this->strength->SetName("strength"); this->lutIdx->SetName("lutIdx");
this->strength->SetNumberOfComponents(1); this->lutIdx->SetNumberOfComponents(1);
this->strength->SetNumberOfTuples((numLats-1)*(numLons-1)); this->lutIdx->SetNumberOfTuples((numLats-1)*(numLons-1));
this->direction = vtkSmartPointer<vtkDoubleArray>::New();
this->direction->SetName("direction");
this->direction->SetNumberOfComponents(1);
this->direction->SetNumberOfTuples((numLats-1)*(numLons-1));
calcMaxStrength();
buildLuts();
readCoordinates(); readCoordinates();
} }
/** void EColLayer::buildLuts() {
* Sets a given rgba colour to a range of values [start, end] in the lut. this->tables.push_back(buildCyclicComplementary());
* @param lut : lookuptable to operate on. this->tables.push_back(buildCyclicContrasting());
* @ param start : starting index of range to assign this->tables.push_back(buildCyclicMonochromatic());
* @ param end: ending index of range to assign this->tables.push_back(buildCyclicDesaturated());
* @param r : red value [0,1]
* @param g : green value [0,1] this->activeColourMode = COMPLEMENTARY;
* @param n : blue value [0,1] this->activeSaturationMode = SATURATED;
* @param a : alpha value [0,1]
*/
void setLutRange(vtkSmartPointer<vtkLookupTable> lut, int start, int end, double r, double g, double b, double a) {
for (int i=start; i <= end; i++) {
lut->SetTableValue(i, r, g, b, a);
}
} }
// builds a 4-way lookuptable, used to encode the directional component void EColLayer::calcMaxStrength() {
vtkSmartPointer<vtkLookupTable> buildLutDirs() { double u1 = uvGrid->uMin,
vtkNew<vtkLookupTable> lut; u2 = uvGrid->uMax,
lut->SetNumberOfColors(360); v1 = uvGrid->vMin,
lut->SetTableRange(0, 359); v2 = uvGrid->vMax;
lut->SetScaleToLinear();
lut->Build();
//currently builds a corkO cyclic colour map, divided into 8 colours (see https://www.fabiocrameri.ch/cycliccolourmaps/)
setLutRange(lut, 000, 020, 0.247, 0.243, 0.227, 1);
setLutRange(lut, 021, 060, 0.243, 0.267, 0.365, 1);
setLutRange(lut, 061, 100, 0.318, 0.416, 0.557, 1);
setLutRange(lut, 101, 140, 0.518, 0.620, 0.729, 1);
setLutRange(lut, 141, 180, 0.667, 0.757, 0.773, 1);
setLutRange(lut, 181, 220, 0.631, 0.769, 0.651, 1);
setLutRange(lut, 221, 260, 0.451, 0.639, 0.435, 1);
setLutRange(lut, 261, 300, 0.298, 0.431, 0.224, 1);
setLutRange(lut, 301, 340, 0.263, 0.310, 0.173, 1);
setLutRange(lut, 341, 359, 0.247, 0.243, 0.227, 1);
lut->SetNanColor(0,0,0,0); 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);
return lut; this->maxStrength = max(a, max(b, max(c, d)));
} }
// TODO: Bit of a superfunction here; can do with some refactoring. // TODO: Bit of a superfunction here; can do with some refactoring.
void EColLayer::readCoordinates() { void EColLayer::readCoordinates() {
vtkNew<vtkPoints> points; vtkNew<vtkPoints> points;
@ -134,35 +117,27 @@ void EColLayer::readCoordinates() {
} }
u /= 4; u /= 4;
v /= 4; v /= 4;
this->strength->SetTuple1(cellId, std::sqrt(u*u + v*v)); this->lutIdx->SetTuple1(cellId++, calcIndex(u,v, this->maxStrength));
this->direction->SetTuple1(cellId++, atan(u/v)*180/pi);
} }
latIndex++; latIndex++;
} }
lonIndex++; lonIndex++;
} }
data->GetCellData()->AddArray(this->strength); data->GetCellData()->AddArray(this->lutIdx);
data->GetCellData()->AddArray(this->direction); data->GetCellData()->SetActiveScalars("lutIdx");
// data->GetCellData()->SetActiveScalars("strength");
vtkNew<vtkPolyDataMapper>(mapper); this->mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputData(data); this->mapper->SetInputData(data);
mapper->SetLookupTable(buildLutDirs()); setColourMode(COMPLEMENTARY);
mapper->UseLookupTableScalarRangeOn(); this->mapper->UseLookupTableScalarRangeOn();
mapper->Update(); this->mapper->Update();
data->GetCellData()->SetActiveScalars("direction");
vtkNew<vtkActor> actor; vtkNew<vtkActor> actor;
actor->SetMapper(mapper); actor->SetMapper(this->mapper);
actor->GetProperty()->SetColor(0, 1, 0); actor->GetProperty()->SetColor(0, 1, 0);
actor->GetProperty()->SetOpacity(0.5); actor->GetProperty()->SetOpacity(0.5);
// vtkNew<vtkActor> act2;
// act2->SetMapper(mapper);
// act2->GetProperty()->SetRepresentationToWireframe();
// ren->AddActor(act2);
this->ren->AddActor(actor); this->ren->AddActor(actor);
} }
@ -181,9 +156,50 @@ void EColLayer::updateData(int t) {
} }
u /= 4; u /= 4;
v /= 4; v /= 4;
this->strength->SetTuple1(i, std::sqrt(u*u + v*v)); this->lutIdx->SetTuple1(i++, calcIndex(u,v, this->maxStrength));
this->direction->SetTuple1(i++, atan(u/v)*180/pi);
} }
} }
this->strength->Modified(); 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;
} }

View File

@ -14,9 +14,13 @@
*/ */
class EColLayer : public Layer { class EColLayer : public Layer {
private: private:
vtkSmartPointer<vtkDoubleArray> strength; vtkSmartPointer<vtkIntArray> lutIdx;
vtkSmartPointer<vtkDoubleArray> direction; vtkSmartPointer<vtkPolyDataMapper> mapper;
std::vector<vtkSmartPointer<vtkLookupTable>> tables;
ColourMode activeColourMode;
SaturationMode activeSaturationMode;
std::shared_ptr<UVGrid> uvGrid; std::shared_ptr<UVGrid> uvGrid;
double maxStrength;
int numLats; int numLats;
int numLons; int numLons;
@ -25,11 +29,13 @@ private:
*/ */
void readCoordinates(); void readCoordinates();
/** This function calculates the maximum strength values for the associated uvGrid
/** This private function builds up the lookup table VTK uses when determning what colour each cell ought to be.
*/ */
// TODO: implement this function. void calcMaxStrength();
void buildLut();
/** This function builds the used lookuptables and adds them to the tables attribute.
*/
void buildLuts();
public: public:
/** Constructor. /** Constructor.
@ -39,9 +45,37 @@ public:
/** updates the map to reflect the given timestamp in the dataset. /** updates the map to reflect the given timestamp in the dataset.
* @param t : the time at which to fetch the data. * @param t : the time at which to fetch the data.
*/ */
void updateData(int t); void updateData(int t) override;
void setColourMode(ColourMode mode) override;
void setSaturationMode(SaturationMode mode) override;
}; };
/** Calculates the discrete index of a given velocity strength, taking into account both strength and direction.
* Maps to a 10*10 lookuptable, in which the columns are discrete colours (mapped to direction), and rows are discrete opacities (mapped to strength).
* @param u : longitudinal velocity
* @param v : latitudinal velocity
* @param maxStren : maximum strength value
* @return index in a 10*10 lookuptable.
*/
int calcIndex(double u, double v, double maxStren);
/** Calculates the discrete index of the angle of a velocity, mapping it to the range [0,10).
* @param u : longitudinal velocity
* @param v : latitudinal velocity
* @return index in the range [0,10)
*/
int calcAngleIndex(double u, double v);
/** Calculates the discrete index of the strength of a velocity, mapping it to the range [0,10).
* @param u : longitudinal velocity
* @param v : latitudinal velocity
* @param maxStren : maximum strength value
* @return index in the range [0,10)
*/
int calcStrengthIndex(double u, double v, double maxStren);
#endif #endif

View File

@ -21,6 +21,9 @@
#include "../CartographicTransformation.h" #include "../CartographicTransformation.h"
// TODO: spawning one particle per event is nice and all, but for a colour map doesnt really look great
// potential solution: spawn a number of particles randomly around the selected point instead.
// Would involve a custom callback function probably.
vtkSmartPointer<SpawnPointCallback> LColLayer::createSpawnPointCallback() { vtkSmartPointer<SpawnPointCallback> LColLayer::createSpawnPointCallback() {
vtkNew<SpawnPointCallback> newPointCallBack; vtkNew<SpawnPointCallback> newPointCallBack;
newPointCallBack->setPoints(this->points); newPointCallBack->setPoints(this->points);
@ -28,6 +31,7 @@ vtkSmartPointer<SpawnPointCallback> LColLayer::createSpawnPointCallback() {
newPointCallBack->setUVGrid(this->uvGrid); newPointCallBack->setUVGrid(this->uvGrid);
newPointCallBack->setBeached(this->particlesBeached); newPointCallBack->setBeached(this->particlesBeached);
newPointCallBack->setAge(this->particlesAge); newPointCallBack->setAge(this->particlesAge);
newPointCallBack->setIdx(this->lutIdx);
return newPointCallBack; return newPointCallBack;
} }
@ -80,6 +84,10 @@ LColLayer::LColLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<AdvectionKe
this->particlesAge->SetName("particlesAge"); this->particlesAge->SetName("particlesAge");
this->particlesAge->SetNumberOfComponents(1); this->particlesAge->SetNumberOfComponents(1);
this->lutIdx = vtkSmartPointer<vtkIntArray>::New();
this->lutIdx->SetName("lutIdx");
this->lutIdx->SetNumberOfComponents(1);
// pipeline 2 // pipeline 2
this->data = vtkSmartPointer<vtkPolyData>::New(); this->data = vtkSmartPointer<vtkPolyData>::New();

View File

@ -16,6 +16,7 @@ private:
vtkSmartPointer<vtkPolyData> data; vtkSmartPointer<vtkPolyData> data;
vtkSmartPointer<vtkIntArray> particlesBeached; vtkSmartPointer<vtkIntArray> particlesBeached;
vtkSmartPointer<vtkIntArray> particlesAge; vtkSmartPointer<vtkIntArray> particlesAge;
vtkSmartPointer<vtkIntArray> lutIdx;
vtkSmartPointer<vtkIntArray> cellParticleDensity; vtkSmartPointer<vtkIntArray> cellParticleDensity;
vtkSmartPointer<SpawnPointCallback> callback; vtkSmartPointer<SpawnPointCallback> callback;
std::unique_ptr<AdvectionKernel> advector; std::unique_ptr<AdvectionKernel> advector;

View File

@ -19,6 +19,7 @@
#include <vtkCamera.h> #include <vtkCamera.h>
#include "../CartographicTransformation.h" #include "../CartographicTransformation.h"
#include "luts.h"
vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() { vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() {
vtkNew<SpawnPointCallback> newPointCallBack; vtkNew<SpawnPointCallback> newPointCallBack;
@ -27,62 +28,13 @@ vtkSmartPointer<SpawnPointCallback> LGlyphLayer::createSpawnPointCallback() {
newPointCallBack->setUVGrid(this->uvGrid); newPointCallBack->setUVGrid(this->uvGrid);
newPointCallBack->setBeached(this->particlesBeached); newPointCallBack->setBeached(this->particlesBeached);
newPointCallBack->setAge(this->particlesAge); newPointCallBack->setAge(this->particlesAge);
newPointCallBack->setIdx(this->lutIdx);
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.
/**
* 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.25, 0.25, 0.25, 1] in n steps.
*/
vtkSmartPointer<vtkLookupTable> buildLutBrightness(int n) {
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfColors(n);
lut->SetTableRange(0, n);
lut->SetScaleToLinear();
lut->Build();
for (int i=0; i < n; i++) {
lut->SetTableValue(i, 1-(0.75*i/(n-1)), 1-(0.75*i/(n-1)), 1-(0.75*i/(n-1)), 1);
}
lut->UseAboveRangeColorOn();
lut->SetAboveRangeColor(0.2, 0.2, 0.2, 1);
// We cheat a little here: any particle with an age of -1 is out of bounds, and thus set invisible.
lut->UseBelowRangeColorOn();
lut->SetBelowRangeColor(1,1,1,0);
return lut;
}
/**
* 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 [1,1,1,0.25] in n steps.
*/
vtkSmartPointer<vtkLookupTable> buildLutOpacity(int n) {
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfColors(n);
lut->SetTableRange(0, n);
lut->SetScaleToLinear();
lut->Build();
for (int i=0; i < n; i++) {
lut->SetTableValue(i, 1, 0, 1, 1-(0.75*i/(n-1)));
}
lut->UseAboveRangeColorOn();
lut->SetAboveRangeColor(1,0,1,0.20);
// We cheat a little here: any particle with an age of -1 is out of bounds, and thus set invisible.
lut->UseBelowRangeColorOn();
lut->SetBelowRangeColor(1,1,1,0);
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->luts.push(buildLutOpacity(512)); buildLuts();
this->luts.push(buildLutBrightness(512));
this->ren = vtkSmartPointer<vtkRenderer>::New(); this->ren = vtkSmartPointer<vtkRenderer>::New();
this->ren->SetLayer(2); this->ren->SetLayer(2);
@ -92,16 +44,14 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<Advecti
data->SetPoints(this->points); data->SetPoints(this->points);
this->particlesBeached = vtkSmartPointer<vtkIntArray>::New(); this->particlesBeached = vtkSmartPointer<vtkIntArray>::New();
this->particlesBeached->SetName("particlesBeached");
this->particlesBeached->SetNumberOfComponents(0);
this->particlesAge = vtkSmartPointer<vtkIntArray>::New(); this->particlesAge = vtkSmartPointer<vtkIntArray>::New();
this->particlesAge->SetName("particlesAge");
this->particlesAge->SetNumberOfComponents(0);
data->GetPointData()->AddArray(this->particlesBeached); this->lutIdx = vtkSmartPointer<vtkIntArray>::New();
data->GetPointData()->AddArray(this->particlesAge); this->lutIdx->SetName("lutIdx");
data->GetPointData()->SetActiveScalars("particlesAge"); this->lutIdx->SetNumberOfComponents(0);
data->GetPointData()->AddArray(this->lutIdx);
data->GetPointData()->SetActiveScalars("lutIdx");
advector = std::move(advectionKernel); advector = std::move(advectionKernel);
this->uvGrid = uvGrid; this->uvGrid = uvGrid;
@ -124,11 +74,7 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<Advecti
this->mapper->SetInputConnection(glyph2D->GetOutputPort()); this->mapper->SetInputConnection(glyph2D->GetOutputPort());
this->mapper->SetColorModeToMapScalars(); this->mapper->SetColorModeToMapScalars();
auto lut = this->luts.front(); setColourMode(COMPLEMENTARY);
mapper->SetLookupTable(lut);
this->luts.pop();
this->luts.push(lut);
this->mapper->UseLookupTableScalarRangeOn(); this->mapper->UseLookupTableScalarRangeOn();
this->mapper->Update(); this->mapper->Update();
@ -140,6 +86,17 @@ LGlyphLayer::LGlyphLayer(std::shared_ptr<UVGrid> uvGrid, std::unique_ptr<Advecti
this->callback = createSpawnPointCallback(); this->callback = createSpawnPointCallback();
} }
void LGlyphLayer::buildLuts() {
this->tables.push_back(buildComplementary());
this->tables.push_back(buildComplementary());
this->tables.push_back(buildComplementary());
this->tables.push_back(buildDesaturated());
this->activeColourMode = COMPLEMENTARY;
this->activeSaturationMode = SATURATED;
}
void LGlyphLayer::spoofPoints() { void LGlyphLayer::spoofPoints() {
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) {
@ -162,8 +119,8 @@ void LGlyphLayer::updateData(int t) {
this->points->GetPoint(n, point); this->points->GetPoint(n, point);
if (point[0] <= this->uvGrid->lonMin() or point[0] >= this->uvGrid->lonMax() or point[1] <= this->uvGrid->latMin() or point[1] >= this->uvGrid->latMax()) { 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. // 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+1);
this->particlesAge->SetValue(n, -1); this->particlesAge->SetValue(n, -1);
this->lutIdx->SetTuple1(n, -1);
continue; continue;
} }
@ -186,11 +143,15 @@ void LGlyphLayer::updateData(int t) {
// if the particle's location remains unchanged, increase beachedFor number. Else, decrease it and update point position. // 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]) { if (oldX == point[0] and oldY == point[1]) {
this->particlesBeached->SetValue(n, beachedFor+1); this->particlesBeached->SetValue(n, beachedFor+1);
this->lutIdx->SetTuple1(n, beachedFor == this->beachedAtNumberOfTimes-2 ? calcIndex(age+1, true) : calcIndex(age+1, false));
} else { } else {
this->particlesBeached->SetValue(n, std::max(beachedFor-1, 0)); this->particlesBeached->SetValue(n, std::max(beachedFor-1, 0));
this->points->SetPoint(n, point); this->points->SetPoint(n, point);
this->lutIdx->SetTuple1(n, calcIndex(age+1, false));
modifiedData = true; modifiedData = true;
} }
} else {
this->lutIdx->SetTuple1(n, calcIndex(age+1, true));
} }
} }
if (modifiedData) { if (modifiedData) {
@ -199,6 +160,17 @@ void LGlyphLayer::updateData(int t) {
} }
} }
int LGlyphLayer::calcIndex(int age, bool beached) {
// particle out of bounds.
if (age == -1) return -1;
int mod = beached ? 50 : 0;
int ageCalc = age/60;
if (ageCalc >= 50) ageCalc = 49;
return ageCalc + mod;
}
void LGlyphLayer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) { void LGlyphLayer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) {
interactor->AddObserver(vtkCommand::LeftButtonPressEvent, this->callback); interactor->AddObserver(vtkCommand::LeftButtonPressEvent, this->callback);
interactor->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->callback); interactor->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->callback);
@ -212,13 +184,25 @@ void LGlyphLayer::removeObservers(vtkSmartPointer<vtkRenderWindowInteractor> int
} }
void LGlyphLayer::cycleGlyphStyle() { void LGlyphLayer::setColourMode(ColourMode mode) {
auto lut = this->luts.front();
this->mapper->SetLookupTable(lut); this->activeColourMode = mode;
this->luts.pop(); if (this->activeSaturationMode == DESATURATED) return;
this->luts.push(lut);
this->mapper->SetLookupTable(this->tables[mode]);
} }
void LGlyphLayer::setSaturationMode(SaturationMode mode) {
this->activeSaturationMode = mode;
if (mode == DESATURATED) {
this->mapper->SetLookupTable(this->tables[mode]);
} else {
this->mapper->SetLookupTable(this->tables[this->activeColourMode]);
}
}
void LGlyphLayer::setDt(int dt) { void LGlyphLayer::setDt(int dt) {
this->dt = dt; this->dt = dt;
} }

View File

@ -4,7 +4,6 @@
#include "Layer.h" #include "Layer.h"
#include "../advection/kernel/AdvectionKernel.h" #include "../advection/kernel/AdvectionKernel.h"
#include "../commands/SpawnPointCallback.h" #include "../commands/SpawnPointCallback.h"
#include <queue>
#include <vtkPolyData.h> #include <vtkPolyData.h>
#include <vtkInteractorStyle.h> #include <vtkInteractorStyle.h>
@ -17,14 +16,21 @@ private:
vtkSmartPointer<vtkPolyData> data; vtkSmartPointer<vtkPolyData> data;
vtkSmartPointer<vtkIntArray> particlesBeached; vtkSmartPointer<vtkIntArray> particlesBeached;
vtkSmartPointer<vtkIntArray> particlesAge; vtkSmartPointer<vtkIntArray> particlesAge;
vtkSmartPointer<vtkIntArray> lutIdx;
vtkSmartPointer<vtkPolyDataMapper> mapper; vtkSmartPointer<vtkPolyDataMapper> mapper;
std::unique_ptr<AdvectionKernel> advector; std::unique_ptr<AdvectionKernel> advector;
std::shared_ptr<UVGrid> uvGrid; std::shared_ptr<UVGrid> uvGrid;
int dt = 3600; int dt = 3600;
int beachedAtNumberOfTimes = 20; int beachedAtNumberOfTimes = 20;
std::queue<vtkSmartPointer<vtkLookupTable>> luts; std::vector<vtkSmartPointer<vtkLookupTable>> tables;
ColourMode activeColourMode;
SaturationMode activeSaturationMode;
vtkSmartPointer<SpawnPointCallback> callback; vtkSmartPointer<SpawnPointCallback> callback;
void buildLuts();
int calcIndex(int age, bool beached);
public: public:
/** Constructor. /** Constructor.
*/ */
@ -46,9 +52,8 @@ public:
void addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) override; void addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) override;
void removeObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) override; void removeObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) override;
/** This function cycles which lut is used for the layer, according to the lookuptables in the luts attribute. void setColourMode(ColourMode mode) override;
*/ void setSaturationMode(SaturationMode mode) override;
void cycleGlyphStyle();
/** /**
* Sets a custom DT value, needed for advect calls to the simulation logic. * Sets a custom DT value, needed for advect calls to the simulation logic.

View File

@ -14,10 +14,11 @@ void Layer::setCamera(vtkSmartPointer<vtkCamera> cam) {
} }
// do nothing by default for these functions.
void Layer::updateData(int t) {} void Layer::updateData(int t) {}
void Layer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) {} void Layer::addObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) {}
void Layer::removeObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) {} void Layer::removeObservers(vtkSmartPointer<vtkRenderWindowInteractor> interactor) {}
void Layer::setColorMode(ColourMode mode) {} void Layer::setColourMode(ColourMode mode) {}
void Layer::setSaturationMode(SaturationMode mode) {} void Layer::setSaturationMode(SaturationMode mode) {}
void Layer::setGlyphStyle(GlyphStyle style) {} void Layer::setGlyphStyle(GlyphStyle style) {}
void Layer::setSamplingMode(SamplingMode mode) {} void Layer::setSamplingMode(SamplingMode mode) {}

View File

@ -41,7 +41,8 @@ public:
virtual void setCamera(vtkSmartPointer<vtkCamera> cam); virtual void setCamera(vtkSmartPointer<vtkCamera> cam);
virtual void setColorMode(ColourMode mode); // TODO: Comments
virtual void setColourMode(ColourMode mode);
virtual void setSaturationMode(SaturationMode mode); virtual void setSaturationMode(SaturationMode mode);
virtual void setGlyphStyle(GlyphStyle style); virtual void setGlyphStyle(GlyphStyle style);
virtual void setSamplingMode(SamplingMode mode); virtual void setSamplingMode(SamplingMode mode);

View File

@ -50,9 +50,9 @@ void Technique::unbind(vtkSmartPointer<vtkRenderWindow> win, vtkSmartPointer<vtk
} }
void Technique::setColorMode(ColourMode mode) { void Technique::setColourMode(ColourMode mode) {
for (Layer *l : this->layers) { for (Layer *l : this->layers) {
l->setColorMode(mode); l->setColourMode(mode);
} }
} }

View File

@ -7,6 +7,7 @@
#include "enums.h" #include "enums.h"
class Technique { class Technique {
//TODO: comments
private: private:
std::vector<Layer *> layers; std::vector<Layer *> layers;
vtkSmartPointer<vtkCamera> cam; vtkSmartPointer<vtkCamera> cam;
@ -20,7 +21,7 @@ public:
void bind(vtkSmartPointer<vtkRenderWindow> win, vtkSmartPointer<vtkRenderWindowInteractor> intr); void bind(vtkSmartPointer<vtkRenderWindow> win, vtkSmartPointer<vtkRenderWindowInteractor> intr);
void unbind(vtkSmartPointer<vtkRenderWindow> win, vtkSmartPointer<vtkRenderWindowInteractor> intr); void unbind(vtkSmartPointer<vtkRenderWindow> win, vtkSmartPointer<vtkRenderWindowInteractor> intr);
void setColorMode(ColourMode mode); void setColourMode(ColourMode mode);
void setSaturationMode(SaturationMode mode); void setSaturationMode(SaturationMode mode);
void setGlyphStyle(GlyphStyle style); void setGlyphStyle(GlyphStyle style);
void setSamplingMode(SamplingMode mode); void setSamplingMode(SamplingMode mode);

View File

@ -2,6 +2,7 @@
#define ENUMS_H #define ENUMS_H
enum ActiveTechnique { enum ActiveTechnique {
NOTECH = -1,
COLGLYPH = 0, COLGLYPH = 0,
GLYPHCOL = 1, GLYPHCOL = 1,
}; };
@ -16,7 +17,7 @@ enum ColourMode {
enum SaturationMode { enum SaturationMode {
SATURATED = 0, SATURATED = 0,
DESATURATED = 1, DESATURATED = 3,
}; };

View File

@ -0,0 +1,205 @@
#include <vtkLookupTable.h>
#include <vtkColorTransferFunction.h>
#include "luts.h"
// LGlyph tables
vtkSmartPointer<vtkLookupTable> buildComplementary() {
// uses a modified navia colour map as base.
// https://www.fabiocrameri.ch/colourmaps/
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfColors(100);
lut->SetTableRange(0, 99);
lut->SetScaleToLinear();
lut->Build();
vtkNew<vtkColorTransferFunction> colorTransferFunction;
colorTransferFunction->AddRGBPoint(0, 0.317647, 0.52549, 0.329412);
colorTransferFunction->AddRGBPoint(49, 0.584, 0.584, 0.584);
double c[3];
int idx=0;
for (double notBeached=1; notBeached >= 0.5; notBeached-=0.5) {
for (int i=0; i < 50; i++) {
colorTransferFunction->GetColor(i, c);
lut->SetTableValue(idx++, c[0], c[1], c[2], notBeached);
}
}
lut->UseBelowRangeColorOn();
lut->SetBelowRangeColor(1,1,1,0);
lut->SetNanColor(0.0,0,0,0);
return lut;
}
vtkSmartPointer<vtkLookupTable> buildDesaturated() {
// uses the grayC colour map.
// https://www.fabiocrameri.ch/colourmaps/
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfColors(100);
lut->SetTableRange(0, 99);
lut->SetScaleToLinear();
lut->Build();
vtkNew<vtkColorTransferFunction> colorTransferFunction;
colorTransferFunction->AddRGBPoint(0, 0.313725, 0.313725, 0.313725);
colorTransferFunction->AddRGBPoint(49, 0.909804, 0.909804, 0.909804);
double c[3];
int idx=0;
for (double notBeached=1; notBeached >= 0.5; notBeached-=0.5) {
for (int i=0; i < 50; i++) {
colorTransferFunction->GetColor(i, c);
lut->SetTableValue(idx++, c[0], c[1], c[2], notBeached);
}
}
lut->UseBelowRangeColorOn();
lut->SetBelowRangeColor(1,1,1,0);
lut->SetNanColor(0.0,0,0,0);
return lut;
}
// ECol tables
vtkSmartPointer<vtkLookupTable> buildCyclicComplementary() {
// uses the corkO cyclic colour map
// https://www.fabiocrameri.ch/colourmaps/
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfColors(100);
lut->SetTableRange(0, 99);
lut->SetScaleToLinear();
lut->Build();
int idx=0;
for (double opacity=0.1; opacity <= 1.0; opacity+=0.1) {
lut->SetTableValue(idx++, 0, 1, 1, opacity);
lut->SetTableValue(idx++, 0, 0.9, 0.9, opacity);
lut->SetTableValue(idx++, 0, 0.8, 0.8, opacity);
lut->SetTableValue(idx++, 0, 0.7, 0.7, opacity);
lut->SetTableValue(idx++, 0, 0.6, 0.6, opacity);
lut->SetTableValue(idx++, 0, 0.5, 0.5, opacity);
lut->SetTableValue(idx++, 0, 0.4, 0.4, opacity);
lut->SetTableValue(idx++, 0, 0.3, 0.3, opacity);
lut->SetTableValue(idx++, 0, 0.2, 0.2, opacity);
lut->SetTableValue(idx++, 0, 0.1, 0.1, opacity);
}
lut->SetNanColor(0.0,0,0,0);
return lut;
}
vtkSmartPointer<vtkLookupTable> buildCyclicContrasting() {
// uses the romaO cyclic colour map.
// https://www.fabiocrameri.ch/colourmaps/
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfColors(100.0);
lut->SetTableRange(0.0, 99);
lut->SetScaleToLinear();
lut->Build();
int idx=0.0;
for (double opacity=0.1; opacity <= 1.0; opacity+=0.1) {
lut->SetTableValue(idx++, 0.45098, 0.223529, 0.341176, opacity);
lut->SetTableValue(idx++, 0.529412, 0.25098, 0.215686, opacity);
lut->SetTableValue(idx++, 0.639216, 0.403922, 0.172549, opacity);
lut->SetTableValue(idx++, 0.764706, 0.639216, 0.294118, opacity);
lut->SetTableValue(idx++, 0.839216, 0.847059, 0.576471, opacity);
lut->SetTableValue(idx++, 0.705882, 0.870588, 0.776471, opacity);
lut->SetTableValue(idx++, 0.454902, 0.733333, 0.803922, opacity);
lut->SetTableValue(idx++, 0.309804, 0.533333, 0.72549, opacity);
lut->SetTableValue(idx++, 0.360784, 0.32549, 0.545098, opacity);
lut->SetTableValue(idx++, 0.447059, 0.223529, 0.34902, opacity);
}
lut->SetNanColor(0.0,0,0,0);
return lut;
}
vtkSmartPointer<vtkLookupTable> buildCyclicMonochromatic() {
// uses a slightly modified 9-class blues
// https://colorbrewer2.org/#type=sequential&scheme=Blues&n=9
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfColors(100.0);
lut->SetTableRange(0.0, 99);
lut->SetScaleToLinear();
lut->Build();
int idx=0.0;
for (double opacity=0.1; opacity <= 1.0; opacity+=0.1) {
lut->SetTableValue(idx++, 0.258824, 0.572549, 0.776471, opacity);
lut->SetTableValue(idx++, 0.129412, 0.443137, 0.709804, opacity);
lut->SetTableValue(idx++, 0.031373, 0.317647, 0.611765, opacity);
lut->SetTableValue(idx++, 0.031373, 0.188235, 0.419608, opacity);
lut->SetTableValue(idx++, 0.968627, 0.984314, 1.0, opacity);
lut->SetTableValue(idx++, 0.870588, 0.921569, 0.968627, opacity);
lut->SetTableValue(idx++, 0.776471, 0.858824, 0.937255, opacity);
lut->SetTableValue(idx++, 0.619608, 0.792157, 0.882353, opacity);
lut->SetTableValue(idx++, 0.419608, 0.682353, 0.839216, opacity);
lut->SetTableValue(idx++, 0.180392, 0.494118, 0.698039, opacity);
}
lut->SetNanColor(0.0,0,0,0);
return lut;
}
vtkSmartPointer<vtkLookupTable> buildCyclicDesaturated() {
// uses a modified brocO colour map
// https://www.fabiocrameri.ch/colourmaps/
vtkNew<vtkLookupTable> lut;
lut->SetNumberOfColors(100.0);
lut->SetTableRange(0.0, 99);
lut->SetScaleToLinear();
lut->Build();
int idx=0.0;
for (double opacity=0.1; opacity <= 1.0; opacity+=0.1) {
lut->SetTableValue(idx++, 44, 44, 44, opacity);
lut->SetTableValue(idx++, 56, 56, 56, opacity);
lut->SetTableValue(idx++, 85, 85, 85, opacity);
lut->SetTableValue(idx++, 114, 114, 114, opacity);
lut->SetTableValue(idx++, 149, 149, 149, opacity);
lut->SetTableValue(idx++, 146, 146, 146, opacity);
lut->SetTableValue(idx++, 110, 110, 110, opacity);
lut->SetTableValue(idx++, 78, 78, 78, opacity);
lut->SetTableValue(idx++, 52, 52, 52, opacity);
lut->SetTableValue(idx++, 45, 45, 45, opacity);
}
lut->SetNanColor(0.0,0,0,0);
return lut;
}
// LCol tables
vtkSmartPointer<vtkLookupTable> buildDensityComplementary() {
vtkNew<vtkLookupTable> lut;
return lut;
}
vtkSmartPointer<vtkLookupTable> buildDensityContrasting() {
vtkNew<vtkLookupTable> lut;
return lut;
}
vtkSmartPointer<vtkLookupTable> buildDensityMonochromatic() {
vtkNew<vtkLookupTable> lut;
return lut;
}
vtkSmartPointer<vtkLookupTable> buildDensityDesaturated() {
vtkNew<vtkLookupTable> lut;
return lut;
}

View File

@ -0,0 +1,20 @@
#ifndef LUTS_H
#define LUTS_H
#include <vtkLookupTable.h>
// LGlyph tables
vtkSmartPointer<vtkLookupTable> buildComplementary();
vtkSmartPointer<vtkLookupTable> buildDesaturated();
// ECol tables
vtkSmartPointer<vtkLookupTable> buildCyclicComplementary();
vtkSmartPointer<vtkLookupTable> buildCyclicContrasting();
vtkSmartPointer<vtkLookupTable> buildCyclicMonochromatic();
vtkSmartPointer<vtkLookupTable> buildCyclicDesaturated();
// LCol tables
vtkSmartPointer<vtkLookupTable> buildDensityComplementary();
vtkSmartPointer<vtkLookupTable> buildDensityContrasting();
vtkSmartPointer<vtkLookupTable> buildDensityMonochromatic();
vtkSmartPointer<vtkLookupTable> buildDensityDesaturated();
#endif